From 1d1989fdabe91a4ddf8fcba4d9a357277894a282 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Jun 2024 22:21:47 -0400 Subject: [PATCH 0001/1301] Fix rustfmt --- src/spanned.rs | 1 + src/types.rs | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/spanned.rs b/src/spanned.rs index 4aaf7fdb27fb0..28911f8af1dfb 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -181,6 +181,7 @@ impl Spanned for ast::GenericBound { match *self { ast::GenericBound::Trait(ref ptr, _) => ptr.span, ast::GenericBound::Outlives(ref l) => l.ident.span, + ast::GenericBound::Use(_, span) => span, } } } diff --git a/src/types.rs b/src/types.rs index 7d14d9e727ab5..c2c192738c910 100644 --- a/src/types.rs +++ b/src/types.rs @@ -563,6 +563,8 @@ impl Rewrite for ast::GenericBound { .map(|s| if has_paren { format!("({})", s) } else { s }) } ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape), + // FIXME(precise_capturing): Should implement formatting before stabilization. + ast::GenericBound::Use(..) => None, } } } @@ -843,11 +845,7 @@ impl Rewrite for ast::Ty { rewrite_macro(mac, None, context, shape, MacroPosition::Expression) } ast::TyKind::ImplicitSelf => Some(String::from("")), - ast::TyKind::ImplTrait(_, ref it, ref captures) => { - // FIXME(precise_capturing): Implement formatting. - if captures.is_some() { - return None; - } + ast::TyKind::ImplTrait(_, ref it) => { // Empty trait is not a parser error. if it.is_empty() { return Some("impl".to_owned()); @@ -932,6 +930,8 @@ fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool { let is_trait = |b: &ast::GenericBound| match b { ast::GenericBound::Outlives(..) => false, ast::GenericBound::Trait(..) => true, + // FIXME(precise_capturing): This ordering fn should be reworked. + ast::GenericBound::Use(..) => false, }; let is_lifetime = |b: &ast::GenericBound| !is_trait(b); let last_trait_index = generic_bounds.iter().rposition(is_trait); @@ -966,6 +966,8 @@ fn join_bounds_inner( let is_bound_extendable = |s: &str, b: &ast::GenericBound| match b { ast::GenericBound::Outlives(..) => true, ast::GenericBound::Trait(..) => last_line_extendable(s), + // FIXME(precise_capturing): This ordering fn should be reworked. + ast::GenericBound::Use(..) => true, }; // Whether a GenericBound item is a PathSegment segment that includes internal array @@ -1110,8 +1112,7 @@ fn join_bounds_inner( pub(crate) fn opaque_ty(ty: &Option>) -> Option<&ast::GenericBounds> { ty.as_ref().and_then(|t| match &t.kind { - // FIXME(precise_capturing): Implement support here - ast::TyKind::ImplTrait(_, bounds, _) => Some(bounds), + ast::TyKind::ImplTrait(_, bounds) => Some(bounds), _ => None, }) } From 0c5a75a61bb2559a3b25cd3656ea36c2a1f4bc0b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 18 Jun 2024 10:35:56 +0000 Subject: [PATCH 0002/1301] Use a dedicated type instead of a reference for the diagnostic context This paves the way for tracking more state (e.g. error tainting) in the diagnostic context handle --- src/parse/macros/cfg_if.rs | 2 +- src/parse/macros/lazy_static.rs | 6 +++--- src/parse/macros/mod.rs | 6 +++--- src/parse/session.rs | 8 +++++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs index 5fc988e431935..b91d203d53118 100644 --- a/src/parse/macros/cfg_if.rs +++ b/src/parse/macros/cfg_if.rs @@ -67,7 +67,7 @@ fn parse_cfg_if_inner<'a>( Ok(None) => continue, Err(err) => { err.cancel(); - parser.psess.dcx.reset_err_count(); + parser.psess.dcx().reset_err_count(); return Err( "Expected item inside cfg_if block, but failed to parse it as an item", ); diff --git a/src/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs index badd956995007..7026935294ace 100644 --- a/src/parse/macros/lazy_static.rs +++ b/src/parse/macros/lazy_static.rs @@ -16,8 +16,8 @@ pub(crate) fn parse_lazy_static( ($method:ident $(,)* $($arg:expr),* $(,)*) => { match parser.$method($($arg,)*) { Ok(val) => { - if parser.psess.dcx.has_errors().is_some() { - parser.psess.dcx.reset_err_count(); + if parser.psess.dcx().has_errors().is_some() { + parser.psess.dcx().reset_err_count(); return None; } else { val @@ -25,7 +25,7 @@ pub(crate) fn parse_lazy_static( } Err(err) => { err.cancel(); - parser.psess.dcx.reset_err_count(); + parser.psess.dcx().reset_err_count(); return None; } } diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index cbcc0b2d63648..89169e10715b2 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -29,8 +29,8 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { if Parser::nonterminal_may_begin_with($nt_kind, &cloned_parser.token) { match $try_parse(&mut cloned_parser) { Ok(x) => { - if parser.psess.dcx.has_errors().is_some() { - parser.psess.dcx.reset_err_count(); + if parser.psess.dcx().has_errors().is_some() { + parser.psess.dcx().reset_err_count(); } else { // Parsing succeeded. *parser = cloned_parser; @@ -39,7 +39,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { } Err(e) => { e.cancel(); - parser.psess.dcx.reset_err_count(); + parser.psess.dcx().reset_err_count(); } } } diff --git a/src/parse/session.rs b/src/parse/session.rs index 1a39d212386bd..f4fbabaf6c91b 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -210,7 +210,9 @@ impl ParseSess { rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - self.raw_psess.dcx.make_silent(fallback_bundle, None, false); + self.raw_psess + .dcx() + .make_silent(fallback_bundle, None, false); } pub(crate) fn span_to_filename(&self, span: Span) -> FileName { @@ -286,11 +288,11 @@ impl ParseSess { } pub(super) fn has_errors(&self) -> bool { - self.raw_psess.dcx.has_errors().is_some() + self.raw_psess.dcx().has_errors().is_some() } pub(super) fn reset_errors(&self) { - self.raw_psess.dcx.reset_err_count(); + self.raw_psess.dcx().reset_err_count(); } } From ebce05434703268c292520cc385568019c5ec353 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sat, 22 Jun 2024 15:33:45 -0500 Subject: [PATCH 0003/1301] Merge commit 'e4944185ae09c99f59b460e358909f329010ea9c' into sync-from-rustfmt-24-06 --- .github/workflows/integration.yml | 1 - CHANGELOG.md | 107 +++++++- Cargo.lock | 61 ++--- Cargo.toml | 11 +- Configurations.md | 39 ++- Contributing.md | 2 +- Processes.md | 2 +- build.rs | 4 +- check_diff/.gitignore | 1 + check_diff/Cargo.lock | 237 ++++++++++++++++++ check_diff/Cargo.toml | 9 + check_diff/src/main.rs | 25 ++ ci/check_diff.sh | 46 +++- ci/integration.sh | 2 +- config_proc_macro/src/attrs.rs | 6 +- docs/index.html | 32 ++- intellij.md | 2 +- rust-toolchain | 2 +- src/attr.rs | 16 +- src/bin/main.rs | 17 +- src/cargo-fmt/main.rs | 9 +- src/cargo-fmt/test/mod.rs | 22 +- src/comment.rs | 49 ++-- src/config/config_type.rs | 22 +- src/config/mod.rs | 21 +- src/config/options.rs | 28 ++- src/config/style_edition.rs | 70 ++++++ src/emitter/checkstyle.rs | 14 +- src/emitter/diff.rs | 2 +- src/emitter/json.rs | 12 +- src/expr.rs | 31 ++- src/format-diff/main.rs | 18 +- src/formatting.rs | 2 +- src/formatting/generated.rs | 7 +- src/ignore_path.rs | 18 ++ src/imports.rs | 4 +- src/items.rs | 50 ++-- src/lib.rs | 16 +- src/macros.rs | 19 +- src/matches.rs | 18 +- src/overflow.rs | 28 ++- src/parse/macros/lazy_static.rs | 4 - src/parse/session.rs | 10 +- src/source_file.rs | 2 +- src/test/configuration_snippet.rs | 26 +- src/test/mod.rs | 24 +- src/types.rs | 31 ++- src/visitor.rs | 2 +- tests/config/issue-5801-v1.toml | 3 + tests/config/issue-5801-v2.toml | 3 + .../skip-files-issue-5065/main.rs | 2 +- tests/rustfmt/main.rs | 25 +- .../arrow_in_single_comment.rs | 10 + .../arrow_in_comments/multiple_arrows.rs | 14 ++ ...with_generated_marker_line_search_limit.rs | 10 + .../false_with_marker_out_scope_size.rs | 10 + .../false_with_zero_search_limit.rs | 9 + .../true_with_marker_in_scope_size.rs | 10 + .../true_with_marker_not_in_header.rs | 9 + tests/source/issue-3805.rs | 65 +++++ ...ute_unexpectedly_wraps_before_max_width.rs | 8 + ...ent_unexpectedly_wraps_before_max_width.rs | 17 ++ tests/source/issue-5987/two.rs | 13 + tests/source/issue-6059/repro.rs | 3 + tests/source/issue-6147/case_rustfmt_v1.rs | 20 ++ tests/source/issue-6147/case_rustfmt_v2.rs | 20 ++ tests/source/issue_5912.rs | 15 ++ tests/source/let_else.rs | 6 +- tests/source/no_arg_with_commnet.rs | 2 +- .../arrow_in_single_comment.rs | 10 + .../arrow_in_comments/multiple_arrows.rs | 19 ++ ...with_generated_marker_line_search_limit.rs | 10 + .../false_with_marker_out_scope_size.rs | 8 + .../false_with_zero_search_limit.rs | 8 + .../true_with_marker_in_scope_size.rs | 8 + .../true_with_marker_not_in_header.rs | 7 + tests/target/impl.rs | 5 + tests/target/issue-3805.rs | 94 +++++++ ...ttribute_does_not_wrap_within_max_width.rs | 8 + ...ute_unexpectedly_wraps_before_max_width.rs | 16 ++ .../comment_does_not_wrap_within_max_width.rs | 16 ++ ...ent_unexpectedly_wraps_before_max_width.rs | 18 ++ tests/target/issue-5885.rs | 3 + tests/target/issue-5987/one.rs | 13 + tests/target/issue-5987/two.rs | 12 + tests/target/issue-6059/additional.rs | 6 + tests/target/issue-6059/repro.rs | 3 + tests/target/issue-6105.rs | 1 + tests/target/issue-6147/case_rustfmt_v1.rs | 20 ++ tests/target/issue-6147/case_rustfmt_v2.rs | 20 ++ tests/target/issue_5912.rs | 15 ++ tests/target/issue_6069.rs | 3 + tests/target/issue_6158.rs | 7 + tests/target/issue_6159.rs | 3 + tests/target/let_else.rs | 6 +- tests/target/no_arg_with_commnet.rs | 2 +- tests/target/unsafe_attributes.rs | 34 +++ tests/target/unsafe_extern_blocks.rs | 18 ++ 98 files changed, 1563 insertions(+), 285 deletions(-) create mode 100644 check_diff/.gitignore create mode 100644 check_diff/Cargo.lock create mode 100644 check_diff/Cargo.toml create mode 100644 check_diff/src/main.rs create mode 100644 src/config/style_edition.rs create mode 100644 tests/config/issue-5801-v1.toml create mode 100644 tests/config/issue-5801-v2.toml create mode 100644 tests/source/arrow_in_comments/arrow_in_single_comment.rs create mode 100644 tests/source/arrow_in_comments/multiple_arrows.rs create mode 100644 tests/source/configs/format_generated_files/false_with_generated_marker_line_search_limit.rs create mode 100644 tests/source/configs/format_generated_files/false_with_marker_out_scope_size.rs create mode 100644 tests/source/configs/format_generated_files/false_with_zero_search_limit.rs create mode 100644 tests/source/configs/format_generated_files/true_with_marker_in_scope_size.rs create mode 100644 tests/source/configs/format_generated_files/true_with_marker_not_in_header.rs create mode 100644 tests/source/issue-3805.rs create mode 100644 tests/source/issue-5801/attribute_unexpectedly_wraps_before_max_width.rs create mode 100644 tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs create mode 100644 tests/source/issue-5987/two.rs create mode 100644 tests/source/issue-6059/repro.rs create mode 100644 tests/source/issue-6147/case_rustfmt_v1.rs create mode 100644 tests/source/issue-6147/case_rustfmt_v2.rs create mode 100644 tests/source/issue_5912.rs create mode 100644 tests/target/arrow_in_comments/arrow_in_single_comment.rs create mode 100644 tests/target/arrow_in_comments/multiple_arrows.rs create mode 100644 tests/target/configs/format_generated_files/false_with_generated_marker_line_search_limit.rs create mode 100644 tests/target/configs/format_generated_files/false_with_marker_out_scope_size.rs create mode 100644 tests/target/configs/format_generated_files/false_with_zero_search_limit.rs create mode 100644 tests/target/configs/format_generated_files/true_with_marker_in_scope_size.rs create mode 100644 tests/target/configs/format_generated_files/true_with_marker_not_in_header.rs create mode 100644 tests/target/issue-3805.rs create mode 100644 tests/target/issue-5801/attribute_does_not_wrap_within_max_width.rs create mode 100644 tests/target/issue-5801/attribute_unexpectedly_wraps_before_max_width.rs create mode 100644 tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs create mode 100644 tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs create mode 100644 tests/target/issue-5885.rs create mode 100644 tests/target/issue-5987/one.rs create mode 100644 tests/target/issue-5987/two.rs create mode 100644 tests/target/issue-6059/additional.rs create mode 100644 tests/target/issue-6059/repro.rs create mode 100644 tests/target/issue-6105.rs create mode 100644 tests/target/issue-6147/case_rustfmt_v1.rs create mode 100644 tests/target/issue-6147/case_rustfmt_v2.rs create mode 100644 tests/target/issue_5912.rs create mode 100644 tests/target/issue_6069.rs create mode 100644 tests/target/issue_6158.rs create mode 100644 tests/target/issue_6159.rs create mode 100644 tests/target/unsafe_attributes.rs create mode 100644 tests/target/unsafe_extern_blocks.rs diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 314ce0e84c61b..f0dd0cf73bbd4 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -19,7 +19,6 @@ jobs: matrix: integration: [ bitflags, - error-chain, log, mdbook, packed_simd, diff --git a/CHANGELOG.md b/CHANGELOG.md index ec4c682d2c4d0..89e90fb17ddbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,99 @@ ## [Unreleased] +### Fixed + +- Fix an idempotency issue when rewriting where clauses in which rustfmt would continuously add a trailing comma `,` to the end of trailing line comments [#5941](https://github.com/rust-lang/rustfmt/issues/5941). +- Prevent enum variant attributes from wrapping one character early when using `version=Two` [#5801](https://github.com/rust-lang/rustfmt/issues/5801) +- Properly wrap macro matchers at the `max_width` when using `version=Two` and `format_macro_matchers=true` [#3805](https://github.com/rust-lang/rustfmt/issues/3805) +- Prevent panic when formatting trait declaration with non [Unicode Normalization Form] C (NFC) identifiers [#6069](https://github.com/rust-lang/rustfmt/issues/6069) + ```rust + // The ó below is two codepoints, ASCII o followed by U+0301 COMBINING ACUTE ACCENT. + // It NFC-normalizes to ó, U+00F3 LATIN SMALL LETTER O WITH ACUTE. + trait Foó: Bar {} + ``` + [unicode normalization form]: https://unicode.org/reports/tr15/ +- Ensure a space is added to a range expression, when the right hand side of the range expression is a binary expression that ends with a trailing period [#6059](https://github.com/rust-lang/rustfmt/issues/6059) + ```rust + let range = 3. / 2. ..4.; + ``` +- When using `version=Two`, comments in match arms that contain `=>` no longer prevent formatting [#5998](https://github.com/rust-lang/rustfmt/issues/5998) + ```rust + match a { + _ => + // comment with => + { + println!("A") + } + } + ``` +- Prevent panics when formatting input that contains the expanded form of `offset_of!` [#5885](https://github.com/rust-lang/rustfmt/issues/5885) [#6105](https://github.com/rust-lang/rustfmt/issues/6105) + ```rust + const _: () = builtin # offset_of(x, x); + ``` +- When using `version=Two` inner attributes in `match` expressions are correctly indented [#6147](https://github.com/rust-lang/rustfmt/issues/6147) + ```rust + pub fn main() { + match x { + #![attr1] + #![attr2] + _ => (), + } + } + ``` +- Output correct syntax for type ascription builtin [#6159](https://github.com/rust-lang/rustfmt/issues/6159) + ```rust + fn main() { + builtin # type_ascribe(10, usize) + } + ``` +- rustfmt no longer removes inner attributes from inline const blocks [#6158](https://github.com/rust-lang/rustfmt/issues/6158) + ```rust + fn main() { + const { + #![allow(clippy::assertions_on_constants)] + + assert!(1 < 2); + } + } + ``` +- rustfmt no longer removes `safe` and `unsafe` keywords from static items in extern blocks. + This helps support [`#![feature(unsafe_extern_blocks)]`](https://github.com/rust-lang/rust/issues/123743) [#6204](https://github.com/rust-lang/rustfmt/pull/6204) + ```rust + #![feature(unsafe_extern_blocks)] + + unsafe extern "C" { + safe static TEST1: i32; + unsafe static TEST2: i32; + } + ``` + + +### Changed + +- `hide_parse_errors` has been soft deprecated and it's been renamed to `show_parse_errors` [#5961](https://github.com/rust-lang/rustfmt/pull/5961). +- The diff output produced by `rustfmt --check` is more compatable with editors that support navigating directly to line numbers [#5971](https://github.com/rust-lang/rustfmt/pull/5971) +- When using `version=Two`, the `trace!` macro from the [log crate] is now formatted similarly to `debug!`, `info!`, `warn!`, and `error!` [#5987](https://github.com/rust-lang/rustfmt/issues/5987). + + [log crate]: https://crates.io/crates/log + + +### Added + +- `generated_marker_line_search_limit` is a new unstable configuration option that allows users to configure how many lines to search for an `@generated` marker when `format_generated_files=false` [#5658](https://github.com/rust-lang/rustfmt/issues/5658) + + +### Misc + +- Updating `dirs 4.0.0 -> 5.0.1` and `cargo_metadata 0.15.4 -> 0.18.0` [#6033] (https://github.com/rust-lang/rustfmt/issues/6033) + - For reference, here's the [dirs v5 changelog](https://github.com/dirs-dev/dirs-rs/blob/main/README.md#5) +- Updated [itertools v0.11 -> v0.12](https://github.com/rust-itertools/itertools/blob/v0.12.1/CHANGELOG.md#0120) [#6093](https://github.com/rust-lang/rustfmt/pull/6093) +- Addressed clap deprecations output when running `cargo check --features clap/deprecated` [#6101](https://github.com/rust-lang/rustfmt/pull/6101) +- Bumped bytecount `0.6.4` -> `0.6.8` to fix compilation issues with the `generic-simd` feature. See [bytecount#92] and [bytecount#93] + + [bytecount#92]: https://github.com/llogiq/bytecount/pull/92 + [bytecount#93]: https://github.com/llogiq/bytecount/pull/93 +- Replace the `lazy_static` dependency with `std::sync::OnceLock` [#6154](https://github.com/rust-lang/rustfmt/pull/6154) ## [1.7.0] 2023-10-22 @@ -27,7 +120,7 @@ } ``` - Prevent ICE when formatting `vec!{}` [#5735](https://github.com/rust-lang/rustfmt/issues/5735) -- Prevent internal trailing whitespace error when formatting an empty `macro_rules!` defintion e.g. `macro_rules! foo {}` [#5882](https://github.com/rust-lang/rustfmt/issues/5882) +- Prevent internal trailing whitespace error when formatting an empty `macro_rules!` definition e.g. `macro_rules! foo {}` [#5882](https://github.com/rust-lang/rustfmt/issues/5882) - Formatting doc comment lines that start with `.` or `)` won't be treated as ordered markdown lists because `.` or `)` must be preceded by a number to start an ordered markdown list [#5835](https://github.com/rust-lang/rustfmt/pull/5835) - Add parenthesis around closures when they're used as method receives, don't have a block body, and end with `.` [#4808](https://github.com/rust-lang/rustfmt/issues/4808) ```rust @@ -184,7 +277,7 @@ - Simplify the rustfmt help text by eliding the full path to the rustfmt binary path from the usage string when running `rustfmt --help` [#5214](https://github.com/rust-lang/rustfmt/issues/5214) -- Bumped the version for serveral dependencies. Most notably `dirs` `v2.0.1` -> `v4.0.0`. This changed the global user config directory on macOS from `$HOME/Library/Preferences` to `$HOME/Library/Application Support` [#5237](https://github.com/rust-lang/rustfmt/pull/5237) +- Bumped the version for several dependencies. Most notably `dirs` `v2.0.1` -> `v4.0.0`. This changed the global user config directory on macOS from `$HOME/Library/Preferences` to `$HOME/Library/Application Support` [#5237](https://github.com/rust-lang/rustfmt/pull/5237) ### Fixed @@ -942,7 +1035,7 @@ from formatting an attribute #3665 ### Fixed -- Do not remove path disambiugator inside macro #3142 +- Do not remove path disambiguator inside macro #3142 - Improve handling of Windows newlines #3141 - Fix alignment of a struct's fields (`struct_field_align_threshold` option) with the Visual `indent_style` #3165 - Fix a bug in formatting markdown lists within comments #3172 @@ -1031,7 +1124,7 @@ from formatting an attribute #3665 ### Changed -- Replace '--conifig-help' with '--config=help' cb10e06 +- Replace '--config-help' with '--config=help' cb10e06 - Improve formatting of slice patterns #2912 ### Fixed @@ -1075,7 +1168,7 @@ from formatting an attribute #3665 - Add max_width option for all heuristics c2ae39e - Add config option `format_macro_matchers` to format the metavariable matching patterns in macros 79c5ee8 - Add config option `format_macro_bodies` to format the bodies of macros 79c5ee8 -- Format exitential type fc307ff +- Format existential type fc307ff - Support raw identifiers in struct expressions f121b1a - Format Async block and async function 0b25f60 @@ -1131,7 +1224,7 @@ from formatting an attribute #3665 ### Changed -- Update rustc-ap-syntax to 128.0.0 and ustc-ap-rustc_target to 128.0.0 195395f +- Update rustc-ap-syntax to 128.0.0 and rustc-ap-rustc_target to 128.0.0 195395f - Put operands on its own line when each fits in a single line f8439ce - Improve CLI options 55ac062 1869888 798bffb 4d9de48 eca7796 8396da1 5d9f5aa @@ -1195,7 +1288,7 @@ from formatting an attribute #3665 - Do not collapse block around expr with condition on match arm 5b9b7d5 - Use vertical layout for complex attributes c77708f - Format array using heuristics for function calls 98c6f7b -- Implement stable ordering for impl items with the the following item priority: type, const, macro, then method fa80ddf +- Implement stable ordering for impl items with the following item priority: type, const, macro, then method fa80ddf - Reorder imports by default 164cf7d - Group `extern crate` by default 3a138a2 - Make `error_on_line_overflow` false by default f146711 diff --git a/Cargo.lock b/Cargo.lock index 8fcefa97489d8..2a1fffa50fed7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,12 +98,9 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.4" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7" -dependencies = [ - "packed_simd", -] +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "camino" @@ -125,9 +122,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.4" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +checksum = "fb9ac64500cc83ce4b9f8dafa78186aa008c8dea77a09b94cd307fd0cd5022a8" dependencies = [ "camino", "cargo-platform", @@ -217,9 +214,9 @@ checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] name = "dirs" -version = "4.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ "dirs-sys", ] @@ -236,13 +233,14 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys", ] [[package]] @@ -343,9 +341,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -368,12 +366,6 @@ version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - [[package]] name = "log" version = "0.4.16" @@ -408,16 +400,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", - "libm", -] - [[package]] name = "once_cell" version = "1.17.1" @@ -425,20 +407,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] -name = "overload" -version = "0.1.1" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "packed_simd" -version = "0.3.9" +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d" -dependencies = [ - "cfg-if", - "num-traits", -] +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pin-project-lite" @@ -521,7 +499,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.7.0" +version = "1.7.1" dependencies = [ "annotate-snippets", "anyhow", @@ -534,7 +512,6 @@ dependencies = [ "getopts", "ignore", "itertools", - "lazy_static", "regex", "rustfmt-config_proc_macro", "serde", diff --git a/Cargo.toml b/Cargo.toml index 032b9b548104b..a16620ed99bc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.7.0" +version = "1.7.1" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" @@ -35,16 +35,15 @@ generic-simd = ["bytecount/generic-simd"] [dependencies] annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1.0" -bytecount = "0.6.4" -cargo_metadata = "0.15.4" +bytecount = "0.6.8" +cargo_metadata = "0.18" clap = { version = "4.4.2", features = ["derive"] } clap-cargo = "0.12.0" diff = "0.1" -dirs = "4.0" +dirs = "5.0" getopts = "0.2" ignore = "0.4" -itertools = "0.11" -lazy_static = "1.4" +itertools = "0.12" regex = "1.7" serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0" diff --git a/Configurations.md b/Configurations.md index ac5747800b258..f52c25731549c 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1,6 +1,6 @@ # Configuring Rustfmt -Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well. +Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/5.0.1/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well. A possible content of `rustfmt.toml` or `.rustfmt.toml` might look like this: @@ -1050,8 +1050,8 @@ Max width for code snippets included in doc comments. Only used if [`format_code ## `format_generated_files` -Format generated files. A file is considered generated -if any of the first five lines contain a `@generated` comment marker. +Format generated files. A file is considered generated if any of the first several lines contain a `@generated` comment marker. The number of lines to check is configured by `generated_marker_line_search_limit`. + By default, generated files are reformatted, i. e. `@generated` marker is ignored. This option is currently ignored for stdin (`@generated` in stdin is ignored.) @@ -1059,6 +1059,16 @@ This option is currently ignored for stdin (`@generated` in stdin is ignored.) - **Possible values**: `true`, `false` - **Stable**: No (tracking issue: [#5080](https://github.com/rust-lang/rustfmt/issues/5080)) +## `generated_marker_line_search_limit` + +Number of lines to check for a `@generated` pragma header, starting from the top of the file. Setting this value to `0` will treat all files as non-generated. When`format_generated_files` is `true`, this option has no effect. + +- **Default value**: `5` +- **Possible values**: any positive integer +- **Stable**: No (tracking issue: [#5080](https://github.com/rust-lang/rustfmt/issues/5080)) + +See also [format_generated_files](#format_generated_files) link here. + ## `format_macro_matchers` Format the metavariable matching patterns in macros. @@ -1098,7 +1108,7 @@ See also [`format_macro_bodies`](#format_macro_bodies). ## `format_macro_bodies` -Format the bodies of macros. +Format the bodies of declarative macro definitions. - **Default value**: `true` - **Possible values**: `true`, `false` @@ -1248,12 +1258,20 @@ Control the case of the letters in hexadecimal literal values ## `hide_parse_errors` -Do not show parse errors if the parser failed to parse files. +This option is deprecated and has been renamed to `show_parse_errors` to avoid confusion around the double negative default of `hide_parse_errors=false`. - **Default value**: `false` - **Possible values**: `true`, `false` - **Stable**: No (tracking issue: [#3390](https://github.com/rust-lang/rustfmt/issues/3390)) +## `show_parse_errors` + +Show parse errors if the parser failed to parse files. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#5977](https://github.com/rust-lang/rustfmt/issues/5977)) + ## `ignore` Skip formatting files and directories that match the specified pattern. @@ -1288,6 +1306,15 @@ If you want to ignore every file under the directory where you put your rustfmt. ignore = ["/"] ``` +If you want to allow specific paths that would otherwise be ignored, prefix those paths with a `!`: + +```toml +ignore = ["bar_dir/*", "!bar_dir/*/what.rs"] +``` + +In this case, all files under `bar_dir` will be ignored, except files like `bar_dir/sub/what.rs` +or `bar_dir/another/what.rs`. + ## `imports_indent` Indent style of imports @@ -1655,7 +1682,7 @@ use core::slice; Controls whether arm bodies are wrapped in cases where the first line of the body cannot fit on the same line as the `=>` operator. -The Style Guide requires that bodies are block wrapped by default if a line break is required after the `=>`, but this option can be used to disable that behavior to prevent wrapping arm bodies in that event, so long as the body does not contain multiple statements nor line comments. +The Style Guide requires that bodies are block wrapped by default if a line break is required after the `=>`, but this option can be used to disable that behavior to prevent wrapping arm bodies in that event, so long as the body contains neither multiple statements nor line comments. - **Default value**: `true` - **Possible values**: `true`, `false` diff --git a/Contributing.md b/Contributing.md index 69a2c76369f97..2f2ccfb175a92 100644 --- a/Contributing.md +++ b/Contributing.md @@ -59,7 +59,7 @@ example, the `issue-1111.rs` test file is configured by the file ## Debugging Some `rewrite_*` methods use the `debug!` macro for printing useful information. -These messages can be printed by using the environment variable `RUSTFMT_LOG=rustfmt=DEBUG`. +These messages can be printed by using the environment variable `RUSTFMT_LOG=debug`. These traces can be helpful in understanding which part of the code was used and get a better grasp on the execution flow. diff --git a/Processes.md b/Processes.md index 61abc87eec9ff..64ef20a9bd76d 100644 --- a/Processes.md +++ b/Processes.md @@ -16,7 +16,7 @@ In this Section, we describe how to stabilise an option of the rustfmt's configu Open a pull request that closes the tracking issue. The tracking issue is listed beside the option in `Configurations.md`. - Update the `Config` enum marking the option as stable. -- Update the the `Configuration.md` file marking the option as stable. +- Update the `Configuration.md` file marking the option as stable. - Update `CHANGELOG.md` marking the option as stable. ## After the stabilisation diff --git a/build.rs b/build.rs index e7b1e1b854c07..9a8bb77a8ed19 100644 --- a/build.rs +++ b/build.rs @@ -40,7 +40,7 @@ fn channel() -> String { fn commit_hash() -> Option { Command::new("git") - .args(&["rev-parse", "--short", "HEAD"]) + .args(["rev-parse", "--short", "HEAD"]) .output() .ok() .and_then(|r| String::from_utf8(r.stdout).ok()) @@ -48,7 +48,7 @@ fn commit_hash() -> Option { fn commit_date() -> Option { Command::new("git") - .args(&["log", "-1", "--date=short", "--pretty=format:%cd"]) + .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .output() .ok() .and_then(|r| String::from_utf8(r.stdout).ok()) diff --git a/check_diff/.gitignore b/check_diff/.gitignore new file mode 100644 index 0000000000000..ea8c4bf7f35f6 --- /dev/null +++ b/check_diff/.gitignore @@ -0,0 +1 @@ +/target diff --git a/check_diff/Cargo.lock b/check_diff/Cargo.lock new file mode 100644 index 0000000000000..6716ccdf9a08c --- /dev/null +++ b/check_diff/Cargo.lock @@ -0,0 +1,237 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "check_diff" +version = "0.1.0" +dependencies = [ + "clap", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "proc-macro2" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/check_diff/Cargo.toml b/check_diff/Cargo.toml new file mode 100644 index 0000000000000..a1ed154481a96 --- /dev/null +++ b/check_diff/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "check_diff" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4.4.2", features = ["derive"] } diff --git a/check_diff/src/main.rs b/check_diff/src/main.rs new file mode 100644 index 0000000000000..6d07c1b0df65e --- /dev/null +++ b/check_diff/src/main.rs @@ -0,0 +1,25 @@ +use clap::Parser; +/// Inputs for the check_diff script +#[derive(Parser)] +struct CliInputs { + /// Git url of a rustfmt fork to compare against the latest master rustfmt + remote_repo_url: String, + /// Name of the feature branch on the forked repo + feature_branch: String, + /// Optional commit hash from the feature branch + #[arg(short, long)] + commit_hash: Option, + /// Optional comma separated list of rustfmt config options to + /// pass when running the feature branch + #[arg(value_delimiter = ',', short, long, num_args = 1..)] + rustfmt_config: Option>, +} + +fn main() { + let args = CliInputs::parse(); + println!( + "remote_repo_url: {:?}, feature_branch: {:?}, + optional_commit_hash: {:?}, optional_rustfmt_config: {:?}", + args.remote_repo_url, args.feature_branch, args.commit_hash, args.rustfmt_config + ); +} diff --git a/ci/check_diff.sh b/ci/check_diff.sh index 50c58b1f49250..2a29cb138ef79 100755 --- a/ci/check_diff.sh +++ b/ci/check_diff.sh @@ -2,9 +2,6 @@ set -e -# https://github.com/rust-lang/rustfmt/issues/5675 -export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH - function print_usage() { echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]" } @@ -31,7 +28,7 @@ function clone_repo() { GIT_TERMINAL_PROMPT=0 git clone --quiet $1 --depth 1 $2 && cd $2 } -# Initialize Git submoduels for the repo. +# Initialize Git submodules for the repo. # # Parameters # $1: list of directories to initialize @@ -46,7 +43,7 @@ function init_submodules() { # $2: Output file path for the diff # $3: Any additional configuration options to pass to rustfmt # -# Globlas: +# Globals: # $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4 function create_diff() { local config; @@ -67,7 +64,7 @@ function create_diff() { # Parameters # $1: Name of the repository (used for logging) # -# Globlas: +# Globals: # $RUSFMT_BIN: Path to the rustfmt master binary. Created when running `compile_rustfmt` # $FEATURE_BIN: Path to the rustfmt feature binary. Created when running `compile_rustfmt` # $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4 @@ -90,7 +87,7 @@ function check_diff() { ) if [ -z "$diff" ]; then - echo "no diff detected between rustfmt and the feture branch" + echo "no diff detected between rustfmt and the feature branch" return 0 else echo "$diff" @@ -104,7 +101,7 @@ function check_diff() { # Parameters: # $1: Directory where rustfmt will be cloned # -# Globlas: +# Globals: # $REMOTE_REPO: Clone URL to the rustfmt fork that we want to test # $FEATURE_BRANCH: Name of the feature branch # $OPTIONAL_COMMIT_HASH: Optional commit hash that will be checked out if provided @@ -114,15 +111,42 @@ function compile_rustfmt() { git remote add feature $REMOTE_REPO git fetch feature $FEATURE_BRANCH - cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt + CARGO_VERSION=$(cargo --version) + echo -e "\ncompiling with $CARGO_VERSION\n" + + # Because we're building standalone binaries we need to set `LD_LIBRARY_PATH` so each + # binary can find it's runtime dependencies. See https://github.com/rust-lang/rustfmt/issues/5675 + # This will prepend the `LD_LIBRARY_PATH` for the master rustfmt binary + export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH + + echo "Building rustfmt from src" + cargo build -q --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt + if [ -z "$OPTIONAL_COMMIT_HASH" ] || [ "$FEATURE_BRANCH" = "$OPTIONAL_COMMIT_HASH" ]; then git switch $FEATURE_BRANCH else git switch $OPTIONAL_COMMIT_HASH --detach fi - cargo build --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt + + # This will prepend the `LD_LIBRARY_PATH` for the feature branch rustfmt binary. + # In most cases the `LD_LIBRARY_PATH` should be the same for both rustfmt binaries that we build + # in `compile_rustfmt`, however, there are scenarios where each binary has different runtime + # dependencies. For example, during subtree syncs we bump the nightly toolchain required to build + # rustfmt, and therefore the feature branch relies on a newer set of runtime dependencies. + export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH + + echo "Building feature rustfmt from src" + cargo build -q --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt + + echo -e "\nRuntime dependencies for rustfmt -- LD_LIBRARY_PATH: $LD_LIBRARY_PATH" + RUSFMT_BIN=$1/rustfmt + RUSTFMT_VERSION=$($RUSFMT_BIN --version) + echo -e "\nRUSFMT_BIN $RUSTFMT_VERSION\n" + FEATURE_BIN=$1/feature_rustfmt + FEATURE_VERSION=$($FEATURE_BIN --version) + echo -e "FEATURE_BIN $FEATURE_VERSION\n" } # Check the diff for running rustfmt and the feature branch on all the .rs files in the repo. @@ -155,7 +179,7 @@ function check_repo() { STATUSES+=($?) set -e - echo "removing tmp_dir $tmp_dir" + echo -e "removing tmp_dir $tmp_dir\n\n" rm -rf $tmp_dir cd $WORKDIR } diff --git a/ci/integration.sh b/ci/integration.sh index 19d502bc5c7bd..ea96e4be1305f 100755 --- a/ci/integration.sh +++ b/ci/integration.sh @@ -104,7 +104,7 @@ case ${INTEGRATION} in check_fmt_with_all_tests cd - ;; - error-chain | tempdir) + tempdir) git clone --depth=1 https://github.com/rust-lang-deprecated/${INTEGRATION}.git cd ${INTEGRATION} show_head diff --git a/config_proc_macro/src/attrs.rs b/config_proc_macro/src/attrs.rs index d8de9aae088d7..e7534b813d706 100644 --- a/config_proc_macro/src/attrs.rs +++ b/config_proc_macro/src/attrs.rs @@ -68,7 +68,11 @@ fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option { match &attr.meta { syn::Meta::NameValue(syn::MetaNameValue { path, - value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }), + value: + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit_str), + .. + }), .. }) if path.is_ident(name) => Some(lit_str.value()), _ => None, diff --git a/docs/index.html b/docs/index.html index ee0339bc50da3..5e588d1db5451 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,13 +3,33 @@ Rustfmt - - - - + + + + + + - - - + -
-
🖌
-
    -
  • {{name}}
  • -
+
+
+
+
Theme
+ + +
@@ -592,7 +239,7 @@

Applicability: - {{lint.applicability.applicability}} + {{lint.applicability}} (?)
@@ -605,8 +252,8 @@

Related Issues

-
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index ed1e090e1b540..1a5330bc0e571 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -50,24 +50,6 @@ ); }; }) - .directive('themeDropdown', function ($document) { - return { - restrict: 'A', - link: function ($scope, $element, $attr) { - $element.bind('click', function () { - $element.toggleClass('open'); - $element.addClass('open-recent'); - }); - - $document.bind('click', function () { - if (!$element.hasClass('open-recent')) { - $element.removeClass('open'); - } - $element.removeClass('open-recent'); - }) - } - } - }) .directive('filterDropdown', function ($document) { return { restrict: 'A', @@ -114,28 +96,19 @@ cargo: true, complexity: true, correctness: true, - deprecated: false, nursery: true, pedantic: true, perf: true, restriction: true, style: true, suspicious: true, + deprecated: false, } $scope.groups = { ...GROUPS_FILTER_DEFAULT }; - const THEMES_DEFAULT = { - light: "Light", - rust: "Rust", - coal: "Coal", - navy: "Navy", - ayu: "Ayu" - }; - $scope.themes = THEMES_DEFAULT; - $scope.versionFilters = { "≥": {enabled: false, minorVersion: null }, "≤": {enabled: false, minorVersion: null }, @@ -153,11 +126,10 @@ ); const APPLICABILITIES_FILTER_DEFAULT = { - Unspecified: true, - Unresolved: true, MachineApplicable: true, MaybeIncorrect: true, - HasPlaceholders: true + HasPlaceholders: true, + Unspecified: true, }; $scope.applicabilities = { @@ -321,10 +293,6 @@ $location.path($scope.search); } - $scope.selectTheme = function (theme) { - setTheme(theme, true); - } - $scope.toggleLevels = function (value) { const levels = $scope.levels; for (const key in levels) { @@ -456,7 +424,7 @@ } $scope.byApplicabilities = function (lint) { - return $scope.applicabilities[lint.applicability.applicability]; + return $scope.applicabilities[lint.applicability]; }; // Show details for one lint @@ -537,6 +505,16 @@ function getQueryVariable(variable) { } } +function storeValue(settingName, value) { + try { + localStorage.setItem(`clippy-lint-list-${settingName}`, value); + } catch (e) { } +} + +function loadValue(settingName) { + return localStorage.getItem(`clippy-lint-list-${settingName}`); +} + function setTheme(theme, store) { let enableHighlight = false; let enableNight = false; @@ -569,14 +547,14 @@ function setTheme(theme, store) { document.getElementById("styleAyu").disabled = !enableAyu; if (store) { - try { - localStorage.setItem('clippy-lint-list-theme', theme); - } catch (e) { } + storeValue("theme", theme); + } else { + document.getElementById(`theme-choice`).value = theme; } } function handleShortcut(ev) { - if (ev.ctrlKey || ev.altKey || ev.metaKey) { + if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { return; } @@ -601,11 +579,51 @@ function handleShortcut(ev) { document.addEventListener("keypress", handleShortcut); document.addEventListener("keydown", handleShortcut); +function changeSetting(elem) { + if (elem.id === "disable-shortcuts") { + disableShortcuts = elem.checked; + storeValue(elem.id, elem.checked); + } +} + +function onEachLazy(lazyArray, func) { + const arr = Array.prototype.slice.call(lazyArray); + for (const el of arr) { + func(el); + } +} + +function handleBlur(event) { + const parent = document.getElementById("settings-dropdown"); + if (!parent.contains(document.activeElement) && + !parent.contains(event.relatedTarget) + ) { + parent.classList.remove("open"); + } +} + +function generateSettings() { + const settings = document.getElementById("settings-dropdown"); + const settingsButton = settings.querySelector(".settings-icon") + settingsButton.onclick = () => settings.classList.toggle("open"); + settingsButton.onblur = handleBlur; + const settingsMenu = settings.querySelector(".settings-menu"); + settingsMenu.onblur = handleBlur; + onEachLazy( + settingsMenu.querySelectorAll("input"), + el => el.onblur = handleBlur, + ); +} + +generateSettings(); + // loading the theme after the initial load const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); -const theme = localStorage.getItem('clippy-lint-list-theme'); +const theme = loadValue('theme'); if (prefersDark.matches && !theme) { setTheme("coal", false); } else { setTheme(theme, false); } +let disableShortcuts = loadValue('disable-shortcuts') === "true"; +document.getElementById("disable-shortcuts").checked = disableShortcuts; diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css new file mode 100644 index 0000000000000..a9485d511047a --- /dev/null +++ b/util/gh-pages/style.css @@ -0,0 +1,398 @@ +blockquote { font-size: 1em; } + +[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { + display: none !important; +} + +.dropdown-menu { + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); +} + +.dropdown-menu .divider { + background-color: var(--theme-popup-border); +} + +.dropdown-menu .checkbox { + display: block; + white-space: nowrap; + margin: 0; +} +.dropdown-menu .checkbox label { + padding: 3px 20px; + width: 100%; +} + +.dropdown-menu .checkbox input { + position: relative; + margin: 0 0.5rem 0; + padding: 0; +} + +.dropdown-menu .checkbox:hover { + background-color: var(--theme-hover); +} + +div.panel div.panel-body button { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); +} + +div.panel div.panel-body button:hover { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +div.panel div.panel-body button.open { + filter: brightness(90%); +} + +.dropdown-toggle .badge { + background-color: #777; +} + +.panel-heading { cursor: pointer; } + +.panel-title { display: flex; flex-wrap: wrap;} +.panel-title .label { display: inline-block; } + +.panel-title-name { flex: 1; min-width: 400px;} +.panel-title-name span { vertical-align: bottom; } + +.panel .panel-title-name .anchor { display: none; } +.panel:hover .panel-title-name .anchor { display: inline;} + +.search-control { + margin-top: 15px; +} + +@media (min-width: 992px) { + .search-control { + margin-top: 0; + } +} + +@media (min-width: 405px) { + #upper-filters { + display: flex; + flex-wrap: wrap; + } +} + +@media (max-width: 430px) { + /* Turn the version filter list to the left */ + #version-filter-selector { + right: 0; + left: auto; + } +} + +@media (max-width: 412px) { + #upper-filters, + .panel-body .search-control { + padding-right: 8px; + padding-left: 8px; + } +} + +.label { + padding-top: 0.3em; + padding-bottom: 0.3em; +} + +.label-lint-group { + min-width: 8em; +} +.label-lint-level { + min-width: 4em; +} + +.label-lint-level-allow { + background-color: #5cb85c; +} +.label-lint-level-warn { + background-color: #f0ad4e; +} +.label-lint-level-deny { + background-color: #d9534f; +} +.label-lint-level-none { + background-color: #777777; + opacity: 0.5; +} + +.label-group-deprecated { + opacity: 0.5; +} + +.label-doc-folding { + color: #000; + background-color: #fff; + border: 1px solid var(--theme-popup-border); +} +.label-doc-folding:hover { + background-color: #e6e6e6; +} + +.lint-doc-md > h3 { + border-top: 1px solid var(--theme-popup-border); + padding: 10px 15px; + margin: 0 -15px; + font-size: 18px; +} +.lint-doc-md > h3:first-child { + border-top: none; + padding-top: 0px; +} + +@media (max-width:749px) { + .lint-additional-info-container { + display: flex; + flex-flow: column; + } + .lint-additional-info-item + .lint-additional-info-item { + border-top: 1px solid var(--theme-popup-border); + } +} +@media (min-width:750px) { + .lint-additional-info-container { + display: flex; + flex-flow: row; + } + .lint-additional-info-item + .lint-additional-info-item { + border-left: 1px solid var(--theme-popup-border); + } +} + +.lint-additional-info-item { + display: inline-flex; + min-width: 200px; + flex-grow: 1; + padding: 9px 5px 5px 15px; +} + +.label-applicability { + background-color: #777777; + margin: auto 5px; +} + +.label-version { + background-color: #777777; + margin: auto 5px; + font-family: monospace; +} + +details { + border-radius: 4px; + padding: .5em .5em 0; +} + +code { + white-space: pre !important; +} + +summary { + font-weight: bold; + margin: -.5em -.5em 0; + padding: .5em; + display: revert; +} + +details[open] { + padding: .5em; +} + +/* Expanding the mdBook theme*/ +.light { + --inline-code-bg: #f6f7f6; +} +.rust { + --inline-code-bg: #f6f7f6; +} +.coal { + --inline-code-bg: #1d1f21; +} +.navy { + --inline-code-bg: #1d1f21; +} +.ayu { + --inline-code-bg: #191f26; +} + +#settings-dropdown { + position: absolute; + margin: 0.7em; + z-index: 10; + display: flex; +} + +/* Applying the mdBook theme */ +.settings-icon { + text-align: center; + width: 2em; + height: 2em; + line-height: 2em; + border: solid 1px var(--icons); + border-radius: 5px; + user-select: none; + cursor: pointer; + background: var(--theme-hover); +} +.settings-menu { + display: none; + list-style: none; + border: 1px solid var(--theme-popup-border); + border-radius: 5px; + color: var(--fg); + background: var(--theme-popup-bg); + overflow: hidden; + padding: 9px; + width: 207px; + position: absolute; + top: 28px; +} + +.settings-icon::before { + /* Wheel */ + content: url('data:image/svg+xml,\ +'); + width: 18px; + height: 18px; + display: block; + filter: invert(0.7); + padding-left: 4px; + padding-top: 3px; +} + +.settings-menu * { + font-weight: normal; +} + +.settings-menu label { + cursor: pointer; +} + +#settings-dropdown.open .settings-menu { + display: block; +} + +#theme-choice { + margin-bottom: 10px; + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); + border-radius: 5px; + cursor: pointer; + width: 100%; + border-width: 1px; + padding: 5px; +} + +.alert { + color: var(--fg); + background: var(--theme-hover); + border: 1px solid var(--theme-popup-border); +} +.page-header { + border-color: var(--theme-popup-border); +} +.panel-default > .panel-heading { + background: var(--theme-hover); + color: var(--fg); + border: 1px solid var(--theme-popup-border); +} +.panel-default > .panel-heading:hover { + filter: brightness(90%); +} +.list-group-item { + background: 0%; + border: 1px solid var(--theme-popup-border); +} +.panel, pre, hr { + background: var(--bg); + border: 1px solid var(--theme-popup-border); +} + +#version-filter-selector .checkbox { + display: flex; +} + +#version-filter { + min-width: available; +} + +#version-filter li label { + padding-right: 0; + width: 35%; +} + +.version-filter-input { + height: 60%; + width: 30%; + text-align: center; + border: none; + border-bottom: 1px solid #000000; +} + +#filter-label, .filter-clear { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); + filter: brightness(95%); +} +#filter-label:hover, .filter-clear:hover { + filter: brightness(90%); +} +.filter-input { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); +} + +.filter-input::-webkit-input-placeholder, +.filter-input::-moz-placeholder { + color: var(--searchbar-fg); + opacity: 30%; +} + +.expansion-group { + margin-top: 15px; + padding: 0px 8px; + display: flex; + flex-wrap: nowrap; +} + +@media (min-width: 992px) { + .expansion-group { + margin-top: 0; + padding: 0px 15px; + } +} + +.expansion-control { + width: 50%; +} + +:not(pre) > code { + color: var(--inline-code-color); + background-color: var(--inline-code-bg); +} +html { + scrollbar-color: var(--scrollbar) var(--bg); +} +body { + background: var(--bg); + color: var(--fg); +} From 6f5e99b7b5a00a1bed4d29f323fce0c989cf28aa Mon Sep 17 00:00:00 2001 From: ding-young Date: Mon, 22 Jul 2024 14:55:54 +0900 Subject: [PATCH 0136/1301] update macro rewrite functions to return RewriteResult --- src/expr.rs | 17 ++-- src/items.rs | 2 +- src/macros.rs | 252 +++++++++++++++++++++++++++++------------------- src/patterns.rs | 2 +- src/rewrite.rs | 28 ++++++ src/types.rs | 2 +- src/visitor.rs | 5 +- 7 files changed, 198 insertions(+), 110 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 138689bc1320e..02372e7be130a 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -243,13 +243,16 @@ pub(crate) fn format_expr( | ast::ExprKind::MethodCall(..) | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape).ok(), ast::ExprKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| { - wrap_str( - context.snippet(expr.span).to_owned(), - context.config.max_width(), - shape, - ) - }) + rewrite_macro(mac, None, context, shape, MacroPosition::Expression) + .or_else(|_| { + wrap_str( + context.snippet(expr.span).to_owned(), + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, expr.span) + }) + .ok() } ast::ExprKind::Ret(None) => Some("return".to_owned()), ast::ExprKind::Ret(Some(ref expr)) => { diff --git a/src/items.rs b/src/items.rs index 35591df0fd8f5..3894ee2cdf8c8 100644 --- a/src/items.rs +++ b/src/items.rs @@ -3427,7 +3427,7 @@ impl Rewrite for ast::ForeignItem { rewrite_type_alias(ty_alias, context, shape.indent, kind, span) } ast::ForeignItemKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Item) + rewrite_macro(mac, None, context, shape, MacroPosition::Item).ok() } }?; diff --git a/src/macros.rs b/src/macros.rs index c520971f40c50..51ded8692296b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -31,7 +31,9 @@ use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::overflow; use crate::parse::macros::lazy_static::parse_lazy_static; use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; -use crate::rewrite::{Rewrite, RewriteContext, RewriteError}; +use crate::rewrite::{ + MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult, +}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; @@ -71,22 +73,30 @@ impl MacroArg { impl Rewrite for ast::Item { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let mut visitor = crate::visitor::FmtVisitor::from_context(context); visitor.block_indent = shape.indent; visitor.last_pos = self.span().lo(); visitor.visit_item(self); - Some(visitor.buffer.to_owned()) + Ok(visitor.buffer.to_owned()) } } impl Rewrite for MacroArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - MacroArg::Expr(ref expr) => expr.rewrite(context, shape), - MacroArg::Ty(ref ty) => ty.rewrite(context, shape), - MacroArg::Pat(ref pat) => pat.rewrite(context, shape), - MacroArg::Item(ref item) => item.rewrite(context, shape), - MacroArg::Keyword(ident, _) => Some(ident.name.to_string()), + MacroArg::Expr(ref expr) => expr.rewrite_result(context, shape), + MacroArg::Ty(ref ty) => ty.rewrite_result(context, shape), + MacroArg::Pat(ref pat) => pat.rewrite_result(context, shape), + MacroArg::Item(ref item) => item.rewrite_result(context, shape), + MacroArg::Keyword(ident, _) => Ok(ident.name.to_string()), } } } @@ -110,12 +120,14 @@ fn rewrite_macro_name( } // Use this on failing to format the macro call. +// TODO(ding-young) We should also report macro parse failure to tell users why given snippet +// is left unformatted. One possible improvement is appending formatting error to context.report fn return_macro_parse_failure_fallback( context: &RewriteContext<'_>, indent: Indent, position: MacroPosition, span: Span, -) -> Option { +) -> RewriteResult { // Mark this as a failure however we format it context.macro_rewrite_failure.replace(true); @@ -133,7 +145,8 @@ fn return_macro_parse_failure_fallback( }) .unwrap_or(false); if is_like_block_indent_style { - return trim_left_preserve_layout(context.snippet(span), indent, context.config); + return trim_left_preserve_layout(context.snippet(span), indent, context.config) + .macro_error(MacroErrorKind::Unknown, span); } context.skipped_range.borrow_mut().push(( @@ -146,7 +159,7 @@ fn return_macro_parse_failure_fallback( if position == MacroPosition::Item { snippet.push(';'); } - Some(snippet) + Ok(snippet) } pub(crate) fn rewrite_macro( @@ -155,13 +168,13 @@ pub(crate) fn rewrite_macro( context: &RewriteContext<'_>, shape: Shape, position: MacroPosition, -) -> Option { +) -> RewriteResult { let should_skip = context .skip_context .macros .skip(context.snippet(mac.path.span)); if should_skip { - None + Err(RewriteError::SkipFormatting) } else { let guard = context.enter_macro(); let result = catch_unwind(AssertUnwindSafe(|| { @@ -175,9 +188,16 @@ pub(crate) fn rewrite_macro( ) })); match result { - Err(..) | Ok(None) => { + Err(..) => { context.macro_rewrite_failure.replace(true); - None + Err(RewriteError::MacroFailure { + kind: MacroErrorKind::Unknown, + span: mac.span(), + }) + } + Ok(Err(e)) => { + context.macro_rewrite_failure.replace(true); + Err(e) } Ok(rw) => rw, } @@ -191,11 +211,11 @@ fn rewrite_macro_inner( shape: Shape, position: MacroPosition, is_nested_macro: bool, -) -> Option { +) -> RewriteResult { if context.config.use_try_shorthand() { if let Some(expr) = convert_try_mac(mac, context) { context.leave_macro(); - return expr.rewrite(context, shape); + return expr.rewrite_result(context, shape); } } @@ -215,21 +235,27 @@ fn rewrite_macro_inner( if ts.is_empty() && !has_comment { return match style { Delimiter::Parenthesis if position == MacroPosition::Item => { - Some(format!("{macro_name}();")) - } - Delimiter::Bracket if position == MacroPosition::Item => { - Some(format!("{macro_name}[];")) + Ok(format!("{macro_name}();")) } - Delimiter::Parenthesis => Some(format!("{macro_name}()")), - Delimiter::Bracket => Some(format!("{macro_name}[]")), - Delimiter::Brace => Some(format!("{macro_name} {{}}")), + Delimiter::Bracket if position == MacroPosition::Item => Ok(format!("{macro_name}[];")), + Delimiter::Parenthesis => Ok(format!("{macro_name}()")), + Delimiter::Bracket => Ok(format!("{macro_name}[]")), + Delimiter::Brace => Ok(format!("{macro_name} {{}}")), _ => unreachable!(), }; } // Format well-known macros which cannot be parsed as a valid AST. if macro_name == "lazy_static!" && !has_comment { - if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) { - return success; + match format_lazy_static(context, shape, ts.clone(), mac.span()) { + Ok(rw) => return Ok(rw), + Err(err) => match err { + // We will move on to parsing macro args just like other macros + // if we could not parse lazy_static! with known syntax + RewriteError::MacroFailure { kind, span: _ } + if kind == MacroErrorKind::ParseFailure => {} + // If formatting fails even though parsing succeeds, return the err early + _ => return Err(err), + }, } } @@ -266,7 +292,7 @@ fn rewrite_macro_inner( Delimiter::Parenthesis => { // Handle special case: `vec!(expr; expr)` if vec_with_semi { - handle_vec_semi(context, shape, arg_vec, macro_name, style) + handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span()) } else { // Format macro invocation as function call, preserve the trailing // comma because not all macros support them. @@ -283,7 +309,6 @@ fn rewrite_macro_inner( Some(SeparatorTactic::Never) }, ) - .ok() .map(|rw| match position { MacroPosition::Item => format!("{};", rw), _ => rw, @@ -293,7 +318,7 @@ fn rewrite_macro_inner( Delimiter::Bracket => { // Handle special case: `vec![expr; expr]` if vec_with_semi { - handle_vec_semi(context, shape, arg_vec, macro_name, style) + handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span()) } else { // If we are rewriting `vec!` macro or other special macros, // then we can rewrite this as a usual array literal. @@ -317,14 +342,13 @@ fn rewrite_macro_inner( shape, force_trailing_comma, Some(original_style), - ) - .ok()?; + )?; let comma = match position { MacroPosition::Item => ";", _ => "", }; - Some(format!("{rewrite}{comma}")) + Ok(format!("{rewrite}{comma}")) } } Delimiter::Brace => { @@ -333,8 +357,8 @@ fn rewrite_macro_inner( // anything in between the braces (for now). let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { - Some(macro_body) => Some(format!("{macro_name} {macro_body}")), - None => Some(format!("{macro_name} {snippet}")), + Some(macro_body) => Ok(format!("{macro_name} {macro_body}")), + None => Ok(format!("{macro_name} {snippet}")), } } _ => unreachable!(), @@ -347,28 +371,32 @@ fn handle_vec_semi( arg_vec: Vec, macro_name: String, delim_token: Delimiter, -) -> Option { + span: Span, +) -> RewriteResult { let (left, right) = match delim_token { Delimiter::Parenthesis => ("(", ")"), Delimiter::Bracket => ("[", "]"), _ => unreachable!(), }; - let mac_shape = shape.offset_left(macro_name.len())?; + // Should we return MaxWidthError, Or Macro failure + let mac_shape = shape + .offset_left(macro_name.len()) + .max_width_error(shape.width, span)?; // 8 = `vec![]` + `; ` or `vec!()` + `; ` let total_overhead = 8; let nested_shape = mac_shape.block_indent(context.config.tab_spaces()); - let lhs = arg_vec[0].rewrite(context, nested_shape)?; - let rhs = arg_vec[1].rewrite(context, nested_shape)?; + let lhs = arg_vec[0].rewrite_result(context, nested_shape)?; + let rhs = arg_vec[1].rewrite_result(context, nested_shape)?; if !lhs.contains('\n') && !rhs.contains('\n') && lhs.len() + rhs.len() + total_overhead <= shape.width { // macro_name(lhs; rhs) or macro_name[lhs; rhs] - Some(format!("{macro_name}{left}{lhs}; {rhs}{right}")) + Ok(format!("{macro_name}{left}{lhs}; {rhs}{right}")) } else { // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] - Some(format!( + Ok(format!( "{}{}{}{};{}{}{}{}", macro_name, left, @@ -386,7 +414,7 @@ fn rewrite_empty_macro_def_body( context: &RewriteContext<'_>, span: Span, shape: Shape, -) -> Option { +) -> RewriteResult { // Create an empty, dummy `ast::Block` representing an empty macro body let block = ast::Block { stmts: vec![].into(), @@ -396,7 +424,7 @@ fn rewrite_empty_macro_def_body( tokens: None, could_be_bare_literal: false, }; - block.rewrite(context, shape) + block.rewrite_result(context, shape) } pub(crate) fn rewrite_macro_def( @@ -407,8 +435,8 @@ pub(crate) fn rewrite_macro_def( ident: symbol::Ident, vis: &ast::Visibility, span: Span, -) -> Option { - let snippet = Some(remove_trailing_white_spaces(context.snippet(span))); +) -> RewriteResult { + let snippet = Ok(remove_trailing_white_spaces(context.snippet(span))); if snippet.as_ref().map_or(true, |s| s.ends_with(';')) { return snippet; } @@ -443,7 +471,7 @@ pub(crate) fn rewrite_macro_def( let lo = context.snippet_provider.span_before(span, "{"); result += " "; result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; - return Some(result); + return Ok(result); } let branch_items = itemize_list( @@ -454,13 +482,14 @@ pub(crate) fn rewrite_macro_def( |branch| branch.span.lo(), |branch| branch.span.hi(), |branch| match branch.rewrite(context, arm_shape, multi_branch_style) { - Some(v) => Ok(v), + Ok(v) => Ok(v), // if the rewrite returned None because a macro could not be rewritten, then return the // original body - None if context.macro_rewrite_failure.get() => { + // TODO(ding-young) report rewrite error even if we return Ok with original snippet + Err(_) if context.macro_rewrite_failure.get() => { Ok(context.snippet(branch.body).trim().to_string()) } - None => Err(RewriteError::Unknown), + Err(e) => Err(e), }, context.snippet_provider.span_after(span, "{"), span.hi(), @@ -488,7 +517,7 @@ pub(crate) fn rewrite_macro_def( result += "}"; } - Some(result) + Ok(result) } fn register_metavariable( @@ -640,12 +669,13 @@ impl MacroArgKind { context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, - ) -> Option { - let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> { + ) -> RewriteResult { + type DelimitedArgsRewrite = Result<(String, String, String), RewriteError>; + let rewrite_delimited_inner = |delim_tok, args| -> DelimitedArgsRewrite { let inner = wrap_macro_args(context, args, shape)?; let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty()); if lhs.len() + inner.len() + rhs.len() <= shape.width { - return Some((lhs, inner, rhs)); + return Ok((lhs, inner, rhs)); } let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false); @@ -653,27 +683,27 @@ impl MacroArgKind { .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let inner = wrap_macro_args(context, args, nested_shape)?; - Some((lhs, inner, rhs)) + Ok((lhs, inner, rhs)) }; match *self { - MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")), + MacroArgKind::MetaVariable(ty, ref name) => Ok(format!("${name}:{ty}")), MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let another = another .as_ref() - .and_then(|a| a.rewrite(context, shape, use_multiple_lines)) + .and_then(|a| a.rewrite(context, shape, use_multiple_lines).ok()) .unwrap_or_else(|| "".to_owned()); let repeat_tok = pprust::token_to_string(tok); - Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) + Ok(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) } MacroArgKind::Delimited(delim_tok, ref args) => { rewrite_delimited_inner(delim_tok, args) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) } - MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")), - MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")), + MacroArgKind::Separator(ref sep, ref prefix) => Ok(format!("{prefix}{sep} ")), + MacroArgKind::Other(ref inner, ref prefix) => Ok(format!("{prefix}{inner}")), } } } @@ -689,7 +719,7 @@ impl ParsedMacroArg { context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, - ) -> Option { + ) -> RewriteResult { self.kind.rewrite(context, shape, use_multiple_lines) } } @@ -967,9 +997,9 @@ fn wrap_macro_args( context: &RewriteContext<'_>, args: &[ParsedMacroArg], shape: Shape, -) -> Option { +) -> RewriteResult { wrap_macro_args_inner(context, args, shape, false) - .or_else(|| wrap_macro_args_inner(context, args, shape, true)) + .or_else(|_| wrap_macro_args_inner(context, args, shape, true)) } fn wrap_macro_args_inner( @@ -977,7 +1007,7 @@ fn wrap_macro_args_inner( args: &[ParsedMacroArg], shape: Shape, use_multiple_lines: bool, -) -> Option { +) -> RewriteResult { let mut result = String::with_capacity(128); let mut iter = args.iter().peekable(); let indent_str = shape.indent.to_string_with_newline(context.config); @@ -1003,9 +1033,9 @@ fn wrap_macro_args_inner( } if !use_multiple_lines && result.len() >= shape.width { - None + Err(RewriteError::Unknown) } else { - Some(result) + Ok(result) } } @@ -1013,22 +1043,21 @@ fn wrap_macro_args_inner( // for some common cases. I hope the basic logic is sufficient. Note that the // meaning of some tokens is a bit different here from usual Rust, e.g., `*` // and `(`/`)` have special meaning. -// -// We always try and format on one line. -// FIXME: Use multi-line when every thing does not fit on one line. fn format_macro_args( context: &RewriteContext<'_>, token_stream: TokenStream, shape: Shape, -) -> Option { +) -> RewriteResult { + let span = span_for_token_stream(&token_stream); if !context.config.format_macro_matchers() { - let span = span_for_token_stream(&token_stream); - return Some(match span { + return Ok(match span { Some(span) => context.snippet(span).to_owned(), None => String::new(), }); } - let parsed_args = MacroArgParser::new().parse(token_stream)?; + let parsed_args = MacroArgParser::new() + .parse(token_stream) + .macro_error(MacroErrorKind::ParseFailure, span.unwrap())?; wrap_macro_args(context, &parsed_args, shape) } @@ -1241,11 +1270,14 @@ impl MacroBranch { context: &RewriteContext<'_>, shape: Shape, multi_branch_style: bool, - ) -> Option { + ) -> RewriteResult { // Only attempt to format function-like macros. if self.args_paren_kind != Delimiter::Parenthesis { // FIXME(#1539): implement for non-sugared macros. - return None; + return Err(RewriteError::MacroFailure { + kind: MacroErrorKind::Unknown, + span: self.span, + }); } let old_body = context.snippet(self.body).trim(); @@ -1256,8 +1288,13 @@ impl MacroBranch { prefix_width = 6; // 6 = " => {{" } } - let mut result = - format_macro_args(context, self.args.clone(), shape.sub_width(prefix_width)?)?; + let mut result = format_macro_args( + context, + self.args.clone(), + shape + .sub_width(prefix_width) + .max_width_error(shape.width, self.span)?, + )?; if multi_branch_style { result += " =>"; @@ -1266,7 +1303,7 @@ impl MacroBranch { if !context.config.format_macro_bodies() { result += " "; result += context.snippet(self.whole_body); - return Some(result); + return Ok(result); } // The macro body is the most interesting part. It might end up as various @@ -1275,7 +1312,8 @@ impl MacroBranch { // `$$`). We'll try and format like an AST node, but we'll substitute // variables for new names with the same length first. - let (body_str, substs) = replace_names(old_body)?; + let (body_str, substs) = + replace_names(old_body).macro_error(MacroErrorKind::ReplaceMacroVariable, self.span)?; let mut config = context.config.clone(); config.set().show_parse_errors(false); @@ -1298,13 +1336,21 @@ impl MacroBranch { config.set().max_width(new_width); match crate::format_code_block(&body_str, &config, true) { Some(new_body) => new_body, - None => return None, + None => { + return Err(RewriteError::MacroFailure { + kind: MacroErrorKind::Unknown, + span: self.span, + }); + } } } }; if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) { - return None; + return Err(RewriteError::ExceedsMaxWidth { + configured_width: shape.width, + span: self.span, + }); } // Indent the body since it is in a block. @@ -1330,7 +1376,10 @@ impl MacroBranch { for (old, new) in &substs { if old_body.contains(new) { debug!("rewrite_macro_def: bailing matching variable: `{}`", new); - return None; + return Err(RewriteError::MacroFailure { + kind: MacroErrorKind::ReplaceMacroVariable, + span: self.span, + }); } new_body = new_body.replace(new, old); } @@ -1345,7 +1394,7 @@ impl MacroBranch { result += "}"; - Some(result) + Ok(result) } } @@ -1365,7 +1414,8 @@ fn format_lazy_static( context: &RewriteContext<'_>, shape: Shape, ts: TokenStream, -) -> Option { + span: Span, +) -> RewriteResult { let mut result = String::with_capacity(1024); let nested_shape = shape .block_indent(context.config.tab_spaces()) @@ -1374,7 +1424,8 @@ fn format_lazy_static( result.push_str("lazy_static! {"); result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); - let parsed_elems = parse_lazy_static(context, ts)?; + let parsed_elems = + parse_lazy_static(context, ts).macro_error(MacroErrorKind::ParseFailure, span)?; let last = parsed_elems.len() - 1; for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { // Rewrite as a static item. @@ -1384,15 +1435,20 @@ fn format_lazy_static( "{}static ref {}: {} =", vis, id, - ty.rewrite(context, nested_shape)? + ty.rewrite_result(context, nested_shape)? )); - result.push_str(&rewrite_assign_rhs( - context, - stmt, - &*expr, - &RhsAssignKind::Expr(&expr.kind, expr.span), - nested_shape.sub_width(1)?, - )?); + result.push_str( + &rewrite_assign_rhs( + context, + stmt, + &*expr, + &RhsAssignKind::Expr(&expr.kind, expr.span), + nested_shape + .sub_width(1) + .max_width_error(nested_shape.width, expr.span)?, + ) + .unknown_error()?, + ); result.push(';'); if i != last { result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); @@ -1402,7 +1458,7 @@ fn format_lazy_static( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push('}'); - Some(result) + Ok(result) } fn rewrite_macro_with_items( @@ -1414,12 +1470,12 @@ fn rewrite_macro_with_items( original_style: Delimiter, position: MacroPosition, span: Span, -) -> Option { +) -> RewriteResult { let style_to_delims = |style| match style { - Delimiter::Parenthesis => Some(("(", ")")), - Delimiter::Bracket => Some(("[", "]")), - Delimiter::Brace => Some((" {", "}")), - _ => None, + Delimiter::Parenthesis => Ok(("(", ")")), + Delimiter::Bracket => Ok(("[", "]")), + Delimiter::Brace => Ok((" {", "}")), + _ => Err(RewriteError::Unknown), }; let (opener, closer) = style_to_delims(style)?; @@ -1441,7 +1497,7 @@ fn rewrite_macro_with_items( for item in items { let item = match item { MacroArg::Item(item) => item, - _ => return None, + _ => return Err(RewriteError::Unknown), }; visitor.visit_item(item); } @@ -1454,5 +1510,5 @@ fn rewrite_macro_with_items( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push_str(closer); result.push_str(trailing_semicolon); - Some(result) + Ok(result) } diff --git a/src/patterns.rs b/src/patterns.rs index 3ae0fbf161f3d..9a4f6528ce8e9 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -327,7 +327,7 @@ impl Rewrite for Pat { shape, ), PatKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Pat).unknown_error() + rewrite_macro(mac, None, context, shape, MacroPosition::Pat) } PatKind::Paren(ref pat) => pat .rewrite_result( diff --git a/src/rewrite.rs b/src/rewrite.rs index 6644e4b27a86e..8cefda1657f9d 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -30,6 +30,23 @@ impl Rewrite for ptr::P { } } +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum MacroErrorKind { + ParseFailure, + ReplaceMacroVariable, + Unknown, +} + +impl std::fmt::Display for MacroErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MacroErrorKind::ParseFailure => write!(f, "(parse failure)"), + MacroErrorKind::ReplaceMacroVariable => write!(f, "(replacing macro variables with $)"), + MacroErrorKind::Unknown => write!(f, ""), + } + } +} + #[derive(Clone, Error, Debug)] pub(crate) enum RewriteError { #[error("Formatting was skipped due to skip attribute or out of file range.")] @@ -38,6 +55,9 @@ pub(crate) enum RewriteError { #[error("It exceeds the required width of {configured_width} for the span: {span:?}")] ExceedsMaxWidth { configured_width: usize, span: Span }, + #[error("Failed to format given macro{} at: {span:?}", kind)] + MacroFailure { kind: MacroErrorKind, span: Span }, + /// Format failure that does not fit to above categories. #[error("An unknown error occurred during formatting.")] Unknown, @@ -46,6 +66,7 @@ pub(crate) enum RewriteError { /// Extension trait used to conveniently convert to RewriteError pub(crate) trait RewriteErrorExt { fn max_width_error(self, width: usize, span: Span) -> Result; + fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result; fn unknown_error(self) -> Result; } @@ -57,6 +78,13 @@ impl RewriteErrorExt for Option { }) } + fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result { + self.ok_or_else(|| RewriteError::MacroFailure { + kind: kind, + span: span, + }) + } + fn unknown_error(self) -> Result { self.ok_or_else(|| RewriteError::Unknown) } diff --git a/src/types.rs b/src/types.rs index f9dafe7d6255d..76eb0ea0529cb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -958,7 +958,7 @@ impl Rewrite for ast::Ty { ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape), ast::TyKind::Never => Ok(String::from("!")), ast::TyKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Expression).unknown_error() + rewrite_macro(mac, None, context, shape, MacroPosition::Expression) } ast::TyKind::ImplicitSelf => Ok(String::from("")), ast::TyKind::ImplTrait(_, ref it) => { diff --git a/src/visitor.rs b/src/visitor.rs index b08aa35f8f886..9859100a038ca 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -584,7 +584,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { item.ident, &item.vis, item.span, - ); + ) + .ok(); self.push_rewrite(item.span, rewrite); } ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => { @@ -683,7 +684,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { // 1 = ; let shape = self.shape().saturating_sub_width(1); - let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos)); + let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos).ok()); // As of v638 of the rustc-ap-* crates, the associated span no longer includes // the trailing semicolon. This determines the correct span to ensure scenarios // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`) From d40e04a1cb08d00736909bfb81be01e4cca9d6d6 Mon Sep 17 00:00:00 2001 From: WeiTheShinobi Date: Sun, 25 Aug 2024 20:51:26 +0800 Subject: [PATCH 0137/1301] `used_underscore_items` will not lint exteranl item --- clippy_lints/src/misc.rs | 8 +++++- tests/ui/auxiliary/external_item.rs | 7 ++++++ tests/ui/used_underscore_items.rs | 12 +++++++++ tests/ui/used_underscore_items.stderr | 36 +++++++++++++-------------- 4 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 tests/ui/auxiliary/external_item.rs diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 3fc3f2b3b7fa7..3e80e48a9485d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -7,6 +7,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def::Res; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::intravisit::FnKind; use rustc_hir::{ BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, @@ -281,9 +282,14 @@ fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }, _ => return, }; + let name = ident.name.as_str(); let definition_span = cx.tcx.def_span(def_id); - if name.starts_with('_') && !name.starts_with("__") && !definition_span.from_expansion() { + if name.starts_with('_') + && !name.starts_with("__") + && !definition_span.from_expansion() + && def_id.krate == LOCAL_CRATE + { span_lint_and_then( cx, USED_UNDERSCORE_ITEMS, diff --git a/tests/ui/auxiliary/external_item.rs b/tests/ui/auxiliary/external_item.rs new file mode 100644 index 0000000000000..ca4bc369e4494 --- /dev/null +++ b/tests/ui/auxiliary/external_item.rs @@ -0,0 +1,7 @@ +pub struct _ExternalStruct {} + +impl _ExternalStruct { + pub fn _foo(self) {} +} + +pub fn _exernal_foo() {} diff --git a/tests/ui/used_underscore_items.rs b/tests/ui/used_underscore_items.rs index 6b2ca44e32c4e..223016a5c9667 100644 --- a/tests/ui/used_underscore_items.rs +++ b/tests/ui/used_underscore_items.rs @@ -1,6 +1,9 @@ +//@aux-build:external_item.rs #![allow(unused)] #![warn(clippy::used_underscore_items)] +extern crate external_item; + // should not lint macro macro_rules! macro_wrap_func { () => { @@ -49,3 +52,12 @@ fn main() { let foo_struct2 = a::b::c::_FooStruct2 {}; foo_struct2._method_call(); } + +// should not lint exteranl crate. +// user cannot control how others name their items +fn external_item_call() { + let foo_struct3 = external_item::_ExternalStruct {}; + foo_struct3._foo(); + + external_item::_exernal_foo(); +} diff --git a/tests/ui/used_underscore_items.stderr b/tests/ui/used_underscore_items.stderr index 127d8248a943d..93ac3a6fec6be 100644 --- a/tests/ui/used_underscore_items.stderr +++ b/tests/ui/used_underscore_items.stderr @@ -1,11 +1,11 @@ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:40:5 + --> tests/ui/used_underscore_items.rs:43:5 | LL | _foo1(); | ^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:19:1 + --> tests/ui/used_underscore_items.rs:22:1 | LL | fn _foo1() {} | ^^^^^^^^^^ @@ -13,97 +13,97 @@ LL | fn _foo1() {} = help: to override `-D warnings` add `#[allow(clippy::used_underscore_items)]` error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:41:13 + --> tests/ui/used_underscore_items.rs:44:13 | LL | let _ = _foo2(); | ^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:21:1 + --> tests/ui/used_underscore_items.rs:24:1 | LL | fn _foo2() -> i32 { | ^^^^^^^^^^^^^^^^^ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:42:5 + --> tests/ui/used_underscore_items.rs:45:5 | LL | a::b::c::_foo3(); | ^^^^^^^^^^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:28:13 + --> tests/ui/used_underscore_items.rs:31:13 | LL | pub fn _foo3() {} | ^^^^^^^^^^^^^^ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:43:14 + --> tests/ui/used_underscore_items.rs:46:14 | LL | let _ = &_FooStruct {}; | ^^^^^^^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:13:1 + --> tests/ui/used_underscore_items.rs:16:1 | LL | struct _FooStruct {} | ^^^^^^^^^^^^^^^^^ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:44:13 + --> tests/ui/used_underscore_items.rs:47:13 | LL | let _ = _FooStruct {}; | ^^^^^^^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:13:1 + --> tests/ui/used_underscore_items.rs:16:1 | LL | struct _FooStruct {} | ^^^^^^^^^^^^^^^^^ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:46:22 + --> tests/ui/used_underscore_items.rs:49:22 | LL | let foo_struct = _FooStruct {}; | ^^^^^^^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:13:1 + --> tests/ui/used_underscore_items.rs:16:1 | LL | struct _FooStruct {} | ^^^^^^^^^^^^^^^^^ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:47:5 + --> tests/ui/used_underscore_items.rs:50:5 | LL | foo_struct._method_call(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:16:5 + --> tests/ui/used_underscore_items.rs:19:5 | LL | fn _method_call(self) {} | ^^^^^^^^^^^^^^^^^^^^^ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:49:23 + --> tests/ui/used_underscore_items.rs:52:23 | LL | let foo_struct2 = a::b::c::_FooStruct2 {}; | ^^^^^^^^^^^^^^^^^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:30:13 + --> tests/ui/used_underscore_items.rs:33:13 | LL | pub struct _FooStruct2 {} | ^^^^^^^^^^^^^^^^^^^^^^ error: used underscore-prefixed item - --> tests/ui/used_underscore_items.rs:50:5 + --> tests/ui/used_underscore_items.rs:53:5 | LL | foo_struct2._method_call(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: item is defined here - --> tests/ui/used_underscore_items.rs:33:17 + --> tests/ui/used_underscore_items.rs:36:17 | LL | pub fn _method_call(self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ From 13d7b546dada1cb2960dec90875b16a42ff7fb84 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Sun, 25 Aug 2024 19:14:00 +0200 Subject: [PATCH 0138/1301] Add binary-size optimized variants for stable and unstable sort as well as select_nth_unstable - Stable sort uses a simple merge-sort that re-uses the existing - rather gnarly - merge function. - Unstable sort jumps directly to the branchless heapsort fallback. - select_nth_unstable jumps directly to the median_of_medians fallback, which is augmented with a custom tiny smallsort and partition impl. Some code is duplicated but de-duplication would bring it's own problems. For example `swap_if_less` is critical for performance, if the sorting networks don't inline it perf drops drastically, however `#[inline(always)]` is also a poor fit, if the provided comparison function is huge, it gives the compiler an out to only instantiate `swap_if_less` once and call it. Another aspect that would suffer when making `swap_if_less` pub, is having to cfg out dozens of functions in in smallsort module. --- library/core/src/slice/sort/mod.rs | 1 + library/core/src/slice/sort/select.rs | 125 +++++++++++++++++- .../core/src/slice/sort/shared/smallsort.rs | 2 +- library/core/src/slice/sort/stable/mod.rs | 58 ++++++-- library/core/src/slice/sort/stable/tiny.rs | 75 +++++++++++ library/core/src/slice/sort/unstable/mod.rs | 37 ++++-- .../core/src/slice/sort/unstable/quicksort.rs | 19 ++- 7 files changed, 284 insertions(+), 33 deletions(-) create mode 100644 library/core/src/slice/sort/stable/tiny.rs diff --git a/library/core/src/slice/sort/mod.rs b/library/core/src/slice/sort/mod.rs index 79852708b81ea..81b99a4364b2e 100644 --- a/library/core/src/slice/sort/mod.rs +++ b/library/core/src/slice/sort/mod.rs @@ -5,4 +5,5 @@ pub mod stable; pub mod unstable; pub(crate) mod select; +#[cfg(not(feature = "optimize_for_size"))] pub(crate) mod shared; diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index f6529f23bcb3f..b60e33b3a2f0d 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -6,9 +6,13 @@ //! for pivot selection. Using this as a fallback ensures O(n) worst case running time with //! better performance than one would get using heapsort as fallback. +use crate::intrinsics; use crate::mem::{self, SizedTypeProperties}; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::unstable::quicksort::partition; /// Reorders the slice such that the element at `index` is at its final sorted position. @@ -40,7 +44,15 @@ where let min_idx = min_index(v, &mut is_less).unwrap(); v.swap(min_idx, index); } else { - partition_at_index_loop(v, index, None, &mut is_less); + #[cfg(not(feature = "optimize_for_size"))] + { + partition_at_index_loop(v, index, None, &mut is_less); + } + + #[cfg(feature = "optimize_for_size")] + { + median_of_medians(v, &mut is_less, index); + } } let (left, right) = v.split_at_mut(index); @@ -53,6 +65,7 @@ where // most once, it doesn't make sense to use something more sophisticated than insertion-sort. const INSERTION_SORT_THRESHOLD: usize = 16; +#[cfg(not(feature = "optimize_for_size"))] fn partition_at_index_loop<'a, T, F>( mut v: &'a mut [T], mut index: usize, @@ -167,8 +180,17 @@ fn median_of_medians bool>(mut v: &mut [T], is_less: &mut loop { if v.len() <= INSERTION_SORT_THRESHOLD { if v.len() >= 2 { - insertion_sort_shift_left(v, 1, is_less); + #[cfg(not(feature = "optimize_for_size"))] + { + insertion_sort_shift_left(v, 1, is_less); + } + + #[cfg(feature = "optimize_for_size")] + { + bubble_sort(v, is_less); + } } + return; } @@ -230,7 +252,15 @@ fn median_of_ninthers bool>(v: &mut [T], is_less: &mut F) median_of_medians(&mut v[lo..lo + frac], is_less, pivot); - partition(v, lo + pivot, is_less) + #[cfg(not(feature = "optimize_for_size"))] + { + partition(v, lo + pivot, is_less) + } + + #[cfg(feature = "optimize_for_size")] + { + partition_size_opt(v, lo + pivot, is_less) + } } /// Moves around the 9 elements at the indices a..i, such that @@ -298,3 +328,92 @@ fn median_idx bool>( } b } + +// It's possible to re-use the insertion sort in the smallsort module, but with optimize_for_size it +// would clutter that module with cfg statements and make it generally harder to read and develop. +// So to decouple things and simplify it, we use a an even smaller bubble sort. +#[cfg(feature = "optimize_for_size")] +fn bubble_sort bool>(v: &mut [T], is_less: &mut F) { + let mut n = v.len(); + let mut did_swap = true; + + while did_swap && n > 1 { + did_swap = false; + for i in 1..n { + // SAFETY: The loop construction implies that `i` and `i - 1` will always be in-bounds. + unsafe { + if is_less(v.get_unchecked(i), v.get_unchecked(i - 1)) { + v.swap_unchecked(i - 1, i); + did_swap = true; + } + } + } + n -= 1; + } +} + +#[cfg(feature = "optimize_for_size")] +fn partition_size_opt(v: &mut [T], pivot: usize, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + // Allows for panic-free code-gen by proving this property to the compiler. + if len == 0 { + return 0; + } + + if pivot >= len { + intrinsics::abort(); + } + + // SAFETY: We checked that `pivot` is in-bounds. + unsafe { + // Place the pivot at the beginning of slice. + v.swap_unchecked(0, pivot); + } + let (pivot, v_without_pivot) = v.split_at_mut(1); + + // Assuming that Rust generates noalias LLVM IR we can be sure that a partition function + // signature of the form `(v: &mut [T], pivot: &T)` guarantees that pivot and v can't alias. + // Having this guarantee is crucial for optimizations. It's possible to copy the pivot value + // into a stack value, but this creates issues for types with interior mutability mandating + // a drop guard. + let pivot = &mut pivot[0]; + + let num_lt = partition_lomuto_branchless_simple(v_without_pivot, pivot, is_less); + + if num_lt >= len { + intrinsics::abort(); + } + + // SAFETY: We checked that `num_lt` is in-bounds. + unsafe { + // Place the pivot between the two partitions. + v.swap_unchecked(0, num_lt); + } + + num_lt +} + +#[cfg(feature = "optimize_for_size")] +fn partition_lomuto_branchless_simple bool>( + v: &mut [T], + pivot: &T, + is_less: &mut F, +) -> usize { + let mut left = 0; + + for right in 0..v.len() { + // SAFETY: `left` can at max be incremented by 1 each loop iteration, which implies that + // left <= right and that both are in-bounds. + unsafe { + let right_is_lt = is_less(v.get_unchecked(right), pivot); + v.swap_unchecked(left, right); + left += right_is_lt as usize; + } + } + + left +} diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index db0c5c72822c0..5027962ccb415 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -378,7 +378,7 @@ where /// Swap two values in the slice pointed to by `v_base` at the position `a_pos` and `b_pos` if the /// value at position `b_pos` is less than the one at position `a_pos`. -pub unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) +unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) where F: FnMut(&T, &T) -> bool, { diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index a383b0f589ccf..a61a95a225455 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -1,15 +1,24 @@ //! This module contains the entry points for `slice::sort`. +#[cfg(not(feature = "optimize_for_size"))] +use crate::cmp; +use crate::intrinsics; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::{ insertion_sort_shift_left, StableSmallSortTypeImpl, SMALL_SORT_GENERAL_SCRATCH_LEN, }; -use crate::{cmp, intrinsics}; -pub(crate) mod drift; pub(crate) mod merge; + +#[cfg(not(feature = "optimize_for_size"))] +pub(crate) mod drift; +#[cfg(not(feature = "optimize_for_size"))] pub(crate) mod quicksort; +#[cfg(feature = "optimize_for_size")] +pub(crate) mod tiny; + /// Stable sort called driftsort by Orson Peters and Lukas Bergdoll. /// Design document: /// @@ -30,25 +39,48 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less return; } - // More advanced sorting methods than insertion sort are faster if called in - // a hot loop for small inputs, but for general-purpose code the small - // binary size of insertion sort is more important. The instruction cache in - // modern processors is very valuable, and for a single sort call in general - // purpose code any gains from an advanced method are cancelled by i-cache - // misses during the sort, and thrashing the i-cache for surrounding code. - const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; - if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { - insertion_sort_shift_left(v, 1, is_less); - return; + #[cfg(not(feature = "optimize_for_size"))] + { + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + driftsort_main::(v, is_less); } - driftsort_main::(v, is_less); + #[cfg(feature = "optimize_for_size")] + { + let alloc_len = len / 2; + + // For small inputs 4KiB of stack storage suffices, which allows us to avoid + // calling the (de-)allocator. Benchmarks showed this was quite beneficial. + let mut stack_buf = AlignedStorage::::new(); + let stack_scratch = stack_buf.as_uninit_slice_mut(); + let mut heap_buf; + let scratch = if stack_scratch.len() >= alloc_len { + stack_scratch + } else { + heap_buf = BufT::with_capacity(alloc_len); + heap_buf.as_uninit_slice_mut() + }; + + tiny::mergesort(v, scratch, is_less); + } } /// See [`sort`] /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. +#[cfg(not(feature = "optimize_for_size"))] #[inline(never)] fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { // By allocating n elements of memory we can ensure the entire input can diff --git a/library/core/src/slice/sort/stable/tiny.rs b/library/core/src/slice/sort/stable/tiny.rs new file mode 100644 index 0000000000000..307011f3ee8c4 --- /dev/null +++ b/library/core/src/slice/sort/stable/tiny.rs @@ -0,0 +1,75 @@ +//! Binary-size optimized mergesort inspired by https://github.com/voultapher/tiny-sort-rs. + +use crate::mem::{ManuallyDrop, MaybeUninit}; +use crate::ptr; +use crate::slice::sort::stable::merge; + +/// Tiny recursive top-down merge sort optimized for binary size. It has no adaptiveness whatsoever, +/// no run detection, etc. +#[inline(always)] +pub fn mergesort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, +) { + let len = v.len(); + + if len > 2 { + let mid = len / 2; + + // SAFETY: mid is in-bounds. + unsafe { + // Sort the left half recursively. + mergesort(v.get_unchecked_mut(..mid), scratch, is_less); + // Sort the right half recursively. + mergesort(v.get_unchecked_mut(mid..), scratch, is_less); + } + + merge::merge(v, scratch, mid, is_less); + } else if len == 2 { + // Branchless swap the two elements. This reduces the recursion depth and improves + // perf significantly at a small binary-size cost. Trades ~10% perf boost for integers + // for ~50 bytes in the binary. + + // SAFETY: We checked the len, the pointers we create are valid and don't overlap. + unsafe { + swap_if_less(v.as_mut_ptr(), 0, 1, is_less); + } + } +} + +/// Swap two values in the slice pointed to by `v_base` at the position `a_pos` and `b_pos` if the +/// value at position `b_pos` is less than the one at position `a_pos`. +unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + // SAFETY: the caller must guarantee that `a` and `b` each added to `v_base` yield valid + // pointers into `v_base`, and are properly aligned, and part of the same allocation. + unsafe { + let v_a = v_base.add(a_pos); + let v_b = v_base.add(b_pos); + + // PANIC SAFETY: if is_less panics, no scratch memory was created and the slice should still be + // in a well defined state, without duplicates. + + // Important to only swap if it is more and not if it is equal. is_less should return false for + // equal, so we don't swap. + let should_swap = is_less(&*v_b, &*v_a); + + // This is a branchless version of swap if. + // The equivalent code with a branch would be: + // + // if should_swap { + // ptr::swap(left, right, 1); + // } + + // The goal is to generate cmov instructions here. + let left_swap = if should_swap { v_b } else { v_a }; + let right_swap = if should_swap { v_a } else { v_b }; + + let right_swap_tmp = ManuallyDrop::new(ptr::read(right_swap)); + ptr::copy(left_swap, v_a, 1); + ptr::copy_nonoverlapping(&*right_swap_tmp, v_b, 1); + } +} diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 932e01f4401e5..43b5cc79fc7f3 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -2,10 +2,13 @@ use crate::intrinsics; use crate::mem::SizedTypeProperties; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::find_existing_run; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; pub(crate) mod heapsort; +#[cfg(not(feature = "optimize_for_size"))] pub(crate) mod quicksort; /// Unstable sort called ipnsort by Lukas Bergdoll and Orson Peters. @@ -28,25 +31,37 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { return; } - // More advanced sorting methods than insertion sort are faster if called in - // a hot loop for small inputs, but for general-purpose code the small - // binary size of insertion sort is more important. The instruction cache in - // modern processors is very valuable, and for a single sort call in general - // purpose code any gains from an advanced method are cancelled by i-cache - // misses during the sort, and thrashing the i-cache for surrounding code. - const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; - if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { - insertion_sort_shift_left(v, 1, is_less); - return; + #[cfg(not(feature = "optimize_for_size"))] + { + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + ipnsort(v, is_less); } - ipnsort(v, is_less); + #[cfg(feature = "optimize_for_size")] + { + // SAFETY: We checked that `len >= 2`. + unsafe { + heapsort::heapsort(v, is_less); + } + } } /// See [`sort`] /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. +#[cfg(not(feature = "optimize_for_size"))] #[inline(never)] fn ipnsort(v: &mut [T], is_less: &mut F) where diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index cd53656e9b4b8..e55ebe67e1306 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -98,13 +98,15 @@ where return 0; } - // Allows for panic-free code-gen by proving this property to the compiler. if pivot >= len { intrinsics::abort(); } - // Place the pivot at the beginning of slice. - v.swap(0, pivot); + // SAFETY: We checked that `pivot` is in-bounds. + unsafe { + // Place the pivot at the beginning of slice. + v.swap_unchecked(0, pivot); + } let (pivot, v_without_pivot) = v.split_at_mut(1); // Assuming that Rust generates noalias LLVM IR we can be sure that a partition function @@ -118,8 +120,15 @@ where // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn. let num_lt = (const { inst_partition::() })(v_without_pivot, pivot, is_less); - // Place the pivot between the two partitions. - v.swap(0, num_lt); + if num_lt >= len { + intrinsics::abort(); + } + + // SAFETY: We checked that `num_lt` is in-bounds. + unsafe { + // Place the pivot between the two partitions. + v.swap_unchecked(0, num_lt); + } num_lt } From 89c9a2908fc371330edc627b9178db0628c3aee0 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Mon, 26 Aug 2024 11:17:38 +0200 Subject: [PATCH 0139/1301] Reduce code duplication by moving partition_lomuto_branchless_simple into quicksort module --- library/core/src/slice/sort/select.rs | 78 +------------------ library/core/src/slice/sort/unstable/mod.rs | 1 - .../core/src/slice/sort/unstable/quicksort.rs | 39 +++++++++- 3 files changed, 39 insertions(+), 79 deletions(-) diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index b60e33b3a2f0d..2b3e14755302c 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -6,13 +6,11 @@ //! for pivot selection. Using this as a fallback ensures O(n) worst case running time with //! better performance than one would get using heapsort as fallback. -use crate::intrinsics; use crate::mem::{self, SizedTypeProperties}; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; -#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::unstable::quicksort::partition; /// Reorders the slice such that the element at `index` is at its final sorted position. @@ -252,15 +250,7 @@ fn median_of_ninthers bool>(v: &mut [T], is_less: &mut F) median_of_medians(&mut v[lo..lo + frac], is_less, pivot); - #[cfg(not(feature = "optimize_for_size"))] - { - partition(v, lo + pivot, is_less) - } - - #[cfg(feature = "optimize_for_size")] - { - partition_size_opt(v, lo + pivot, is_less) - } + partition(v, lo + pivot, is_less) } /// Moves around the 9 elements at the indices a..i, such that @@ -351,69 +341,3 @@ fn bubble_sort bool>(v: &mut [T], is_less: &mut F) { n -= 1; } } - -#[cfg(feature = "optimize_for_size")] -fn partition_size_opt(v: &mut [T], pivot: usize, is_less: &mut F) -> usize -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - - // Allows for panic-free code-gen by proving this property to the compiler. - if len == 0 { - return 0; - } - - if pivot >= len { - intrinsics::abort(); - } - - // SAFETY: We checked that `pivot` is in-bounds. - unsafe { - // Place the pivot at the beginning of slice. - v.swap_unchecked(0, pivot); - } - let (pivot, v_without_pivot) = v.split_at_mut(1); - - // Assuming that Rust generates noalias LLVM IR we can be sure that a partition function - // signature of the form `(v: &mut [T], pivot: &T)` guarantees that pivot and v can't alias. - // Having this guarantee is crucial for optimizations. It's possible to copy the pivot value - // into a stack value, but this creates issues for types with interior mutability mandating - // a drop guard. - let pivot = &mut pivot[0]; - - let num_lt = partition_lomuto_branchless_simple(v_without_pivot, pivot, is_less); - - if num_lt >= len { - intrinsics::abort(); - } - - // SAFETY: We checked that `num_lt` is in-bounds. - unsafe { - // Place the pivot between the two partitions. - v.swap_unchecked(0, num_lt); - } - - num_lt -} - -#[cfg(feature = "optimize_for_size")] -fn partition_lomuto_branchless_simple bool>( - v: &mut [T], - pivot: &T, - is_less: &mut F, -) -> usize { - let mut left = 0; - - for right in 0..v.len() { - // SAFETY: `left` can at max be incremented by 1 each loop iteration, which implies that - // left <= right and that both are in-bounds. - unsafe { - let right_is_lt = is_less(v.get_unchecked(right), pivot); - v.swap_unchecked(left, right); - left += right_is_lt as usize; - } - } - - left -} diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 43b5cc79fc7f3..faac97eab02b8 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -8,7 +8,6 @@ use crate::slice::sort::shared::find_existing_run; use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; pub(crate) mod heapsort; -#[cfg(not(feature = "optimize_for_size"))] pub(crate) mod quicksort; /// Unstable sort called ipnsort by Lukas Bergdoll and Orson Peters. diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index e55ebe67e1306..83751d99cc261 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -1,7 +1,9 @@ //! This module contains an unstable quicksort and two partition implementations. use crate::mem::{self, ManuallyDrop}; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; use crate::{intrinsics, ptr}; @@ -11,6 +13,7 @@ use crate::{intrinsics, ptr}; /// /// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero, /// this function will immediately switch to heapsort. +#[cfg(not(feature = "optimize_for_size"))] pub(crate) fn quicksort<'a, T, F>( mut v: &'a mut [T], mut ancestor_pivot: Option<&'a T>, @@ -138,7 +141,16 @@ const fn inst_partition bool>() -> fn(&mut [T], &T, &mut if mem::size_of::() <= MAX_BRANCHLESS_PARTITION_SIZE { // Specialize for types that are relatively cheap to copy, where branchless optimizations // have large leverage e.g. `u64` and `String`. - partition_lomuto_branchless_cyclic:: + + #[cfg(not(feature = "optimize_for_size"))] + { + partition_lomuto_branchless_cyclic:: + } + + #[cfg(feature = "optimize_for_size")] + { + partition_lomuto_branchless_simple:: + } } else { partition_hoare_branchy_cyclic:: } @@ -224,6 +236,7 @@ where } } +#[cfg(not(feature = "optimize_for_size"))] struct PartitionState { // The current element that is being looked at, scans left to right through slice. right: *mut T, @@ -234,6 +247,7 @@ struct PartitionState { gap: GapGuardRaw, } +#[cfg(not(feature = "optimize_for_size"))] fn partition_lomuto_branchless_cyclic(v: &mut [T], pivot: &T, is_less: &mut F) -> usize where F: FnMut(&T, &T) -> bool, @@ -325,6 +339,27 @@ where } } +#[cfg(feature = "optimize_for_size")] +fn partition_lomuto_branchless_simple bool>( + v: &mut [T], + pivot: &T, + is_less: &mut F, +) -> usize { + let mut left = 0; + + for right in 0..v.len() { + // SAFETY: `left` can at max be incremented by 1 each loop iteration, which implies that + // left <= right and that both are in-bounds. + unsafe { + let right_is_lt = is_less(v.get_unchecked(right), pivot); + v.swap_unchecked(left, right); + left += right_is_lt as usize; + } + } + + left +} + struct GapGuard { pos: *mut T, value: ManuallyDrop, @@ -342,11 +377,13 @@ impl Drop for GapGuard { /// Ideally this wouldn't be needed and we could just use the regular GapGuard. /// See comment in [`partition_lomuto_branchless_cyclic`]. +#[cfg(not(feature = "optimize_for_size"))] struct GapGuardRaw { pos: *mut T, value: *mut T, } +#[cfg(not(feature = "optimize_for_size"))] impl Drop for GapGuardRaw { fn drop(&mut self) { // SAFETY: `self` MUST be constructed in a way that makes copying the gap value into From 8756ba56b00fdc8d76a02e654a188439f1344398 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Mon, 26 Aug 2024 12:21:37 +0200 Subject: [PATCH 0140/1301] Convert cfg blocks to cfg_if --- library/core/src/slice/sort/select.rs | 28 ++++---- library/core/src/slice/sort/stable/mod.rs | 64 +++++++++---------- library/core/src/slice/sort/unstable/mod.rs | 40 ++++++------ .../core/src/slice/sort/unstable/quicksort.rs | 15 ++--- 4 files changed, 68 insertions(+), 79 deletions(-) diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index 2b3e14755302c..28ca9dcad39a7 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -42,14 +42,12 @@ where let min_idx = min_index(v, &mut is_less).unwrap(); v.swap(min_idx, index); } else { - #[cfg(not(feature = "optimize_for_size"))] - { - partition_at_index_loop(v, index, None, &mut is_less); - } - - #[cfg(feature = "optimize_for_size")] - { - median_of_medians(v, &mut is_less, index); + cfg_if! { + if #[cfg(feature = "optimize_for_size")] { + median_of_medians(v, &mut is_less, index); + } else { + partition_at_index_loop(v, index, None, &mut is_less); + } } } @@ -178,14 +176,12 @@ fn median_of_medians bool>(mut v: &mut [T], is_less: &mut loop { if v.len() <= INSERTION_SORT_THRESHOLD { if v.len() >= 2 { - #[cfg(not(feature = "optimize_for_size"))] - { - insertion_sort_shift_left(v, 1, is_less); - } - - #[cfg(feature = "optimize_for_size")] - { - bubble_sort(v, is_less); + cfg_if! { + if #[cfg(feature = "optimize_for_size")] { + bubble_sort(v, is_less); + } else { + insertion_sort_shift_left(v, 1, is_less); + } } } diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index a61a95a225455..3472401c4dcf8 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -39,40 +39,38 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less return; } - #[cfg(not(feature = "optimize_for_size"))] - { - // More advanced sorting methods than insertion sort are faster if called in - // a hot loop for small inputs, but for general-purpose code the small - // binary size of insertion sort is more important. The instruction cache in - // modern processors is very valuable, and for a single sort call in general - // purpose code any gains from an advanced method are cancelled by i-cache - // misses during the sort, and thrashing the i-cache for surrounding code. - const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; - if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { - insertion_sort_shift_left(v, 1, is_less); - return; - } - - driftsort_main::(v, is_less); - } - - #[cfg(feature = "optimize_for_size")] - { - let alloc_len = len / 2; - - // For small inputs 4KiB of stack storage suffices, which allows us to avoid - // calling the (de-)allocator. Benchmarks showed this was quite beneficial. - let mut stack_buf = AlignedStorage::::new(); - let stack_scratch = stack_buf.as_uninit_slice_mut(); - let mut heap_buf; - let scratch = if stack_scratch.len() >= alloc_len { - stack_scratch + cfg_if! { + if #[cfg(feature = "optimize_for_size")] { + let alloc_len = len / 2; + + // For small inputs 4KiB of stack storage suffices, which allows us to avoid + // calling the (de-)allocator. Benchmarks showed this was quite beneficial. + let mut stack_buf = AlignedStorage::::new(); + let stack_scratch = stack_buf.as_uninit_slice_mut(); + let mut heap_buf; + let scratch = if stack_scratch.len() >= alloc_len { + stack_scratch + } else { + heap_buf = BufT::with_capacity(alloc_len); + heap_buf.as_uninit_slice_mut() + }; + + tiny::mergesort(v, scratch, is_less); } else { - heap_buf = BufT::with_capacity(alloc_len); - heap_buf.as_uninit_slice_mut() - }; - - tiny::mergesort(v, scratch, is_less); + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + driftsort_main::(v, is_less); + } } } diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index faac97eab02b8..130be21ee3fe8 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -30,28 +30,26 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { return; } - #[cfg(not(feature = "optimize_for_size"))] - { - // More advanced sorting methods than insertion sort are faster if called in - // a hot loop for small inputs, but for general-purpose code the small - // binary size of insertion sort is more important. The instruction cache in - // modern processors is very valuable, and for a single sort call in general - // purpose code any gains from an advanced method are cancelled by i-cache - // misses during the sort, and thrashing the i-cache for surrounding code. - const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; - if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { - insertion_sort_shift_left(v, 1, is_less); - return; - } - - ipnsort(v, is_less); - } + cfg_if! { + if #[cfg(feature = "optimize_for_size")] { + // SAFETY: We checked that `len >= 2`. + unsafe { + heapsort::heapsort(v, is_less); + } + } else { + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } - #[cfg(feature = "optimize_for_size")] - { - // SAFETY: We checked that `len >= 2`. - unsafe { - heapsort::heapsort(v, is_less); + ipnsort(v, is_less); } } } diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index 83751d99cc261..9c59ccdb70005 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -141,15 +141,12 @@ const fn inst_partition bool>() -> fn(&mut [T], &T, &mut if mem::size_of::() <= MAX_BRANCHLESS_PARTITION_SIZE { // Specialize for types that are relatively cheap to copy, where branchless optimizations // have large leverage e.g. `u64` and `String`. - - #[cfg(not(feature = "optimize_for_size"))] - { - partition_lomuto_branchless_cyclic:: - } - - #[cfg(feature = "optimize_for_size")] - { - partition_lomuto_branchless_simple:: + cfg_if! { + if #[cfg(feature = "optimize_for_size")] { + partition_lomuto_branchless_simple:: + } else { + partition_lomuto_branchless_cyclic:: + } } } else { partition_hoare_branchy_cyclic:: From 5ee4d3b532a4f329fdb9319453a22d5ee9c3a7b0 Mon Sep 17 00:00:00 2001 From: beetrees Date: Sat, 24 Aug 2024 20:50:16 +0100 Subject: [PATCH 0141/1301] Remove outdated comment from `mac.yml` --- .github/workflows/mac.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 89a980c42c5a0..f98c9830a4d22 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -8,7 +8,6 @@ on: jobs: test: # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources - # macOS Catalina 10.15 runs-on: macos-latest name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) env: From 494112e51f7a2b59866c835810c3e87a10912c0a Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Mon, 26 Aug 2024 20:01:29 +0500 Subject: [PATCH 0142/1301] Fix manual_range_patterns case with one element at OR --- clippy_lints/src/manual_range_patterns.rs | 5 +++-- tests/ui/manual_range_patterns.fixed | 8 ++++++++ tests/ui/manual_range_patterns.rs | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index 9afb2b5bc84cd..66d2ab6b24a4c 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -77,9 +77,10 @@ impl Num { impl LateLintPass<'_> for ManualRangePatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) { // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives - // or at least one range + // or more then one range (exclude triggering on stylistic using OR with one element + // like described https://github.com/rust-lang/rust-clippy/issues/11825) if let PatKind::Or(pats) = pat.kind - && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))) + && (pats.len() >= 3 || (pats.len() > 1 && pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))) && !in_external_macro(cx.sess(), pat.span) { let mut min = Num::dummy(i128::MAX); diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed index f1b99637afdba..60467bf9e8848 100644 --- a/tests/ui/manual_range_patterns.fixed +++ b/tests/ui/manual_range_patterns.fixed @@ -45,4 +45,12 @@ fn main() { }; } mac!(f); + + #[rustfmt::skip] + let _ = match f { + | 2..=15 => 4, + | 241..=254 => 5, + | 255 => 6, + | _ => 7, + }; } diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs index 869ffbe80b973..9cd803334499d 100644 --- a/tests/ui/manual_range_patterns.rs +++ b/tests/ui/manual_range_patterns.rs @@ -45,4 +45,12 @@ fn main() { }; } mac!(f); + + #[rustfmt::skip] + let _ = match f { + | 2..=15 => 4, + | 241..=254 => 5, + | 255 => 6, + | _ => 7, + }; } From 7815d77ee002facace45d6747e6d9b3686d55097 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Tue, 27 Aug 2024 10:01:48 +0200 Subject: [PATCH 0143/1301] Use last swap optimization in bubblesort --- library/core/src/slice/sort/select.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index 28ca9dcad39a7..7bda54e792528 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -317,23 +317,32 @@ fn median_idx bool>( // It's possible to re-use the insertion sort in the smallsort module, but with optimize_for_size it // would clutter that module with cfg statements and make it generally harder to read and develop. -// So to decouple things and simplify it, we use a an even smaller bubble sort. +// So to decouple things and simplify it, we use an even smaller bubble sort. #[cfg(feature = "optimize_for_size")] fn bubble_sort bool>(v: &mut [T], is_less: &mut F) { + use crate::ptr; + let mut n = v.len(); - let mut did_swap = true; - while did_swap && n > 1 { - did_swap = false; - for i in 1..n { + let v_base = v.as_mut_ptr(); + + while n > 1 { + let loop_n = n; + n = 0; + for i in 1..loop_n { // SAFETY: The loop construction implies that `i` and `i - 1` will always be in-bounds. unsafe { - if is_less(v.get_unchecked(i), v.get_unchecked(i - 1)) { - v.swap_unchecked(i - 1, i); - did_swap = true; + // Even if `is_less` erroneously always returns true, we are guaranteed that `n` + // reduces by one each out loop iteration, because `1..n` is exclusive. This + // guarantees a bounded run-time should `Ord` be implemented incorrectly. + let v_i = v_base.add(i); + let v_i_minus_one = v_base.add(i - 1); + + if is_less(&*v_i, &*v_i_minus_one) { + ptr::swap_nonoverlapping(v_i, v_i_minus_one, 1); + n = i; } } } - n -= 1; } } From 6e387a3fb1a1ab68410fc00b6a981d76b42bcbaa Mon Sep 17 00:00:00 2001 From: Ivar Scholten Date: Tue, 27 Aug 2024 13:59:01 +0200 Subject: [PATCH 0144/1301] fix: do not assume rustup is installed in xtask codegen When formatting generated code the xtask crate attempts to run `rustup run stable rustfmt`, which fails if `rustup` is not installed. This results in test failures when another source manages the compiler toolchain, for example when using Nix (or any other distro-specific packaging solution): * xtask::codegen::grammar::test * xtask::codegen::assists_doc_tests::test With this commit xtask will first attempt to run `rustup run stable rustfmt`, and if that fails just plain `rustfmt`. It still validates a stable version is being used. This allows `cargo test` to pass on systems that do not use `rustup`. --- src/tools/rust-analyzer/xtask/src/codegen.rs | 34 +++++++++++--------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs index aeb0c00ae6aad..09bfed19b0e97 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen.rs @@ -126,27 +126,31 @@ impl fmt::Display for Location { } } -fn ensure_rustfmt(sh: &Shell) { - let version = cmd!(sh, "rustup run stable rustfmt --version").read().unwrap_or_default(); - if !version.contains("stable") { - panic!( - "Failed to run rustfmt from toolchain 'stable'. \ - Please run `rustup component add rustfmt --toolchain stable` to install it.", - ); +fn rustfmt_executable(sh: &Shell) -> &str { + // First try explicitly requesting the stable channel via rustup in case nightly is being used by default, + // then plain rustfmt in case rustup isn't being used to manage the compiler (e.g. when using Nix). + for executable in ["rustup run stable rustfmt", "rustfmt"] { + let version = cmd!(sh, "{executable} --version").read().unwrap_or_default(); + if version.contains("stable") { + return executable; + } } + + panic!( + "Failed to run rustfmt from toolchain 'stable'. \ + Please run `rustup component add rustfmt --toolchain stable` to install it.", + ); } fn reformat(text: String) -> String { let sh = Shell::new().unwrap(); - ensure_rustfmt(&sh); + let rustfmt_exe = rustfmt_executable(&sh); let rustfmt_toml = project_root().join("rustfmt.toml"); - let mut stdout = cmd!( - sh, - "rustup run stable rustfmt --config-path {rustfmt_toml} --config fn_single_line=true" - ) - .stdin(text) - .read() - .unwrap(); + let mut stdout = + cmd!(sh, "{rustfmt_exe} --config-path {rustfmt_toml} --config fn_single_line=true") + .stdin(text) + .read() + .unwrap(); if !stdout.ends_with('\n') { stdout.push('\n'); } From e8ac4ea4187498052849531b86114a1eec5314a1 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:14:20 +0200 Subject: [PATCH 0145/1301] new lint: `zombie_processes` --- CHANGELOG.md | 1 + clippy_dev/src/serve.rs | 3 +- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/zombie_processes.rs | 334 +++++++++++++++++++ clippy_utils/src/paths.rs | 4 + tests/ui/suspicious_command_arg_space.fixed | 1 + tests/ui/suspicious_command_arg_space.rs | 1 + tests/ui/suspicious_command_arg_space.stderr | 4 +- tests/ui/zombie_processes.rs | 138 ++++++++ tests/ui/zombie_processes.stderr | 64 ++++ tests/ui/zombie_processes_fixable.fixed | 26 ++ tests/ui/zombie_processes_fixable.rs | 26 ++ tests/ui/zombie_processes_fixable.stderr | 40 +++ 14 files changed, 642 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/zombie_processes.rs create mode 100644 tests/ui/zombie_processes.rs create mode 100644 tests/ui/zombie_processes.stderr create mode 100644 tests/ui/zombie_processes_fixable.fixed create mode 100644 tests/ui/zombie_processes_fixable.rs create mode 100644 tests/ui/zombie_processes_fixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index c5d7991e073fd..21252a3f43a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6048,6 +6048,7 @@ Released 2018-09-13 [`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space +[`zombie_processes`]: https://rust-lang.github.io/rust-clippy/master/index.html#zombie_processes [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 19560b31fd3e2..cc14cd8dae69e 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -29,7 +29,7 @@ pub fn run(port: u16, lint: Option) -> ! { } if let Some(url) = url.take() { thread::spawn(move || { - Command::new(PYTHON) + let mut child = Command::new(PYTHON) .arg("-m") .arg("http.server") .arg(port.to_string()) @@ -40,6 +40,7 @@ pub fn run(port: u16, lint: Option) -> ! { thread::sleep(Duration::from_millis(500)); // Launch browser after first export.py has completed and http.server is up let _result = opener::open(url); + child.wait().unwrap(); }); } thread::sleep(Duration::from_millis(1000)); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 94da5752bebf6..10e041fbc09a2 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -768,4 +768,5 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, + crate::zombie_processes::ZOMBIE_PROCESSES_INFO, ]; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2ac06b360bea4..f24933683b1a0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -386,6 +386,7 @@ mod write; mod zero_div_zero; mod zero_repeat_side_effects; mod zero_sized_map_values; +mod zombie_processes; // end lints modules, do not remove this comment, it’s used in `update_lints` use clippy_config::{get_configuration_metadata, Conf}; @@ -933,5 +934,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); + store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs new file mode 100644 index 0000000000000..eda3d7820c169 --- /dev/null +++ b/clippy_lints/src/zombie_processes.rs @@ -0,0 +1,334 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{fn_def_id, get_enclosing_block, match_any_def_paths, match_def_path, path_to_local_id, paths}; +use rustc_ast::Mutability; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_block, walk_expr, walk_local, Visitor}; +use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; +use std::ops::ControlFlow; +use ControlFlow::{Break, Continue}; + +declare_clippy_lint! { + /// ### What it does + /// Looks for code that spawns a process but never calls `wait()` on the child. + /// + /// ### Why is this bad? + /// As explained in the [standard library documentation](https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning), + /// calling `wait()` is necessary on Unix platforms to properly release all OS resources associated with the process. + /// Not doing so will effectively leak process IDs and/or other limited global resources, + /// which can eventually lead to resource exhaustion, so it's recommended to call `wait()` in long-running applications. + /// Such processes are called "zombie processes". + /// + /// ### Example + /// ```rust + /// use std::process::Command; + /// + /// let _child = Command::new("ls").spawn().expect("failed to execute child"); + /// ``` + /// Use instead: + /// ```rust + /// use std::process::Command; + /// + /// let mut child = Command::new("ls").spawn().expect("failed to execute child"); + /// child.wait().expect("failed to wait on child"); + /// ``` + #[clippy::version = "1.74.0"] + pub ZOMBIE_PROCESSES, + suspicious, + "not waiting on a spawned child process" +} +declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]); + +impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind + && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def() + && match_def_path(cx, child_adt.did(), &paths::CHILD) + { + match cx.tcx.parent_hir_node(expr.hir_id) { + Node::LetStmt(local) + if let PatKind::Binding(_, local_id, ..) = local.pat.kind + && let Some(enclosing_block) = get_enclosing_block(cx, expr.hir_id) => + { + let mut vis = WaitFinder::WalkUpTo(cx, local_id); + + // If it does have a `wait()` call, we're done. Don't lint. + if let Break(BreakReason::WaitFound) = walk_block(&mut vis, enclosing_block) { + return; + } + + // Don't emit a suggestion since the binding is used later + check(cx, expr, false); + }, + Node::LetStmt(&LetStmt { pat, .. }) if let PatKind::Wild = pat.kind => { + // `let _ = child;`, also dropped immediately without `wait()`ing + check(cx, expr, true); + }, + Node::Stmt(&Stmt { + kind: StmtKind::Semi(_), + .. + }) => { + // Immediately dropped. E.g. `std::process::Command::new("echo").spawn().unwrap();` + check(cx, expr, true); + }, + _ => {}, + } + } + } +} + +enum BreakReason { + WaitFound, + EarlyReturn, +} + +/// A visitor responsible for finding a `wait()` call on a local variable. +/// +/// Conditional `wait()` calls are assumed to not call wait: +/// ```ignore +/// let mut c = Command::new("").spawn().unwrap(); +/// if true { +/// c.wait(); +/// } +/// ``` +/// +/// Note that this visitor does NOT explicitly look for `wait()` calls directly, but rather does the +/// inverse -- checking if all uses of the local are either: +/// - a field access (`child.{stderr,stdin,stdout}`) +/// - calling `id` or `kill` +/// - no use at all (e.g. `let _x = child;`) +/// - taking a shared reference (`&`), `wait()` can't go through that +/// +/// None of these are sufficient to prevent zombie processes. +/// Doing it like this means more FNs, but FNs are better than FPs. +/// +/// `return` expressions, conditional or not, short-circuit the visitor because +/// if a `wait()` call hadn't been found at that point, it might never reach one at a later point: +/// ```ignore +/// let mut c = Command::new("").spawn().unwrap(); +/// if true { +/// return; // Break(BreakReason::EarlyReturn) +/// } +/// c.wait(); // this might not be reachable +/// ``` +enum WaitFinder<'a, 'tcx> { + WalkUpTo(&'a LateContext<'tcx>, HirId), + Found(&'a LateContext<'tcx>, HirId), +} + +impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> { + type Result = ControlFlow; + + fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { + if let Self::WalkUpTo(cx, local_id) = *self + && let PatKind::Binding(_, pat_id, ..) = l.pat.kind + && local_id == pat_id + { + *self = Self::Found(cx, local_id); + } + + walk_local(self, l) + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { + let Self::Found(cx, local_id) = *self else { + return walk_expr(self, ex); + }; + + if path_to_local_id(ex, local_id) { + match cx.tcx.parent_hir_node(ex.hir_id) { + Node::Stmt(Stmt { + kind: StmtKind::Semi(_), + .. + }) => {}, + Node::Expr(expr) if let ExprKind::Field(..) = expr.kind => {}, + Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {}, + Node::Expr(expr) + if let Some(fn_did) = fn_def_id(cx, expr) + && match_any_def_paths(cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => {}, + + // Conservatively assume that all other kinds of nodes call `.wait()` somehow. + _ => return Break(BreakReason::WaitFound), + } + } else { + match ex.kind { + ExprKind::Ret(..) => return Break(BreakReason::EarlyReturn), + ExprKind::If(cond, then, None) => { + walk_expr(self, cond)?; + + // A `wait()` call in an if expression with no else is not enough: + // + // let c = spawn(); + // if true { + // c.wait(); + // } + // + // This might not call wait(). However, early returns are propagated, + // because they might lead to a later wait() not being called. + if let Break(BreakReason::EarlyReturn) = walk_expr(self, then) { + return Break(BreakReason::EarlyReturn); + } + + return Continue(()); + }, + + ExprKind::If(cond, then, Some(else_)) => { + walk_expr(self, cond)?; + + #[expect(clippy::unnested_or_patterns)] + match (walk_expr(self, then), walk_expr(self, else_)) { + (Continue(()), Continue(())) + + // `wait()` in one branch but nothing in the other does not count + | (Continue(()), Break(BreakReason::WaitFound)) + | (Break(BreakReason::WaitFound), Continue(())) => {}, + + // `wait()` in both branches is ok + (Break(BreakReason::WaitFound), Break(BreakReason::WaitFound)) => { + return Break(BreakReason::WaitFound); + }, + + // Propagate early returns in either branch + (Break(BreakReason::EarlyReturn), _) | (_, Break(BreakReason::EarlyReturn)) => { + return Break(BreakReason::EarlyReturn); + }, + } + + return Continue(()); + }, + _ => {}, + } + } + + walk_expr(self, ex) + } +} + +/// This function has shared logic between the different kinds of nodes that can trigger the lint. +/// +/// In particular, `let = ;` requires some custom additional logic +/// such as checking that the binding is not used in certain ways, which isn't necessary for +/// `let _ = ;`. +/// +/// This checks if the program doesn't unconditionally exit after the spawn expression. +fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_suggestion: bool) { + let Some(block) = get_enclosing_block(cx, spawn_expr.hir_id) else { + return; + }; + + let mut vis = ExitPointFinder { + cx, + state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + }; + if let Break(ExitCallFound) = vis.visit_block(block) { + // Visitor found an unconditional `exit()` call, so don't lint. + return; + } + + span_lint_and_then( + cx, + ZOMBIE_PROCESSES, + spawn_expr.span, + "spawned process is never `wait()`ed on", + |diag| { + if emit_suggestion { + diag.span_suggestion( + spawn_expr.span.shrink_to_hi(), + "try", + ".wait()", + Applicability::MaybeIncorrect, + ); + } else { + diag.note("consider calling `.wait()`"); + } + + diag.note("not doing so might leave behind zombie processes") + .note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning"); + }, + ); +} + +/// Checks if the given expression exits the process. +fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + fn_def_id(cx, expr).is_some_and(|fn_did| { + cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || match_def_path(cx, fn_did, &paths::ABORT) + }) +} + +#[derive(Debug)] +enum ExitPointState { + /// Still walking up to the expression that initiated the visitor. + WalkUpTo(HirId), + /// We're inside of a control flow construct (e.g. `if`, `match`, `loop`) + /// Within this, we shouldn't accept any `exit()` calls in here, but we can leave all of these + /// constructs later and still continue looking for an `exit()` call afterwards. Example: + /// ```ignore + /// Command::new("").spawn().unwrap(); + /// + /// if true { // depth=1 + /// if true { // depth=2 + /// match () { // depth=3 + /// () => loop { // depth=4 + /// + /// std::process::exit(); + /// ^^^^^^^^^^^^^^^^^^^^^ conditional exit call, ignored + /// + /// } // depth=3 + /// } // depth=2 + /// } // depth=1 + /// } // depth=0 + /// + /// std::process::exit(); + /// ^^^^^^^^^^^^^^^^^^^^^ this exit call is accepted because we're now unconditionally calling it + /// ``` + /// We can only get into this state from `NoExit`. + InControlFlow { depth: u32 }, + /// No exit call found yet, but looking for one. + NoExit, +} + +fn expr_enters_control_flow(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Loop(..)) +} + +struct ExitPointFinder<'a, 'tcx> { + state: ExitPointState, + cx: &'a LateContext<'tcx>, +} + +struct ExitCallFound; + +impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> { + type Result = ControlFlow; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { + match self.state { + ExitPointState::WalkUpTo(id) if expr.hir_id == id => { + self.state = ExitPointState::NoExit; + walk_expr(self, expr) + }, + ExitPointState::NoExit if expr_enters_control_flow(expr) => { + self.state = ExitPointState::InControlFlow { depth: 1 }; + walk_expr(self, expr)?; + if let ExitPointState::InControlFlow { .. } = self.state { + self.state = ExitPointState::NoExit; + } + Continue(()) + }, + ExitPointState::NoExit if is_exit_expression(self.cx, expr) => Break(ExitCallFound), + ExitPointState::InControlFlow { ref mut depth } if expr_enters_control_flow(expr) => { + *depth += 1; + walk_expr(self, expr)?; + match self.state { + ExitPointState::InControlFlow { depth: 1 } => self.state = ExitPointState::NoExit, + ExitPointState::InControlFlow { ref mut depth } => *depth -= 1, + _ => {}, + } + Continue(()) + }, + _ => Continue(()), + } + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a767798a9c36e..930fb9fb6f2d5 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,6 +4,7 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. +pub const ABORT: [&str; 3] = ["std", "process", "abort"]; pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ ["rustc_lint_defs", "Applicability", "Unspecified"], @@ -23,6 +24,9 @@ pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; +pub const CHILD: [&str; 3] = ["std", "process", "Child"]; +pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"]; +pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; pub const FILE_OPTIONS: [&str; 4] = ["std", "fs", "File", "options"]; diff --git a/tests/ui/suspicious_command_arg_space.fixed b/tests/ui/suspicious_command_arg_space.fixed index 5d7b1e0c17f25..704d6ea1bb837 100644 --- a/tests/ui/suspicious_command_arg_space.fixed +++ b/tests/ui/suspicious_command_arg_space.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::zombie_processes)] fn main() { // Things it should warn about: std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap(); diff --git a/tests/ui/suspicious_command_arg_space.rs b/tests/ui/suspicious_command_arg_space.rs index 8abd9803a0c6f..2a2a7557381c4 100644 --- a/tests/ui/suspicious_command_arg_space.rs +++ b/tests/ui/suspicious_command_arg_space.rs @@ -1,3 +1,4 @@ +#![allow(clippy::zombie_processes)] fn main() { // Things it should warn about: std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); diff --git a/tests/ui/suspicious_command_arg_space.stderr b/tests/ui/suspicious_command_arg_space.stderr index d2517b66b5661..6fd07d07d7be6 100644 --- a/tests/ui/suspicious_command_arg_space.stderr +++ b/tests/ui/suspicious_command_arg_space.stderr @@ -1,5 +1,5 @@ error: single argument that looks like it should be multiple arguments - --> tests/ui/suspicious_command_arg_space.rs:3:44 + --> tests/ui/suspicious_command_arg_space.rs:4:44 | LL | std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); | ^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap | ~~~~ ~~~~~~~~~~~~~~~ error: single argument that looks like it should be multiple arguments - --> tests/ui/suspicious_command_arg_space.rs:6:43 + --> tests/ui/suspicious_command_arg_space.rs:7:43 | LL | std::process::Command::new("cat").arg("--number file").spawn().unwrap(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/zombie_processes.rs b/tests/ui/zombie_processes.rs new file mode 100644 index 0000000000000..a2abc7fc3a179 --- /dev/null +++ b/tests/ui/zombie_processes.rs @@ -0,0 +1,138 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::if_same_then_else, clippy::ifs_same_cond)] + +use std::process::{Child, Command}; + +fn main() { + { + // Check that #[expect] works + #[expect(clippy::zombie_processes)] + let mut x = Command::new("").spawn().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + x.kill(); + x.id(); + } + { + let mut x = Command::new("").spawn().unwrap(); + x.wait().unwrap(); // OK + } + { + let x = Command::new("").spawn().unwrap(); + x.wait_with_output().unwrap(); // OK + } + { + let mut x = Command::new("").spawn().unwrap(); + x.try_wait().unwrap(); // OK + } + { + let mut x = Command::new("").spawn().unwrap(); + let mut r = &mut x; + r.wait().unwrap(); // OK, not calling `.wait()` directly on `x` but through `r` -> `x` + } + { + let mut x = Command::new("").spawn().unwrap(); + process_child(x); // OK, other function might call `.wait()` so assume it does + } + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + let v = &x; + // (allow shared refs is fine because one cannot call `.wait()` through that) + } + + // https://github.com/rust-lang/rust-clippy/pull/11476#issuecomment-1718456033 + // Unconditionally exiting the process in various ways (should not lint) + { + let mut x = Command::new("").spawn().unwrap(); + std::process::exit(0); + } + { + let mut x = Command::new("").spawn().unwrap(); + std::process::abort(); // same as above, but abort instead of exit + } + { + let mut x = Command::new("").spawn().unwrap(); + if true { /* nothing */ } + std::process::abort(); // still unconditionally exits + } + + // Conditionally exiting + // It should assume that it might not exit and still lint + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + std::process::exit(0); + } + } + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + while false {} + // Calling `exit()` after leaving a while loop should still be linted. + std::process::exit(0); + } + } + + { + let mut x = { Command::new("").spawn().unwrap() }; + x.wait().unwrap(); + } + + { + struct S { + c: Child, + } + + let mut s = S { + c: Command::new("").spawn().unwrap(), + }; + s.c.wait().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + return; + } + x.wait().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + x.wait().unwrap(); + } + } + + { + let mut x = Command::new("").spawn().unwrap(); + if true { + x.wait().unwrap(); + } else if true { + x.wait().unwrap(); + } else { + x.wait().unwrap(); + } + } + + { + let mut x = Command::new("").spawn().unwrap(); + if true { + x.wait().unwrap(); + return; + } + x.wait().unwrap(); + } +} + +fn process_child(c: Child) { + todo!() +} diff --git a/tests/ui/zombie_processes.stderr b/tests/ui/zombie_processes.stderr new file mode 100644 index 0000000000000..eec821a4c8f18 --- /dev/null +++ b/tests/ui/zombie_processes.stderr @@ -0,0 +1,64 @@ +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:14:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + = note: `-D clippy::zombie-processes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]` + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:41:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:66:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:73:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:99:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:108:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: aborting due to 6 previous errors + diff --git a/tests/ui/zombie_processes_fixable.fixed b/tests/ui/zombie_processes_fixable.fixed new file mode 100644 index 0000000000000..6045262f519a7 --- /dev/null +++ b/tests/ui/zombie_processes_fixable.fixed @@ -0,0 +1,26 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::needless_return)] + +use std::process::{Child, Command}; + +fn main() { + let _ = Command::new("").spawn().unwrap().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + Command::new("").spawn().unwrap().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait().unwrap(); // OK +} + +fn not_main() { + Command::new("").spawn().unwrap().wait(); +} + +fn spawn_proc() -> Child { + Command::new("").spawn().unwrap() +} + +fn spawn_proc_2() -> Child { + return Command::new("").spawn().unwrap(); +} diff --git a/tests/ui/zombie_processes_fixable.rs b/tests/ui/zombie_processes_fixable.rs new file mode 100644 index 0000000000000..e1ecb771641e2 --- /dev/null +++ b/tests/ui/zombie_processes_fixable.rs @@ -0,0 +1,26 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::needless_return)] + +use std::process::{Child, Command}; + +fn main() { + let _ = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait().unwrap(); // OK +} + +fn not_main() { + Command::new("").spawn().unwrap(); +} + +fn spawn_proc() -> Child { + Command::new("").spawn().unwrap() +} + +fn spawn_proc_2() -> Child { + return Command::new("").spawn().unwrap(); +} diff --git a/tests/ui/zombie_processes_fixable.stderr b/tests/ui/zombie_processes_fixable.stderr new file mode 100644 index 0000000000000..e1c40472c325e --- /dev/null +++ b/tests/ui/zombie_processes_fixable.stderr @@ -0,0 +1,40 @@ +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:7:13 + | +LL | let _ = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + = note: `-D clippy::zombie-processes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]` + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:9:5 + | +LL | Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:11:5 + | +LL | spawn_proc(); + | ^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:17:5 + | +LL | Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: aborting due to 4 previous errors + From 13e36cf8870a88ab0ac5651d9513b13085baa8cc Mon Sep 17 00:00:00 2001 From: Matthew Giordano Date: Tue, 27 Aug 2024 18:06:00 -0700 Subject: [PATCH 0146/1301] add new_cyclic_in for rc --- library/alloc/src/rc.rs | 60 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index f153aa6d3be9a..151eec532613c 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -687,6 +687,54 @@ impl Rc { } } + /// TODO: document + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_cyclic_in(data_fn: F, alloc: A) -> Rc + where + F: FnOnce(&Weak) -> T, + { + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let uninit_ptr: NonNull<_> = Box::leak( + Box::new_in(RcBox { + strong: Cell::new(0), + weak: Cell::new(1), + value: mem::MaybeUninit::::uninit(), + }), + alloc, + ) + .into(); + + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc: Global }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + let strong = unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(ptr::addr_of_mut!((*inner).value), data); + + let prev_value = (*inner).strong.get(); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + (*inner).strong.set(1); + + Rc::from_inner(init_ptr) + }; + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + mem::forget(weak); + strong + } + /// Constructs a new `Rc` with uninitialized contents in the provided allocator. /// /// # Examples @@ -1662,7 +1710,11 @@ impl Rc { #[inline] #[stable(feature = "rc_unique", since = "1.4.0")] pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if Rc::is_unique(this) { unsafe { Some(Rc::get_mut_unchecked(this)) } } else { None } + if Rc::is_unique(this) { + unsafe { Some(Rc::get_mut_unchecked(this)) } + } else { + None + } } /// Returns a mutable reference into the given `Rc`, @@ -3239,7 +3291,11 @@ impl Weak { #[must_use] #[stable(feature = "weak_counts", since = "1.41.0")] pub fn strong_count(&self) -> usize { - if let Some(inner) = self.inner() { inner.strong() } else { 0 } + if let Some(inner) = self.inner() { + inner.strong() + } else { + 0 + } } /// Gets the number of `Weak` pointers pointing to this allocation. From 53d5ccd5e965086f7d43d43a1eabdb7064780cb5 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 1 Aug 2024 18:42:45 -0500 Subject: [PATCH 0147/1301] refactor: use style edition when loading from partial config --- src/config/config_type.rs | 2 +- src/config/mod.rs | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 91efb71744b7a..4b83e974932fc 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -210,7 +210,7 @@ macro_rules! create_config { )+ #[allow(unreachable_pub)] - pub fn default_with_style_edition(style_edition: StyleEdition) -> Config { + pub(super) fn default_with_style_edition(style_edition: StyleEdition) -> Config { Config { $( $i: ( diff --git a/src/config/mod.rs b/src/config/mod.rs index 5d6047c385d65..6462c62a35809 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -217,9 +217,37 @@ impl PartialConfig { ::toml::to_string(&cloned).map_err(ToTomlError) } + + pub(super) fn to_parsed_config( + self, + style_edition_override: Option, + edition_override: Option, + dir: &Path, + ) -> Config { + Config::default_for_possible_style_edition( + style_edition_override.or(self.style_edition), + edition_override.or(self.edition), + ) + .fill_from_parsed_config(self, dir) + } } impl Config { + pub fn default_for_possible_style_edition( + style_edition: Option, + edition: Option, + ) -> Config { + style_edition.map_or_else( + || { + edition.map_or_else( + || Config::default(), + |e| Self::default_with_style_edition(e.into()), + ) + }, + |se| Self::default_with_style_edition(se), + ) + } + pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { let version = env!("CARGO_PKG_VERSION"); @@ -324,12 +352,13 @@ impl Config { err.push_str(msg) } } - match parsed.try_into() { + + match parsed.try_into::() { Ok(parsed_config) => { if !err.is_empty() { eprint!("{err}"); } - Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) + Ok(parsed_config.to_parsed_config(None, None, dir)) } Err(e) => { err.push_str("Error: Decoding config file failed:\n"); From c0c3dc7a25f6e4079b41acf46a513e574d9f2855 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 1 Aug 2024 22:14:23 -0500 Subject: [PATCH 0148/1301] refactor: include edition & style edition in CliOptions --- src/bin/main.rs | 12 ++++++++++++ src/config/mod.rs | 10 +++++++--- src/config/options.rs | 2 ++ src/git-rustfmt/main.rs | 6 ++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 1185454c8e7d1..2e4877c7569d5 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -734,6 +734,18 @@ impl CliOptions for GetOptsOptions { fn config_path(&self) -> Option<&Path> { self.config_path.as_deref() } + + fn edition(&self) -> Option { + self.inline_config + .get("edition") + .map_or(self.edition, |e| Edition::from_str(e).ok()) + } + + fn style_edition(&self) -> Option { + self.inline_config + .get("style_edition") + .map_or(self.style_edition, |se| StyleEdition::from_str(se).ok()) + } } fn edition_from_edition_str(edition_str: &str) -> Result { diff --git a/src/config/mod.rs b/src/config/mod.rs index 6462c62a35809..3b77902fa2ad9 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -376,9 +376,13 @@ pub fn load_config( file_path: Option<&Path>, options: Option, ) -> Result<(Config, Option), Error> { - let over_ride = match options { - Some(ref opts) => config_path(opts)?, - None => None, + let (over_ride, _edition, _style_edition) = match options { + Some(ref opts) => ( + config_path(opts)?, + opts.edition(), + opts.style_edition(), + ), + None => (None, None, None), }; let result = if let Some(over_ride) = over_ride { diff --git a/src/config/options.rs b/src/config/options.rs index 40cfafed29d2e..f7a8c114330f7 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -419,6 +419,8 @@ pub trait CliOptions { /// It is ok if the returned path doesn't exist or is not canonicalized /// (i.e. the callers are expected to handle such cases). fn config_path(&self) -> Option<&Path>; + fn edition(&self) -> Option; + fn style_edition(&self) -> Option; } /// The edition of the syntax and semantics of code (RFC 2052). diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index 5674f40bef91f..14ac81322b9e5 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -89,6 +89,12 @@ impl CliOptions for NullOptions { fn config_path(&self) -> Option<&Path> { unreachable!(); } + fn edition(&self) -> Option { + unreachable!(); + } + fn style_edition(&self) -> Option { + unreachable!(); + } } fn uncommitted_files() -> Vec { From 0c6515cacc4465ae29d90f4bacc7f95ace7afe0b Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 1 Aug 2024 23:17:26 -0500 Subject: [PATCH 0149/1301] feat: support style edition differing defaults in config loading --- src/config/mod.rs | 61 ++++++++++++++++++++++++++++++++++------------- src/test/mod.rs | 55 +++++++++++++++++++++++++++++++++++------- 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 3b77902fa2ad9..da7adea5a7494 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -271,12 +271,21 @@ impl Config { /// /// Returns a `Config` if the config could be read and parsed from /// the file, otherwise errors. - pub(super) fn from_toml_path(file_path: &Path) -> Result { + pub(super) fn from_toml_path( + file_path: &Path, + edition: Option, + style_edition: Option, + ) -> Result { let mut file = File::open(&file_path)?; let mut toml = String::new(); file.read_to_string(&mut toml)?; - Config::from_toml(&toml, file_path.parent().unwrap()) - .map_err(|err| Error::new(ErrorKind::InvalidData, err)) + Config::from_toml_for_style_edition( + &toml, + file_path.parent().unwrap(), + edition, + style_edition, + ) + .map_err(|err| Error::new(ErrorKind::InvalidData, err)) } /// Resolves the config for input in `dir`. @@ -288,7 +297,11 @@ impl Config { /// /// Returns the `Config` to use, and the path of the project file if there was /// one. - pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option), Error> { + pub(super) fn from_resolved_toml_path( + dir: &Path, + edition: Option, + style_edition: Option, + ) -> Result<(Config, Option), Error> { /// Try to find a project file in the given directory and its parents. /// Returns the path of the nearest project file if one exists, /// or `None` if no project file was found. @@ -333,12 +346,26 @@ impl Config { } match resolve_project_file(dir)? { - None => Ok((Config::default(), None)), - Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))), + None => Ok(( + Config::default_for_possible_style_edition(style_edition, edition), + None, + )), + Some(path) => Config::from_toml_path(&path, edition, style_edition) + .map(|config| (config, Some(path))), } } - pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result { + #[allow(dead_code)] + pub(super) fn from_toml(toml: &str, dir: &Path) -> Result { + Self::from_toml_for_style_edition(toml, dir, None, None) + } + + pub(crate) fn from_toml_for_style_edition( + toml: &str, + dir: &Path, + edition: Option, + style_edition: Option, + ) -> Result { let parsed: ::toml::Value = toml .parse() .map_err(|e| format!("Could not parse TOML: {}", e))?; @@ -358,7 +385,7 @@ impl Config { if !err.is_empty() { eprint!("{err}"); } - Ok(parsed_config.to_parsed_config(None, None, dir)) + Ok(parsed_config.to_parsed_config(style_edition, edition, dir)) } Err(e) => { err.push_str("Error: Decoding config file failed:\n"); @@ -376,21 +403,21 @@ pub fn load_config( file_path: Option<&Path>, options: Option, ) -> Result<(Config, Option), Error> { - let (over_ride, _edition, _style_edition) = match options { - Some(ref opts) => ( - config_path(opts)?, - opts.edition(), - opts.style_edition(), - ), + let (over_ride, edition, style_edition) = match options { + Some(ref opts) => (config_path(opts)?, opts.edition(), opts.style_edition()), None => (None, None, None), }; let result = if let Some(over_ride) = over_ride { - Config::from_toml_path(over_ride.as_ref()).map(|p| (p, Some(over_ride.to_owned()))) + Config::from_toml_path(over_ride.as_ref(), edition, style_edition) + .map(|p| (p, Some(over_ride.to_owned()))) } else if let Some(file_path) = file_path { - Config::from_resolved_toml_path(file_path) + Config::from_resolved_toml_path(file_path, edition, style_edition) } else { - Ok((Config::default(), None)) + Ok(( + Config::default_for_possible_style_edition(style_edition, edition), + None, + )) }; result.map(|(mut c, p)| { diff --git a/src/test/mod.rs b/src/test/mod.rs index 7c563801c3243..96706efb16158 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -6,14 +6,17 @@ use std::iter::Peekable; use std::mem; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::str::Chars; +use std::str::{Chars, FromStr}; use std::thread; use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle}; use crate::formatting::{ReportedErrors, SourceFile}; use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter}; use crate::source_file; -use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session}; +use crate::{ + is_nightly_channel, Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, + StyleEdition, +}; use rustfmt_config_proc_macro::nightly_only_test; @@ -710,13 +713,22 @@ fn print_mismatches String>( fn read_config(filename: &Path) -> Config { let sig_comments = read_significant_comments(filename); + let (edition, style_edition) = get_editions_from_comments(&sig_comments); // Look for a config file. If there is a 'config' property in the significant comments, use // that. Otherwise, if there are no significant comments at all, look for a config file with // the same name as the test file. let mut config = if !sig_comments.is_empty() { - get_config(sig_comments.get("config").map(Path::new)) + get_config( + sig_comments.get("config").map(Path::new), + edition, + style_edition, + ) } else { - get_config(filename.with_extension("toml").file_name().map(Path::new)) + get_config( + filename.with_extension("toml").file_name().map(Path::new), + edition, + style_edition, + ) }; for (key, val) in &sig_comments { @@ -747,13 +759,28 @@ enum IdempotentCheckError { Parse, } +fn get_editions_from_comments( + comments: &HashMap, +) -> (Option, Option) { + ( + comments + .get("edition") + .map(|e| Edition::from_str(e).expect(&format!("invalid edition value: '{}'", e))), + comments.get("style_edition").map(|se| { + StyleEdition::from_str(se).expect(&format!("invalid style_edition value: '{}'", se)) + }), + ) +} + fn idempotent_check( filename: &PathBuf, opt_config: &Option, ) -> Result { let sig_comments = read_significant_comments(filename); let config = if let Some(ref config_file_path) = opt_config { - Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found") + let (edition, style_edition) = get_editions_from_comments(&sig_comments); + Config::from_toml_path(config_file_path, edition, style_edition) + .expect("`rustfmt.toml` not found") } else { read_config(filename) }; @@ -777,14 +804,18 @@ fn idempotent_check( // Reads test config file using the supplied (optional) file name. If there's no file name or the // file doesn't exist, just return the default config. Otherwise, the file must be read // successfully. -fn get_config(config_file: Option<&Path>) -> Config { +fn get_config( + config_file: Option<&Path>, + edition: Option, + style_edition: Option, +) -> Config { let config_file_name = match config_file { - None => return Default::default(), + None => return Config::default_for_possible_style_edition(style_edition, edition), Some(file_name) => { let mut full_path = PathBuf::from("tests/config/"); full_path.push(file_name); if !full_path.exists() { - return Default::default(); + return Config::default_for_possible_style_edition(style_edition, edition); }; full_path } @@ -796,7 +827,13 @@ fn get_config(config_file: Option<&Path>) -> Config { .read_to_string(&mut def_config) .expect("Couldn't read config"); - Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML") + Config::from_toml_for_style_edition( + &def_config, + Path::new("tests/config/"), + edition, + style_edition, + ) + .expect("invalid TOML") } // Reads significant comments of the form: `// rustfmt-key: value` into a hash map. From 065258659db720bfbb4987fa56137450e63d3cc4 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Thu, 1 Aug 2024 23:26:00 -0500 Subject: [PATCH 0150/1301] feat: implement 2024 Style Edition for expr overflows --- rustfmt.toml | 1 + src/bin/main.rs | 32 ++++ src/config/mod.rs | 2 +- src/config/options.rs | 2 +- .../style-edition/overrides/rustfmt.toml | 2 + .../style_edition/overflow_delim_expr_2015.rs | 155 ++++++++++++++++++ .../style_edition/overflow_delim_expr_2024.rs | 155 ++++++++++++++++++ .../style_edition/overflow_delim_expr_2015.rs | 135 +++++++++++++++ .../style_edition/overflow_delim_expr_2024.rs | 120 ++++++++++++++ 9 files changed, 602 insertions(+), 2 deletions(-) create mode 100644 tests/config/style-edition/overrides/rustfmt.toml create mode 100644 tests/source/configs/style_edition/overflow_delim_expr_2015.rs create mode 100644 tests/source/configs/style_edition/overflow_delim_expr_2024.rs create mode 100644 tests/target/configs/style_edition/overflow_delim_expr_2015.rs create mode 100644 tests/target/configs/style_edition/overflow_delim_expr_2024.rs diff --git a/rustfmt.toml b/rustfmt.toml index 52e4d728b641e..86447eac2e67b 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,4 @@ error_on_line_overflow = true error_on_unformatted = true style_edition = "2024" +overflow_delimited_expr = false diff --git a/src/bin/main.rs b/src/bin/main.rs index 2e4877c7569d5..9e7476b1f8127 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -915,4 +915,36 @@ mod test { let config = get_config(config_file, Some(options)); assert_eq!(config.style_edition(), StyleEdition::Edition2021); } + + #[nightly_only_test] + #[test] + fn correct_defaults_for_style_edition_loaded() { + let mut options = GetOptsOptions::default(); + options.style_edition = Some(StyleEdition::Edition2024); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); + } + + #[nightly_only_test] + #[test] + fn style_edition_defaults_overridden_from_config() { + let options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/overrides")); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), false); + } + + #[nightly_only_test] + #[test] + fn style_edition_defaults_overridden_from_cli() { + let mut options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/just-style-edition")); + options.inline_config = + HashMap::from([("overflow_delimited_expr".to_owned(), "false".to_owned())]); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), false); + } } diff --git a/src/config/mod.rs b/src/config/mod.rs index da7adea5a7494..cd6870e38902b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -828,7 +828,7 @@ binop_separator = "Front" remove_nested_parens = true combine_control_expr = true short_array_element_width_threshold = 10 -overflow_delimited_expr = false +overflow_delimited_expr = true struct_field_align_threshold = 0 enum_discrim_align_threshold = 0 match_arm_blocks = true diff --git a/src/config/options.rs b/src/config/options.rs index f7a8c114330f7..46db5186b116e 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -627,7 +627,7 @@ config_option_with_style_edition_default!( RemoveNestedParens, bool, _ => true; CombineControlExpr, bool, _ => true; ShortArrayElementWidthThreshold, usize, _ => 10; - OverflowDelimitedExpr, bool, _ => false; + OverflowDelimitedExpr, bool, Edition2024 => true, _ => false; StructFieldAlignThreshold, usize, _ => 0; EnumDiscrimAlignThreshold, usize, _ => 0; MatchArmBlocks, bool, _ => true; diff --git a/tests/config/style-edition/overrides/rustfmt.toml b/tests/config/style-edition/overrides/rustfmt.toml new file mode 100644 index 0000000000000..24205692b1f5f --- /dev/null +++ b/tests/config/style-edition/overrides/rustfmt.toml @@ -0,0 +1,2 @@ +style_edition = "2024" +overflow_delimited_expr = false diff --git a/tests/source/configs/style_edition/overflow_delim_expr_2015.rs b/tests/source/configs/style_edition/overflow_delim_expr_2015.rs new file mode 100644 index 0000000000000..5cb4a870fc10a --- /dev/null +++ b/tests/source/configs/style_edition/overflow_delim_expr_2015.rs @@ -0,0 +1,155 @@ +// rustfmt-style_edition: 2015 + +fn combine_blocklike() { + do_thing( + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing( + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + ( + 1, + 2, + 3, + |param| { + action(); + foo(param) + }, + ), + ); +} + +fn combine_struct_sample() { + let identity = verify( + &ctx, + VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + }, + )?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount( + "/", + routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ], + ) + .launch(); +} diff --git a/tests/source/configs/style_edition/overflow_delim_expr_2024.rs b/tests/source/configs/style_edition/overflow_delim_expr_2024.rs new file mode 100644 index 0000000000000..66c95e71aa9a1 --- /dev/null +++ b/tests/source/configs/style_edition/overflow_delim_expr_2024.rs @@ -0,0 +1,155 @@ +// rustfmt-style_edition: 2024 + +fn combine_blocklike() { + do_thing( + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing( + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + ( + 1, + 2, + 3, + |param| { + action(); + foo(param) + }, + ), + ); +} + +fn combine_struct_sample() { + let identity = verify( + &ctx, + VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + }, + )?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount( + "/", + routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ], + ) + .launch(); +} diff --git a/tests/target/configs/style_edition/overflow_delim_expr_2015.rs b/tests/target/configs/style_edition/overflow_delim_expr_2015.rs new file mode 100644 index 0000000000000..05d4b8b6d334f --- /dev/null +++ b/tests/target/configs/style_edition/overflow_delim_expr_2015.rs @@ -0,0 +1,135 @@ +// rustfmt-style_edition: 2015 + +fn combine_blocklike() { + do_thing(|param| { + action(); + foo(param) + }); + + do_thing(x, |param| { + action(); + foo(param) + }); + + do_thing( + x, + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing(Bar { + x: value, + y: value2, + }); + + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing(&[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing(vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + (1, 2, 3, |param| { + action(); + foo(param) + }), + ); +} + +fn combine_struct_sample() { + let identity = verify( + &ctx, + VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + }, + )?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount( + "/", + routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ], + ) + .launch(); +} diff --git a/tests/target/configs/style_edition/overflow_delim_expr_2024.rs b/tests/target/configs/style_edition/overflow_delim_expr_2024.rs new file mode 100644 index 0000000000000..ecd2e8ca79708 --- /dev/null +++ b/tests/target/configs/style_edition/overflow_delim_expr_2024.rs @@ -0,0 +1,120 @@ +// rustfmt-style_edition: 2024 + +fn combine_blocklike() { + do_thing(|param| { + action(); + foo(param) + }); + + do_thing(x, |param| { + action(); + foo(param) + }); + + do_thing( + x, + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing(Bar { + x: value, + y: value2, + }); + + do_thing(x, Bar { + x: value, + y: value2, + }); + + do_thing( + x, + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing(&[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing(x, &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing(vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing(x, vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + (1, 2, 3, |param| { + action(); + foo(param) + }), + ); +} + +fn combine_struct_sample() { + let identity = verify(&ctx, VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + })?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount("/", routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ]) + .launch(); +} From 46cb7d322055625c07823dea8fdfad4c07f98754 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 14 Aug 2024 16:05:24 -0500 Subject: [PATCH 0151/1301] refactor: improve mapping of legacy 'version' to 'style_edition' --- src/bin/main.rs | 21 ++++++- src/config/config_type.rs | 25 -------- src/config/mod.rs | 58 +++++++++++++------ src/config/options.rs | 1 + src/git-rustfmt/main.rs | 7 ++- src/lib.rs | 2 +- src/test/mod.rs | 21 ++++--- .../style-edition/just-version/rustfmt.toml | 1 + 8 files changed, 82 insertions(+), 54 deletions(-) create mode 100644 tests/config/style-edition/just-version/rustfmt.toml diff --git a/src/bin/main.rs b/src/bin/main.rs index 9e7476b1f8127..14299434bc747 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -19,7 +19,7 @@ use getopts::{Matches, Options}; use crate::rustfmt::{ load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, - FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, + FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, }; const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug"; @@ -746,6 +746,13 @@ impl CliOptions for GetOptsOptions { .get("style_edition") .map_or(self.style_edition, |se| StyleEdition::from_str(se).ok()) } + + fn version(&self) -> Option { + self.inline_config + .get("version") + .map(|version| Version::from_str(version).ok()) + .flatten() + } } fn edition_from_edition_str(edition_str: &str) -> Result { @@ -814,6 +821,17 @@ mod test { options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]); let config = get_config(None, Some(options)); assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); + } + + #[nightly_only_test] + #[test] + fn version_config_file_sets_style_edition_override_correctly() { + let options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/just-version")); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); } #[nightly_only_test] @@ -858,6 +876,7 @@ mod test { ]); let config = get_config(None, Some(options)); assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); } #[nightly_only_test] diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 4b83e974932fc..14217caba0afe 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -134,7 +134,6 @@ macro_rules! create_config { "fn_args_layout" => self.0.set_fn_args_layout(), "hide_parse_errors" => self.0.set_hide_parse_errors(), "version" => self.0.set_version(), - "edition" => self.0.set_edition(), &_ => (), } } @@ -165,7 +164,6 @@ macro_rules! create_config { "fn_args_layout" => self.0.set_fn_args_layout(), "hide_parse_errors" => self.0.set_hide_parse_errors(), "version" => self.0.set_version(), - "edition" => self.0.set_edition(), &_ => (), } } @@ -264,7 +262,6 @@ macro_rules! create_config { self.set_fn_args_layout(); self.set_hide_parse_errors(); self.set_version(); - self.set_edition(); self } @@ -367,7 +364,6 @@ macro_rules! create_config { "fn_args_layout" => self.set_fn_args_layout(), "hide_parse_errors" => self.set_hide_parse_errors(), "version" => self.set_version(), - "edition" => self.set_edition(), &_ => (), } } @@ -585,30 +581,9 @@ macro_rules! create_config { option which takes precedence. \ The value of the `version` option will be ignored." ); - } else if matches!(self.version(), Version::Two) { - self.style_edition.2 = StyleEdition::Edition2024; - } else { - self.style_edition.2 = StyleEdition::Edition2015; } } - fn set_edition(&mut self) { - let style_edition_set = self.was_set().style_edition() - || self.was_set_cli().style_edition(); - - if style_edition_set || self.was_set().version() { - return; - } - - // User has explicitly specified an Edition value without - // explicitly specifying a Style Edition value, so the Style Edition - // must default to whatever value was provided for Edition - // as per: https://rust-lang.github.io/rfcs/3338-style-evolution.html#explanation - self.style_edition.2 = self.edition().into(); - } - - - #[allow(unreachable_pub)] /// Returns `true` if the config key was explicitly set and is the default value. pub fn is_default(&self, key: &str) -> bool { diff --git a/src/config/mod.rs b/src/config/mod.rs index cd6870e38902b..8a95cc55fcbb2 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -222,11 +222,13 @@ impl PartialConfig { self, style_edition_override: Option, edition_override: Option, + version_override: Option, dir: &Path, ) -> Config { Config::default_for_possible_style_edition( style_edition_override.or(self.style_edition), edition_override.or(self.edition), + version_override.or(self.version), ) .fill_from_parsed_config(self, dir) } @@ -236,16 +238,25 @@ impl Config { pub fn default_for_possible_style_edition( style_edition: Option, edition: Option, + version: Option, ) -> Config { - style_edition.map_or_else( - || { - edition.map_or_else( - || Config::default(), - |e| Self::default_with_style_edition(e.into()), - ) - }, - |se| Self::default_with_style_edition(se), - ) + // Ensures the configuration defaults associated with Style Editions + // follow the precedence set in + // https://rust-lang.github.io/rfcs/3338-style-evolution.html + // 'version' is a legacy alias for 'style_edition' that we'll support + // for some period of time + // FIXME(calebcartwright) - remove 'version' at some point + match (style_edition, version, edition) { + (Some(se), _, _) => Self::default_with_style_edition(se), + (None, Some(Version::Two), _) => { + Self::default_with_style_edition(StyleEdition::Edition2024) + } + (None, Some(Version::One), _) => { + Self::default_with_style_edition(StyleEdition::Edition2015) + } + (None, None, Some(e)) => Self::default_with_style_edition(e.into()), + (None, None, None) => Config::default(), + } } pub(crate) fn version_meets_requirement(&self) -> bool { @@ -275,6 +286,7 @@ impl Config { file_path: &Path, edition: Option, style_edition: Option, + version: Option, ) -> Result { let mut file = File::open(&file_path)?; let mut toml = String::new(); @@ -284,6 +296,7 @@ impl Config { file_path.parent().unwrap(), edition, style_edition, + version, ) .map_err(|err| Error::new(ErrorKind::InvalidData, err)) } @@ -301,6 +314,7 @@ impl Config { dir: &Path, edition: Option, style_edition: Option, + version: Option, ) -> Result<(Config, Option), Error> { /// Try to find a project file in the given directory and its parents. /// Returns the path of the nearest project file if one exists, @@ -347,17 +361,17 @@ impl Config { match resolve_project_file(dir)? { None => Ok(( - Config::default_for_possible_style_edition(style_edition, edition), + Config::default_for_possible_style_edition(style_edition, edition, version), None, )), - Some(path) => Config::from_toml_path(&path, edition, style_edition) + Some(path) => Config::from_toml_path(&path, edition, style_edition, version) .map(|config| (config, Some(path))), } } #[allow(dead_code)] pub(super) fn from_toml(toml: &str, dir: &Path) -> Result { - Self::from_toml_for_style_edition(toml, dir, None, None) + Self::from_toml_for_style_edition(toml, dir, None, None, None) } pub(crate) fn from_toml_for_style_edition( @@ -365,6 +379,7 @@ impl Config { dir: &Path, edition: Option, style_edition: Option, + version: Option, ) -> Result { let parsed: ::toml::Value = toml .parse() @@ -385,7 +400,7 @@ impl Config { if !err.is_empty() { eprint!("{err}"); } - Ok(parsed_config.to_parsed_config(style_edition, edition, dir)) + Ok(parsed_config.to_parsed_config(style_edition, edition, version, dir)) } Err(e) => { err.push_str("Error: Decoding config file failed:\n"); @@ -403,19 +418,24 @@ pub fn load_config( file_path: Option<&Path>, options: Option, ) -> Result<(Config, Option), Error> { - let (over_ride, edition, style_edition) = match options { - Some(ref opts) => (config_path(opts)?, opts.edition(), opts.style_edition()), - None => (None, None, None), + let (over_ride, edition, style_edition, version) = match options { + Some(ref opts) => ( + config_path(opts)?, + opts.edition(), + opts.style_edition(), + opts.version(), + ), + None => (None, None, None, None), }; let result = if let Some(over_ride) = over_ride { - Config::from_toml_path(over_ride.as_ref(), edition, style_edition) + Config::from_toml_path(over_ride.as_ref(), edition, style_edition, version) .map(|p| (p, Some(over_ride.to_owned()))) } else if let Some(file_path) = file_path { - Config::from_resolved_toml_path(file_path, edition, style_edition) + Config::from_resolved_toml_path(file_path, edition, style_edition, version) } else { Ok(( - Config::default_for_possible_style_edition(style_edition, edition), + Config::default_for_possible_style_edition(style_edition, edition, version), None, )) }; diff --git a/src/config/options.rs b/src/config/options.rs index 46db5186b116e..b9e79b9a7de59 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -421,6 +421,7 @@ pub trait CliOptions { fn config_path(&self) -> Option<&Path>; fn edition(&self) -> Option; fn style_edition(&self) -> Option; + fn version(&self) -> Option; } /// The edition of the syntax and semantics of code (RFC 2052). diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index 14ac81322b9e5..b5a71588e9e4e 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -15,7 +15,9 @@ use getopts::{Matches, Options}; use rustfmt_nightly as rustfmt; use tracing_subscriber::EnvFilter; -use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; +use crate::rustfmt::{ + load_config, CliOptions, FormatReportFormatterBuilder, Input, Session, Version, +}; fn prune_files(files: Vec<&str>) -> Vec<&str> { let prefixes: Vec<_> = files @@ -95,6 +97,9 @@ impl CliOptions for NullOptions { fn style_edition(&self) -> Option { unreachable!(); } + fn version(&self) -> Option { + unreachable!(); + } } fn uncommitted_files() -> Vec { diff --git a/src/lib.rs b/src/lib.rs index 1e9efc5ce946c..7dc698dd92c42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ use crate::utils::indent_next_line; pub use crate::config::{ load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, - Range, StyleEdition, Verbosity, + Range, StyleEdition, Verbosity, Version, }; pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; diff --git a/src/test/mod.rs b/src/test/mod.rs index 96706efb16158..4d22f64f3526c 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -15,7 +15,7 @@ use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChu use crate::source_file; use crate::{ is_nightly_channel, Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, - StyleEdition, + StyleEdition, Version, }; use rustfmt_config_proc_macro::nightly_only_test; @@ -713,7 +713,7 @@ fn print_mismatches String>( fn read_config(filename: &Path) -> Config { let sig_comments = read_significant_comments(filename); - let (edition, style_edition) = get_editions_from_comments(&sig_comments); + let (edition, style_edition, version) = get_editions_from_comments(&sig_comments); // Look for a config file. If there is a 'config' property in the significant comments, use // that. Otherwise, if there are no significant comments at all, look for a config file with // the same name as the test file. @@ -722,12 +722,14 @@ fn read_config(filename: &Path) -> Config { sig_comments.get("config").map(Path::new), edition, style_edition, + version, ) } else { get_config( filename.with_extension("toml").file_name().map(Path::new), edition, style_edition, + version, ) }; @@ -761,7 +763,7 @@ enum IdempotentCheckError { fn get_editions_from_comments( comments: &HashMap, -) -> (Option, Option) { +) -> (Option, Option, Option) { ( comments .get("edition") @@ -769,6 +771,9 @@ fn get_editions_from_comments( comments.get("style_edition").map(|se| { StyleEdition::from_str(se).expect(&format!("invalid style_edition value: '{}'", se)) }), + comments + .get("version") + .map(|v| Version::from_str(v).expect(&format!("invalid version value: '{}'", v))), ) } @@ -778,8 +783,8 @@ fn idempotent_check( ) -> Result { let sig_comments = read_significant_comments(filename); let config = if let Some(ref config_file_path) = opt_config { - let (edition, style_edition) = get_editions_from_comments(&sig_comments); - Config::from_toml_path(config_file_path, edition, style_edition) + let (edition, style_edition, version) = get_editions_from_comments(&sig_comments); + Config::from_toml_path(config_file_path, edition, style_edition, version) .expect("`rustfmt.toml` not found") } else { read_config(filename) @@ -808,14 +813,15 @@ fn get_config( config_file: Option<&Path>, edition: Option, style_edition: Option, + version: Option, ) -> Config { let config_file_name = match config_file { - None => return Config::default_for_possible_style_edition(style_edition, edition), + None => return Config::default_for_possible_style_edition(style_edition, edition, version), Some(file_name) => { let mut full_path = PathBuf::from("tests/config/"); full_path.push(file_name); if !full_path.exists() { - return Config::default_for_possible_style_edition(style_edition, edition); + return Config::default_for_possible_style_edition(style_edition, edition, version); }; full_path } @@ -832,6 +838,7 @@ fn get_config( Path::new("tests/config/"), edition, style_edition, + version, ) .expect("invalid TOML") } diff --git a/tests/config/style-edition/just-version/rustfmt.toml b/tests/config/style-edition/just-version/rustfmt.toml new file mode 100644 index 0000000000000..1082fd8888942 --- /dev/null +++ b/tests/config/style-edition/just-version/rustfmt.toml @@ -0,0 +1 @@ +version = "Two" From 36c126f94a689e12ea5b5d0e06d55cdbce26ea8d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:25:03 +0000 Subject: [PATCH 0152/1301] Rustup to rustc 1.82.0-nightly (1f12b9b0f 2024-08-27) --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index d4781c01c8f44..2c76d34b65d3c 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-08-22" +channel = "nightly-2024-08-28" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" From 7e4cafb653980b4dc1ca95eb67df8ac4608eab61 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:21:24 +0000 Subject: [PATCH 0153/1301] Fix rustc test suite --- scripts/test_rustc_tests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index bb5af9127b96e..fcfbaf053deec 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -59,6 +59,7 @@ rm -r tests/run-make/mismatching-target-triples # same rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't work entirely correctly rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported rm tests/ui/simd/simd-bitmask-notpow2.rs # non-pow-of-2 simd vector sizes +rm -r tests/run-make/embed-source-dwarf # embedding sources in debuginfo # requires LTO rm -r tests/run-make/cdylib @@ -75,6 +76,8 @@ rm -r tests/ui/instrument-coverage/ # missing f16/f128 support rm tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs +rm tests/ui/asm/aarch64/type-f16.rs +rm tests/ui/consts/const-float-bits-conv.rs # optimization tests # ================== @@ -138,6 +141,7 @@ rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation war rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue rm tests/ui/backtrace/synchronized-panic-handler.rs # missing needs-unwind annotation rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables +rm -r tests/run-make/libtest-thread-limit # doesn't work with -Zpanic-abort-tests, missing needs-unwind annotation rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd From 648545276aa656019cd875e7bce4bd15509c3bd1 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 19:03:00 +0200 Subject: [PATCH 0154/1301] Make cargo_ workspace again --- .../crates/rust-analyzer/src/cli/scip.rs | 2 +- .../crates/rust-analyzer/src/config.rs | 275 +++++++++--------- .../crates/rust-analyzer/src/global_state.rs | 10 + .../src/handlers/notification.rs | 30 +- .../rust-analyzer/src/handlers/request.rs | 6 +- .../crates/rust-analyzer/src/main_loop.rs | 6 +- .../crates/rust-analyzer/src/reload.rs | 16 +- .../crates/rust-analyzer/src/target_spec.rs | 2 +- 8 files changed, 190 insertions(+), 157 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index a8a02712fe291..01bde39579a5e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -51,7 +51,7 @@ impl flags::Scip { // FIXME @alibektas : What happens to errors without logging? error!(?error_sink, "Config Error(s)"); } - let cargo_config = config.cargo(); + let cargo_config = config.cargo(None); let (db, vfs, _) = load_workspace_at( root.as_path().as_ref(), &cargo_config, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 2889af844b14e..9a8075043ed99 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -76,87 +76,6 @@ config_data! { /// How many worker threads to handle priming caches. The default `0` means to pick automatically. cachePriming_numThreads: NumThreads = NumThreads::Physical, - /// Pass `--all-targets` to cargo invocation. - cargo_allTargets: bool = true, - /// Automatically refresh project info via `cargo metadata` on - /// `Cargo.toml` or `.cargo/config.toml` changes. - pub(crate) cargo_autoreload: bool = true, - /// Run build scripts (`build.rs`) for more precise code analysis. - cargo_buildScripts_enable: bool = true, - /// Specifies the invocation strategy to use when running the build scripts command. - /// If `per_workspace` is set, the command will be executed for each Rust workspace with the - /// workspace as the working directory. - /// If `once` is set, the command will be executed once with the opened project as the - /// working directory. - /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` - /// is set. - cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, - /// Override the command rust-analyzer uses to run build scripts and - /// build procedural macros. The command is required to output json - /// and should therefore include `--message-format=json` or a similar - /// option. - /// - /// If there are multiple linked projects/workspaces, this command is invoked for - /// each of them, with the working directory being the workspace root - /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten - /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. - /// - /// By default, a cargo invocation will be constructed for the configured - /// targets and features, with the following base command line: - /// - /// ```bash - /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going - /// ``` - /// . - cargo_buildScripts_overrideCommand: Option> = None, - /// Rerun proc-macros building/build-scripts running when proc-macro - /// or build-script sources change and are saved. - cargo_buildScripts_rebuildOnSave: bool = true, - /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to - /// avoid checking unnecessary things. - cargo_buildScripts_useRustcWrapper: bool = true, - /// List of cfg options to enable with the given values. - cargo_cfgs: FxHashMap> = { - let mut m = FxHashMap::default(); - m.insert("debug_assertions".to_owned(), None); - m.insert("miri".to_owned(), None); - m - }, - /// Extra arguments that are passed to every cargo invocation. - cargo_extraArgs: Vec = vec![], - /// Extra environment variables that will be set when running cargo, rustc - /// or other commands within the workspace. Useful for setting RUSTFLAGS. - cargo_extraEnv: FxHashMap = FxHashMap::default(), - /// List of features to activate. - /// - /// Set this to `"all"` to pass `--all-features` to cargo. - cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), - /// Whether to pass `--no-default-features` to cargo. - cargo_noDefaultFeatures: bool = false, - /// Relative path to the sysroot, or "discover" to try to automatically find it via - /// "rustc --print sysroot". - /// - /// Unsetting this disables sysroot loading. - /// - /// This option does not take effect until rust-analyzer is restarted. - cargo_sysroot: Option = Some("discover".to_owned()), - /// Relative path to the sysroot library sources. If left unset, this will default to - /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. - /// - /// This option does not take effect until rust-analyzer is restarted. - cargo_sysrootSrc: Option = None, - /// Compilation target override (target triple). - // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work - // than `checkOnSave_target` - cargo_target: Option = None, - /// Optional path to a rust-analyzer specific target directory. - /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro - /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts. - /// - /// Set to `true` to use a subdirectory of the existing target directory or - /// set to a path relative to the workspace to use that path. - cargo_targetDir | rust_analyzerTargetDir: Option = None, - /// Run the check command for diagnostics on save. checkOnSave | checkOnSave_enable: bool = true, @@ -430,6 +349,88 @@ config_data! { config_data! { workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { + +/// Pass `--all-targets` to cargo invocation. + cargo_allTargets: bool = true, + /// Automatically refresh project info via `cargo metadata` on + /// `Cargo.toml` or `.cargo/config.toml` changes. + pub(crate) cargo_autoreload: bool = true, + /// Run build scripts (`build.rs`) for more precise code analysis. + cargo_buildScripts_enable: bool = true, + /// Specifies the invocation strategy to use when running the build scripts command. + /// If `per_workspace` is set, the command will be executed for each Rust workspace with the + /// workspace as the working directory. + /// If `once` is set, the command will be executed once with the opened project as the + /// working directory. + /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// is set. + cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, + /// Override the command rust-analyzer uses to run build scripts and + /// build procedural macros. The command is required to output json + /// and should therefore include `--message-format=json` or a similar + /// option. + /// + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. + /// + /// By default, a cargo invocation will be constructed for the configured + /// targets and features, with the following base command line: + /// + /// ```bash + /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going + /// ``` + /// . + cargo_buildScripts_overrideCommand: Option> = None, + /// Rerun proc-macros building/build-scripts running when proc-macro + /// or build-script sources change and are saved. + cargo_buildScripts_rebuildOnSave: bool = true, + /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to + /// avoid checking unnecessary things. + cargo_buildScripts_useRustcWrapper: bool = true, + /// List of cfg options to enable with the given values. + cargo_cfgs: FxHashMap> = { + let mut m = FxHashMap::default(); + m.insert("debug_assertions".to_owned(), None); + m.insert("miri".to_owned(), None); + m + }, + /// Extra arguments that are passed to every cargo invocation. + cargo_extraArgs: Vec = vec![], + /// Extra environment variables that will be set when running cargo, rustc + /// or other commands within the workspace. Useful for setting RUSTFLAGS. + cargo_extraEnv: FxHashMap = FxHashMap::default(), + /// List of features to activate. + /// + /// Set this to `"all"` to pass `--all-features` to cargo. + cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), + /// Whether to pass `--no-default-features` to cargo. + cargo_noDefaultFeatures: bool = false, + /// Relative path to the sysroot, or "discover" to try to automatically find it via + /// "rustc --print sysroot". + /// + /// Unsetting this disables sysroot loading. + /// + /// This option does not take effect until rust-analyzer is restarted. + cargo_sysroot: Option = Some("discover".to_owned()), + /// Relative path to the sysroot library sources. If left unset, this will default to + /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. + /// + /// This option does not take effect until rust-analyzer is restarted. + cargo_sysrootSrc: Option = None, + /// Compilation target override (target triple). + // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work + // than `checkOnSave_target` + cargo_target: Option = None, + /// Optional path to a rust-analyzer specific target directory. + /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro + /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts. + /// + /// Set to `true` to use a subdirectory of the existing target directory or + /// set to a path relative to the workspace to use that path. + cargo_targetDir | rust_analyzerTargetDir: Option = None, + /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = vec![], /// Advanced option, fully override the command rust-analyzer uses for @@ -1728,22 +1729,22 @@ impl Config { } } - pub fn extra_args(&self) -> &Vec { - self.cargo_extraArgs() + pub fn extra_args(&self, source_root: Option) -> &Vec { + self.cargo_extraArgs(source_root) } - pub fn extra_env(&self) -> &FxHashMap { - self.cargo_extraEnv() + pub fn extra_env(&self, source_root: Option) -> &FxHashMap { + self.cargo_extraEnv(source_root) } - pub fn check_extra_args(&self) -> Vec { - let mut extra_args = self.extra_args().clone(); + pub fn check_extra_args(&self, source_root: Option) -> Vec { + let mut extra_args = self.extra_args(source_root).clone(); extra_args.extend_from_slice(self.check_extraArgs()); extra_args } - pub fn check_extra_env(&self) -> FxHashMap { - let mut extra_env = self.cargo_extraEnv().clone(); + pub fn check_extra_env(&self, source_root: Option) -> FxHashMap { + let mut extra_env = self.cargo_extraEnv(source_root).clone(); extra_env.extend(self.check_extraEnv().clone()); extra_env } @@ -1787,15 +1788,15 @@ impl Config { } } - pub fn cargo_autoreload_config(&self) -> bool { - self.cargo_autoreload().to_owned() + pub fn cargo_autoreload_config(&self, source_root: Option) -> bool { + self.cargo_autoreload(source_root).to_owned() } - pub fn run_build_scripts(&self) -> bool { - self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned() + pub fn run_build_scripts(&self, source_root: Option) -> bool { + self.cargo_buildScripts_enable(source_root).to_owned() || self.procMacro_enable().to_owned() } - pub fn cargo(&self) -> CargoConfig { + pub fn cargo(&self, source_root: Option) -> CargoConfig { let rustc_source = self.rustc_source().as_ref().map(|rustc_src| { if rustc_src == "discover" { RustLibSource::Discover @@ -1803,7 +1804,7 @@ impl Config { RustLibSource::Path(self.root_path.join(rustc_src)) } }); - let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| { + let sysroot = self.cargo_sysroot(source_root).as_ref().map(|sysroot| { if sysroot == "discover" { RustLibSource::Discover } else { @@ -1811,24 +1812,24 @@ impl Config { } }); let sysroot_src = - self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot)); + self.cargo_sysrootSrc(source_root).as_ref().map(|sysroot| self.root_path.join(sysroot)); CargoConfig { - all_targets: *self.cargo_allTargets(), - features: match &self.cargo_features() { + all_targets: *self.cargo_allTargets(source_root), + features: match &self.cargo_features(source_root) { CargoFeaturesDef::All => CargoFeatures::All, CargoFeaturesDef::Selected(features) => CargoFeatures::Selected { features: features.clone(), - no_default_features: self.cargo_noDefaultFeatures().to_owned(), + no_default_features: self.cargo_noDefaultFeatures(source_root).to_owned(), }, }, - target: self.cargo_target().clone(), + target: self.cargo_target(source_root).clone(), sysroot, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { global: CfgDiff::new( - self.cargo_cfgs() + self.cargo_cfgs(source_root) .iter() .map(|(key, val)| match val { Some(val) => CfgAtom::KeyValue { @@ -1843,15 +1844,15 @@ impl Config { .unwrap(), selective: Default::default(), }, - wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(), - invocation_strategy: match self.cargo_buildScripts_invocationStrategy() { + wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(source_root), + invocation_strategy: match self.cargo_buildScripts_invocationStrategy(source_root) { InvocationStrategy::Once => project_model::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, - run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(), - extra_args: self.cargo_extraArgs().clone(), - extra_env: self.cargo_extraEnv().clone(), - target_dir: self.target_dir_from_config(), + run_build_script_command: self.cargo_buildScripts_overrideCommand(source_root).clone(), + extra_args: self.cargo_extraArgs(source_root).clone(), + extra_env: self.cargo_extraEnv(source_root).clone(), + target_dir: self.target_dir_from_config(source_root), } } @@ -1873,24 +1874,24 @@ impl Config { *self.check_workspace() } - pub(crate) fn cargo_test_options(&self) -> CargoOptions { + pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { CargoOptions { - target_triples: self.cargo_target().clone().into_iter().collect(), + target_triples: self.cargo_target(source_root).clone().into_iter().collect(), all_targets: false, - no_default_features: *self.cargo_noDefaultFeatures(), - all_features: matches!(self.cargo_features(), CargoFeaturesDef::All), - features: match self.cargo_features().clone() { + no_default_features: *self.cargo_noDefaultFeatures(source_root), + all_features: matches!(self.cargo_features(source_root), CargoFeaturesDef::All), + features: match self.cargo_features(source_root).clone() { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, - extra_args: self.extra_args().clone(), + extra_args: self.extra_args(source_root).clone(), extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), - extra_env: self.extra_env().clone(), - target_dir: self.target_dir_from_config(), + extra_env: self.extra_env(source_root).clone(), + target_dir: self.target_dir_from_config(source_root), } } - pub(crate) fn flycheck(&self) -> FlycheckConfig { + pub(crate) fn flycheck(&self, source_root: Option) -> FlycheckConfig { match &self.check_overrideCommand() { Some(args) if !args.is_empty() => { let mut args = args.clone(); @@ -1898,7 +1899,7 @@ impl Config { FlycheckConfig::CustomCommand { command, args, - extra_env: self.check_extra_env(), + extra_env: self.check_extra_env(source_root), invocation_strategy: match self.check_invocationStrategy() { InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => { @@ -1917,35 +1918,39 @@ impl Config { [] => None, targets => Some(targets.into()), }) - .unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()), - all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()), + .unwrap_or_else(|| { + self.cargo_target(source_root).clone().into_iter().collect() + }), + all_targets: self + .check_allTargets() + .unwrap_or(*self.cargo_allTargets(source_root)), no_default_features: self .check_noDefaultFeatures() - .unwrap_or(*self.cargo_noDefaultFeatures()), + .unwrap_or(*self.cargo_noDefaultFeatures(source_root)), all_features: matches!( - self.check_features().as_ref().unwrap_or(self.cargo_features()), + self.check_features().as_ref().unwrap_or(self.cargo_features(source_root)), CargoFeaturesDef::All ), features: match self .check_features() .clone() - .unwrap_or_else(|| self.cargo_features().clone()) + .unwrap_or_else(|| self.cargo_features(source_root).clone()) { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, - extra_args: self.check_extra_args(), + extra_args: self.check_extra_args(source_root), extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), - extra_env: self.check_extra_env(), - target_dir: self.target_dir_from_config(), + extra_env: self.check_extra_env(source_root), + target_dir: self.target_dir_from_config(source_root), }, ansi_color_output: self.color_diagnostic_output(), }, } } - fn target_dir_from_config(&self) -> Option { - self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir { + fn target_dir_from_config(&self, source_root: Option) -> Option { + self.cargo_targetDir(source_root).as_ref().and_then(|target_dir| match target_dir { TargetDirectory::UseSubdirectory(true) => { Some(Utf8PathBuf::from("target/rust-analyzer")) } @@ -1959,8 +1964,8 @@ impl Config { *self.checkOnSave() } - pub fn script_rebuild_on_save(&self) -> bool { - *self.cargo_buildScripts_rebuildOnSave() + pub fn script_rebuild_on_save(&self, source_root: Option) -> bool { + *self.cargo_buildScripts_rebuildOnSave(source_root) } pub fn runnables(&self) -> RunnablesConfig { @@ -3522,9 +3527,9 @@ mod tests { })); (config, _, _) = config.apply_change(change); - assert_eq!(config.cargo_targetDir(), &None); + assert_eq!(config.cargo_targetDir(None), &None); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none()) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none()) ); } @@ -3540,9 +3545,9 @@ mod tests { (config, _, _) = config.apply_change(change); - assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true))); + assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true))); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); } @@ -3559,11 +3564,11 @@ mod tests { (config, _, _) = config.apply_change(change); assert_eq!( - config.cargo_targetDir(), + config.cargo_targetDir(None), &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 9d0082c370c7e..35e1da80bf673 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -631,6 +631,10 @@ impl GlobalStateSnapshot { file_id_to_url(&self.vfs_read(), id) } + pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result { + vfs_path_to_file_id(&self.vfs_read(), vfs_path) + } + pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable { let endings = self.vfs.read().1[&file_id]; let index = self.analysis.file_line_index(file_id)?; @@ -725,3 +729,9 @@ pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result anyhow::Result { + let res = + vfs.file_id(vfs_path).ok_or_else(|| anyhow::format_err!("file not found: {vfs_path}"))?; + Ok(res) +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 38b88ff2d0403..1cbdb5086792b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -145,14 +145,21 @@ pub(crate) fn handle_did_save_text_document( state: &mut GlobalState, params: DidSaveTextDocumentParams, ) -> anyhow::Result<()> { - if state.config.script_rebuild_on_save() && state.build_deps_changed { - state.build_deps_changed = false; - state - .fetch_build_data_queue - .request_op("build_deps_changed - save notification".to_owned(), ()); - } + let mut deps_change_processed = false; if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { + let snap = state.snapshot(); + let file_id = snap.vfs_path_to_file_id(&vfs_path)?; + let sr = snap.analysis.source_root_id(file_id)?; + deps_change_processed = true; + + if state.config.script_rebuild_on_save(Some(sr)) && state.build_deps_changed { + state.build_deps_changed = false; + state + .fetch_build_data_queue + .request_op("build_deps_changed - save notification".to_owned(), ()); + } + // Re-fetch workspaces if a workspace related file has changed if let Some(path) = vfs_path.as_path() { let additional_files = &state @@ -191,6 +198,17 @@ pub(crate) fn handle_did_save_text_document( flycheck.restart_workspace(None); } } + + if !deps_change_processed + && state.config.script_rebuild_on_save(None) + && state.build_deps_changed + { + state.build_deps_changed = false; + state + .fetch_build_data_queue + .request_op("build_deps_changed - save notification".to_owned(), ()); + } + Ok(()) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 1ad5ff0c8cdc1..d220809be888c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -256,7 +256,7 @@ pub(crate) fn handle_run_test( let handle = CargoTestHandle::new( test_path, - state.config.cargo_test_options(), + state.config.cargo_test_options(None), cargo.workspace_root(), test_target, state.test_run_sender.clone(), @@ -2119,7 +2119,7 @@ fn run_rustfmt( RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); - cmd.envs(snap.config.extra_env()); + cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(extra_args); if let Some(edition) = edition { @@ -2177,7 +2177,7 @@ fn run_rustfmt( _ => process::Command::new(cmd), }; - cmd.envs(snap.config.extra_env()); + cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(args); cmd } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 1d4ee71e5c1a0..372f060234d7f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -455,7 +455,7 @@ impl GlobalState { } } - if self.config.cargo_autoreload_config() + if self.config.cargo_autoreload_config(None) || self.config.discover_workspace_config().is_some() { if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) = @@ -973,9 +973,9 @@ impl GlobalState { // When we're running multiple flychecks, we have to include a disambiguator in // the title, or the editor complains. Note that this is a user-facing string. let title = if self.flycheck.len() == 1 { - format!("{}", self.config.flycheck()) + format!("{}", self.config.flycheck(None)) } else { - format!("{} (#{})", self.config.flycheck(), id + 1) + format!("{} (#{})", self.config.flycheck(None), id + 1) }; self.report_progress( &title, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 68366136eda60..7087d7fbd5757 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -100,7 +100,7 @@ impl GlobalState { { let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false }; self.fetch_workspaces_queue.request_op("discovered projects changed".to_owned(), req) - } else if self.config.flycheck() != old_config.flycheck() { + } else if self.config.flycheck(None) != old_config.flycheck(None) { self.reload_flycheck(); } @@ -122,7 +122,7 @@ impl GlobalState { }; let mut message = String::new(); - if !self.config.cargo_autoreload() + if !self.config.cargo_autoreload(None) && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() && self.config.discover_workspace_config().is_none() @@ -264,7 +264,7 @@ impl GlobalState { .map(ManifestPath::try_from) .filter_map(Result::ok) .collect(); - let cargo_config = self.config.cargo(); + let cargo_config = self.config.cargo(None); let discover_command = self.config.discover_workspace_config().cloned(); let is_quiescent = !(self.discover_workspace_queue.op_in_progress() || self.vfs_progress_config_version < self.vfs_config_version @@ -357,7 +357,7 @@ impl GlobalState { pub(crate) fn fetch_build_data(&mut self, cause: Cause) { info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); - let config = self.config.cargo(); + let config = self.config.cargo(None); let root_path = self.config.root_path().clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { @@ -507,7 +507,7 @@ impl GlobalState { // FIXME: can we abort the build scripts here if they are already running? self.workspaces = Arc::new(workspaces); - if self.config.run_build_scripts() { + if self.config.run_build_scripts(None) { self.build_deps_changed = false; self.fetch_build_data_queue.request_op("workspace updated".to_owned(), ()); } @@ -627,7 +627,7 @@ impl GlobalState { .. } => cargo_config_extra_env .iter() - .chain(self.config.extra_env()) + .chain(self.config.extra_env(None)) .map(|(a, b)| (a.clone(), b.clone())) .chain( ws.sysroot @@ -702,7 +702,7 @@ impl GlobalState { vfs.file_id(&vfs_path) }; - ws_to_crate_graph(&self.workspaces, self.config.extra_env(), load) + ws_to_crate_graph(&self.workspaces, self.config.extra_env(None), load) }; let mut change = ChangeWithProcMacros::new(); if self.config.expand_proc_macros() { @@ -791,7 +791,7 @@ impl GlobalState { fn reload_flycheck(&mut self) { let _p = tracing::info_span!("GlobalState::reload_flycheck").entered(); - let config = self.config.flycheck(); + let config = self.config.flycheck(None); let sender = self.flycheck_sender.clone(); let invocation_strategy = match config { FlycheckConfig::CargoCommand { .. } => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 954e13cbf2727..87e964e5a89c2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -168,7 +168,7 @@ impl CargoTargetSpec { (Default::default(), Default::default()) }; - let cargo_config = snap.config.cargo(); + let cargo_config = snap.config.cargo(None); match &cargo_config.features { CargoFeatures::All => { From 15a1505551f98b54828a1f755190d3ff1ec72118 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 19:07:43 +0200 Subject: [PATCH 0155/1301] Make checkOnSave workspace --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 11 ++++++----- .../crates/rust-analyzer/src/handlers/notification.rs | 4 ++-- .../crates/rust-analyzer/src/main_loop.rs | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 9a8075043ed99..211e91f7f0284 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -76,8 +76,6 @@ config_data! { /// How many worker threads to handle priming caches. The default `0` means to pick automatically. cachePriming_numThreads: NumThreads = NumThreads::Physical, - /// Run the check command for diagnostics on save. - checkOnSave | checkOnSave_enable: bool = true, /// Check all targets and tests (`--all-targets`). Defaults to /// `#rust-analyzer.cargo.allTargets#`. @@ -350,7 +348,7 @@ config_data! { workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { -/// Pass `--all-targets` to cargo invocation. + /// Pass `--all-targets` to cargo invocation. cargo_allTargets: bool = true, /// Automatically refresh project info via `cargo metadata` on /// `Cargo.toml` or `.cargo/config.toml` changes. @@ -431,6 +429,9 @@ config_data! { /// set to a path relative to the workspace to use that path. cargo_targetDir | rust_analyzerTargetDir: Option = None, + /// Run the check command for diagnostics on save. + checkOnSave | checkOnSave_enable: bool = true, + /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = vec![], /// Advanced option, fully override the command rust-analyzer uses for @@ -1960,8 +1961,8 @@ impl Config { }) } - pub fn check_on_save(&self) -> bool { - *self.checkOnSave() + pub fn check_on_save(&self, source_root: Option) -> bool { + *self.checkOnSave(source_root) } pub fn script_rebuild_on_save(&self, source_root: Option) -> bool { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 1cbdb5086792b..336b7ea05a62d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -189,10 +189,10 @@ pub(crate) fn handle_did_save_text_document( } } - if !state.config.check_on_save() || run_flycheck(state, vfs_path) { + if !state.config.check_on_save(Some(sr)) || run_flycheck(state, vfs_path) { return Ok(()); } - } else if state.config.check_on_save() { + } else if state.config.check_on_save(None) { // No specific flycheck was triggered, so let's trigger all of them. for flycheck in state.flycheck.iter() { flycheck.restart_workspace(None); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 372f060234d7f..a90b988907976 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -404,7 +404,7 @@ impl GlobalState { if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { - if self.config.check_on_save() { + if self.config.check_on_save(None) { // Project has loaded properly, kick off initial flycheck self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None)); } From 55078f0b8e2d277c84b26ddc8a0641ac777a9589 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 20:44:27 +0200 Subject: [PATCH 0156/1301] Make check workspace --- .../crates/rust-analyzer/src/config.rs | 165 +++++++++--------- .../src/handlers/notification.rs | 7 +- .../crates/rust-analyzer/src/main_loop.rs | 2 +- 3 files changed, 89 insertions(+), 85 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 211e91f7f0284..da3dda7e29313 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -77,72 +77,6 @@ config_data! { cachePriming_numThreads: NumThreads = NumThreads::Physical, - /// Check all targets and tests (`--all-targets`). Defaults to - /// `#rust-analyzer.cargo.allTargets#`. - check_allTargets | checkOnSave_allTargets: Option = None, - /// Cargo command to use for `cargo check`. - check_command | checkOnSave_command: String = "check".to_owned(), - /// Extra arguments for `cargo check`. - check_extraArgs | checkOnSave_extraArgs: Vec = vec![], - /// Extra environment variables that will be set when running `cargo check`. - /// Extends `#rust-analyzer.cargo.extraEnv#`. - check_extraEnv | checkOnSave_extraEnv: FxHashMap = FxHashMap::default(), - /// List of features to activate. Defaults to - /// `#rust-analyzer.cargo.features#`. - /// - /// Set to `"all"` to pass `--all-features` to Cargo. - check_features | checkOnSave_features: Option = None, - /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. - /// - /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... - check_ignore: FxHashSet = FxHashSet::default(), - /// Specifies the invocation strategy to use when running the check command. - /// If `per_workspace` is set, the command will be executed for each workspace. - /// If `once` is set, the command will be executed once. - /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` - /// is set. - check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, - /// Whether to pass `--no-default-features` to Cargo. Defaults to - /// `#rust-analyzer.cargo.noDefaultFeatures#`. - check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = None, - /// Override the command rust-analyzer uses instead of `cargo check` for - /// diagnostics on save. The command is required to output json and - /// should therefore include `--message-format=json` or a similar option - /// (if your client supports the `colorDiagnosticOutput` experimental - /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`). - /// - /// If you're changing this because you're using some tool wrapping - /// Cargo, you might also want to change - /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. - /// - /// If there are multiple linked projects/workspaces, this command is invoked for - /// each of them, with the working directory being the workspace root - /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten - /// by changing `#rust-analyzer.check.invocationStrategy#`. - /// - /// If `$saved_file` is part of the command, rust-analyzer will pass - /// the absolute path of the saved file to the provided command. This is - /// intended to be used with non-Cargo build systems. - /// Note that `$saved_file` is experimental and may be removed in the future. - /// - /// An example command would be: - /// - /// ```bash - /// cargo check --workspace --message-format=json --all-targets - /// ``` - /// . - check_overrideCommand | checkOnSave_overrideCommand: Option> = None, - /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. - /// - /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. - /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. - /// - /// Aliased as `"checkOnSave.targets"`. - check_targets | checkOnSave_targets | checkOnSave_target: Option = None, - /// Whether `--workspace` should be passed to `cargo check`. - /// If false, `-p ` will be passed instead. - check_workspace: bool = true, - /// List of rust-analyzer diagnostics to disable. diagnostics_disabled: FxHashSet = FxHashSet::default(), /// Whether to show native rust-analyzer diagnostics. @@ -432,6 +366,73 @@ config_data! { /// Run the check command for diagnostics on save. checkOnSave | checkOnSave_enable: bool = true, + +/// Check all targets and tests (`--all-targets`). Defaults to + /// `#rust-analyzer.cargo.allTargets#`. + check_allTargets | checkOnSave_allTargets: Option = None, + /// Cargo command to use for `cargo check`. + check_command | checkOnSave_command: String = "check".to_owned(), + /// Extra arguments for `cargo check`. + check_extraArgs | checkOnSave_extraArgs: Vec = vec![], + /// Extra environment variables that will be set when running `cargo check`. + /// Extends `#rust-analyzer.cargo.extraEnv#`. + check_extraEnv | checkOnSave_extraEnv: FxHashMap = FxHashMap::default(), + /// List of features to activate. Defaults to + /// `#rust-analyzer.cargo.features#`. + /// + /// Set to `"all"` to pass `--all-features` to Cargo. + check_features | checkOnSave_features: Option = None, + /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + /// + /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + check_ignore: FxHashSet = FxHashSet::default(), + /// Specifies the invocation strategy to use when running the check command. + /// If `per_workspace` is set, the command will be executed for each workspace. + /// If `once` is set, the command will be executed once. + /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` + /// is set. + check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, + /// Whether to pass `--no-default-features` to Cargo. Defaults to + /// `#rust-analyzer.cargo.noDefaultFeatures#`. + check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = None, + /// Override the command rust-analyzer uses instead of `cargo check` for + /// diagnostics on save. The command is required to output json and + /// should therefore include `--message-format=json` or a similar option + /// (if your client supports the `colorDiagnosticOutput` experimental + /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`). + /// + /// If you're changing this because you're using some tool wrapping + /// Cargo, you might also want to change + /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. + /// + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.check.invocationStrategy#`. + /// + /// If `$saved_file` is part of the command, rust-analyzer will pass + /// the absolute path of the saved file to the provided command. This is + /// intended to be used with non-Cargo build systems. + /// Note that `$saved_file` is experimental and may be removed in the future. + /// + /// An example command would be: + /// + /// ```bash + /// cargo check --workspace --message-format=json --all-targets + /// ``` + /// . + check_overrideCommand | checkOnSave_overrideCommand: Option> = None, + /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + /// + /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. + /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + /// + /// Aliased as `"checkOnSave.targets"`. + check_targets | checkOnSave_targets | checkOnSave_target: Option = None, + /// Whether `--workspace` should be passed to `cargo check`. + /// If false, `-p ` will be passed instead. + check_workspace: bool = true, + /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = vec![], /// Advanced option, fully override the command rust-analyzer uses for @@ -1018,7 +1019,7 @@ impl Config { config.source_root_parent_map = source_root_map; } - if config.check_command().is_empty() { + if config.check_command(None).is_empty() { config.validation_errors.0.push(Arc::new(ConfigErrorInner::Json { config_key: "/check/command".to_owned(), error: serde_json::Error::custom("expected a non-empty string"), @@ -1721,12 +1722,12 @@ impl Config { self.diagnostics_enable().to_owned() } - pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { + pub fn diagnostics_map(&self, source_root: Option) -> DiagnosticsMapConfig { DiagnosticsMapConfig { remap_prefix: self.diagnostics_remapPrefix().clone(), warnings_as_info: self.diagnostics_warningsAsInfo().clone(), warnings_as_hint: self.diagnostics_warningsAsHint().clone(), - check_ignore: self.check_ignore().clone(), + check_ignore: self.check_ignore(source_root).clone(), } } @@ -1740,13 +1741,13 @@ impl Config { pub fn check_extra_args(&self, source_root: Option) -> Vec { let mut extra_args = self.extra_args(source_root).clone(); - extra_args.extend_from_slice(self.check_extraArgs()); + extra_args.extend_from_slice(self.check_extraArgs(source_root)); extra_args } pub fn check_extra_env(&self, source_root: Option) -> FxHashMap { let mut extra_env = self.cargo_extraEnv(source_root).clone(); - extra_env.extend(self.check_extraEnv().clone()); + extra_env.extend(self.check_extraEnv(source_root).clone()); extra_env } @@ -1871,8 +1872,8 @@ impl Config { } } - pub fn flycheck_workspace(&self) -> bool { - *self.check_workspace() + pub fn flycheck_workspace(&self, source_root: Option) -> bool { + *self.check_workspace(source_root) } pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { @@ -1893,7 +1894,7 @@ impl Config { } pub(crate) fn flycheck(&self, source_root: Option) -> FlycheckConfig { - match &self.check_overrideCommand() { + match &self.check_overrideCommand(source_root) { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); @@ -1901,7 +1902,7 @@ impl Config { command, args, extra_env: self.check_extra_env(source_root), - invocation_strategy: match self.check_invocationStrategy() { + invocation_strategy: match self.check_invocationStrategy(source_root) { InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => { crate::flycheck::InvocationStrategy::PerWorkspace @@ -1910,10 +1911,10 @@ impl Config { } } Some(_) | None => FlycheckConfig::CargoCommand { - command: self.check_command().clone(), + command: self.check_command(source_root).clone(), options: CargoOptions { target_triples: self - .check_targets() + .check_targets(source_root) .clone() .and_then(|targets| match &targets.0[..] { [] => None, @@ -1923,17 +1924,19 @@ impl Config { self.cargo_target(source_root).clone().into_iter().collect() }), all_targets: self - .check_allTargets() + .check_allTargets(source_root) .unwrap_or(*self.cargo_allTargets(source_root)), no_default_features: self - .check_noDefaultFeatures() + .check_noDefaultFeatures(source_root) .unwrap_or(*self.cargo_noDefaultFeatures(source_root)), all_features: matches!( - self.check_features().as_ref().unwrap_or(self.cargo_features(source_root)), + self.check_features(source_root) + .as_ref() + .unwrap_or(self.cargo_features(source_root)), CargoFeaturesDef::All ), features: match self - .check_features() + .check_features(source_root) .clone() .unwrap_or_else(|| self.cargo_features(source_root).clone()) { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 336b7ea05a62d..dc8d40e0ee075 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -306,6 +306,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some(file_id) = file_id { let world = state.snapshot(); + let source_root_id = world.analysis.source_root_id(file_id).ok(); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { // Is the target binary? If so we let flycheck run only for the workspace that contains the crate. @@ -391,9 +392,9 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { for (id, package) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - match package - .filter(|_| !world.config.flycheck_workspace() || target.is_some()) - { + match package.filter(|_| { + !world.config.flycheck_workspace(source_root_id) || target.is_some() + }) { Some(package) => flycheck .restart_for_package(package, target.clone().map(TupleExt::head)), None => flycheck.restart_workspace(saved_file.clone()), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index a90b988907976..e034badbcbc9a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -925,7 +925,7 @@ impl GlobalState { FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( - &self.config.diagnostics_map(), + &self.config.diagnostics_map(None), &diagnostic, &workspace_root, &snap, From 2ddb4e14807691588d21d0d1ae886f2913c3ecf3 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 20:48:30 +0200 Subject: [PATCH 0157/1301] Make runnables workspace --- .../crates/rust-analyzer/src/config.rs | 38 +++++++++---------- .../rust-analyzer/src/handlers/request.rs | 3 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 3 +- .../crates/rust-analyzer/src/target_spec.rs | 2 +- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index da3dda7e29313..675882e55f1a8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -124,20 +124,6 @@ config_data! { /// This config takes a map of crate names with the exported proc-macro names to ignore as values. procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), - /// Command to be executed instead of 'cargo' for runnables. - runnables_command: Option = None, - /// Additional arguments to be passed to cargo for runnables such as - /// tests or binaries. For example, it may be `--release`. - runnables_extraArgs: Vec = vec![], - /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or - /// doc-tests. - /// - /// Unless the launched target uses a - /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), - /// they will end up being interpreted as options to - /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). - runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], - /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private /// projects, or "discover" to try to automatically find it if the `rustc-dev` component /// is installed. @@ -367,7 +353,7 @@ config_data! { checkOnSave | checkOnSave_enable: bool = true, -/// Check all targets and tests (`--all-targets`). Defaults to + /// Check all targets and tests (`--all-targets`). Defaults to /// `#rust-analyzer.cargo.allTargets#`. check_allTargets | checkOnSave_allTargets: Option = None, /// Cargo command to use for `cargo check`. @@ -433,6 +419,20 @@ config_data! { /// If false, `-p ` will be passed instead. check_workspace: bool = true, + /// Command to be executed instead of 'cargo' for runnables. + runnables_command: Option = None, + /// Additional arguments to be passed to cargo for runnables such as + /// tests or binaries. For example, it may be `--release`. + runnables_extraArgs: Vec = vec![], + /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or + /// doc-tests. + /// + /// Unless the launched target uses a + /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), + /// they will end up being interpreted as options to + /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). + runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], + /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = vec![], /// Advanced option, fully override the command rust-analyzer uses for @@ -1972,11 +1972,11 @@ impl Config { *self.cargo_buildScripts_rebuildOnSave(source_root) } - pub fn runnables(&self) -> RunnablesConfig { + pub fn runnables(&self, source_root: Option) -> RunnablesConfig { RunnablesConfig { - override_cargo: self.runnables_command().clone(), - cargo_extra_args: self.runnables_extraArgs().clone(), - extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(), + override_cargo: self.runnables_command(source_root).clone(), + cargo_extra_args: self.runnables_extraArgs(source_root).clone(), + extra_test_binary_args: self.runnables_extraTestBinaryArgs(source_root).clone(), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index d220809be888c..b39552a172e5c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -852,6 +852,7 @@ pub(crate) fn handle_runnables( ) -> anyhow::Result> { let _p = tracing::info_span!("handle_runnables").entered(); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let source_root = snap.analysis.source_root_id(file_id).ok(); let line_index = snap.file_line_index(file_id)?; let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok()); let target_spec = TargetSpec::for_file(&snap, file_id)?; @@ -894,7 +895,7 @@ pub(crate) fn handle_runnables( } // Add `cargo check` and `cargo test` for all targets of the whole package - let config = snap.config.runnables(); + let config = snap.config.runnables(source_root); match target_spec { Some(TargetSpec::Cargo(spec)) => { let is_crate_no_std = snap.analysis.is_crate_no_std(spec.crate_id)?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index eb6bc2a9ce9b0..f5122a722ab04 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1365,8 +1365,9 @@ pub(crate) fn runnable( snap: &GlobalStateSnapshot, runnable: Runnable, ) -> Cancellable> { - let config = snap.config.runnables(); let target_spec = TargetSpec::for_file(snap, runnable.nav.file_id)?; + let source_root = snap.analysis.source_root_id(runnable.nav.file_id).ok(); + let config = snap.config.runnables(source_root); match target_spec { Some(TargetSpec::Cargo(spec)) => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 87e964e5a89c2..b4aa73d2780d6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -113,7 +113,7 @@ impl CargoTargetSpec { kind: &RunnableKind, cfg: &Option, ) -> (Vec, Vec) { - let config = snap.config.runnables(); + let config = snap.config.runnables(None); let extra_test_binary_args = config.extra_test_binary_args; let mut cargo_args = Vec::new(); From 7360dfe3f1b79acd54d3d8522bfb917d517e5866 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 20:49:51 +0200 Subject: [PATCH 0158/1301] Make rustc_* workspace --- .../crates/rust-analyzer/src/config.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 675882e55f1a8..3499cf9259518 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -124,16 +124,6 @@ config_data! { /// This config takes a map of crate names with the exported proc-macro names to ignore as values. procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), - /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private - /// projects, or "discover" to try to automatically find it if the `rustc-dev` component - /// is installed. - /// - /// Any project which uses rust-analyzer with the rustcPrivate - /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. - /// - /// This option does not take effect until rust-analyzer is restarted. - rustc_source: Option = None, - /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. /// @@ -433,6 +423,16 @@ config_data! { /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], + /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private + /// projects, or "discover" to try to automatically find it if the `rustc-dev` component + /// is installed. + /// + /// Any project which uses rust-analyzer with the rustcPrivate + /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. + /// + /// This option does not take effect until rust-analyzer is restarted. + rustc_source: Option = None, + /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = vec![], /// Advanced option, fully override the command rust-analyzer uses for @@ -1799,7 +1799,7 @@ impl Config { } pub fn cargo(&self, source_root: Option) -> CargoConfig { - let rustc_source = self.rustc_source().as_ref().map(|rustc_src| { + let rustc_source = self.rustc_source(source_root).as_ref().map(|rustc_src| { if rustc_src == "discover" { RustLibSource::Discover } else { From 887dd4e344b71bb8a126b2855847775fbc2ee94f Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 20:51:49 +0200 Subject: [PATCH 0159/1301] Make procMacro_* workspace --- .../crates/rust-analyzer/src/config.rs | 16 ++++++++++------ .../crates/rust-analyzer/src/reload.rs | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 3499cf9259518..e459e7b7f3bf9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -119,10 +119,6 @@ config_data! { /// Sets the LRU capacity of the specified queries. lru_query_capacities: FxHashMap, u16> = FxHashMap::default(), - /// These proc-macros will be ignored when trying to expand them. - /// - /// This config takes a map of crate names with the exported proc-macro names to ignore as values. - procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. @@ -409,6 +405,11 @@ config_data! { /// If false, `-p ` will be passed instead. check_workspace: bool = true, + /// These proc-macros will be ignored when trying to expand them. + /// + /// This config takes a map of crate names with the exported proc-macro names to ignore as values. + procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), + /// Command to be executed instead of 'cargo' for runnables. runnables_command: Option = None, /// Additional arguments to be passed to cargo for runnables such as @@ -1764,8 +1765,11 @@ impl Config { Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path))) } - pub fn ignored_proc_macros(&self) -> &FxHashMap, Box<[Box]>> { - self.procMacro_ignored() + pub fn ignored_proc_macros( + &self, + source_root: Option, + ) -> &FxHashMap, Box<[Box]>> { + self.procMacro_ignored(source_root) } pub fn expand_proc_macros(&self) -> bool { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 7087d7fbd5757..5d77b7aa67ef0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -382,7 +382,7 @@ impl GlobalState { pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) { info!(%cause, "will load proc macros"); - let ignored_proc_macros = self.config.ignored_proc_macros().clone(); + let ignored_proc_macros = self.config.ignored_proc_macros(None).clone(); let proc_macro_clients = self.proc_macro_clients.clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { From fcf505be37c55e46a08543cf666625640fcd9f84 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 20:53:40 +0200 Subject: [PATCH 0160/1301] Make workspace_symbol_search_* workspace --- .../crates/rust-analyzer/src/config.rs | 28 +++++++++---------- .../rust-analyzer/src/handlers/request.rs | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e459e7b7f3bf9..925d650b5b30e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -252,8 +252,6 @@ config_data! { config_data! { workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { - - /// Pass `--all-targets` to cargo invocation. cargo_allTargets: bool = true, /// Automatically refresh project info via `cargo metadata` on @@ -447,6 +445,16 @@ config_data! { /// available on a nightly build. rustfmt_rangeFormatting_enable: bool = false, + + /// Workspace symbol search kind. + workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes, + /// Limits the number of items returned from a workspace symbol search (Defaults to 128). + /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. + /// Other clients requires all results upfront and might require a higher limit. + workspace_symbol_search_limit: usize = 128, + /// Workspace symbol search scope. + workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace, + } } @@ -731,14 +739,6 @@ config_data! { /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. typing_autoClosingAngleBrackets_enable: bool = false, - /// Workspace symbol search kind. - workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes, - /// Limits the number of items returned from a workspace symbol search (Defaults to 128). - /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. - /// Other clients requires all results upfront and might require a higher limit. - workspace_symbol_search_limit: usize = 128, - /// Workspace symbol search scope. - workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace, } } @@ -2019,19 +2019,19 @@ impl Config { } } - pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig { + pub fn workspace_symbol(&self, source_root: Option) -> WorkspaceSymbolConfig { WorkspaceSymbolConfig { - search_scope: match self.workspace_symbol_search_scope() { + search_scope: match self.workspace_symbol_search_scope(source_root) { WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace, WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => { WorkspaceSymbolSearchScope::WorkspaceAndDependencies } }, - search_kind: match self.workspace_symbol_search_kind() { + search_kind: match self.workspace_symbol_search_kind(source_root) { WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes, WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols, }, - search_limit: *self.workspace_symbol_search_limit(), + search_limit: *self.workspace_symbol_search_limit(source_root), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index b39552a172e5c..94487bdbb2168 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -565,7 +565,7 @@ pub(crate) fn handle_workspace_symbol( ) -> anyhow::Result> { let _p = tracing::info_span!("handle_workspace_symbol").entered(); - let config = snap.config.workspace_symbol(); + let config = snap.config.workspace_symbol(None); let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &config); let query = { From 5e6bfaefbd9e6ded2ed9b7637cc1fafdd5d7eac5 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 20:57:08 +0200 Subject: [PATCH 0161/1301] Make diagnostics_* local --- .../crates/rust-analyzer/src/config.rs | 63 ++++++++++--------- .../crates/rust-analyzer/src/main_loop.rs | 2 +- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 925d650b5b30e..df03e679f04b9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -77,28 +77,6 @@ config_data! { cachePriming_numThreads: NumThreads = NumThreads::Physical, - /// List of rust-analyzer diagnostics to disable. - diagnostics_disabled: FxHashSet = FxHashSet::default(), - /// Whether to show native rust-analyzer diagnostics. - diagnostics_enable: bool = true, - /// Whether to show experimental rust-analyzer diagnostics that might - /// have more false positives than usual. - diagnostics_experimental_enable: bool = false, - /// Map of prefixes to be substituted when parsing diagnostic file paths. - /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. - diagnostics_remapPrefix: FxHashMap = FxHashMap::default(), - /// Whether to run additional style lints. - diagnostics_styleLints_enable: bool = false, - /// List of warnings that should be displayed with hint severity. - /// - /// The warnings will be indicated by faded text or three dots in code - /// and will not show up in the `Problems Panel`. - diagnostics_warningsAsHint: Vec = vec![], - /// List of warnings that should be displayed with info severity. - /// - /// The warnings will be indicated by a blue squiggly underline in code - /// and a blue icon in the `Problems Panel`. - diagnostics_warningsAsInfo: Vec = vec![], /// These directories will be ignored by rust-analyzer. They are /// relative to the workspace root, and globs are not supported. You may @@ -231,6 +209,29 @@ config_data! { /// Term search fuel in "units of work" for assists (Defaults to 1800). assist_termSearch_fuel: usize = 1800, + /// List of rust-analyzer diagnostics to disable. + diagnostics_disabled: FxHashSet = FxHashSet::default(), + /// Whether to show native rust-analyzer diagnostics. + diagnostics_enable: bool = true, + /// Whether to show experimental rust-analyzer diagnostics that might + /// have more false positives than usual. + diagnostics_experimental_enable: bool = false, + /// Map of prefixes to be substituted when parsing diagnostic file paths. + /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + diagnostics_remapPrefix: FxHashMap = FxHashMap::default(), + /// Whether to run additional style lints. + diagnostics_styleLints_enable: bool = false, + /// List of warnings that should be displayed with hint severity. + /// + /// The warnings will be indicated by faded text or three dots in code + /// and will not show up in the `Problems Panel`. + diagnostics_warningsAsHint: Vec = vec![], + /// List of warnings that should be displayed with info severity. + /// + /// The warnings will be indicated by a blue squiggly underline in code + /// and a blue icon in the `Problems Panel`. + diagnostics_warningsAsInfo: Vec = vec![], + /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. imports_granularity_enforce: bool = false, /// How imports should be grouped into use statements. @@ -1447,11 +1448,11 @@ impl Config { pub fn diagnostics(&self, source_root: Option) -> DiagnosticsConfig { DiagnosticsConfig { - enabled: *self.diagnostics_enable(), + enabled: *self.diagnostics_enable(source_root), proc_attr_macros_enabled: self.expand_proc_attr_macros(), proc_macros_enabled: *self.procMacro_enable(), - disable_experimental: !self.diagnostics_experimental_enable(), - disabled: self.diagnostics_disabled().clone(), + disable_experimental: !self.diagnostics_experimental_enable(source_root), + disabled: self.diagnostics_disabled(source_root).clone(), expr_fill_default: match self.assist_expressionFillDefault(source_root) { ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, @@ -1461,7 +1462,7 @@ impl Config { prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(), - style_lints: self.diagnostics_styleLints_enable().to_owned(), + style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(), term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64, term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(), } @@ -1719,15 +1720,15 @@ impl Config { self.cachePriming_enable().to_owned() } - pub fn publish_diagnostics(&self) -> bool { - self.diagnostics_enable().to_owned() + pub fn publish_diagnostics(&self, source_root: Option) -> bool { + self.diagnostics_enable(source_root).to_owned() } pub fn diagnostics_map(&self, source_root: Option) -> DiagnosticsMapConfig { DiagnosticsMapConfig { - remap_prefix: self.diagnostics_remapPrefix().clone(), - warnings_as_info: self.diagnostics_warningsAsInfo().clone(), - warnings_as_hint: self.diagnostics_warningsAsHint().clone(), + remap_prefix: self.diagnostics_remapPrefix(source_root).clone(), + warnings_as_info: self.diagnostics_warningsAsInfo(source_root).clone(), + warnings_as_hint: self.diagnostics_warningsAsHint(source_root).clone(), check_ignore: self.check_ignore(source_root).clone(), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index e034badbcbc9a..3e46138aa38dd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -434,7 +434,7 @@ impl GlobalState { let project_or_mem_docs_changed = became_quiescent || state_changed || memdocs_added_or_removed; - if project_or_mem_docs_changed && self.config.publish_diagnostics() { + if project_or_mem_docs_changed && self.config.publish_diagnostics(None) { self.update_diagnostics(); } if project_or_mem_docs_changed && self.config.test_explorer() { From f98c12121805d96ce5d1f58539743cd89e7b2f31 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 20:59:14 +0200 Subject: [PATCH 0162/1301] Make completion_* local --- .../crates/rust-analyzer/src/config.rs | 148 +++++++++--------- 1 file changed, 76 insertions(+), 72 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index df03e679f04b9..ac72193be54b3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -209,6 +209,70 @@ config_data! { /// Term search fuel in "units of work" for assists (Defaults to 1800). assist_termSearch_fuel: usize = 1800, + + /// Toggles the additional completions that automatically add imports when completed. + /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. + completion_autoimport_enable: bool = true, + /// Toggles the additional completions that automatically show method calls and field accesses + /// with `self` prefixed to them when inside a method. + completion_autoself_enable: bool = true, + /// Whether to add parenthesis and argument snippets when completing function. + completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, + /// Whether to show full function/method signatures in completion docs. + completion_fullFunctionSignatures_enable: bool = false, + /// Maximum number of completions to return. If `None`, the limit is infinite. + completion_limit: Option = None, + /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. + completion_postfix_enable: bool = true, + /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. + completion_privateEditable_enable: bool = false, + /// Custom completion snippets. + completion_snippets_custom: FxHashMap = serde_json::from_str(r#"{ + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + } + }"#).unwrap(), + /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. + completion_termSearch_enable: bool = false, + /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). + completion_termSearch_fuel: usize = 1000, + /// List of rust-analyzer diagnostics to disable. diagnostics_disabled: FxHashSet = FxHashSet::default(), /// Whether to show native rust-analyzer diagnostics. @@ -463,68 +527,6 @@ config_data! { /// Configs that only make sense when they are set by a client. As such they can only be defined /// by setting them using client's settings (e.g `settings.json` on VS Code). client: struct ClientDefaultConfigData <- ClientConfigInput -> { - /// Toggles the additional completions that automatically add imports when completed. - /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. - completion_autoimport_enable: bool = true, - /// Toggles the additional completions that automatically show method calls and field accesses - /// with `self` prefixed to them when inside a method. - completion_autoself_enable: bool = true, - /// Whether to add parenthesis and argument snippets when completing function. - completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, - /// Whether to show full function/method signatures in completion docs. - completion_fullFunctionSignatures_enable: bool = false, - /// Maximum number of completions to return. If `None`, the limit is infinite. - completion_limit: Option = None, - /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. - completion_postfix_enable: bool = true, - /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. - completion_privateEditable_enable: bool = false, - /// Custom completion snippets. - completion_snippets_custom: FxHashMap = serde_json::from_str(r#"{ - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - }"#).unwrap(), - /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. - completion_termSearch_enable: bool = false, - /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). - completion_termSearch_fuel: usize = 1000, /// Controls file watching implementation. files_watcher: FilesWatcherDef = FilesWatcherDef::Client, @@ -879,7 +881,7 @@ impl Config { // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`. config.snippets.clear(); - let snips = self.completion_snippets_custom().to_owned(); + let snips = self.completion_snippets_custom(None).to_owned(); for (name, def) in snips.iter() { if def.prefix.is_empty() && def.postfix.is_empty() { @@ -1417,13 +1419,15 @@ impl Config { pub fn completion(&self, source_root: Option) -> CompletionConfig { CompletionConfig { - enable_postfix_completions: self.completion_postfix_enable().to_owned(), - enable_imports_on_the_fly: self.completion_autoimport_enable().to_owned() + enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), + enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() && self.caps.completion_item_edit_resolve(), - enable_self_on_the_fly: self.completion_autoself_enable().to_owned(), - enable_private_editable: self.completion_privateEditable_enable().to_owned(), - full_function_signatures: self.completion_fullFunctionSignatures_enable().to_owned(), - callable: match self.completion_callable_snippets() { + enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), + enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), + full_function_signatures: self + .completion_fullFunctionSignatures_enable(source_root) + .to_owned(), + callable: match self.completion_callable_snippets(source_root) { CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), CallableCompletionDef::None => None, @@ -1434,9 +1438,9 @@ impl Config { prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(), snippets: self.snippets.clone().to_vec(), - limit: self.completion_limit().to_owned(), - enable_term_search: self.completion_termSearch_enable().to_owned(), - term_search_fuel: self.completion_termSearch_fuel().to_owned() as u64, + limit: self.completion_limit(source_root).to_owned(), + enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), + term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, } } From 042a932bfe1edb1edde55db0ca620f1986fa5c95 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 21:03:58 +0200 Subject: [PATCH 0163/1301] Make almost every client config global --- .../crates/rust-analyzer/src/config.rs | 421 +++++++++--------- 1 file changed, 211 insertions(+), 210 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index ac72193be54b3..e7c5f989d93e7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -84,6 +84,154 @@ config_data! { files_excludeDirs: Vec = vec![], + + /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. + highlightRelated_breakPoints_enable: bool = true, + /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + highlightRelated_closureCaptures_enable: bool = true, + /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). + highlightRelated_exitPoints_enable: bool = true, + /// Enables highlighting of related references while the cursor is on any identifier. + highlightRelated_references_enable: bool = true, + /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. + highlightRelated_yieldPoints_enable: bool = true, + + /// Whether to show `Debug` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_debug_enable: bool = true, + /// Whether to show HoverActions in Rust files. + hover_actions_enable: bool = true, + /// Whether to show `Go to Type Definition` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_gotoTypeDef_enable: bool = true, + /// Whether to show `Implementations` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_implementations_enable: bool = true, + /// Whether to show `References` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_references_enable: bool = false, + /// Whether to show `Run` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_run_enable: bool = true, + + /// Whether to show documentation on hover. + hover_documentation_enable: bool = true, + /// Whether to show keyword hover popups. Only applies when + /// `#rust-analyzer.hover.documentation.enable#` is set. + hover_documentation_keywords_enable: bool = true, + /// Use markdown syntax for links on hover. + hover_links_enable: bool = true, + /// How to render the align information in a memory layout hover. + hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// Whether to show memory layout data on hover. + hover_memoryLayout_enable: bool = true, + /// How to render the niche information in a memory layout hover. + hover_memoryLayout_niches: Option = Some(false), + /// How to render the offset information in a memory layout hover. + hover_memoryLayout_offset: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// How to render the size information in a memory layout hover. + hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), + + /// How many variants of an enum to display when hovering on. Show none if empty. + hover_show_enumVariants: Option = Some(5), + /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. + hover_show_fields: Option = Some(5), + /// How many associated items of a trait to display when hovering a trait. + hover_show_traitAssocItems: Option = None, + + /// Whether to show inlay type hints for binding modes. + inlayHints_bindingModeHints_enable: bool = false, + /// Whether to show inlay type hints for method chains. + inlayHints_chainingHints_enable: bool = true, + /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. + inlayHints_closingBraceHints_enable: bool = true, + /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 + /// to always show them). + inlayHints_closingBraceHints_minLines: usize = 25, + /// Whether to show inlay hints for closure captures. + inlayHints_closureCaptureHints_enable: bool = false, + /// Whether to show inlay type hints for return types of closures. + inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, + /// Closure notation in type and chaining inlay hints. + inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, + /// Whether to show enum variant discriminant hints. + inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, + /// Whether to show inlay hints for type adjustments. + inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, + /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. + inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, + /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). + inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix, + /// Whether to show const generic parameter name inlay hints. + inlayHints_genericParameterHints_const_enable: bool= true, + /// Whether to show generic lifetime parameter name inlay hints. + inlayHints_genericParameterHints_lifetime_enable: bool = false, + /// Whether to show generic type parameter name inlay hints. + inlayHints_genericParameterHints_type_enable: bool = false, + /// Whether to show implicit drop hints. + inlayHints_implicitDrops_enable: bool = false, + /// Whether to show inlay type hints for elided lifetimes in function signatures. + inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, + /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. + inlayHints_lifetimeElisionHints_useParameterNames: bool = false, + /// Maximum length for inlay hints. Set to null to have an unlimited length. + inlayHints_maxLength: Option = Some(25), + /// Whether to show function parameter name inlay hints at the call + /// site. + inlayHints_parameterHints_enable: bool = true, + /// Whether to show exclusive range inlay hints. + inlayHints_rangeExclusiveHints_enable: bool = false, + /// Whether to show inlay hints for compiler inserted reborrows. + /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. + inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, + /// Whether to render leading colons for type hints, and trailing colons for parameter hints. + inlayHints_renderColons: bool = true, + /// Whether to show inlay type hints for variables. + inlayHints_typeHints_enable: bool = true, + /// Whether to hide inlay type hints for `let` statements that initialize to a closure. + /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + inlayHints_typeHints_hideClosureInitialization: bool = false, + /// Whether to hide inlay type hints for constructors. + inlayHints_typeHints_hideNamedConstructor: bool = false, + + /// Enables the experimental support for interpreting tests. + interpret_tests: bool = false, + + /// Join lines merges consecutive declaration and initialization of an assignment. + joinLines_joinAssignments: bool = true, + /// Join lines inserts else between consecutive ifs. + joinLines_joinElseIf: bool = true, + /// Join lines removes trailing commas. + joinLines_removeTrailingComma: bool = true, + /// Join lines unwraps trivial blocks. + joinLines_unwrapTrivialBlock: bool = true, + + /// Whether to show `Debug` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_debug_enable: bool = true, + /// Whether to show CodeLens in Rust files. + lens_enable: bool = true, + /// Whether to show `Implementations` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_implementations_enable: bool = true, + /// Where to render annotations. + lens_location: AnnotationLocation = AnnotationLocation::AboveName, + /// Whether to show `References` lens for Struct, Enum, and Union. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_adt_enable: bool = false, + /// Whether to show `References` lens for Enum Variants. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_enumVariant_enable: bool = false, + /// Whether to show `Method References` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_references_method_enable: bool = false, + /// Whether to show `References` lens for Trait. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_trait_enable: bool = false, + /// Whether to show `Run` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_run_enable: bool = true, + /// Disable project auto-discovery in favor of explicitly specified set /// of projects. /// @@ -97,6 +245,69 @@ config_data! { /// Sets the LRU capacity of the specified queries. lru_query_capacities: FxHashMap, u16> = FxHashMap::default(), + /// Whether to show `can't find Cargo.toml` error message. + notifications_cargoTomlNotFound: bool = true, + + /// How many worker threads in the main loop. The default `null` means to pick automatically. + numThreads: Option = None, + + /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. + procMacro_attributes_enable: bool = true, + /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. + procMacro_enable: bool = true, + /// Internal config, path to proc-macro server executable. + procMacro_server: Option = None, + + /// Exclude imports from find-all-references. + references_excludeImports: bool = false, + + /// Exclude tests from find-all-references. + references_excludeTests: bool = false, + + /// Inject additional highlighting into doc comments. + /// + /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra + /// doc links. + semanticHighlighting_doc_comment_inject_enable: bool = true, + /// Whether the server is allowed to emit non-standard tokens and modifiers. + semanticHighlighting_nonStandardTokens: bool = true, + /// Use semantic tokens for operators. + /// + /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when + /// they are tagged with modifiers. + semanticHighlighting_operator_enable: bool = true, + /// Use specialized semantic tokens for operators. + /// + /// When enabled, rust-analyzer will emit special token types for operator tokens instead + /// of the generic `operator` token type. + semanticHighlighting_operator_specialization_enable: bool = false, + /// Use semantic tokens for punctuation. + /// + /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when + /// they are tagged with modifiers or have a special role. + semanticHighlighting_punctuation_enable: bool = false, + /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro + /// calls. + semanticHighlighting_punctuation_separate_macro_bang: bool = false, + /// Use specialized semantic tokens for punctuation. + /// + /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead + /// of the generic `punctuation` token type. + semanticHighlighting_punctuation_specialization_enable: bool = false, + /// Use semantic tokens for strings. + /// + /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. + /// By disabling semantic tokens for strings, other grammars can be used to highlight + /// their contents. + semanticHighlighting_strings_enable: bool = true, + + /// Show full signature of the callable. Only shows parameters if disabled. + signatureInfo_detail: SignatureDetail = SignatureDetail::Full, + /// Show documentation. + signatureInfo_documentation_enable: bool = true, + + /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. + typing_autoClosingAngleBrackets_enable: bool = false, /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. @@ -531,216 +742,6 @@ config_data! { /// Controls file watching implementation. files_watcher: FilesWatcherDef = FilesWatcherDef::Client, - /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. - highlightRelated_breakPoints_enable: bool = true, - /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. - highlightRelated_closureCaptures_enable: bool = true, - /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). - highlightRelated_exitPoints_enable: bool = true, - /// Enables highlighting of related references while the cursor is on any identifier. - highlightRelated_references_enable: bool = true, - /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. - highlightRelated_yieldPoints_enable: bool = true, - - /// Whether to show `Debug` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_debug_enable: bool = true, - /// Whether to show HoverActions in Rust files. - hover_actions_enable: bool = true, - /// Whether to show `Go to Type Definition` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_gotoTypeDef_enable: bool = true, - /// Whether to show `Implementations` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_implementations_enable: bool = true, - /// Whether to show `References` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_references_enable: bool = false, - /// Whether to show `Run` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_run_enable: bool = true, - - /// Whether to show documentation on hover. - hover_documentation_enable: bool = true, - /// Whether to show keyword hover popups. Only applies when - /// `#rust-analyzer.hover.documentation.enable#` is set. - hover_documentation_keywords_enable: bool = true, - /// Use markdown syntax for links on hover. - hover_links_enable: bool = true, - /// How to render the align information in a memory layout hover. - hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), - /// Whether to show memory layout data on hover. - hover_memoryLayout_enable: bool = true, - /// How to render the niche information in a memory layout hover. - hover_memoryLayout_niches: Option = Some(false), - /// How to render the offset information in a memory layout hover. - hover_memoryLayout_offset: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), - /// How to render the size information in a memory layout hover. - hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - - /// How many variants of an enum to display when hovering on. Show none if empty. - hover_show_enumVariants: Option = Some(5), - /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. - hover_show_fields: Option = Some(5), - /// How many associated items of a trait to display when hovering a trait. - hover_show_traitAssocItems: Option = None, - - /// Whether to show inlay type hints for binding modes. - inlayHints_bindingModeHints_enable: bool = false, - /// Whether to show inlay type hints for method chains. - inlayHints_chainingHints_enable: bool = true, - /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. - inlayHints_closingBraceHints_enable: bool = true, - /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 - /// to always show them). - inlayHints_closingBraceHints_minLines: usize = 25, - /// Whether to show inlay hints for closure captures. - inlayHints_closureCaptureHints_enable: bool = false, - /// Whether to show inlay type hints for return types of closures. - inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, - /// Closure notation in type and chaining inlay hints. - inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, - /// Whether to show enum variant discriminant hints. - inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, - /// Whether to show inlay hints for type adjustments. - inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, - /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. - inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, - /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). - inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix, - /// Whether to show const generic parameter name inlay hints. - inlayHints_genericParameterHints_const_enable: bool= true, - /// Whether to show generic lifetime parameter name inlay hints. - inlayHints_genericParameterHints_lifetime_enable: bool = false, - /// Whether to show generic type parameter name inlay hints. - inlayHints_genericParameterHints_type_enable: bool = false, - /// Whether to show implicit drop hints. - inlayHints_implicitDrops_enable: bool = false, - /// Whether to show inlay type hints for elided lifetimes in function signatures. - inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, - /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. - inlayHints_lifetimeElisionHints_useParameterNames: bool = false, - /// Maximum length for inlay hints. Set to null to have an unlimited length. - inlayHints_maxLength: Option = Some(25), - /// Whether to show function parameter name inlay hints at the call - /// site. - inlayHints_parameterHints_enable: bool = true, - /// Whether to show exclusive range inlay hints. - inlayHints_rangeExclusiveHints_enable: bool = false, - /// Whether to show inlay hints for compiler inserted reborrows. - /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. - inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, - /// Whether to render leading colons for type hints, and trailing colons for parameter hints. - inlayHints_renderColons: bool = true, - /// Whether to show inlay type hints for variables. - inlayHints_typeHints_enable: bool = true, - /// Whether to hide inlay type hints for `let` statements that initialize to a closure. - /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. - inlayHints_typeHints_hideClosureInitialization: bool = false, - /// Whether to hide inlay type hints for constructors. - inlayHints_typeHints_hideNamedConstructor: bool = false, - - /// Enables the experimental support for interpreting tests. - interpret_tests: bool = false, - - /// Join lines merges consecutive declaration and initialization of an assignment. - joinLines_joinAssignments: bool = true, - /// Join lines inserts else between consecutive ifs. - joinLines_joinElseIf: bool = true, - /// Join lines removes trailing commas. - joinLines_removeTrailingComma: bool = true, - /// Join lines unwraps trivial blocks. - joinLines_unwrapTrivialBlock: bool = true, - - /// Whether to show `Debug` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_debug_enable: bool = true, - /// Whether to show CodeLens in Rust files. - lens_enable: bool = true, - /// Whether to show `Implementations` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_implementations_enable: bool = true, - /// Where to render annotations. - lens_location: AnnotationLocation = AnnotationLocation::AboveName, - /// Whether to show `References` lens for Struct, Enum, and Union. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_adt_enable: bool = false, - /// Whether to show `References` lens for Enum Variants. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_enumVariant_enable: bool = false, - /// Whether to show `Method References` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_references_method_enable: bool = false, - /// Whether to show `References` lens for Trait. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_trait_enable: bool = false, - /// Whether to show `Run` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_run_enable: bool = true, - - /// Whether to show `can't find Cargo.toml` error message. - notifications_cargoTomlNotFound: bool = true, - - /// How many worker threads in the main loop. The default `null` means to pick automatically. - numThreads: Option = None, - - /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. - procMacro_attributes_enable: bool = true, - /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. - procMacro_enable: bool = true, - /// Internal config, path to proc-macro server executable. - procMacro_server: Option = None, - - /// Exclude imports from find-all-references. - references_excludeImports: bool = false, - - /// Exclude tests from find-all-references. - references_excludeTests: bool = false, - - /// Inject additional highlighting into doc comments. - /// - /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra - /// doc links. - semanticHighlighting_doc_comment_inject_enable: bool = true, - /// Whether the server is allowed to emit non-standard tokens and modifiers. - semanticHighlighting_nonStandardTokens: bool = true, - /// Use semantic tokens for operators. - /// - /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when - /// they are tagged with modifiers. - semanticHighlighting_operator_enable: bool = true, - /// Use specialized semantic tokens for operators. - /// - /// When enabled, rust-analyzer will emit special token types for operator tokens instead - /// of the generic `operator` token type. - semanticHighlighting_operator_specialization_enable: bool = false, - /// Use semantic tokens for punctuation. - /// - /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when - /// they are tagged with modifiers or have a special role. - semanticHighlighting_punctuation_enable: bool = false, - /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro - /// calls. - semanticHighlighting_punctuation_separate_macro_bang: bool = false, - /// Use specialized semantic tokens for punctuation. - /// - /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead - /// of the generic `punctuation` token type. - semanticHighlighting_punctuation_specialization_enable: bool = false, - /// Use semantic tokens for strings. - /// - /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. - /// By disabling semantic tokens for strings, other grammars can be used to highlight - /// their contents. - semanticHighlighting_strings_enable: bool = true, - - /// Show full signature of the callable. Only shows parameters if disabled. - signatureInfo_detail: SignatureDetail = SignatureDetail::Full, - /// Show documentation. - signatureInfo_documentation_enable: bool = true, - - /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. - typing_autoClosingAngleBrackets_enable: bool = false, } } From 0e5270672d5e95460c4dfbdfd4775e106697195d Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 22 Aug 2024 21:50:40 +0200 Subject: [PATCH 0164/1301] Remove invalid tests --- .../crates/rust-analyzer/src/config.rs | 72 ------------------- 1 file changed, 72 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e7c5f989d93e7..8e41b43bfe0b5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -3585,76 +3585,4 @@ mod tests { matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } - - #[test] - fn toml_unknown_key() { - let config = - Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None); - - let mut change = ConfigChange::default(); - - change.change_user_config(Some( - toml::toml! { - [cargo.cfgs] - these = "these" - should = "should" - be = "be" - valid = "valid" - - [invalid.config] - err = "error" - - [cargo] - target = "ok" - - // FIXME: This should be an error - [cargo.sysroot] - non-table = "expected" - } - .to_string() - .into(), - )); - - let (config, e, _) = config.apply_change(change); - expect_test::expect![[r#" - ConfigErrors( - [ - Toml { - config_key: "invalid/config/err", - error: Error { - inner: Error { - inner: TomlError { - message: "unexpected field", - raw: None, - keys: [], - span: None, - }, - }, - }, - }, - ], - ) - "#]] - .assert_debug_eq(&e); - let mut change = ConfigChange::default(); - - change.change_user_config(Some( - toml::toml! { - [cargo.cfgs] - these = "these" - should = "should" - be = "be" - valid = "valid" - } - .to_string() - .into(), - )); - let (_, e, _) = config.apply_change(change); - expect_test::expect![[r#" - ConfigErrors( - [], - ) - "#]] - .assert_debug_eq(&e); - } } From 9edea1eea060e8b176164bcb05a9e1de11606b88 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Wed, 28 Aug 2024 19:40:58 +0200 Subject: [PATCH 0165/1301] Minor errors --- src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 8e41b43bfe0b5..0a1bdf49bdbd2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1897,7 +1897,7 @@ impl Config { CargoFeaturesDef::Selected(it) => it, }, extra_args: self.extra_args(source_root).clone(), - extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), + extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), extra_env: self.extra_env(source_root).clone(), target_dir: self.target_dir_from_config(source_root), } @@ -1954,7 +1954,7 @@ impl Config { CargoFeaturesDef::Selected(it) => it, }, extra_args: self.check_extra_args(source_root), - extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), + extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), extra_env: self.check_extra_env(source_root), target_dir: self.target_dir_from_config(source_root), }, From e691f6d040321b9c872ab702ecf115bbd747957a Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 28 Aug 2024 16:29:18 -0700 Subject: [PATCH 0166/1301] internal: Add doc comments to OpQueue --- .../crates/rust-analyzer/src/op_queue.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs index eab9733872402..5c4c858e1509b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs @@ -37,9 +37,13 @@ impl Default for OpQueue { } impl OpQueue { + /// Request an operation to start. pub(crate) fn request_op(&mut self, reason: Cause, args: Args) { self.op_requested = Some((reason, args)); } + + /// If there was an operation requested, mark this queue as + /// started and return the request arguments. pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> { if self.op_in_progress { return None; @@ -47,18 +51,25 @@ impl OpQueue { self.op_in_progress = self.op_requested.is_some(); self.op_requested.take() } + + /// Mark an operation as completed. pub(crate) fn op_completed(&mut self, result: Output) { assert!(self.op_in_progress); self.op_in_progress = false; self.last_op_result = result; } + /// Get the result of the last operation. pub(crate) fn last_op_result(&self) -> &Output { &self.last_op_result } + + // Is there an operation that has started, but hasn't yet finished? pub(crate) fn op_in_progress(&self) -> bool { self.op_in_progress } + + // Has an operation been requested? pub(crate) fn op_requested(&self) -> bool { self.op_requested.is_some() } From e759e0b7397b4343ac9dee881e177a9b8421c436 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 28 Aug 2024 21:30:05 -0400 Subject: [PATCH 0167/1301] add guarantee about remove_dir and remove_file error kinds approved in ACP https://github.com/rust-lang/libs-team/issues/433 --- library/std/src/fs.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 6a0d9f47960ec..14e6ee467979f 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1989,6 +1989,11 @@ impl AsInner for DirEntry { /// * The file doesn't exist. /// * The user lacks permissions to remove the file. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run @@ -2446,6 +2451,11 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// * The user lacks permissions to remove the directory at the provided `path`. /// * The directory isn't empty. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run From 5bbb0d41f1637d3b6a54ded065e807a48c67aec0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 29 Aug 2024 00:17:40 -0400 Subject: [PATCH 0168/1301] Stop using ty::GenericPredicates for non-predicates_of queries --- clippy_lints/src/implied_bounds_in_impls.rs | 2 +- clippy_lints/src/methods/type_id_on_box.rs | 3 +-- clippy_lints/src/needless_maybe_sized.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index 67b48878ca513..6794c6cabfeef 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -246,7 +246,7 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds && let [.., path] = poly_trait.trait_ref.path.segments && poly_trait.bound_generic_params.is_empty() && let Some(trait_def_id) = path.res.opt_def_id() - && let predicates = cx.tcx.explicit_super_predicates_of(trait_def_id).predicates + && let predicates = cx.tcx.explicit_super_predicates_of(trait_def_id).skip_binder() // If the trait has no supertrait, there is no need to collect anything from that bound && !predicates.is_empty() { diff --git a/clippy_lints/src/methods/type_id_on_box.rs b/clippy_lints/src/methods/type_id_on_box.rs index b62ecef0069af..db8cc4595d4df 100644 --- a/clippy_lints/src/methods/type_id_on_box.rs +++ b/clippy_lints/src/methods/type_id_on_box.rs @@ -25,8 +25,7 @@ fn is_subtrait_of_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { || cx .tcx .explicit_super_predicates_of(tr.def_id) - .predicates - .iter() + .iter_identity_copied() .any(|(clause, _)| { matches!(clause.kind().skip_binder(), ty::ClauseKind::Trait(super_tr) if cx.tcx.is_diagnostic_item(sym::Any, super_tr.def_id())) diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index a1d8ec3b32ec9..62e41088f3708 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -91,7 +91,7 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> return true; } - for &(predicate, _) in cx.tcx.explicit_super_predicates_of(trait_def_id).predicates { + for (predicate, _) in cx.tcx.explicit_super_predicates_of(trait_def_id).iter_identity_copied() { if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() && trait_predicate.polarity == PredicatePolarity::Positive && !path.contains(&trait_predicate.def_id()) From 00eca77b7230732f0ba9a88b434022177de7da17 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Thu, 29 Aug 2024 10:32:59 +0200 Subject: [PATCH 0169/1301] Use simpler branchy swap logic in tiny merge sort Avoids the code duplication issue and results in smaller binary size, which after all is the purpose of the feature. --- .../core/src/slice/sort/shared/smallsort.rs | 5 ++ library/core/src/slice/sort/stable/tiny.rs | 50 +++---------------- 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 5027962ccb415..aaa9f9f1c7fd1 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -378,6 +378,11 @@ where /// Swap two values in the slice pointed to by `v_base` at the position `a_pos` and `b_pos` if the /// value at position `b_pos` is less than the one at position `a_pos`. +/// +/// Purposefully not marked `#[inline]`, despite us wanting it to be inlined for integers like +/// types. `is_less` could be a huge function and we want to give the compiler an option to +/// not inline this function. For the same reasons that this function is very perf critical +/// it should be in the same module as the functions that use it. unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) where F: FnMut(&T, &T) -> bool, diff --git a/library/core/src/slice/sort/stable/tiny.rs b/library/core/src/slice/sort/stable/tiny.rs index 307011f3ee8c4..071ab8e107fe3 100644 --- a/library/core/src/slice/sort/stable/tiny.rs +++ b/library/core/src/slice/sort/stable/tiny.rs @@ -1,6 +1,6 @@ //! Binary-size optimized mergesort inspired by https://github.com/voultapher/tiny-sort-rs. -use crate::mem::{ManuallyDrop, MaybeUninit}; +use crate::mem::MaybeUninit; use crate::ptr; use crate::slice::sort::stable::merge; @@ -27,49 +27,15 @@ pub fn mergesort bool>( merge::merge(v, scratch, mid, is_less); } else if len == 2 { - // Branchless swap the two elements. This reduces the recursion depth and improves - // perf significantly at a small binary-size cost. Trades ~10% perf boost for integers - // for ~50 bytes in the binary. - // SAFETY: We checked the len, the pointers we create are valid and don't overlap. unsafe { - swap_if_less(v.as_mut_ptr(), 0, 1, is_less); - } - } -} - -/// Swap two values in the slice pointed to by `v_base` at the position `a_pos` and `b_pos` if the -/// value at position `b_pos` is less than the one at position `a_pos`. -unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - // SAFETY: the caller must guarantee that `a` and `b` each added to `v_base` yield valid - // pointers into `v_base`, and are properly aligned, and part of the same allocation. - unsafe { - let v_a = v_base.add(a_pos); - let v_b = v_base.add(b_pos); + let v_base = v.as_mut_ptr(); + let v_a = v_base; + let v_b = v_base.add(1); - // PANIC SAFETY: if is_less panics, no scratch memory was created and the slice should still be - // in a well defined state, without duplicates. - - // Important to only swap if it is more and not if it is equal. is_less should return false for - // equal, so we don't swap. - let should_swap = is_less(&*v_b, &*v_a); - - // This is a branchless version of swap if. - // The equivalent code with a branch would be: - // - // if should_swap { - // ptr::swap(left, right, 1); - // } - - // The goal is to generate cmov instructions here. - let left_swap = if should_swap { v_b } else { v_a }; - let right_swap = if should_swap { v_a } else { v_b }; - - let right_swap_tmp = ManuallyDrop::new(ptr::read(right_swap)); - ptr::copy(left_swap, v_a, 1); - ptr::copy_nonoverlapping(&*right_swap_tmp, v_b, 1); + if is_less(&*v_b, &*v_a) { + ptr::swap_nonoverlapping(v_a, v_b, 1); + } + } } } From a54a7a8a6afb6d67a5a66806068e60f55a0947e6 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Wed, 7 Aug 2024 03:16:24 +0900 Subject: [PATCH 0170/1301] feat: Implement object safety --- .../crates/hir-ty/src/chalk_db.rs | 6 +- .../crates/hir-ty/src/consteval/tests.rs | 8 +- .../hir-ty/src/consteval/tests/intrinsics.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 6 +- .../crates/hir-ty/src/generics.rs | 17 + .../rust-analyzer/crates/hir-ty/src/lib.rs | 1 + .../rust-analyzer/crates/hir-ty/src/lower.rs | 18 +- .../crates/hir-ty/src/mir/eval/tests.rs | 2 +- .../crates/hir-ty/src/object_safety.rs | 631 ++++++++++++++++++ .../crates/hir-ty/src/object_safety/tests.rs | 363 ++++++++++ .../crates/hir-ty/src/tests/regression.rs | 2 +- .../crates/hir-ty/src/tests/traits.rs | 62 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 7 +- .../replace_filter_map_next_with_find_map.rs | 2 +- .../crates/ide/src/hover/render.rs | 74 +- .../crates/ide/src/hover/tests.rs | 24 +- .../crates/ide/src/inlay_hints/adjustment.rs | 4 +- .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/test-utils/src/minicore.rs | 30 +- 19 files changed, 1194 insertions(+), 66 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index a3e4da5d1afd7..e74e3d789883e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -381,9 +381,9 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { TyKind::Error.intern(Interner) } - fn is_object_safe(&self, _trait_id: chalk_ir::TraitId) -> bool { - // FIXME: implement actual object safety - true + fn is_object_safe(&self, trait_id: chalk_ir::TraitId) -> bool { + let trait_ = from_chalk_trait_id(trait_id); + crate::object_safety::object_safety(self.db, trait_).is_none() } fn closure_kind( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 86228250c2005..c4cbaaa30175b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -2007,7 +2007,7 @@ fn function_traits() { ); check_number( r#" - //- minicore: coerce_unsized, fn + //- minicore: coerce_unsized, fn, dispatch_from_dyn fn add2(x: u8) -> u8 { x + 2 } @@ -2062,7 +2062,7 @@ fn function_traits() { fn dyn_trait() { check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait Foo { fn foo(&self) -> u8 { 10 } } @@ -2085,7 +2085,7 @@ fn dyn_trait() { ); check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait Foo { fn foo(&self) -> i32 { 10 } } @@ -2109,7 +2109,7 @@ fn dyn_trait() { ); check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait A { fn x(&self) -> i32; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 5972b80d1696b..6f294494c01eb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -89,7 +89,7 @@ fn size_of_val() { ); check_number( r#" - //- minicore: coerce_unsized, fmt, builtin_impls + //- minicore: coerce_unsized, fmt, builtin_impls, dispatch_from_dyn extern "rust-intrinsic" { pub fn size_of_val(_: *const T) -> usize; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9a1f2158bf753..ebbe8a448cc4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -24,6 +24,7 @@ use crate::{ lower::{GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, + object_safety::ObjectSafetyViolation, Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, }; @@ -107,6 +108,9 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Result, Arc>; + #[salsa::invoke(crate::object_safety::object_safety_of_trait_query)] + fn object_safety_of_trait(&self, trait_: TraitId) -> Option; + #[salsa::invoke(crate::lower::ty_query)] #[salsa::cycle(crate::lower::ty_recover)] fn ty(&self, def: TyDefId) -> Binders; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index a96c101a388be..89ca707c2e697 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -225,6 +225,23 @@ impl Generics { } } +pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { + match def { + GenericDefId::TraitId(_) | GenericDefId::TraitAliasId(_) => { + let params = db.generic_params(def); + params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) + } + GenericDefId::ImplId(_) => None, + _ => { + let parent_def = parent_generic_def(db, def)?; + let parent_params = db.generic_params(parent_def); + let parent_self_idx = parent_params.trait_self_param()?.into_raw().into_u32() as usize; + let self_params = db.generic_params(def); + Some(self_params.len() + parent_self_idx) + } + } +} + fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 46611ae0de926..efd6e51b3704c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -40,6 +40,7 @@ pub mod lang_items; pub mod layout; pub mod method_resolution; pub mod mir; +pub mod object_safety; pub mod primitive; pub mod traits; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 370d9ba99cb0c..841e0216ae2b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -58,7 +58,7 @@ use crate::{ }, db::HirDatabase, error_lifetime, - generics::{generics, Generics}, + generics::{generics, trait_self_param_idx, Generics}, make_binders, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, @@ -1747,21 +1747,7 @@ fn implicitly_sized_clauses<'a, 'subst: 'a>( .lang_item(resolver.krate(), LangItem::Sized) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id))?; - let get_trait_self_idx = |container: ItemContainerId| { - if matches!(container, ItemContainerId::TraitId(_)) { - let generics = generics(db.upcast(), def); - Some(generics.len_self()) - } else { - None - } - }; - let trait_self_idx = match def { - GenericDefId::TraitId(_) => Some(0), - GenericDefId::FunctionId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - GenericDefId::ConstId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - GenericDefId::TypeAliasId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - _ => None, - }; + let trait_self_idx = trait_self_param_idx(db.upcast(), def); Some( substitution diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 371a5278dc9a5..6825bc4862ac9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -849,7 +849,7 @@ fn main() { fn regression_14966() { check_pass( r#" -//- minicore: fn, copy, coerce_unsized +//- minicore: fn, copy, coerce_unsized, dispatch_from_dyn trait A { fn a(&self) {} } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs new file mode 100644 index 0000000000000..f3bbb6b04f973 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs @@ -0,0 +1,631 @@ +//! Compute the object-safety of a trait + +use std::ops::ControlFlow; + +use chalk_ir::{ + cast::Cast, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, +}; +use chalk_solve::rust_ir::InlineBound; +use hir_def::{ + lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, + TypeAliasId, +}; +use rustc_hash::{FxHashMap, FxHashSet}; +use smallvec::SmallVec; + +use crate::{ + all_super_traits, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, + generics::{generics, trait_self_param_idx}, + lower::callable_item_sig, + to_assoc_type_id, to_chalk_trait_id, + utils::elaborate_clause_supertraits, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, + ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ObjectSafetyViolation { + SizedSelf, + SelfReferential, + Method(FunctionId, MethodViolationCode), + AssocConst(ConstId), + GAT(TypeAliasId), + // This doesn't exist in rustc, but added for better visualization + HasNonSafeSuperTrait(TraitId), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum MethodViolationCode { + StaticMethod, + ReferencesSelfInput, + ReferencesSelfOutput, + ReferencesImplTraitInTrait, + AsyncFn, + WhereClauseReferencesSelf, + Generic, + UndispatchableReceiver, +} + +pub fn object_safety(db: &dyn HirDatabase, trait_: TraitId) -> Option { + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.object_safety_of_trait(super_trait).is_some() { + return Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait)); + } + } + + db.object_safety_of_trait(trait_) +} + +pub fn object_safety_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.object_safety_of_trait(super_trait).is_some() { + cb(ObjectSafetyViolation::HasNonSafeSuperTrait(trait_))?; + } + } + + object_safety_of_trait_with_callback(db, trait_, cb) +} + +pub fn object_safety_of_trait_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + // Check whether this has a `Sized` bound + if generics_require_sized_self(db, trait_.into()) { + cb(ObjectSafetyViolation::SizedSelf)?; + } + + // Check if there exist bounds that referencing self + if predicates_reference_self(db, trait_) { + cb(ObjectSafetyViolation::SelfReferential)?; + } + if bounds_reference_self(db, trait_) { + cb(ObjectSafetyViolation::SelfReferential)?; + } + + // rustc checks for non-lifetime binders here, but we don't support HRTB yet + + let trait_data = db.trait_data(trait_); + for (_, assoc_item) in &trait_data.items { + object_safety_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; + } + + ControlFlow::Continue(()) +} + +pub fn object_safety_of_trait_query( + db: &dyn HirDatabase, + trait_: TraitId, +) -> Option { + let mut res = None; + object_safety_of_trait_with_callback(db, trait_, &mut |osv| { + res = Some(osv); + ControlFlow::Break(()) + }); + + res +} + +fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool { + let krate = def.module(db.upcast()).krate(); + let Some(sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else { + return false; + }; + + let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { + return false; + }; + + let predicates = &*db.generic_predicates(def); + let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); + elaborate_clause_supertraits(db, predicates).any(|pred| match pred { + WhereClause::Implemented(trait_ref) => { + if from_chalk_trait_id(trait_ref.trait_id) == sized { + if let TyKind::BoundVar(it) = + *trait_ref.self_type_parameter(Interner).kind(Interner) + { + // Since `generic_predicates` is `Binder>`, the `DebrujinIndex` of + // self-parameter is `1` + return it + .index_if_bound_at(DebruijnIndex::ONE) + .is_some_and(|idx| idx == trait_self_param_idx); + } + } + false + } + _ => false, + }) +} + +// rustc gathers all the spans that references `Self` for error rendering, +// but we don't have good way to render such locations. +// So, just return single boolean value for existence of such `Self` reference +fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { + db.generic_predicates(trait_.into()) + .iter() + .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) +} + +// Same as the above, `predicates_reference_self` +fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { + let trait_data = db.trait_data(trait_); + trait_data + .items + .iter() + .filter_map(|(_, it)| match *it { + AssocItemId::TypeAliasId(id) => { + let assoc_ty_id = to_assoc_type_id(id); + let assoc_ty_data = db.associated_ty_data(assoc_ty_id); + Some(assoc_ty_data) + } + _ => None, + }) + .any(|assoc_ty_data| { + assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| { + let def = from_assoc_type_id(assoc_ty_data.id).into(); + match bound.skip_binders() { + InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| { + contains_illegal_self_type_reference( + db, + def, + trait_, + arg, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) + }), + InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| { + contains_illegal_self_type_reference( + db, + def, + trait_, + arg, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) + }), + } + }) + }) +} + +#[derive(Clone, Copy)] +enum AllowSelfProjection { + Yes, + No, +} + +fn predicate_references_self( + db: &dyn HirDatabase, + trait_: TraitId, + predicate: &Binders>, + allow_self_projection: AllowSelfProjection, +) -> bool { + match predicate.skip_binders().skip_binders() { + WhereClause::Implemented(trait_ref) => { + trait_ref.substitution.iter(Interner).skip(1).any(|arg| { + contains_illegal_self_type_reference( + db, + trait_.into(), + trait_, + arg, + DebruijnIndex::ONE, + allow_self_projection, + ) + }) + } + WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => { + proj.substitution.iter(Interner).skip(1).any(|arg| { + contains_illegal_self_type_reference( + db, + trait_.into(), + trait_, + arg, + DebruijnIndex::ONE, + allow_self_projection, + ) + }) + } + _ => false, + } +} + +fn contains_illegal_self_type_reference>( + db: &dyn HirDatabase, + def: GenericDefId, + trait_: TraitId, + t: &T, + outer_binder: DebruijnIndex, + allow_self_projection: AllowSelfProjection, +) -> bool { + let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { + return false; + }; + struct IllegalSelfTypeVisitor<'a> { + db: &'a dyn HirDatabase, + trait_: TraitId, + super_traits: Option>, + trait_self_param_idx: usize, + allow_self_projection: AllowSelfProjection, + } + impl<'a> TypeVisitor for IllegalSelfTypeVisitor<'a> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + match ty.kind(Interner) { + TyKind::BoundVar(BoundVar { debruijn, index }) => { + if *debruijn == outer_binder && *index == self.trait_self_param_idx { + ControlFlow::Break(()) + } else { + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + TyKind::Alias(AliasTy::Projection(proj)) => match self.allow_self_projection { + AllowSelfProjection::Yes => { + let trait_ = proj.trait_(self.db); + if self.super_traits.is_none() { + self.super_traits = + Some(all_super_traits(self.db.upcast(), self.trait_)); + } + if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + AllowSelfProjection::No => ty.super_visit_with(self.as_dyn(), outer_binder), + }, + _ => ty.super_visit_with(self.as_dyn(), outer_binder), + } + } + + fn visit_const( + &mut self, + constant: &chalk_ir::Const, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + constant.data(Interner).ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + + let mut visitor = IllegalSelfTypeVisitor { + db, + trait_, + super_traits: None, + trait_self_param_idx, + allow_self_projection, + }; + t.visit_with(visitor.as_dyn(), outer_binder).is_break() +} + +fn object_safety_violation_for_assoc_item( + db: &dyn HirDatabase, + trait_: TraitId, + item: AssocItemId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + // Any item that has a `Self : Sized` requisite is otherwise + // exempt from the regulations. + if generics_require_sized_self(db, item.into()) { + return ControlFlow::Continue(()); + } + + match item { + AssocItemId::ConstId(it) => cb(ObjectSafetyViolation::AssocConst(it)), + AssocItemId::FunctionId(it) => { + virtual_call_violations_for_method(db, trait_, it, &mut |mvc| { + cb(ObjectSafetyViolation::Method(it, mvc)) + }) + } + AssocItemId::TypeAliasId(it) => { + let def_map = db.crate_def_map(trait_.krate(db.upcast())); + if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { + ControlFlow::Continue(()) + } else { + let generic_params = db.generic_params(item.into()); + if generic_params.len_type_or_consts() > 0 { + cb(ObjectSafetyViolation::GAT(it)) + } else { + ControlFlow::Continue(()) + } + } + } + } +} + +fn virtual_call_violations_for_method( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(MethodViolationCode) -> ControlFlow<()>, +{ + let func_data = db.function_data(func); + if !func_data.has_self_param() { + cb(MethodViolationCode::StaticMethod)?; + } + + if func_data.is_async() { + cb(MethodViolationCode::AsyncFn)?; + } + + let sig = callable_item_sig(db, func.into()); + if sig.skip_binders().params().iter().skip(1).any(|ty| { + contains_illegal_self_type_reference( + db, + func.into(), + trait_, + ty, + DebruijnIndex::INNERMOST, + AllowSelfProjection::Yes, + ) + }) { + cb(MethodViolationCode::ReferencesSelfInput)?; + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + sig.skip_binders().ret(), + DebruijnIndex::INNERMOST, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::ReferencesSelfOutput)?; + } + + if !func_data.is_async() { + if let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) { + cb(mvc)?; + } + } + + let generic_params = db.generic_params(func.into()); + if generic_params.len_type_or_consts() > 0 { + cb(MethodViolationCode::Generic)?; + } + + if func_data.has_self_param() && !receiver_is_dispatchable(db, trait_, func, &sig) { + cb(MethodViolationCode::UndispatchableReceiver)?; + } + + let predicates = &*db.generic_predicates(func.into()); + let mut parent_predicates = (*db.generic_predicates(trait_.into())) + .iter() + .map(|b| b.skip_binders().skip_binders().clone()) + .fold(FxHashMap::default(), |mut acc, item| { + acc.entry(item) + .and_modify(|cnt| { + *cnt += 1; + }) + .or_insert(1); + acc + }); + let trait_self_idx = trait_self_param_idx(db.upcast(), func.into()); + for pred in predicates { + let pred = pred.skip_binders().skip_binders(); + + // Skip predicates from parent, i.e. the trait that contains this method + if let Some(cnt) = parent_predicates.get_mut(pred) { + if *cnt > 0 { + *cnt -= 1; + continue; + } + } + + if matches!(pred, WhereClause::TypeOutlives(_)) { + continue; + } + + // Allow `impl AutoTrait` predicates + if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { + let trait_data = db.trait_data(from_chalk_trait_id(*trait_id)); + if trait_data.is_auto + && substitution + .as_slice(Interner) + .first() + .and_then(|arg| arg.ty(Interner)) + .and_then(|ty| ty.bound_var(Interner)) + .is_some_and(|b| { + b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx + }) + { + continue; + } + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + pred, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::WhereClauseReferencesSelf)?; + break; + } + } + + ControlFlow::Continue(()) +} + +fn receiver_is_dispatchable( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + sig: &Binders, +) -> bool { + let Some(trait_self_idx) = trait_self_param_idx(db.upcast(), func.into()) else { + return false; + }; + + // `self: Self` can't be dispatched on, but this is already considered object safe. + // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437 + if sig + .skip_binders() + .params() + .first() + .and_then(|receiver| receiver.bound_var(Interner)) + .is_some_and(|b| { + b == BoundVar { debruijn: DebruijnIndex::INNERMOST, index: trait_self_idx } + }) + { + return true; + } + + let placeholder_subst = generics(db.upcast(), func.into()).placeholder_subst(db); + + let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); + let Some(receiver_ty) = substituted_sig.params().first() else { + return false; + }; + + let krate = func.module(db.upcast()).krate(); + let traits = ( + db.lang_item(krate, LangItem::Unsize).and_then(|it| it.as_trait()), + db.lang_item(krate, LangItem::DispatchFromDyn).and_then(|it| it.as_trait()), + ); + let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { + return false; + }; + + // Type `U` + let unsized_self_ty = + TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner); + // `Receiver[Self => U]` + let Some(unsized_receiver_ty) = receiver_for_self_ty(db, func, unsized_self_ty.clone()) else { + return false; + }; + + let self_ty = placeholder_subst.as_slice(Interner)[trait_self_idx].assert_ty_ref(Interner); + let unsized_predicate = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(unsize_did), + substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), + }); + let trait_predicate = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(trait_), + substitution: Substitution::from_iter( + Interner, + std::iter::once(unsized_self_ty.clone().cast(Interner)) + .chain(placeholder_subst.iter(Interner).skip(1).cloned()), + ), + }); + + let generic_predicates = &*db.generic_predicates(func.into()); + + let clauses = std::iter::once(unsized_predicate) + .chain(std::iter::once(trait_predicate)) + .chain(generic_predicates.iter().map(|pred| { + pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 + })) + .map(|pred| { + pred.cast::>(Interner).into_from_env_clause(Interner) + }); + let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + + let obligation = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(dispatch_from_dyn_did), + substitution: Substitution::from_iter(Interner, [receiver_ty.clone(), unsized_receiver_ty]), + }); + let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(obligation)).intern(Interner); + + let in_env = chalk_ir::InEnvironment::new(&env, goal); + + let mut table = chalk_solve::infer::InferenceTable::::new(); + let canonicalized = table.canonicalize(Interner, in_env); + let solution = db.trait_solve(krate, None, canonicalized.quantified); + + matches!(solution, Some(Solution::Unique(_))) +} + +fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { + let generics = generics(db.upcast(), func.into()); + let trait_self_idx = trait_self_param_idx(db.upcast(), func.into())?; + let subst = generics.placeholder_subst(db); + let subst = Substitution::from_iter( + Interner, + subst.iter(Interner).enumerate().map(|(idx, arg)| { + if idx == trait_self_idx { + ty.clone().cast(Interner) + } else { + arg.clone() + } + }), + ); + let sig = callable_item_sig(db, func.into()); + let sig = sig.substitute(Interner, &subst); + sig.params_and_return.first().cloned() +} + +fn contains_illegal_impl_trait_in_trait( + db: &dyn HirDatabase, + sig: &Binders, +) -> Option { + struct OpaqueTypeCollector(FxHashSet); + + impl TypeVisitor for OpaqueTypeCollector { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { + self.0.insert(*opaque_ty_id); + } + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + + let ret = sig.skip_binders().ret(); + let mut visitor = OpaqueTypeCollector(FxHashSet::default()); + ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + + // Since we haven't implemented RPITIT in proper way like rustc yet, + // just check whether `ret` contains RPIT for now + for opaque_ty in visitor.0 { + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.into()); + if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { + return Some(MethodViolationCode::ReferencesImplTraitInTrait); + } + } + + None +} + +#[cfg(test)] +mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs new file mode 100644 index 0000000000000..585313e5b9bf8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs @@ -0,0 +1,363 @@ +use std::ops::ControlFlow; + +use hir_def::db::DefDatabase; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::ToSmolStr; +use test_fixture::WithFixture; + +use crate::{object_safety::object_safety_with_callback, test_db::TestDB}; + +use super::{ + MethodViolationCode::{self, *}, + ObjectSafetyViolation, +}; + +use ObjectSafetyViolationKind::*; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum ObjectSafetyViolationKind { + SizedSelf, + SelfReferential, + Method(MethodViolationCode), + AssocConst, + GAT, + HasNonSafeSuperTrait, +} + +fn check_object_safety<'a>( + ra_fixture: &str, + expected: impl IntoIterator)>, +) { + let mut expected: FxHashMap<_, _> = + expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect(); + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + for (trait_id, name) in file_ids.into_iter().flat_map(|file_id| { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + scope + .declarations() + .filter_map(|def| { + if let hir_def::ModuleDefId::TraitId(trait_id) = def { + let name = + db.trait_data(trait_id).name.display_no_db(file_id.edition()).to_smolstr(); + Some((trait_id, name)) + } else { + None + } + }) + .collect::>() + }) { + let Some(expected) = expected.remove(name.as_str()) else { + continue; + }; + let mut osvs = FxHashSet::default(); + object_safety_with_callback(&db, trait_id, &mut |osv| { + osvs.insert(match osv { + ObjectSafetyViolation::SizedSelf => SizedSelf, + ObjectSafetyViolation::SelfReferential => SelfReferential, + ObjectSafetyViolation::Method(_, mvc) => Method(mvc), + ObjectSafetyViolation::AssocConst(_) => AssocConst, + ObjectSafetyViolation::GAT(_) => GAT, + ObjectSafetyViolation::HasNonSafeSuperTrait(_) => HasNonSafeSuperTrait, + }); + ControlFlow::Continue(()) + }); + assert_eq!(osvs, expected, "Object safety violations for `{name}` do not match;"); + } + + let remains: Vec<_> = expected.keys().collect(); + assert!(remains.is_empty(), "Following traits do not exist in the test fixture; {remains:?}"); +} + +#[test] +fn item_bounds_can_reference_self() { + check_object_safety( + r#" +//- minicore: eq +pub trait Foo { + type X: PartialEq; + type Y: PartialEq; + type Z: PartialEq; +} +"#, + [("Foo", vec![])], + ); +} + +#[test] +fn associated_consts() { + check_object_safety( + r#" +trait Bar { + const X: usize; +} +"#, + [("Bar", vec![AssocConst])], + ); +} + +#[test] +fn bounds_reference_self() { + check_object_safety( + r#" +//- minicore: eq +trait X { + type U: PartialEq; +} +"#, + [("X", vec![SelfReferential])], + ); +} + +#[test] +fn by_value_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(self); +} + +trait Baz { + fn baz(self: Self); +} + +trait Quux { + // Legal because of the where clause: + fn baz(self: Self) where Self : Sized; +} +"#, + [("Bar", vec![]), ("Baz", vec![]), ("Quux", vec![])], + ); +} + +#[test] +fn generic_methods() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, t: T); +} + +trait Quux { + fn bar(&self, t: T) + where Self : Sized; +} + +trait Qax { + fn bar<'a>(&self, t: &'a ()); +} +"#, + [("Bar", vec![Method(Generic)]), ("Quux", vec![]), ("Qax", vec![])], + ); +} + +#[test] +fn mentions_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &Self); +} + +trait Baz { + fn baz(&self) -> Self; +} + +trait Quux { + fn quux(&self, s: &Self) -> Self where Self : Sized; +} +"#, + [ + ("Bar", vec![Method(ReferencesSelfInput)]), + ("Baz", vec![Method(ReferencesSelfOutput)]), + ("Quux", vec![]), + ], + ); +} + +#[test] +fn no_static() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo() {} +} +"#, + [("Foo", vec![Method(StaticMethod)])], + ); +} + +#[test] +fn sized_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar: Sized { + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); + + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar + where Self : Sized +{ + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); +} + +#[test] +fn supertrait_gat() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait GatTrait { + type Gat; +} + +trait SuperTrait: GatTrait {} +"#, + [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonSafeSuperTrait])], + ); +} + +#[test] +fn supertrait_mentions_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &T); +} + +trait Baz : Bar { +} +"#, + [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])], + ); +} + +#[test] +fn rustc_issue_19538() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo(&self, val: T); +} + +trait Bar: Foo {} +"#, + [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonSafeSuperTrait])], + ); +} + +#[test] +fn rustc_issue_22040() { + check_object_safety( + r#" +//- minicore: fmt, eq, dispatch_from_dyn +use core::fmt::Debug; + +trait Expr: Debug + PartialEq { + fn print_element_count(&self); +} +"#, + [("Expr", vec![SelfReferential])], + ); +} + +#[test] +fn rustc_issue_102762() { + check_object_safety( + r#" +//- minicore: future, send, sync, dispatch_from_dyn, deref +use core::pin::Pin; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +struct Vec {} + +pub trait Fetcher: Send + Sync { + fn get<'a>(self: &'a Box) -> Pin> + 'a>> + where + Self: Sync, + { + loop {} + } +} +"#, + [("Fetcher", vec![Method(UndispatchableReceiver)])], + ); +} + +#[test] +fn rustc_issue_102933() { + check_object_safety( + r#" +//- minicore: future, dispatch_from_dyn, deref +use core::future::Future; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +pub trait Service { + type Response; + type Future: Future; +} + +pub trait A1: Service {} + +pub trait A2: Service>> + A1 { + fn foo(&self) {} +} + +pub trait B1: Service>> {} + +pub trait B2: Service + B1 { + fn foo(&self) {} +} + "#, + [("A2", vec![]), ("B2", vec![])], + ); +} + +#[test] +fn rustc_issue_106247() { + check_object_safety( + r#" +//- minicore: sync, dispatch_from_dyn +pub trait Trait { + fn method(&self) where Self: Sync; +} +"#, + [("Trait", vec![])], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 17fbe4db31795..98f30f0f6f749 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1065,7 +1065,7 @@ fn test() { fn bare_dyn_trait_binders_9639() { check_no_mismatches( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn fn infix_parse(_state: S, _level_code: &Fn(S)) -> T { loop {} } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index a98cff2a08b26..0b2d6bdd2593f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1448,14 +1448,20 @@ fn foo() -> Foo> { fn dyn_trait() { check_infer( r#" -//- minicore: sized +//- minicore: deref, dispatch_from_dyn trait Trait { fn foo(&self) -> T; fn foo2(&self) -> i64; } -fn bar() -> dyn Trait {} -fn test(x: dyn Trait, y: &dyn Trait) { +struct Box {} +impl core::ops::Deref for Box { + type Target = T; +} + +fn bar() -> Box> {} + +fn test(x: Box>, y: &dyn Trait) { x; y; let z = bar(); @@ -1469,27 +1475,27 @@ fn test(x: dyn Trait, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 97..99 '{}': dyn Trait - 109..110 'x': dyn Trait - 128..129 'y': &'? dyn Trait - 148..265 '{ ...2(); }': () - 154..155 'x': dyn Trait - 161..162 'y': &'? dyn Trait - 172..173 'z': dyn Trait - 176..179 'bar': fn bar() -> dyn Trait - 176..181 'bar()': dyn Trait - 187..188 'x': dyn Trait - 187..194 'x.foo()': u64 - 200..201 'y': &'? dyn Trait - 200..207 'y.foo()': u64 - 213..214 'z': dyn Trait - 213..220 'z.foo()': u64 - 226..227 'x': dyn Trait - 226..234 'x.foo2()': i64 - 240..241 'y': &'? dyn Trait - 240..248 'y.foo2()': i64 - 254..255 'z': dyn Trait - 254..262 'z.foo2()': i64 + 198..200 '{}': Box> + 210..211 'x': Box> + 234..235 'y': &'? dyn Trait + 254..371 '{ ...2(); }': () + 260..261 'x': Box> + 267..268 'y': &'? dyn Trait + 278..279 'z': Box> + 282..285 'bar': fn bar() -> Box> + 282..287 'bar()': Box> + 293..294 'x': Box> + 293..300 'x.foo()': u64 + 306..307 'y': &'? dyn Trait + 306..313 'y.foo()': u64 + 319..320 'z': Box> + 319..326 'z.foo()': u64 + 332..333 'x': Box> + 332..340 'x.foo2()': i64 + 346..347 'y': &'? dyn Trait + 346..354 'y.foo2()': i64 + 360..361 'z': Box> + 360..368 'z.foo2()': i64 "#]], ); } @@ -1534,7 +1540,7 @@ fn test(s: S) { fn dyn_trait_bare() { check_infer( r#" -//- minicore: sized +//- minicore: sized, dispatch_from_dyn trait Trait { fn foo(&self) -> u64; } @@ -1570,7 +1576,7 @@ fn test(x: Trait, y: &Trait) -> u64 { check_infer_with_mismatches( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn struct S; impl S { fn foo(&self) {} @@ -3106,7 +3112,7 @@ fn dyn_fn_param_informs_call_site_closure_signature() { cov_mark::check!(dyn_fn_param_informs_call_site_closure_signature); check_types( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn struct S; impl S { fn inherent(&self) -> u8 { 0 } @@ -3151,7 +3157,7 @@ fn infer_box_fn_arg() { // The type mismatch is because we don't define Unsize and CoerceUnsized check_infer_with_mismatches( r#" -//- minicore: fn, deref, option +//- minicore: fn, deref, option, dispatch_from_dyn #[lang = "owned_box"] pub struct Box { inner: *mut T, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 2ff5f0d538af0..6b0cc44d769ca 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -66,7 +66,7 @@ use hir_ty::{ diagnostics::BodyValidationDiagnostic, error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, - method_resolution::{self}, + method_resolution, mir::{interpret_mir, MutBorrowKind}, primitive::UintTy, traits::FnTrait, @@ -144,6 +144,7 @@ pub use { display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, layout::LayoutError, mir::{MirEvalError, MirLowerError}, + object_safety::{MethodViolationCode, ObjectSafetyViolation}, FnAbi, PointerCast, Safety, }, // FIXME: Properly encapsulate mir @@ -2640,6 +2641,10 @@ impl Trait { .count() } + pub fn object_safety(&self, db: &dyn HirDatabase) -> Option { + hir_ty::object_safety::object_safety(db, self.id) + } + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { db.trait_data(self.id) .macro_calls diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 87932bf989f04..18647206236c9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -84,7 +84,7 @@ fn foo() { fn replace_filter_map_next_dont_work_for_not_sized_issues_16596() { check_diagnostics( r#" -//- minicore: iterators +//- minicore: iterators, dispatch_from_dyn fn foo() { let mut j = [0].into_iter(); let i: &mut dyn Iterator = &mut j; diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 3e41b42be44b1..ead2d45847d0d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -4,7 +4,8 @@ use std::{mem, ops::Not}; use either::Either; use hir::{ Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout, - LayoutError, Name, Semantics, Trait, Type, TypeInfo, + LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics, Trait, Type, + TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -526,6 +527,14 @@ pub(super) fn definition( _ => None, }; + let object_safety_info = if let Definition::Trait(it) = def { + let mut object_safety_info = String::new(); + render_object_safety(db, &mut object_safety_info, it.object_safety(db)); + Some(object_safety_info) + } else { + None + }; + let mut desc = String::new(); if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits, edition) { desc.push_str(¬able_traits); @@ -535,6 +544,10 @@ pub(super) fn definition( desc.push_str(&layout_info); desc.push('\n'); } + if let Some(object_safety_info) = object_safety_info { + desc.push_str(&object_safety_info); + desc.push('\n'); + } desc.push_str(&label); if let Some(value) = value { desc.push_str(" = "); @@ -964,3 +977,62 @@ fn keyword_hints( _ => KeywordHint::new(token.text().to_owned(), format!("{}_keyword", token.text())), } } + +fn render_object_safety( + db: &RootDatabase, + buf: &mut String, + safety: Option, +) { + let Some(osv) = safety else { + buf.push_str("// Object Safety: Yes"); + return; + }; + buf.push_str("// Object Safety: No\n// - Reason: "); + match osv { + ObjectSafetyViolation::SizedSelf => { + buf.push_str("has a `Self: Sized` bound"); + } + ObjectSafetyViolation::SelfReferential => { + buf.push_str("has a bound that references `Self`"); + } + ObjectSafetyViolation::Method(func, mvc) => { + let name = hir::Function::from(func).name(db); + format_to!( + buf, + "has a method `{}` that is non dispatchable because of:\n// - ", + name.as_str() + ); + let desc = match mvc { + MethodViolationCode::StaticMethod => "missing a receiver", + MethodViolationCode::ReferencesSelfInput => "a parameter references `Self`", + MethodViolationCode::ReferencesSelfOutput => "the return type references `Self`", + MethodViolationCode::ReferencesImplTraitInTrait => { + "the return type contains `impl Trait`" + } + MethodViolationCode::AsyncFn => "being async", + MethodViolationCode::WhereClauseReferencesSelf => { + "a where clause references `Self`" + } + MethodViolationCode::Generic => "a non-lifetime generic parameter", + MethodViolationCode::UndispatchableReceiver => "a non-dispatchable receiver type", + }; + buf.push_str(desc); + } + ObjectSafetyViolation::AssocConst(const_) => { + let name = hir::Const::from(const_).name(db); + if let Some(name) = name { + format_to!(buf, "has an associated constant `{}`", name.as_str()); + } else { + buf.push_str("has an associated constant"); + } + } + ObjectSafetyViolation::GAT(alias) => { + let name = hir::TypeAlias::from(alias).name(db); + format_to!(buf, "has a generic associated type `{}`", name.as_str()); + } + ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait) => { + let name = hir::Trait::from(super_trait).name(db); + format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str()); + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 9585bdbe4c545..f2f83d538ccb9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -5442,7 +5442,7 @@ const FOO$0: Option<&i32> = Some(2).as_ref(); fn hover_const_eval_dyn_trait() { check( r#" -//- minicore: fmt, coerce_unsized, builtin_impls +//- minicore: fmt, coerce_unsized, builtin_impls, dispatch_from_dyn use core::fmt::Debug; const FOO$0: &dyn Debug = &2i32; @@ -7107,6 +7107,7 @@ impl T$0 for () {} ``` ```rust + // Object Safety: Yes trait T {} ``` "#]], @@ -7126,6 +7127,7 @@ impl T$0 for () {} ``` ```rust + // Object Safety: Yes trait T {} ``` "#]], @@ -7149,6 +7151,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { /* … */ } ``` "#]], @@ -7172,6 +7177,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7199,6 +7207,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7226,6 +7237,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -8465,8 +8479,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7800..8042, - focus_range: 7865..7871, + full_range: 7801..8043, + focus_range: 7866..7872, name: "Future", kind: Trait, container_name: "future", @@ -8479,8 +8493,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 8672..9171, - focus_range: 8749..8757, + full_range: 8673..9172, + focus_range: 8750..8758, name: "Iterator", kind: Trait, container_name: "iterator", diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 756198d0c0110..31c1a991d53d7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -288,7 +288,7 @@ mod tests { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq, index +//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn fn main() { let _: u32 = loop {}; //^^^^^^^ @@ -428,7 +428,7 @@ impl core::ops::IndexMut for Struct {} ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq, index +//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn fn main() { Struct.consume(); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 7eb8e4a5e2e52..f48daba54e7b7 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -242,6 +242,7 @@ define_symbols! { future_output, Future, ge, + generic_associated_type_extended, get_context, global_allocator, global_asm, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7dbc498ead196..b2f250e392237 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -33,6 +33,7 @@ //! from: sized //! future: pin //! coroutine: pin +//! dispatch_from_dyn: unsize, pin //! hash: //! include: //! index: sized @@ -821,6 +822,24 @@ pub mod ops { } pub use self::coroutine::{Coroutine, CoroutineState}; // endregion:coroutine + + // region:dispatch_from_dyn + mod dispatch_from_dyn { + use crate::marker::Unsize; + + #[lang = "dispatch_from_dyn"] + pub trait DispatchFromDyn {} + + impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} + + impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} + + impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} + + impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} + } + pub use self::dispatch_from_dyn::DispatchFromDyn; + // endregion:dispatch_from_dyn } // region:eq @@ -1182,6 +1201,12 @@ pub mod pin { } } // endregion:deref + // region:dispatch_from_dyn + impl crate::ops::DispatchFromDyn> for Pin where + Ptr: crate::ops::DispatchFromDyn + { + } + // endregion:dispatch_from_dyn } // endregion:pin @@ -1308,7 +1333,10 @@ pub mod iter { self } // region:iterators - fn take(self, n: usize) -> crate::iter::Take { + fn take(self, n: usize) -> crate::iter::Take + where + Self: Sized, + { loop {} } fn filter_map(self, _f: F) -> crate::iter::FilterMap From 92f27dc57df1e01186a2ab515ced9125a8ba2673 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 30 Aug 2024 02:00:38 +0900 Subject: [PATCH 0171/1301] fix: `std::error::Error` is object unsafe --- .../rust-analyzer/crates/hir-ty/src/db.rs | 3 +++ .../rust-analyzer/crates/hir-ty/src/lower.rs | 23 +++++++++++++++++++ .../crates/hir-ty/src/object_safety.rs | 23 ++----------------- .../crates/hir-ty/src/object_safety/tests.rs | 17 ++++++++++++++ 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index ebbe8a448cc4f..ce5a821ea2bc4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -154,6 +154,9 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; + #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)] + fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates; + #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 841e0216ae2b8..213400d04a01e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1700,6 +1700,28 @@ pub(crate) fn generic_predicates_query( db: &dyn HirDatabase, def: GenericDefId, ) -> GenericPredicates { + generic_predicates_filtered_by(db, def, |_, _| true) +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates { + generic_predicates_filtered_by(db, def, |_, d| *d == def) +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +fn generic_predicates_filtered_by( + db: &dyn HirDatabase, + def: GenericDefId, + filter: F, +) -> GenericPredicates +where + F: Fn(&WherePredicate, &GenericDefId) -> bool, +{ let resolver = def.resolver(db.upcast()); let (impl_trait_lowering, param_lowering) = match def { GenericDefId::FunctionId(_) => { @@ -1714,6 +1736,7 @@ pub(crate) fn generic_predicates_query( let mut predicates = resolver .where_predicates_in_scope() + .filter(|(pred, def)| filter(pred, def)) .flat_map(|(pred, def)| { ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p)) }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs index f3bbb6b04f973..89bf3619a0c37 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs @@ -12,7 +12,7 @@ use hir_def::{ lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, }; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use smallvec::SmallVec; use crate::{ @@ -417,30 +417,11 @@ where cb(MethodViolationCode::UndispatchableReceiver)?; } - let predicates = &*db.generic_predicates(func.into()); - let mut parent_predicates = (*db.generic_predicates(trait_.into())) - .iter() - .map(|b| b.skip_binders().skip_binders().clone()) - .fold(FxHashMap::default(), |mut acc, item| { - acc.entry(item) - .and_modify(|cnt| { - *cnt += 1; - }) - .or_insert(1); - acc - }); + let predicates = &*db.generic_predicates_without_parent(func.into()); let trait_self_idx = trait_self_param_idx(db.upcast(), func.into()); for pred in predicates { let pred = pred.skip_binders().skip_binders(); - // Skip predicates from parent, i.e. the trait that contains this method - if let Some(cnt) = parent_predicates.get_mut(pred) { - if *cnt > 0 { - *cnt -= 1; - continue; - } - } - if matches!(pred, WhereClause::TypeOutlives(_)) { continue; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs index 585313e5b9bf8..3dc08c4619efd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs @@ -361,3 +361,20 @@ pub trait Trait { [("Trait", vec![])], ); } + +#[test] +fn std_error_is_object_safe() { + check_object_safety( + r#" +//- minicore: fmt, dispatch_from_dyn +trait Erased<'a>: 'a {} + +pub struct Request<'a>(dyn Erased<'a> + 'a); + +pub trait Error: core::fmt::Debug + core::fmt::Display { + fn provide<'a>(&'a self, request: &mut Request<'a>); +} +"#, + [("Error", vec![])], + ); +} From 74a2344dc12b5659357272f4cd8b6e837195ab24 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 Mar 2024 16:51:40 +0100 Subject: [PATCH 0172/1301] Extend `implicit_saturation_sub` lint --- clippy_lints/src/implicit_saturating_sub.rs | 278 +++++++++++++++----- 1 file changed, 218 insertions(+), 60 deletions(-) diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 127c73ed637b2..453ff09bf4fb2 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,11 +1,13 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::{higher, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, SpanlessEq}; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -50,73 +52,229 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Check if the conditional expression is a binary operation && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind + { + check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then); + } else if let Some(higher::If { + cond, + then: if_block, + r#else: Some(else_block), + }) = higher::If::hir(expr) + && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind + { + check_manual_check(cx, expr, cond_op, cond_left, cond_right, if_block, else_block); + } + } +} - // Ensure that the binary operator is >, !=, or < - && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node) +fn check_manual_check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + condition: &BinOp, + left_hand: &Expr<'tcx>, + right_hand: &Expr<'tcx>, + if_block: &Expr<'tcx>, + else_block: &Expr<'tcx>, +) { + let ty = cx.typeck_results().expr_ty(left_hand); + if ty.is_numeric() && !ty.is_signed() { + match condition.node { + BinOpKind::Gt | BinOpKind::Ge => check_gt( + cx, + condition.span, + expr.span, + left_hand, + right_hand, + if_block, + else_block, + ), + BinOpKind::Lt | BinOpKind::Le => check_gt( + cx, + condition.span, + expr.span, + right_hand, + left_hand, + if_block, + else_block, + ), + _ => {}, + } + } +} - // Check if assign operation is done - && let Some(target) = subtracts_one(cx, then) +fn check_gt( + cx: &LateContext<'_>, + condition_span: Span, + expr_span: Span, + big_var: &Expr<'_>, + little_var: &Expr<'_>, + if_block: &Expr<'_>, + else_block: &Expr<'_>, +) { + if let Some(big_var) = Var::new(big_var) + && let Some(little_var) = Var::new(little_var) + { + check_subtraction(cx, condition_span, expr_span, big_var, little_var, if_block, else_block); + } +} - // Extracting out the variable name - && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind +struct Var { + span: Span, + hir_id: HirId, +} + +impl Var { + fn new(expr: &Expr<'_>) -> Option { + path_to_local(expr).map(|hir_id| Self { + span: expr.span, + hir_id, + }) + } +} + +fn check_subtraction( + cx: &LateContext<'_>, + condition_span: Span, + expr_span: Span, + big_var: Var, + little_var: Var, + if_block: &Expr<'_>, + else_block: &Expr<'_>, +) { + let if_block = peel_blocks(if_block); + let else_block = peel_blocks(else_block); + if is_integer_literal(if_block, 0) { + // We need to check this case as well to prevent infinite recursion. + if is_integer_literal(else_block, 0) { + // Well, seems weird but who knows? + return; + } + // If the subtraction is done in the `else` block, then we need to also revert the two + // variables as it means that the check was reverted too. + check_subtraction(cx, condition_span, expr_span, little_var, big_var, else_block, if_block); + return; + } + if is_integer_literal(else_block, 0) + && let ExprKind::Binary(op, left, right) = if_block.kind + && let BinOpKind::Sub = op.node + { + let local_left = path_to_local(left); + let local_right = path_to_local(right); + if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right { + // This part of the condition is voluntarily split from the one before to ensure that + // if `snippet_opt` fails, it won't try the next conditions. + if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) + && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + { + span_lint_and_sugg( + cx, + IMPLICIT_SATURATING_SUB, + expr_span, + "manual arithmetic check found", + "replace it with", + format!("{big_var_snippet}.saturating_sub({little_var_snippet})"), + Applicability::MachineApplicable, + ); + } + } else if Some(little_var.hir_id) == local_left + && Some(big_var.hir_id) == local_right + && let Some(big_var_snippet) = snippet_opt(cx, big_var.span) + && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) { - // Handle symmetric conditions in the if statement - let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { - if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_left, cond_right) - } else { - return; - } - } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { - if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_right, cond_left) - } else { - return; - } + span_lint_and_then( + cx, + IMPLICIT_SATURATING_SUB, + condition_span, + "inverted arithmetic check before subtraction", + |diag| { + diag.span_note( + if_block.span, + format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"), + ); + diag.span_suggestion( + if_block.span, + "try replacing it with", + format!("{big_var_snippet} - {little_var_snippet}"), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } +} + +fn check_with_condition<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + cond_op: BinOpKind, + cond_left: &Expr<'tcx>, + cond_right: &Expr<'tcx>, + then: &Expr<'tcx>, +) { + // Ensure that the binary operator is >, !=, or < + if (BinOpKind::Ne == cond_op || BinOpKind::Gt == cond_op || BinOpKind::Lt == cond_op) + + // Check if assign operation is done + && let Some(target) = subtracts_one(cx, then) + + // Extracting out the variable name + && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind + { + // Handle symmetric conditions in the if statement + let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { + if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op { + (cond_left, cond_right) } else { return; - }; - - // Check if the variable in the condition statement is an integer - if !cx.typeck_results().expr_ty(cond_var).is_integral() { + } + } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { + if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op { + (cond_right, cond_left) + } else { return; } + } else { + return; + }; - // Get the variable name - let var_name = ares_path.segments[0].ident.name.as_str(); - match cond_num_val.kind { - ExprKind::Lit(cond_lit) => { - // Check if the constant is zero - if let LitKind::Int(Pu128(0), _) = cond_lit.node { - if cx.typeck_results().expr_ty(cond_left).is_signed() { - } else { - print_lint_and_sugg(cx, var_name, expr); - }; - } - }, - ExprKind::Path(QPath::TypeRelative(_, name)) => { - if name.ident.as_str() == "MIN" - && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(const_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl - && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() - { - print_lint_and_sugg(cx, var_name, expr); - } - }, - ExprKind::Call(func, []) => { - if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind - && name.ident.as_str() == "min_value" - && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(func_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl - && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() - { + // Check if the variable in the condition statement is an integer + if !cx.typeck_results().expr_ty(cond_var).is_integral() { + return; + } + + // Get the variable name + let var_name = ares_path.segments[0].ident.name.as_str(); + match cond_num_val.kind { + ExprKind::Lit(cond_lit) => { + // Check if the constant is zero + if let LitKind::Int(Pu128(0), _) = cond_lit.node { + if cx.typeck_results().expr_ty(cond_left).is_signed() { + } else { print_lint_and_sugg(cx, var_name, expr); - } - }, - _ => (), - } + }; + } + }, + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if name.ident.as_str() == "MIN" + && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(const_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + ExprKind::Call(func, []) => { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind + && name.ident.as_str() == "min_value" + && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(func_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + _ => (), } } } From 2622a87587ada00d6aac1d036da8a2f579a675ba Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 Mar 2024 16:59:08 +0100 Subject: [PATCH 0173/1301] Add ui regression tests for `implicit_saturation_sub` lint extension --- tests/ui/implicit_saturating_sub.rs | 7 +++ tests/ui/manual_arithmetic_check-2.rs | 35 +++++++++++ tests/ui/manual_arithmetic_check-2.stderr | 76 +++++++++++++++++++++++ tests/ui/manual_arithmetic_check.fixed | 16 +++++ tests/ui/manual_arithmetic_check.rs | 20 ++++++ tests/ui/manual_arithmetic_check.stderr | 29 +++++++++ 6 files changed, 183 insertions(+) create mode 100644 tests/ui/manual_arithmetic_check-2.rs create mode 100644 tests/ui/manual_arithmetic_check-2.stderr create mode 100644 tests/ui/manual_arithmetic_check.fixed create mode 100644 tests/ui/manual_arithmetic_check.rs create mode 100644 tests/ui/manual_arithmetic_check.stderr diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 5d7b95d2c652f..05f7d85238307 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -260,4 +260,11 @@ fn main() { } else if u_32 > 0 { u_32 -= 1; } + + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; } diff --git a/tests/ui/manual_arithmetic_check-2.rs b/tests/ui/manual_arithmetic_check-2.rs new file mode 100644 index 0000000000000..e97e3bdfef769 --- /dev/null +++ b/tests/ui/manual_arithmetic_check-2.rs @@ -0,0 +1,35 @@ +//@no-rustfix +#![warn(clippy::implicit_saturating_sub)] +#![allow(arithmetic_overflow)] + +fn main() { + let a = 12u32; + let b = 13u32; + + let result = if a > b { b - a } else { 0 }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b < a { b - a } else { 0 }; + //~^ ERROR: inverted arithmetic check before subtraction + + let result = if a > b { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if a >= b { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b < a { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b <= a { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + + let af = 12f32; + let bf = 13f32; + // Should not lint! + let result = if bf < af { 0. } else { af - bf }; + + // Should not lint! + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; +} diff --git a/tests/ui/manual_arithmetic_check-2.stderr b/tests/ui/manual_arithmetic_check-2.stderr new file mode 100644 index 0000000000000..d49e536ce3fbf --- /dev/null +++ b/tests/ui/manual_arithmetic_check-2.stderr @@ -0,0 +1,76 @@ +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:9:23 + | +LL | let result = if a > b { b - a } else { 0 }; + | ^ ----- help: try replacing it with: `a - b` + | +note: this subtraction underflows when `b < a` + --> tests/ui/manual_arithmetic_check-2.rs:9:29 + | +LL | let result = if a > b { b - a } else { 0 }; + | ^^^^^ + = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]` + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:11:23 + | +LL | let result = if b < a { b - a } else { 0 }; + | ^ ----- help: try replacing it with: `a - b` + | +note: this subtraction underflows when `b < a` + --> tests/ui/manual_arithmetic_check-2.rs:11:29 + | +LL | let result = if b < a { b - a } else { 0 }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:14:23 + | +LL | let result = if a > b { 0 } else { a - b }; + | ^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:14:40 + | +LL | let result = if a > b { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:16:23 + | +LL | let result = if a >= b { 0 } else { a - b }; + | ^^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:16:41 + | +LL | let result = if a >= b { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:18:23 + | +LL | let result = if b < a { 0 } else { a - b }; + | ^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:18:40 + | +LL | let result = if b < a { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:20:23 + | +LL | let result = if b <= a { 0 } else { a - b }; + | ^^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:20:41 + | +LL | let result = if b <= a { 0 } else { a - b }; + | ^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/manual_arithmetic_check.fixed b/tests/ui/manual_arithmetic_check.fixed new file mode 100644 index 0000000000000..afb2abcbe9071 --- /dev/null +++ b/tests/ui/manual_arithmetic_check.fixed @@ -0,0 +1,16 @@ +#![warn(clippy::implicit_saturating_sub)] + +fn main() { + let a = 12u32; + let b = 13u32; + + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found +} diff --git a/tests/ui/manual_arithmetic_check.rs b/tests/ui/manual_arithmetic_check.rs new file mode 100644 index 0000000000000..c11ac7097fc4e --- /dev/null +++ b/tests/ui/manual_arithmetic_check.rs @@ -0,0 +1,20 @@ +#![warn(clippy::implicit_saturating_sub)] + +fn main() { + let a = 12u32; + let b = 13u32; + let c = 8u32; + + let result = if a > b { a - b } else { 0 }; + //~^ ERROR: manual arithmetic check found + let result = if b < a { a - b } else { 0 }; + //~^ ERROR: manual arithmetic check found + + let result = if a < b { 0 } else { a - b }; + //~^ ERROR: manual arithmetic check found + let result = if b > a { 0 } else { a - b }; + //~^ ERROR: manual arithmetic check found + + // Should not warn! + let result = if a > b { a - b } else { a - c }; +} diff --git a/tests/ui/manual_arithmetic_check.stderr b/tests/ui/manual_arithmetic_check.stderr new file mode 100644 index 0000000000000..153a8bb57346f --- /dev/null +++ b/tests/ui/manual_arithmetic_check.stderr @@ -0,0 +1,29 @@ +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:7:18 + | +LL | let result = if a > b { a - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + | + = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:9:18 + | +LL | let result = if b < a { a - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:12:18 + | +LL | let result = if a < b { 0 } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:14:18 + | +LL | let result = if b > a { 0 } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: aborting due to 4 previous errors + From 27c63433659773c70dca86a3d6f3b9bd7654c2c3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 Mar 2024 17:00:31 +0100 Subject: [PATCH 0174/1301] Add ui test to ensure that if `0` is returned from both `if` and `else`, it will not break clippy --- tests/ui/implicit_saturating_sub.fixed | 13 ++++++++++--- tests/ui/implicit_saturating_sub.rs | 6 +++--- tests/ui/manual_arithmetic_check.fixed | 8 ++++++++ tests/ui/manual_arithmetic_check.rs | 4 ++++ tests/ui/manual_arithmetic_check.stderr | 8 ++++---- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index 27f679797dd9e..81cc149491442 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -184,18 +184,18 @@ fn main() { let mut m = Mock; let mut u_32 = 3000; let a = 200; - let mut _b = 8; + let mut b = 8; if m != 0 { m -= 1; } if a > 0 { - _b -= 1; + b -= 1; } if 0 > a { - _b -= 1; + b -= 1; } if u_32 > 0 { @@ -214,4 +214,11 @@ fn main() { } else if u_32 > 0 { u_32 -= 1; } + + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; } diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 05f7d85238307..f73396ebd27e2 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -230,18 +230,18 @@ fn main() { let mut m = Mock; let mut u_32 = 3000; let a = 200; - let mut _b = 8; + let mut b = 8; if m != 0 { m -= 1; } if a > 0 { - _b -= 1; + b -= 1; } if 0 > a { - _b -= 1; + b -= 1; } if u_32 > 0 { diff --git a/tests/ui/manual_arithmetic_check.fixed b/tests/ui/manual_arithmetic_check.fixed index afb2abcbe9071..600fcd3765d81 100644 --- a/tests/ui/manual_arithmetic_check.fixed +++ b/tests/ui/manual_arithmetic_check.fixed @@ -1,8 +1,10 @@ #![warn(clippy::implicit_saturating_sub)] +#![allow(clippy::if_same_then_else)] fn main() { let a = 12u32; let b = 13u32; + let c = 8u32; let result = a.saturating_sub(b); //~^ ERROR: manual arithmetic check found @@ -13,4 +15,10 @@ fn main() { //~^ ERROR: manual arithmetic check found let result = a.saturating_sub(b); //~^ ERROR: manual arithmetic check found + + // Should not warn! + let result = if a > b { a - b } else { a - c }; + + // Just to check it won't break clippy. + let result = if b > a { 0 } else { 0 }; } diff --git a/tests/ui/manual_arithmetic_check.rs b/tests/ui/manual_arithmetic_check.rs index c11ac7097fc4e..33f5adafe7e40 100644 --- a/tests/ui/manual_arithmetic_check.rs +++ b/tests/ui/manual_arithmetic_check.rs @@ -1,4 +1,5 @@ #![warn(clippy::implicit_saturating_sub)] +#![allow(clippy::if_same_then_else)] fn main() { let a = 12u32; @@ -17,4 +18,7 @@ fn main() { // Should not warn! let result = if a > b { a - b } else { a - c }; + + // Just to check it won't break clippy. + let result = if b > a { 0 } else { 0 }; } diff --git a/tests/ui/manual_arithmetic_check.stderr b/tests/ui/manual_arithmetic_check.stderr index 153a8bb57346f..b0cf73cd9151d 100644 --- a/tests/ui/manual_arithmetic_check.stderr +++ b/tests/ui/manual_arithmetic_check.stderr @@ -1,5 +1,5 @@ error: manual arithmetic check found - --> tests/ui/manual_arithmetic_check.rs:7:18 + --> tests/ui/manual_arithmetic_check.rs:9:18 | LL | let result = if a > b { a - b } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` @@ -8,19 +8,19 @@ LL | let result = if a > b { a - b } else { 0 }; = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]` error: manual arithmetic check found - --> tests/ui/manual_arithmetic_check.rs:9:18 + --> tests/ui/manual_arithmetic_check.rs:11:18 | LL | let result = if b < a { a - b } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` error: manual arithmetic check found - --> tests/ui/manual_arithmetic_check.rs:12:18 + --> tests/ui/manual_arithmetic_check.rs:14:18 | LL | let result = if a < b { 0 } else { a - b }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` error: manual arithmetic check found - --> tests/ui/manual_arithmetic_check.rs:14:18 + --> tests/ui/manual_arithmetic_check.rs:16:18 | LL | let result = if b > a { 0 } else { a - b }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` From d20fc38f0a17561166fb57c83ff5660b3cd41392 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 19 Jul 2024 14:51:56 +0200 Subject: [PATCH 0175/1301] Create new `inverted_saturating_sub` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/implicit_saturating_sub.rs | 34 +++++++++++++++++++-- tests/ui/manual_arithmetic_check-2.stderr | 3 +- tests/ui/manual_arithmetic_check.fixed | 2 +- tests/ui/manual_arithmetic_check.rs | 2 +- 6 files changed, 37 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fddc2fd994e87..0bef35529662a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5500,6 +5500,7 @@ Released 2018-09-13 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked +[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 8754a4dff87b1..a6dd6ae731289 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -216,6 +216,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::implicit_return::IMPLICIT_RETURN_INFO, crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, + crate::implicit_saturating_sub::INVERTED_SATURATING_SUB_INFO, crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 453ff09bf4fb2..04c890620396d 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -41,7 +41,37 @@ declare_clippy_lint! { "Perform saturating subtraction instead of implicitly checking lower bound of data type" } -declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]); +declare_clippy_lint! { + /// ### What it does + /// Checks for comparisons between integers, followed by subtracting the greater value from the + /// lower one. + /// + /// ### Why is this bad? + /// This could result in an underflow and is most likely not what the user wants. If this was + /// intended to be a saturated subtraction, consider using the `saturating_sub` method directly. + /// + /// ### Example + /// ```no_run + /// let a = 12u32; + /// let b = 13u32; + /// + /// let result = if a > b { b - a } else { 0 }; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let a = 12u32; + /// let b = 13u32; + /// + /// let result = a.saturating_sub(b); + /// ``` + #[clippy::version = "1.44.0"] + pub INVERTED_SATURATING_SUB, + correctness, + "Check if a variable is smaller than another one and still subtract from it even if smaller" +} + +declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]); impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -182,7 +212,7 @@ fn check_subtraction( { span_lint_and_then( cx, - IMPLICIT_SATURATING_SUB, + INVERTED_SATURATING_SUB, condition_span, "inverted arithmetic check before subtraction", |diag| { diff --git a/tests/ui/manual_arithmetic_check-2.stderr b/tests/ui/manual_arithmetic_check-2.stderr index d49e536ce3fbf..4121aa7464f05 100644 --- a/tests/ui/manual_arithmetic_check-2.stderr +++ b/tests/ui/manual_arithmetic_check-2.stderr @@ -9,8 +9,7 @@ note: this subtraction underflows when `b < a` | LL | let result = if a > b { b - a } else { 0 }; | ^^^^^ - = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]` + = note: `#[deny(clippy::inverted_saturating_sub)]` on by default error: inverted arithmetic check before subtraction --> tests/ui/manual_arithmetic_check-2.rs:11:23 diff --git a/tests/ui/manual_arithmetic_check.fixed b/tests/ui/manual_arithmetic_check.fixed index 600fcd3765d81..29ecbb9ad2adb 100644 --- a/tests/ui/manual_arithmetic_check.fixed +++ b/tests/ui/manual_arithmetic_check.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::implicit_saturating_sub)] +#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)] #![allow(clippy::if_same_then_else)] fn main() { diff --git a/tests/ui/manual_arithmetic_check.rs b/tests/ui/manual_arithmetic_check.rs index 33f5adafe7e40..69554c6b61caa 100644 --- a/tests/ui/manual_arithmetic_check.rs +++ b/tests/ui/manual_arithmetic_check.rs @@ -1,4 +1,4 @@ -#![warn(clippy::implicit_saturating_sub)] +#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)] #![allow(clippy::if_same_then_else)] fn main() { From 8bb235baa2b8c46c02557ad0500e75c81d270075 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 29 Aug 2024 22:03:57 +0300 Subject: [PATCH 0176/1301] Add diagnostic for accessing an `extern` static --- .../hir-ty/src/diagnostics/unsafe_check.rs | 3 ++- .../src/handlers/missing_unsafe.rs | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3f54cdd20cee3..1582cbba88055 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -87,7 +87,8 @@ fn walk_unsafe( let g = resolver.update_to_inner_scope(db.upcast(), def, current); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { - if db.static_data(id).mutable { + let static_data = db.static_data(id); + if static_data.mutable || static_data.is_extern { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index af8ac6005d7a7..7407faee350b6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -163,6 +163,31 @@ fn main() { ); } + #[test] + fn missing_unsafe_diagnostic_with_extern_static() { + check_diagnostics( + r#" +//- minicore: copy + +extern "C" { + static EXTERN: i32; + static mut EXTERN_MUT: i32; +} + +fn main() { + let _x = EXTERN; + //^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block + let _x = EXTERN_MUT; + //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block + unsafe { + let _x = EXTERN; + let _x = EXTERN_MUT; + } +} +"#, + ); + } + #[test] fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { check_diagnostics( From 21cb84763cf942dc2fa70d582cf2b8ba50163750 Mon Sep 17 00:00:00 2001 From: Matthew Giordano Date: Thu, 29 Aug 2024 12:32:33 -0700 Subject: [PATCH 0177/1301] fix fmt --- library/alloc/src/rc.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 151eec532613c..0afb138db8ada 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1710,11 +1710,7 @@ impl Rc { #[inline] #[stable(feature = "rc_unique", since = "1.4.0")] pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if Rc::is_unique(this) { - unsafe { Some(Rc::get_mut_unchecked(this)) } - } else { - None - } + if Rc::is_unique(this) { unsafe { Some(Rc::get_mut_unchecked(this)) } } else { None } } /// Returns a mutable reference into the given `Rc`, @@ -3291,11 +3287,7 @@ impl Weak { #[must_use] #[stable(feature = "weak_counts", since = "1.41.0")] pub fn strong_count(&self) -> usize { - if let Some(inner) = self.inner() { - inner.strong() - } else { - 0 - } + if let Some(inner) = self.inner() { inner.strong() } else { 0 } } /// Gets the number of `Weak` pointers pointing to this allocation. From 9e7473f08daf4a8ab1fec5186376ad1299555344 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 29 Aug 2024 21:31:46 +0200 Subject: [PATCH 0178/1301] Update version attribute for 1.81 lints --- clippy_lints/src/byte_char_slices.rs | 2 +- clippy_lints/src/cfg_not_test.rs | 2 +- clippy_lints/src/field_scoped_visibility_modifiers.rs | 2 +- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/needless_maybe_sized.rs | 2 +- clippy_lints/src/set_contains_or_insert.rs | 2 +- clippy_lints/src/string_patterns.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/byte_char_slices.rs b/clippy_lints/src/byte_char_slices.rs index a9fe190f1777e..dd2620b0b9df9 100644 --- a/clippy_lints/src/byte_char_slices.rs +++ b/clippy_lints/src/byte_char_slices.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// ```ignore /// b"Hello" /// ``` - #[clippy::version = "1.68.0"] + #[clippy::version = "1.81.0"] pub BYTE_CHAR_SLICES, style, "hard to read byte char slice" diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs index d820c1e0720b8..884d15cabffcb 100644 --- a/clippy_lints/src/cfg_not_test.rs +++ b/clippy_lints/src/cfg_not_test.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// # fn important_check() {} /// important_check(); /// ``` - #[clippy::version = "1.73.0"] + #[clippy::version = "1.81.0"] pub CFG_NOT_TEST, restriction, "enforce against excluding code from test builds" diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs index 95b8e882da792..ba2b37fbf11a3 100644 --- a/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.81.0"] pub FIELD_SCOPED_VISIBILITY_MODIFIERS, restriction, "checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields" diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d7126990edb1d..0420f5c7628d3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3964,7 +3964,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = 0; /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.81.0"] pub UNNECESSARY_MIN_OR_MAX, complexity, "using 'min()/max()' when there is no need for it" @@ -4099,7 +4099,7 @@ declare_clippy_lint! { /// ```no_run /// "foo".is_ascii(); /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub NEEDLESS_CHARACTER_ITERATION, suspicious, "is_ascii() called on a char iterator" diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index a1d8ec3b32ec9..c0847342caf12 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// /// // or choose alternative bounds for `T` so that it can be unsized /// ``` - #[clippy::version = "1.79.0"] + #[clippy::version = "1.81.0"] pub NEEDLESS_MAYBE_SIZED, suspicious, "a `?Sized` bound that is unusable due to a `Sized` requirement" diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index e6fe76493974a..bc2a71c42b9f8 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { /// println!("inserted {value:?}"); /// } /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub SET_CONTAINS_OR_INSERT, nursery, "call to `::contains` followed by `::insert`" diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index 7e211d64da141..8af50ca87d631 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ```no_run /// "Hello World!".trim_end_matches(['.', ',', '!', '?']); /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub MANUAL_PATTERN_CHAR_COMPARISON, style, "manual char comparison in string patterns" From 9aa23b8317de0fe68415f120b1cbf589fcf3d052 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 29 Aug 2024 21:38:41 +0200 Subject: [PATCH 0179/1301] Changelog for Clippy 1.81 :beginner: --- CHANGELOG.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21252a3f43a00..4364e0f7085c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,61 @@ document. ## Unreleased / Beta / In Rust Nightly -[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master) +[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master) + +## Rust 1.81 + +Current stable, released 2024-09-05 + +### New Lints + +* Added [`cfg_not_test`] to `restriction` + [#11293](https://github.com/rust-lang/rust-clippy/pull/11293) +* Added [`byte_char_slices`] to `style` + [#10155](https://github.com/rust-lang/rust-clippy/pull/10155) +* Added [`set_contains_or_insert`] to `nursery` + [#12873](https://github.com/rust-lang/rust-clippy/pull/12873) +* Added [`manual_rotate`] to `style` + [#12983](https://github.com/rust-lang/rust-clippy/pull/12983) +* Added [`unnecessary_min_or_max`] to `complexity` + [#12368](https://github.com/rust-lang/rust-clippy/pull/12368) +* Added [`manual_inspect`] to `complexity` + [#12287](https://github.com/rust-lang/rust-clippy/pull/12287) +* Added [`field_scoped_visibility_modifiers`] to `restriction` + [#12893](https://github.com/rust-lang/rust-clippy/pull/12893) +* Added [`manual_pattern_char_comparison`] to `style` + [#12849](https://github.com/rust-lang/rust-clippy/pull/12849) +* Added [`needless_maybe_sized`] to `suspicious` + [#10632](https://github.com/rust-lang/rust-clippy/pull/10632) +* Added [`needless_character_iteration`] to `suspicious` + [#12815](https://github.com/rust-lang/rust-clippy/pull/12815) + +### Moves and Deprecations + +* [`allow_attributes`], [`allow_attributes_without_reason`]: Now work on stable + [rust#120924](https://github.com/rust-lang/rust/pull/120924) +* Renamed `overflow_check_conditional` to [`panicking_overflow_checks`] + [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) +* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now warn-by-default) + [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) +* Renamed `thread_local_initializer_can_be_made_const` to [`missing_const_for_thread_local`] + [#12974](https://github.com/rust-lang/rust-clippy/pull/12974) +* Deprecated [`maybe_misused_cfg`] and [`mismatched_target_os`] as they are now caught by cargo + and rustc + [#12875](https://github.com/rust-lang/rust-clippy/pull/12875) + +### Enhancements + +* [`significant_drop_in_scrutinee`]: Now also checks scrutinies of `while let` and `for let` + expressions + [#12870](https://github.com/rust-lang/rust-clippy/pull/12870) +* [`std_instead_of_core`]: Now respects the `msrv` configuration + [#13168](https://github.com/rust-lang/rust-clippy/pull/13168) + +### ICE Fixes + +* [`suboptimal_flops`]: No longer crashes on custom `.log()` functions + [#12884](https://github.com/rust-lang/rust-clippy/pull/12884) ## Rust 1.80 From 0dfe40f0a38ed31ff9ef0e14fd24c5585bb869cd Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 29 Aug 2024 22:58:26 +0300 Subject: [PATCH 0180/1301] Do not report missing unsafe on `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)` The compiler no longer does as well; see https://github.com/rust-lang/rust/pull/125834. --- .../hir-ty/src/diagnostics/unsafe_check.rs | 8 ++++++ .../src/handlers/missing_unsafe.rs | 25 +++++++++++++++++++ .../crates/ide/src/hover/tests.rs | 12 ++++----- .../crates/test-utils/src/minicore.rs | 12 +++++++++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 1582cbba88055..309e208a9aa86 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -5,6 +5,7 @@ use hir_def::{ body::Body, hir::{Expr, ExprId, UnaryOp}, resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, + type_ref::Rawness, DefWithBodyId, }; @@ -94,6 +95,13 @@ fn walk_unsafe( } resolver.reset_to_guard(g); } + Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => { + if let Expr::Path(_) = body.exprs[*expr] { + // Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`, + // see https://github.com/rust-lang/rust/pull/125834. + return; + } + } Expr::MethodCall { .. } => { if infer .method_resolution(current) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 7407faee350b6..7e70a27f789bc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -188,6 +188,31 @@ fn main() { ); } + #[test] + fn no_unsafe_diagnostic_with_addr_of_static() { + check_diagnostics( + r#" +//- minicore: copy, addr_of + +use core::ptr::{addr_of, addr_of_mut}; + +extern "C" { + static EXTERN: i32; + static mut EXTERN_MUT: i32; +} +static mut STATIC_MUT: i32 = 0; + +fn main() { + let _x = addr_of!(EXTERN); + let _x = addr_of!(EXTERN_MUT); + let _x = addr_of!(STATIC_MUT); + let _x = addr_of_mut!(EXTERN_MUT); + let _x = addr_of_mut!(STATIC_MUT); +} +"#, + ); + } + #[test] fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index f2f83d538ccb9..d7ae6c7508789 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -483,8 +483,8 @@ fn main() { file_id: FileId( 1, ), - full_range: 632..867, - focus_range: 693..699, + full_range: 633..868, + focus_range: 694..700, name: "FnOnce", kind: Trait, container_name: "function", @@ -8479,8 +8479,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7801..8043, - focus_range: 7866..7872, + full_range: 7802..8044, + focus_range: 7867..7873, name: "Future", kind: Trait, container_name: "future", @@ -8493,8 +8493,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 8673..9172, - focus_range: 8750..8758, + full_range: 8674..9173, + focus_range: 8751..8759, name: "Iterator", kind: Trait, container_name: "iterator", diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7e1518c25c71f..4b7e23388c7e9 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -65,6 +65,7 @@ //! todo: panic //! unimplemented: panic //! column: +//! addr_of: #![rustc_coherence_is_core] @@ -421,6 +422,17 @@ pub mod ptr { } // endregion:coerce_unsized // endregion:non_null + + // region:addr_of + #[rustc_macro_transparency = "semitransparent"] + pub macro addr_of($place:expr) { + &raw const $place + } + #[rustc_macro_transparency = "semitransparent"] + pub macro addr_of_mut($place:expr) { + &raw mut $place + } + // endregion:addr_of } pub mod ops { From 29ecaa1edf1b0dc04514852404ab2e87318d502b Mon Sep 17 00:00:00 2001 From: Ivar Scholten Date: Wed, 28 Aug 2024 11:30:09 +0200 Subject: [PATCH 0181/1301] fix: consider indentation in the "Generate impl" and "Generate trait impl" assists This makes the generated impl's indentation match the ADT it targets, improving formatting when using nested modules inside of the same file or when defining types inside of a function. --- .../ide-assists/src/handlers/generate_impl.rs | 89 ++++++++++++++++--- 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 821783c283134..7b7dac9a3d6c4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -1,10 +1,22 @@ use syntax::{ - ast::{self, make, AstNode, HasName}, + ast::{self, edit_in_place::Indent, make, AstNode, HasName}, ted, }; use crate::{utils, AssistContext, AssistId, AssistKind, Assists}; +fn insert_impl(impl_: ast::Impl, nominal: &ast::Adt) { + let indent = nominal.indent_level(); + ted::insert_all_raw( + ted::Position::after(nominal.syntax()), + vec![ + // Add a blank line after the ADT, and indentation for the impl to match the ADT + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + impl_.syntax().clone().into(), + ], + ); +} + // Assist: generate_impl // // Adds a new inherent impl for a type. @@ -46,12 +58,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio } } - // Add the impl after the adt - let nominal = edit.make_mut(nominal); - ted::insert_all_raw( - ted::Position::after(nominal.syntax()), - vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()], - ); + insert_impl(impl_, &edit.make_mut(nominal)); }, ) } @@ -97,12 +104,7 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> } } - // Add the impl after the adt - let nominal = edit.make_mut(nominal); - ted::insert_all_raw( - ted::Position::after(nominal.syntax()), - vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()], - ); + insert_impl(impl_, &edit.make_mut(nominal)); }, ) } @@ -418,4 +420,65 @@ mod tests { "/// Has a lifetime parameter\nstruct Foo<'a, T: Foo<'a>> {}", ); } + + #[test] + fn add_impl_with_indent() { + check_assist( + generate_impl, + r#" + mod foo { + struct Bar$0 {} + } + "#, + r#" + mod foo { + struct Bar {} + + impl Bar {$0} + } + "#, + ); + } + + #[test] + fn add_impl_with_multiple_indent() { + check_assist( + generate_impl, + r#" + mod foo { + fn bar() { + struct Baz$0 {} + } + } + "#, + r#" + mod foo { + fn bar() { + struct Baz {} + + impl Baz {$0} + } + } + "#, + ); + } + + #[test] + fn add_trait_impl_with_indent() { + check_assist( + generate_trait_impl, + r#" + mod foo { + struct Bar$0 {} + } + "#, + r#" + mod foo { + struct Bar {} + + impl ${0:_} for Bar {} + } + "#, + ); + } } From cbc6910c35ddb682275c9ea83d94037cacc47a77 Mon Sep 17 00:00:00 2001 From: Fridtjof Stoldt Date: Thu, 29 Aug 2024 22:15:18 +0200 Subject: [PATCH 0182/1301] Changelog: Correct lint level Co-authored-by: Timo <30553356+y21@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4364e0f7085c0..2488da057b58e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ Current stable, released 2024-09-05 [rust#120924](https://github.com/rust-lang/rust/pull/120924) * Renamed `overflow_check_conditional` to [`panicking_overflow_checks`] [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) -* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now warn-by-default) +* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now deny-by-default) [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) * Renamed `thread_local_initializer_can_be_made_const` to [`missing_const_for_thread_local`] [#12974](https://github.com/rust-lang/rust-clippy/pull/12974) From 4abb8e2d0d1821a98e77109dae4001a613229074 Mon Sep 17 00:00:00 2001 From: Matthew Giordano Date: Thu, 29 Aug 2024 13:26:39 -0700 Subject: [PATCH 0183/1301] fix new_cyclic_in for rc --- library/alloc/src/rc.rs | 127 +++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 48 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 0afb138db8ada..726c0f2de9f25 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -687,54 +687,6 @@ impl Rc { } } - /// TODO: document - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_cyclic_in(data_fn: F, alloc: A) -> Rc - where - F: FnOnce(&Weak) -> T, - { - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let uninit_ptr: NonNull<_> = Box::leak( - Box::new_in(RcBox { - strong: Cell::new(0), - weak: Cell::new(1), - value: mem::MaybeUninit::::uninit(), - }), - alloc, - ) - .into(); - - let init_ptr: NonNull> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc: Global }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - let strong = unsafe { - let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).value), data); - - let prev_value = (*inner).strong.get(); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - (*inner).strong.set(1); - - Rc::from_inner(init_ptr) - }; - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - mem::forget(weak); - strong - } - /// Constructs a new `Rc` with uninitialized contents in the provided allocator. /// /// # Examples @@ -2312,6 +2264,85 @@ impl Clone for Rc { } } +impl Rc { + /// Constructs a new `Rc` in the given allocator while giving you a `Weak` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Rc` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic` first allocates the managed allocation for the `Rc`, + /// then calls your closure, giving it a `Weak` to this allocation, + /// and only afterwards completes the construction of the `Rc` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Rc` is not fully-constructed until `Rc::new_cyclic` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. + /// + /// # Examples + /// See [`new_cyclic`]. + /// + /// [`new_cyclic`]: Rc::new_cyclic + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_cyclic_in(data_fn: F, alloc: A) -> Rc + where + F: FnOnce(&Weak) -> T, + { + // Note: comments and implementation are copied from Rc::new_cyclic. + + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let uninit_ptr: NonNull<_> = Box::leak(Box::new_in( + RcBox { + strong: Cell::new(0), + weak: Cell::new(1), + value: mem::MaybeUninit::::uninit(), + }, + alloc.clone(), + )) + .into(); + + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc: alloc.clone() }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + let strong = unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(ptr::addr_of_mut!((*inner).value), data); + + let prev_value = (*inner).strong.get(); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + (*inner).strong.set(1); + + Rc::from_inner_in(init_ptr, alloc) + }; + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + mem::forget(weak); + strong + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Rc { From 2383cc991020b9dd678074072fcd3ed4f459eb6c Mon Sep 17 00:00:00 2001 From: Matthew Giordano Date: Thu, 29 Aug 2024 13:41:37 -0700 Subject: [PATCH 0184/1301] improve comments --- library/alloc/src/rc.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 726c0f2de9f25..f6d4174f5ebb5 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2279,7 +2279,7 @@ impl Rc { /// and only afterwards completes the construction of the `Rc` by placing /// the `T` returned from your closure into the allocation. /// - /// Since the new `Rc` is not fully-constructed until `Rc::new_cyclic` + /// Since the new `Rc` is not fully-constructed until `Rc::new_cyclic_in` /// returns, calling [`upgrade`] on the weak reference inside your closure will /// fail and result in a `None` value. /// @@ -2289,6 +2289,7 @@ impl Rc { /// temporary [`Weak`] is dropped normally. /// /// # Examples + /// /// See [`new_cyclic`]. /// /// [`new_cyclic`]: Rc::new_cyclic From 68169d3103dca63330b7e3e0e03f41b43b3b0490 Mon Sep 17 00:00:00 2001 From: Matthew Giordano Date: Thu, 29 Aug 2024 13:41:57 -0700 Subject: [PATCH 0185/1301] add new_cyclic_in for Arc --- library/alloc/src/sync.rs | 93 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 4a3522f1a641b..f808f3313297b 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1322,6 +1322,99 @@ impl Arc<[mem::MaybeUninit], A> { } } +impl Arc { + /// Constructs a new `Arc` in the given allocator while giving you a `Weak` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Arc` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic` first allocates the managed allocation for the `Arc`, + /// then calls your closure, giving it a `Weak` to this allocation, + /// and only afterwards completes the construction of the `Arc` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Arc` is not fully-constructed until `Arc::new_cyclic_in` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. + /// + /// # Example + /// + /// See [`new_cyclic`] + /// + /// [`new_cyclic`]: Arc::new_cyclic + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "arc_new_cyclic", since = "1.60.0")] + pub fn new_cyclic_in(data_fn: F, alloc: A) -> Arc + where + F: FnOnce(&Weak) -> T, + { + // Note: these comments and much of the implementation is copied from Arc::new_cyclic. + + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let uninit_ptr: NonNull<_> = Box::leak(Box::new_in( + ArcInner { + strong: atomic::AtomicUsize::new(0), + weak: atomic::AtomicUsize::new(1), + data: mem::MaybeUninit::::uninit(), + }, + alloc.clone(), + )) + .into(); + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc: alloc.clone() }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + // Now we can properly initialize the inner value and turn our weak + // reference into a strong reference. + let strong = unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(ptr::addr_of_mut!((*inner).data), data); + + // The above write to the data field must be visible to any threads which + // observe a non-zero strong count. Therefore we need at least "Release" ordering + // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. + // + // "Acquire" ordering is not required. When considering the possible behaviours + // of `data_fn` we only need to look at what it could do with a reference to a + // non-upgradeable `Weak`: + // - It can *clone* the `Weak`, increasing the weak reference count. + // - It can drop those clones, decreasing the weak reference count (but never to zero). + // + // These side effects do not impact us in any way, and no other side effects are + // possible with safe code alone. + let prev_value = (*inner).strong.fetch_add(1, Release); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + + Arc::from_inner_in(init_ptr, alloc) + }; + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + mem::forget(weak); + strong + } +} + impl Arc { /// Constructs an `Arc` from a raw pointer. /// From 1fcfd57e2fbd59d2b4edbb6e3e86846646823357 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Aug 2024 13:31:05 +1000 Subject: [PATCH 0186/1301] Remove `#[macro_use] extern crate tracing` from rustfmt helpers. --- src/format-diff/main.rs | 4 +--- src/git-rustfmt/main.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/format-diff/main.rs b/src/format-diff/main.rs index f9eb70f6e0466..d8616c63df9df 100644 --- a/src/format-diff/main.rs +++ b/src/format-diff/main.rs @@ -4,12 +4,10 @@ #![deny(warnings)] -#[macro_use] -extern crate tracing; - use serde::{Deserialize, Serialize}; use serde_json as json; use thiserror::Error; +use tracing::debug; use tracing_subscriber::EnvFilter; use std::collections::HashSet; diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index 5674f40bef91f..b8b0432aa9595 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -2,9 +2,6 @@ // `rustc_driver`. #![feature(rustc_private)] -#[macro_use] -extern crate tracing; - use std::env; use std::io::stdout; use std::path::{Path, PathBuf}; @@ -13,6 +10,7 @@ use std::str::FromStr; use getopts::{Matches, Options}; use rustfmt_nightly as rustfmt; +use tracing::debug; use tracing_subscriber::EnvFilter; use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; From c3beb056871f477c23462c4cf838759dc214dfe2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Aug 2024 13:35:05 +1000 Subject: [PATCH 0187/1301] Remove `#[macro_use] extern crate tracing` from rustfmt. --- src/attr.rs | 1 + src/chains.rs | 1 + src/closures.rs | 1 + src/comment.rs | 1 + src/expr.rs | 1 + src/formatting.rs | 1 + src/items.rs | 1 + src/lib.rs | 4 ++-- src/macros.rs | 1 + src/matches.rs | 1 + src/missed_spans.rs | 1 + src/modules/visitor.rs | 1 + src/overflow.rs | 1 + src/test/mod.rs | 1 + src/types.rs | 1 + src/visitor.rs | 1 + 16 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/attr.rs b/src/attr.rs index 433b92562023f..a2c0a28d66e09 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -3,6 +3,7 @@ use rustc_ast::ast; use rustc_ast::HasAttrs; use rustc_span::{symbol::sym, Span}; +use tracing::debug; use self::doc_comment::DocCommentFormatter; use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle}; diff --git a/src/chains.rs b/src/chains.rs index ea23690caed57..96fbe7a963a60 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -60,6 +60,7 @@ use std::cmp::min; use rustc_ast::{ast, ptr}; use rustc_span::{symbol, BytePos, Span}; +use tracing::debug; use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar}; use crate::config::{IndentStyle, Version}; diff --git a/src/closures.rs b/src/closures.rs index 5bf29441b5421..b5c26235e7706 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -1,6 +1,7 @@ use rustc_ast::{ast, ptr}; use rustc_span::Span; use thin_vec::thin_vec; +use tracing::debug; use crate::attr::get_attrs_from_stmt; use crate::config::lists::*; diff --git a/src/comment.rs b/src/comment.rs index 24a5a1be2c398..e76be0fd162fa 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -4,6 +4,7 @@ use std::{borrow::Cow, iter}; use itertools::{multipeek, MultiPeek}; use rustc_span::Span; +use tracing::{debug, trace}; use crate::config::Config; use crate::rewrite::RewriteContext; diff --git a/src/expr.rs b/src/expr.rs index c7c4b680b07fd..6ba5217bc920d 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -5,6 +5,7 @@ use itertools::Itertools; use rustc_ast::token::{Delimiter, Lit, LitKind}; use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind}; use rustc_span::{BytePos, Span}; +use tracing::debug; use crate::chains::rewrite_chain; use crate::closures; diff --git a/src/formatting.rs b/src/formatting.rs index 7f442230b12be..5e71fe107eb65 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -6,6 +6,7 @@ use std::time::{Duration, Instant}; use rustc_ast::ast; use rustc_span::Span; +use tracing::debug; use self::newline_style::apply_newline_style; use crate::comment::{CharClasses, FullCodeCharKind}; diff --git a/src/items.rs b/src/items.rs index eb11604b2771f..cbf7ce90e37a3 100644 --- a/src/items.rs +++ b/src/items.rs @@ -7,6 +7,7 @@ use regex::Regex; use rustc_ast::visit; use rustc_ast::{ast, ptr}; use rustc_span::{symbol, BytePos, Span, DUMMY_SP}; +use tracing::debug; use crate::attr::filter_inline_attrs; use crate::comment::{ diff --git a/src/lib.rs b/src/lib.rs index ff2b14af07866..4263a49fd720d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,8 +5,8 @@ #![allow(clippy::match_like_matches_macro)] #![allow(unreachable_pub)] -#[macro_use] -extern crate tracing; +// #[macro_use] +// extern crate tracing; // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; diff --git a/src/macros.rs b/src/macros.rs index a0582b061c050..b14b74a493814 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -20,6 +20,7 @@ use rustc_span::{ symbol::{self, kw}, BytePos, Span, Symbol, DUMMY_SP, }; +use tracing::debug; use crate::comment::{ contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, diff --git a/src/matches.rs b/src/matches.rs index e4fa077073c5b..30bf6271b2eba 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -4,6 +4,7 @@ use std::iter::repeat; use rustc_ast::{ast, ptr, MatchKind}; use rustc_span::{BytePos, Span}; +use tracing::debug; use crate::comment::{combine_strs_with_missing_comments, rewrite_comment, FindUncommented}; use crate::config::lists::*; diff --git a/src/missed_spans.rs b/src/missed_spans.rs index b1a7769c21bf4..56adb2e5e6916 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -1,4 +1,5 @@ use rustc_span::{BytePos, Pos, Span}; +use tracing::debug; use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices}; use crate::config::file_lines::FileLines; diff --git a/src/modules/visitor.rs b/src/modules/visitor.rs index 3e05b6d0c5d49..8b46899eaf2e2 100644 --- a/src/modules/visitor.rs +++ b/src/modules/visitor.rs @@ -1,6 +1,7 @@ use rustc_ast::ast; use rustc_ast::visit::Visitor; use rustc_span::Symbol; +use tracing::debug; use crate::attr::MetaVisitor; use crate::parse::macros::cfg_if::parse_cfg_if; diff --git a/src/overflow.rs b/src/overflow.rs index a1de71a35be84..cdb735be8a4ce 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -6,6 +6,7 @@ use itertools::Itertools; use rustc_ast::token::Delimiter; use rustc_ast::{ast, ptr}; use rustc_span::Span; +use tracing::debug; use crate::closures; use crate::config::Version; diff --git a/src/test/mod.rs b/src/test/mod.rs index 7c563801c3243..286e8b8760a97 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -16,6 +16,7 @@ use crate::source_file; use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session}; use rustfmt_config_proc_macro::nightly_only_test; +use tracing::{debug, warn}; mod configuration_snippet; mod mod_resolver; diff --git a/src/types.rs b/src/types.rs index c826547e9d001..875c61113c528 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; use rustc_ast::ptr; use rustc_span::{symbol::kw, BytePos, Pos, Span}; +use tracing::debug; use crate::comment::{combine_strs_with_missing_comments, contains_comment}; use crate::config::lists::*; diff --git a/src/visitor.rs b/src/visitor.rs index 1de9f5a952493..ac68fc5fecf34 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use rustc_ast::{ast, token::Delimiter, visit}; use rustc_data_structures::sync::Lrc; use rustc_span::{symbol, BytePos, Pos, Span}; +use tracing::debug; use crate::attr::*; use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; From 0fba9b4dbb902617fee1e34bc2536c30c1c54e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 28 Aug 2024 13:54:32 +0000 Subject: [PATCH 0188/1301] introduce `PrettyPrintMirOptions` for cosmetic MIR dump options initially starting with `-Z mir-include-spans` because we want them in the NLL mir dump pass --- src/base.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/base.rs b/src/base.rs index f1d885bf1bce8..4af4b39cc5b9e 100644 --- a/src/base.rs +++ b/src/base.rs @@ -44,8 +44,9 @@ pub(crate) fn codegen_fn<'tcx>( let _mir_guard = crate::PrintOnPanic(|| { let mut buf = Vec::new(); with_no_trimmed_paths!({ - rustc_middle::mir::pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf) - .unwrap(); + use rustc_middle::mir::pretty; + let options = pretty::PrettyPrintMirOptions::from_cli(tcx); + pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf, options).unwrap(); }); String::from_utf8_lossy(&buf).into_owned() }); From 9afdc3a1d21117232e73718bfb10b4d81570650b Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 30 Aug 2024 19:46:32 +0800 Subject: [PATCH 0189/1301] fix(ide-completion): fix handling of `for` in `impl T for A` in function body --- .../ide-completion/src/completions/keyword.rs | 62 +++++++++++++++++++ .../ide-completion/src/context/analysis.rs | 24 +++++-- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index 3f50cd55cb365..0acb87872f5e3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -150,6 +150,68 @@ fn foo(a: A) { a.$0 } ); } + #[test] + fn for_in_impl() { + check_edit( + "for", + r#" +struct X; +impl X $0 {} +"#, + r#" +struct X; +impl X for $0 {} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X $0 {} +} +"#, + r#" +fn foo() { + struct X; + impl X for $0 {} +} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X $0 +} +"#, + r#" +fn foo() { + struct X; + impl X for $0 +} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X { fn bar() { $0 } } +} +"#, + r#" +fn foo() { + struct X; + impl X { fn bar() { for $1 in $2 { + $0 +} } } +} +"#, + ); + } + #[test] fn let_semi() { cov_mark::check!(let_semi); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index ed359394f1c49..292c419498d16 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1132,10 +1132,18 @@ fn classify_name_ref( ast::PathType(it) => make_path_kind_type(it.into()), ast::PathExpr(it) => { if let Some(p) = it.syntax().parent() { - if ast::ExprStmt::can_cast(p.kind()) { - if let Some(kind) = inbetween_body_and_decl_check(p) { - return Some(make_res(NameRefKind::Keyword(kind))); - } + let p_kind = p.kind(); + // The syntax node of interest, for which we want to check whether + // it is sandwiched between an item decl signature and its body. + let probe = if ast::ExprStmt::can_cast(p_kind) { + Some(p) + } else if ast::StmtList::can_cast(p_kind) { + Some(it.syntax().clone()) + } else { + None + }; + if let Some(kind) = probe.and_then(inbetween_body_and_decl_check) { + return Some(make_res(NameRefKind::Keyword(kind))); } } @@ -1199,7 +1207,13 @@ fn classify_name_ref( } } }, - ast::RecordExpr(it) => make_path_kind_expr(it.into()), + ast::RecordExpr(it) => { + // A record expression in this position is usually a result of parsing recovery, so check that + if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { + return Some(make_res(NameRefKind::Keyword(kind))); + } + make_path_kind_expr(it.into()) + }, _ => return None, } }; From be33a2e1bd21dbb7d2a75ef949137e48d93e985e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 30 Aug 2024 15:45:12 +0200 Subject: [PATCH 0190/1301] Improve inlay hint resolution reliability --- .../crates/ide/src/inlay_hints.rs | 398 ++++++++++-------- .../crates/ide/src/inlay_hints/adjustment.rs | 6 +- .../crates/ide/src/inlay_hints/bind_pat.rs | 1 + .../ide/src/inlay_hints/binding_mode.rs | 10 +- .../crates/ide/src/inlay_hints/chaining.rs | 1 + .../ide/src/inlay_hints/closing_brace.rs | 4 +- .../ide/src/inlay_hints/closure_captures.rs | 5 + .../crates/ide/src/inlay_hints/closure_ret.rs | 1 + .../ide/src/inlay_hints/discriminant.rs | 4 +- .../ide/src/inlay_hints/fn_lifetime_fn.rs | 8 +- .../ide/src/inlay_hints/generic_param.rs | 1 + .../ide/src/inlay_hints/implicit_drop.rs | 11 +- .../ide/src/inlay_hints/implicit_static.rs | 5 + .../crates/ide/src/inlay_hints/param_name.rs | 7 +- .../ide/src/inlay_hints/range_exclusive.rs | 5 + src/tools/rust-analyzer/crates/ide/src/lib.rs | 4 +- .../rust-analyzer/src/handlers/request.rs | 4 +- .../crates/rust-analyzer/src/lsp/ext.rs | 1 + .../crates/rust-analyzer/src/lsp/to_proto.rs | 20 +- .../rust-analyzer/docs/dev/lsp-extensions.md | 2 +- 20 files changed, 282 insertions(+), 216 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 6a5d5e26a4f05..93dd56a450d94 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -15,7 +15,7 @@ use span::{Edition, EditionedFileId}; use stdx::never; use syntax::{ ast::{self, AstNode}, - match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, + match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, WalkEvent, }; use text_edit::TextEdit; @@ -36,6 +36,192 @@ mod implicit_static; mod param_name; mod range_exclusive; +// Feature: Inlay Hints +// +// rust-analyzer shows additional information inline with the source code. +// Editors usually render this using read-only virtual text snippets interspersed with code. +// +// rust-analyzer by default shows hints for +// +// * types of local variables +// * names of function arguments +// * names of const generic parameters +// * types of chained expressions +// +// Optionally, one can enable additional hints for +// +// * return types of closure expressions +// * elided lifetimes +// * compiler inserted reborrows +// * names of generic type and lifetime parameters +// +// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if +// any of the +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] +// are met: +// +// * the parameter name is a suffix of the function's name +// * the argument is a qualified constructing or call expression where the qualifier is an ADT +// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix +// of argument with _ splitting it off +// * the parameter name starts with `ra_fixture` +// * the parameter name is a +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] +// in a unary function +// * the parameter name is a +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] +// in a unary function +// +// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] +pub(crate) fn inlay_hints( + db: &RootDatabase, + file_id: FileId, + range_limit: Option, + config: &InlayHintsConfig, +) -> Vec { + let _p = tracing::info_span!("inlay_hints").entered(); + let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let file = file.syntax(); + + let mut acc = Vec::new(); + + let Some(scope) = sema.scope(file) else { + return acc; + }; + let famous_defs = FamousDefs(&sema, scope.krate()); + + let parent_impl = &mut None; + let hints = |node| hints(&mut acc, parent_impl, &famous_defs, config, file_id, node); + match range_limit { + // FIXME: This can miss some hints that require the parent of the range to calculate + Some(range) => match file.covering_element(range) { + NodeOrToken::Token(_) => return acc, + NodeOrToken::Node(n) => n + .preorder() + .filter(|event| matches!(event, WalkEvent::Enter(node) if range.intersect(node.text_range()).is_some())) + .for_each(hints), + }, + None => file.preorder().for_each(hints), + }; + + acc +} + +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + resolve_range: TextRange, + hash: u64, + config: &InlayHintsConfig, + hasher: impl Fn(&InlayHint) -> u64, +) -> Option { + let _p = tracing::info_span!("inlay_hints_resolve").entered(); + let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let file = file.syntax(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let parent_impl = &mut None; + let hints = |node| hints(&mut acc, parent_impl, &famous_defs, config, file_id, node); + + let mut res = file.clone(); + let res = loop { + res = match res.child_or_token_at_range(resolve_range) { + Some(NodeOrToken::Node(n)) if n.text_range() == resolve_range => break n, + Some(NodeOrToken::Node(n)) => n, + _ => break res, + }; + }; + res.preorder().for_each(hints); + acc.into_iter().find(|hint| hasher(hint) == hash) +} + +fn hints( + hints: &mut Vec, + parent_impl: &mut Option, + famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + node: WalkEvent, +) { + let node = match node { + WalkEvent::Enter(node) => node, + WalkEvent::Leave(n) => { + if ast::Impl::can_cast(n.kind()) { + parent_impl.take(); + } + return; + } + }; + closing_brace::hints(hints, sema, config, file_id, node.clone()); + if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { + generic_param::hints(hints, sema, config, any_has_generic_args); + } + + match_ast! { + match node { + ast::Expr(expr) => { + chaining::hints(hints, famous_defs, config, file_id, &expr); + adjustment::hints(hints, famous_defs, config, file_id, &expr); + match expr { + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), + ast::Expr::MethodCallExpr(it) => { + param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) + } + ast::Expr::ClosureExpr(it) => { + closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); + closure_ret::hints(hints, famous_defs, config, file_id, it) + }, + ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id, it), + _ => None, + } + }, + ast::Pat(it) => { + binding_mode::hints(hints, famous_defs, config, file_id, &it); + match it { + ast::Pat::IdentPat(it) => { + bind_pat::hints(hints, famous_defs, config, file_id, &it); + } + ast::Pat::RangePat(it) => { + range_exclusive::hints(hints, famous_defs, config, file_id, it); + } + _ => {} + } + Some(()) + }, + ast::Item(it) => match it { + // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints + ast::Item::Impl(impl_) => { + *parent_impl = Some(impl_); + None + }, + ast::Item::Fn(it) => { + implicit_drop::hints(hints, famous_defs, config, file_id, &it); + fn_lifetime_fn::hints(hints, famous_defs, config, file_id, it) + }, + // static type elisions + ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)), + ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)), + ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it), + _ => None, + }, + // FIXME: fn-ptr type, dyn fn type, and trait object type elisions + ast::Type(_) => None, + _ => None, + } + }; +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { pub render_colons: bool, @@ -162,6 +348,9 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option, + /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the + /// hint does not support resolving. + pub resolve_parent: Option, } impl std::hash::Hash for InlayHint { @@ -186,6 +375,7 @@ impl InlayHint { position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, } } @@ -198,11 +388,12 @@ impl InlayHint { position: InlayHintPosition::Before, pad_left: false, pad_right: false, + resolve_parent: None, } } - pub fn needs_resolve(&self) -> bool { - self.text_edit.is_some() || self.label.needs_resolve() + pub fn needs_resolve(&self) -> Option { + self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve()) } } @@ -434,190 +625,6 @@ fn label_of_ty( Some(r) } -fn ty_to_text_edit( - sema: &Semantics<'_, RootDatabase>, - node_for_hint: &SyntaxNode, - ty: &hir::Type, - offset_to_insert: TextSize, - prefix: String, -) -> Option { - let scope = sema.scope(node_for_hint)?; - // FIXME: Limit the length and bail out on excess somehow? - let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; - - let mut builder = TextEdit::builder(); - builder.insert(offset_to_insert, prefix); - builder.insert(offset_to_insert, rendered); - Some(builder.finish()) -} - -// Feature: Inlay Hints -// -// rust-analyzer shows additional information inline with the source code. -// Editors usually render this using read-only virtual text snippets interspersed with code. -// -// rust-analyzer by default shows hints for -// -// * types of local variables -// * names of function arguments -// * names of const generic parameters -// * types of chained expressions -// -// Optionally, one can enable additional hints for -// -// * return types of closure expressions -// * elided lifetimes -// * compiler inserted reborrows -// * names of generic type and lifetime parameters -// -// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if -// any of the -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] -// are met: -// -// * the parameter name is a suffix of the function's name -// * the argument is a qualified constructing or call expression where the qualifier is an ADT -// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix -// of argument with _ splitting it off -// * the parameter name starts with `ra_fixture` -// * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] -// in a unary function -// * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] -// in a unary function -// -// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] -pub(crate) fn inlay_hints( - db: &RootDatabase, - file_id: FileId, - range_limit: Option, - config: &InlayHintsConfig, -) -> Vec { - let _p = tracing::info_span!("inlay_hints").entered(); - let sema = Semantics::new(db); - let file_id = sema - .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); - let file = sema.parse(file_id); - let file = file.syntax(); - - let mut acc = Vec::new(); - - if let Some(scope) = sema.scope(file) { - let famous_defs = FamousDefs(&sema, scope.krate()); - - let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); - match range_limit { - Some(range) => match file.covering_element(range) { - NodeOrToken::Token(_) => return acc, - NodeOrToken::Node(n) => n - .descendants() - .filter(|descendant| range.intersect(descendant.text_range()).is_some()) - .for_each(hints), - }, - None => file.descendants().for_each(hints), - }; - } - - acc -} - -pub(crate) fn inlay_hints_resolve( - db: &RootDatabase, - file_id: FileId, - position: TextSize, - hash: u64, - config: &InlayHintsConfig, - hasher: impl Fn(&InlayHint) -> u64, -) -> Option { - let _p = tracing::info_span!("inlay_hints_resolve").entered(); - let sema = Semantics::new(db); - let file_id = sema - .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); - let file = sema.parse(file_id); - let file = file.syntax(); - - let scope = sema.scope(file)?; - let famous_defs = FamousDefs(&sema, scope.krate()); - let mut acc = Vec::new(); - - let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); - let token = file.token_at_offset(position).left_biased()?; - if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { - parent_item.syntax().descendants().for_each(hints) - } else { - return None; - } - - acc.into_iter().find(|hint| hasher(hint) == hash) -} - -fn hints( - hints: &mut Vec, - famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, - file_id: EditionedFileId, - node: SyntaxNode, -) { - closing_brace::hints(hints, sema, config, file_id, node.clone()); - if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { - generic_param::hints(hints, sema, config, any_has_generic_args); - } - match_ast! { - match node { - ast::Expr(expr) => { - chaining::hints(hints, famous_defs, config, file_id, &expr); - adjustment::hints(hints, sema, config, file_id, &expr); - match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, sema, config, ast::Expr::from(it)), - ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, sema, config, ast::Expr::from(it)) - } - ast::Expr::ClosureExpr(it) => { - closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); - closure_ret::hints(hints, famous_defs, config, file_id, it) - }, - ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, config, it), - _ => None, - } - }, - ast::Pat(it) => { - binding_mode::hints(hints, sema, config, &it); - match it { - ast::Pat::IdentPat(it) => { - bind_pat::hints(hints, famous_defs, config, file_id, &it); - } - ast::Pat::RangePat(it) => { - range_exclusive::hints(hints, config, it); - } - _ => {} - } - Some(()) - }, - ast::Item(it) => match it { - // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints - ast::Item::Impl(_) => None, - ast::Item::Fn(it) => { - implicit_drop::hints(hints, sema, config, file_id, &it); - fn_lifetime_fn::hints(hints, config, it) - }, - // static type elisions - ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)), - ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)), - ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it), - _ => None, - }, - // FIXME: fn-ptr type, dyn fn type, and trait object type elisions - ast::Type(_) => None, - _ => None, - } - }; -} - /// Checks if the type is an Iterator from std::iter and returns the iterator trait and the item type of the concrete iterator. fn hint_iterator( sema: &Semantics<'_, RootDatabase>, @@ -653,6 +660,23 @@ fn hint_iterator( None } +fn ty_to_text_edit( + sema: &Semantics<'_, RootDatabase>, + node_for_hint: &SyntaxNode, + ty: &hir::Type, + offset_to_insert: TextSize, + prefix: String, +) -> Option { + let scope = sema.scope(node_for_hint)?; + // FIXME: Limit the length and bail out on excess somehow? + let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; + + let mut builder = TextEdit::builder(); + builder.insert(offset_to_insert, prefix); + builder.insert(offset_to_insert, rendered); + Some(builder.finish()) +} + fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { matches!(closure.body(), Some(ast::Expr::BlockExpr(_))) } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 31c1a991d53d7..dc390f8f67af8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -6,9 +6,8 @@ use either::Either; use hir::{ Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, - Semantics, }; -use ide_db::RootDatabase; +use ide_db::famous_defs::FamousDefs; use span::EditionedFileId; use stdx::never; @@ -24,7 +23,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: EditionedFileId, expr: &ast::Expr, @@ -156,6 +155,7 @@ pub(super) fn hints( kind: InlayKind::Adjustment, label, text_edit: None, + resolve_parent: Some(expr.syntax().text_range()), }); } if !postfix && needs_inner_parens { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 82b0a6ffcf135..7a808fb4a929d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -110,6 +110,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: !render_colons, pad_right: false, + resolve_parent: Some(pat.syntax().text_range()), }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index f27390ee898bb..d1c0677863db7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -2,17 +2,19 @@ //! ```no_run //! let /* & */ (/* ref */ x,) = &(0,); //! ``` -use hir::{Mutability, Semantics}; -use ide_db::RootDatabase; +use hir::Mutability; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::ast::{self, AstNode}; use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, pat: &ast::Pat, ) -> Option<()> { if !config.binding_mode_hints { @@ -57,6 +59,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: mut_reference, + resolve_parent: Some(pat.syntax().text_range()), }); }); match pat { @@ -75,6 +78,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: true, + resolve_parent: Some(pat.syntax().text_range()), }); } ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 35f4d46e187c4..df34e4aa2458d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -67,6 +67,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: true, pad_right: false, + resolve_parent: Some(expr.syntax().text_range()), }); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index d78fd64bdf4dc..8af5bd5661a2d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -18,12 +18,13 @@ pub(super) fn hints( sema: &Semantics<'_, RootDatabase>, config: &InlayHintsConfig, file_id: EditionedFileId, - mut node: SyntaxNode, + original_node: SyntaxNode, ) -> Option<()> { let min_lines = config.closing_brace_hints_min_lines?; let name = |it: ast::Name| it.syntax().text_range(); + let mut node = original_node.clone(); let mut closing_token; let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) { closing_token = item_list.r_curly_token()?; @@ -145,6 +146,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: true, pad_right: false, + resolve_parent: Some(original_node.text_range()), }); None diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index e87e10d8504ed..adf7cbc3656cd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -40,6 +40,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); range } @@ -52,6 +53,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, }); let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { @@ -85,6 +87,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); if idx != last { @@ -96,6 +99,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, }); } } @@ -107,6 +111,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 325c2040691b1..6827540fa82c0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -72,6 +72,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); Some(()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index eca0ebe629f48..35b62878329fc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -35,7 +35,7 @@ pub(super) fn enum_hints( return None; } for variant in enum_.variant_list()?.variants() { - variant_hints(acc, sema, &variant); + variant_hints(acc, sema, &enum_, &variant); } Some(()) } @@ -43,6 +43,7 @@ pub(super) fn enum_hints( fn variant_hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, + enum_: &ast::Enum, variant: &ast::Variant, ) -> Option<()> { if variant.expr().is_some() { @@ -90,6 +91,7 @@ fn variant_hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(enum_.syntax().text_range()), }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index d3666754e2be9..4d35e71a06fe6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -2,8 +2,9 @@ //! ```no_run //! fn example/* <'0> */(a: &/* '0 */()) {} //! ``` -use ide_db::{syntax_helpers::node_ext::walk_ty, FxHashMap}; +use ide_db::{famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap}; use itertools::Itertools; +use span::EditionedFileId; use syntax::{ ast::{self, AstNode, HasGenericParams, HasName}, SyntaxToken, @@ -14,7 +15,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE pub(super) fn hints( acc: &mut Vec, + FamousDefs(_, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, func: ast::Fn, ) -> Option<()> { if config.lifetime_elision_hints == LifetimeElisionHints::Never { @@ -29,6 +32,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }; let param_list = func.param_list()?; @@ -195,6 +199,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }); } (None, allocated_lifetimes) => acc.push(InlayHint { @@ -205,6 +210,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, }), } Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index b60a80a8ac6b5..ed7ebc3b1e7ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -92,6 +92,7 @@ pub(crate) fn hints( kind: InlayKind::GenericParameter, label, text_edit: None, + resolve_parent: Some(node.syntax().text_range()), }) }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index b4695a2b3519a..dd4b3efeecfe1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -8,9 +8,9 @@ use hir::{ db::{DefDatabase as _, HirDatabase as _}, mir::{MirSpan, TerminatorKind}, - ChalkTyInterner, DefWithBody, Semantics, + ChalkTyInterner, DefWithBody, }; -use ide_db::{FileRange, RootDatabase}; +use ide_db::{famous_defs::FamousDefs, FileRange}; use span::EditionedFileId; use syntax::{ @@ -22,16 +22,16 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: EditionedFileId, - def: &ast::Fn, + node: &ast::Fn, ) -> Option<()> { if !config.implicit_drop_hints { return None; } - let def = sema.to_def(def)?; + let def = sema.to_def(node)?; let def: DefWithBody = def.into(); let (hir, source_map) = sema.db.body_with_source_map(def.into()); @@ -121,6 +121,7 @@ pub(super) fn hints( kind: InlayKind::Drop, label, text_edit: None, + resolve_parent: Some(node.syntax().text_range()), }) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index 42223ddf580e3..8d422478cbfcc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -3,6 +3,8 @@ //! static S: &/* 'static */str = ""; //! ``` use either::Either; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::{ ast::{self, AstNode}, SyntaxKind, @@ -12,7 +14,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE pub(super) fn hints( acc: &mut Vec, + FamousDefs(_sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, statik_or_const: Either, ) -> Option<()> { if config.lifetime_elision_hints != LifetimeElisionHints::Always { @@ -38,6 +42,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 0f3142ef3f88b..28b0fa6dd4d1a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -7,8 +7,9 @@ use std::fmt::Display; use either::Either; use hir::{Callable, Semantics}; -use ide_db::RootDatabase; +use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use span::EditionedFileId; use stdx::to_lower_snake_case; use syntax::{ ast::{self, AstNode, HasArgList, HasName, UnaryOp}, @@ -19,8 +20,9 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -60,6 +62,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: true, + resolve_parent: Some(expr.syntax().text_range()), } }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs index bfb92838857ca..de9b0e98a4beb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs @@ -3,13 +3,17 @@ //! for i in 0../* < */10 {} //! if let ../* < */100 = 50 {} //! ``` +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::{ast, SyntaxToken, T}; use crate::{InlayHint, InlayHintsConfig}; pub(super) fn hints( acc: &mut Vec, + FamousDefs(_sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, range: impl ast::RangeItem, ) -> Option<()> { (config.range_exclusive_hints && range.end().is_some()) @@ -30,6 +34,7 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { kind: crate::InlayKind::RangeExclusive, label: crate::InlayHintLabel::from("<"), text_edit: None, + resolve_parent: None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index ba0aaae19c9fd..cdadfeea4b9a8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -439,12 +439,12 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - position: TextSize, + resolve_range: TextRange, hash: u64, hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { self.with_db(|db| { - inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config, hasher) + inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) }) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 1ad5ff0c8cdc1..50df9dc876020 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1602,14 +1602,14 @@ pub(crate) fn handle_inlay_hints_resolve( anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); let line_index = snap.file_line_index(file_id)?; - let hint_position = from_proto::offset(&line_index, original_hint.position)?; + let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?; let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, - hint_position, + range, hash, |hint| { std::hash::BuildHasher::hash_one( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 8d1a686dc4d41..ed6f16a17302b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -819,6 +819,7 @@ pub struct InlayHintResolveData { pub file_id: u32, // This is a string instead of a u64 as javascript can't represent u64 fully pub hash: String, + pub resolve_range: lsp_types::Range, pub version: Option, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index eb6bc2a9ce9b0..7eed9ce2f38a3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -452,10 +452,13 @@ pub(crate) fn inlay_hint( file_id: FileId, mut inlay_hint: InlayHint, ) -> Cancellable { - let resolve_hash = inlay_hint.needs_resolve().then(|| { - std::hash::BuildHasher::hash_one( - &std::hash::BuildHasherDefault::::default(), - &inlay_hint, + let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| { + ( + range, + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &inlay_hint, + ), ) }); @@ -465,7 +468,7 @@ pub(crate) fn inlay_hint( .visual_studio_code_version() // https://github.com/microsoft/vscode/issues/193124 .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && resolve_hash.is_some() + && resolve_range_and_hash.is_some() && fields_to_resolve.resolve_text_edits { something_to_resolve |= inlay_hint.text_edit.is_some(); @@ -477,16 +480,17 @@ pub(crate) fn inlay_hint( snap, fields_to_resolve, &mut something_to_resolve, - resolve_hash.is_some(), + resolve_range_and_hash.is_some(), inlay_hint.label, )?; - let data = match resolve_hash { - Some(hash) if something_to_resolve => Some( + let data = match resolve_range_and_hash { + Some((resolve_range, hash)) if something_to_resolve => Some( to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash: hash.to_string(), version: snap.file_version(file_id), + resolve_range: range(line_index, resolve_range), }) .unwrap(), ), diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 4786bd54d59be..68afacf2db5cb 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ tests/ui/single_match.rs:371:5 + | +LL | / match DATA { +LL | | DATA => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `{ println!() }` + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:376:5 + | +LL | / match CONST_I32 { +LL | | CONST_I32 => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `{ println!() }` + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:382:5 + | +LL | / match i { +LL | | i => { +LL | | let a = 1; +LL | | let b = 2; +LL | | }, +LL | | _ => {}, +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let a = 1; +LL + let b = 2; +LL + } + | + +error: aborting due to 23 previous errors diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index 163be16ad8be7..fb57411aaa426 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -171,3 +171,7 @@ fn issue_10808(bar: Option) { }, } } + +fn irrefutable_match() -> Option<&'static ExprNode> { + { Some(&NODE) } +} diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 3f1fd2b31832f..2d9e877ee0fee 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -199,3 +199,13 @@ fn issue_10808(bar: Option) { }, } } + +fn irrefutable_match() -> Option<&'static ExprNode> { + match ExprNode::Butterflies { + ExprNode::Butterflies => Some(&NODE), + _ => { + let x = 5; + None + }, + } +} diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 61c348260d05e..61209053fd0fc 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -197,5 +197,17 @@ LL + println!("None"); LL + } | -error: aborting due to 9 previous errors +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match_else.rs:204:5 + | +LL | / match ExprNode::Butterflies { +LL | | ExprNode::Butterflies => Some(&NODE), +LL | | _ => { +LL | | let x = 5; +LL | | None +LL | | }, +LL | | } + | |_____^ help: try: `{ Some(&NODE) }` + +error: aborting due to 10 previous errors From 1350769f4796b88eca83568433448038178174cb Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 31 Aug 2024 03:01:54 +0200 Subject: [PATCH 0195/1301] Apply changes, fix path information in a comment in config.rs --- .../crates/rust-analyzer/src/config.rs | 8 +++----- .../rust-analyzer/src/handlers/notification.rs | 13 ------------- .../crates/rust-analyzer/src/reload.rs | 2 +- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 0a1bdf49bdbd2..c884216b3e3cd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -62,12 +62,10 @@ mod patch_old_style; // To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep // parsing the old name. config_data! { - /// Configs that apply on a workspace-wide scope. There are 3 levels on which a global configuration can be configured - // FIXME: 1. and 3. should be split, some configs do not make sense per project + /// Configs that apply on a workspace-wide scope. There are 2 levels on which a global configuration can be configured /// - /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer.toml) + /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer/rust-analyzer.toml) /// 2. Client's own configurations (e.g `settings.json` on VS Code) - /// 3. `rust-analyzer.toml` file located at the workspace root /// /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle. global: struct GlobalDefaultConfigData <- GlobalConfigInput -> { @@ -532,7 +530,7 @@ config_data! { cargo_allTargets: bool = true, /// Automatically refresh project info via `cargo metadata` on /// `Cargo.toml` or `.cargo/config.toml` changes. - pub(crate) cargo_autoreload: bool = true, + cargo_autoreload: bool = true, /// Run build scripts (`build.rs`) for more precise code analysis. cargo_buildScripts_enable: bool = true, /// Specifies the invocation strategy to use when running the build scripts command. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index dc8d40e0ee075..e8d1a7e4df68f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -145,13 +145,10 @@ pub(crate) fn handle_did_save_text_document( state: &mut GlobalState, params: DidSaveTextDocumentParams, ) -> anyhow::Result<()> { - let mut deps_change_processed = false; - if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { let snap = state.snapshot(); let file_id = snap.vfs_path_to_file_id(&vfs_path)?; let sr = snap.analysis.source_root_id(file_id)?; - deps_change_processed = true; if state.config.script_rebuild_on_save(Some(sr)) && state.build_deps_changed { state.build_deps_changed = false; @@ -199,16 +196,6 @@ pub(crate) fn handle_did_save_text_document( } } - if !deps_change_processed - && state.config.script_rebuild_on_save(None) - && state.build_deps_changed - { - state.build_deps_changed = false; - state - .fetch_build_data_queue - .request_op("build_deps_changed - save notification".to_owned(), ()); - } - Ok(()) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 5d77b7aa67ef0..12667e3c90dee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -122,7 +122,7 @@ impl GlobalState { }; let mut message = String::new(); - if !self.config.cargo_autoreload(None) + if !self.config.cargo_autoreload_config(None) && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() && self.config.discover_workspace_config().is_none() From 5acbc4f5e1f91f8f86df85766fd6084631d5d805 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 31 Aug 2024 10:51:13 +0200 Subject: [PATCH 0196/1301] fix: Fix lifetime elision inlay hints breaking for ranged requests --- .../crates/ide/src/inlay_hints.rs | 114 +++++++++--------- .../crates/ide/src/inlay_hints/lifetime.rs | 2 +- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index be99510af2a17..2e49af49145a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -15,7 +15,7 @@ use span::{Edition, EditionedFileId}; use stdx::never; use syntax::{ ast::{self, AstNode, HasGenericParams}, - format_smolstr, match_ast, NodeOrToken, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent, + format_smolstr, match_ast, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent, }; use text_edit::TextEdit; @@ -95,26 +95,27 @@ pub(crate) fn inlay_hints( let famous_defs = FamousDefs(&sema, scope.krate()); let ctx = &mut InlayHintCtx::default(); - let hints = |node| hints(&mut acc, ctx, &famous_defs, config, file_id, node); - match range_limit { - // FIXME: This can miss some hints that require the parent of the range to calculate - Some(range) => match file.covering_element(range) { - NodeOrToken::Token(_) => return acc, - NodeOrToken::Node(n) => n - .preorder() - .filter(|event| matches!(event, WalkEvent::Enter(node) if range.intersect(node.text_range()).is_some())) - .for_each(hints), - }, - None => file.preorder().for_each(hints), + let mut hints = |event| { + if let Some(node) = handle_event(ctx, event) { + hints(&mut acc, ctx, &famous_defs, config, file_id, node); + } }; - + let mut preorder = file.preorder(); + while let Some(event) = preorder.next() { + // FIXME: This can miss some hints that require the parent of the range to calculate + if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); + } acc } #[derive(Default)] struct InlayHintCtx { lifetime_stacks: Vec>, - is_param_list: bool, } pub(crate) fn inlay_hints_resolve( @@ -138,20 +139,52 @@ pub(crate) fn inlay_hints_resolve( let mut acc = Vec::new(); let ctx = &mut InlayHintCtx::default(); - let hints = |node| hints(&mut acc, ctx, &famous_defs, config, file_id, node); - - let mut res = file.clone(); - let res = loop { - res = match res.child_or_token_at_range(resolve_range) { - Some(NodeOrToken::Node(n)) if n.text_range() == resolve_range => break n, - Some(NodeOrToken::Node(n)) => n, - _ => break res, - }; + let mut hints = |event| { + if let Some(node) = handle_event(ctx, event) { + hints(&mut acc, ctx, &famous_defs, config, file_id, node); + } }; - res.preorder().for_each(hints); + + let mut preorder = file.preorder(); + while let Some(event) = preorder.next() { + // FIXME: This can miss some hints that require the parent of the range to calculate + if matches!(&event, WalkEvent::Enter(node) if resolve_range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); + } acc.into_iter().find(|hint| hasher(hint) == hash) } +fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent) -> Option { + match node { + WalkEvent::Enter(node) => { + if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) { + let params = node + .generic_param_list() + .map(|it| { + it.lifetime_params() + .filter_map(|it| { + it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..])) + }) + .collect() + }) + .unwrap_or_default(); + ctx.lifetime_stacks.push(params); + } + Some(node) + } + WalkEvent::Leave(n) => { + if ast::AnyHasGenericParams::can_cast(n.kind()) { + ctx.lifetime_stacks.pop(); + } + None + } + } +} + // FIXME: At some point when our hir infra is fleshed out enough we should flip this and traverse the // HIR instead of the syntax tree. fn hints( @@ -160,35 +193,8 @@ fn hints( famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: EditionedFileId, - node: WalkEvent, + node: SyntaxNode, ) { - let node = match node { - WalkEvent::Enter(node) => node, - WalkEvent::Leave(n) => { - if ast::AnyHasGenericParams::can_cast(n.kind()) { - ctx.lifetime_stacks.pop(); - // pop - } - if ast::ParamList::can_cast(n.kind()) { - ctx.is_param_list = false; - // pop - } - return; - } - }; - - if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) { - let params = node - .generic_param_list() - .map(|it| { - it.lifetime_params() - .filter_map(|it| it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..]))) - .collect() - }) - .unwrap_or_default(); - ctx.lifetime_stacks.push(params); - } - closing_brace::hints(hints, sema, config, file_id, node.clone()); if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { generic_param::hints(hints, sema, config, any_has_generic_args); @@ -242,10 +248,6 @@ fn hints( ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path), _ => Some(()), }, - ast::ParamList(_) => { - ctx.is_param_list = true; - Some(()) - }, _ => Some(()), } }; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs index de463670677c2..653e3a6ef1df9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -532,7 +532,7 @@ fn fn_ptr(a: fn(&()) -> &fn(&()) -> &()) {} //^^ for<'1> //^'1 //^'1 -fn fn_ptr2(a: for<'a> fn(&()) -> &())) {} +fn fn_ptr2(a: for<'a> fn(&()) -> &()) {} //^'0, $ //^'0 //^'0 From ce3ca9904869ae065eda79bab7fe9114ed0e83cd Mon Sep 17 00:00:00 2001 From: riverbl <94326797+riverbl@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:26:51 +0100 Subject: [PATCH 0197/1301] Add explicit enum discriminant assist Add assist for adding explicit discriminants to all variants of an enum. --- .../crates/hir-ty/src/consteval.rs | 22 +- .../handlers/explicit_enum_discriminant.rs | 202 ++++++++++++++++++ .../crates/ide-assists/src/lib.rs | 2 + 3 files changed, 221 insertions(+), 5 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 8b6cde975f63e..3f60834e02dd2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -11,7 +11,7 @@ use hir_def::{ ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId, }; use hir_expand::Lookup; -use stdx::never; +use stdx::{never, IsNoneOr}; use triomphe::Arc; use crate::{ @@ -169,15 +169,23 @@ pub fn usize_const(db: &dyn HirDatabase, value: Option, krate: CrateId) -> } pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { + try_const_usize_sign_extend(db, c, false) +} + +pub fn try_const_usize_sign_extend( + db: &dyn HirDatabase, + c: &Const, + is_signed: bool, +) -> Option { match &c.data(Interner).value { chalk_ir::ConstValue::BoundVar(_) => None, chalk_ir::ConstValue::InferenceVar(_) => None, chalk_ir::ConstValue::Placeholder(_) => None, chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, false))), + ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, is_signed))), ConstScalar::UnevaluatedConst(c, subst) => { let ec = db.const_eval(*c, subst.clone(), None).ok()?; - try_const_usize(db, &ec) + try_const_usize_sign_extend(db, &ec, is_signed) } _ => None, }, @@ -256,8 +264,8 @@ pub(crate) fn const_eval_discriminant_variant( ) -> Result { let def = variant_id.into(); let body = db.body(def); + let loc = variant_id.lookup(db.upcast()); if body.exprs[body.body_expr] == Expr::Missing { - let loc = variant_id.lookup(db.upcast()); let prev_idx = loc.index.checked_sub(1); let value = match prev_idx { Some(prev_idx) => { @@ -269,13 +277,17 @@ pub(crate) fn const_eval_discriminant_variant( }; return Ok(value); } + + let repr = db.enum_data(loc.parent).repr; + let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); + let mir_body = db.monomorphized_mir_body( def, Substitution::empty(Interner), db.trait_environment_for_body(def), )?; let c = interpret_mir(db, mir_body, false, None).0?; - let c = try_const_usize(db, &c).unwrap() as i128; + let c = try_const_usize_sign_extend(db, &c, is_signed).unwrap() as i128; Ok(c) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs new file mode 100644 index 0000000000000..b5b7da69f7529 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs @@ -0,0 +1,202 @@ +use hir::Semantics; +use ide_db::{ + assists::{AssistId, AssistKind}, + source_change::SourceChangeBuilder, + RootDatabase, +}; +use syntax::{ast, AstNode}; + +use crate::{AssistContext, Assists}; + +// Assist: explicit_enum_discriminant +// +// Adds explicit discriminant to all enum variants. +// +// ``` +// enum TheEnum$0 { +// Foo, +// Bar, +// Baz = 42, +// Quux, +// } +// ``` +// -> +// ``` +// enum TheEnum { +// Foo = 0, +// Bar = 1, +// Baz = 42, +// Quux = 43, +// } +// ``` +pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let enum_node = ctx.find_node_at_offset::()?; + let enum_def = ctx.sema.to_def(&enum_node)?; + + let is_data_carrying = enum_def.is_data_carrying(ctx.db()); + let has_primitive_repr = enum_def.repr(ctx.db()).and_then(|repr| repr.int).is_some(); + + // Data carrying enums without a primitive repr have no stable discriminants. + if is_data_carrying && !has_primitive_repr { + return None; + } + + let variant_list = enum_node.variant_list()?; + + // Don't offer the assist if the enum has no variants or if all variants already have an + // explicit discriminant. + if variant_list.variants().all(|variant_node| variant_node.expr().is_some()) { + return None; + } + + acc.add( + AssistId("explicit_enum_discriminant", AssistKind::RefactorRewrite), + "Add explicit enum discriminants", + enum_node.syntax().text_range(), + |builder| { + for variant_node in variant_list.variants() { + add_variant_discriminant(&ctx.sema, builder, &variant_node); + } + }, + ); + + Some(()) +} + +fn add_variant_discriminant( + sema: &Semantics<'_, RootDatabase>, + builder: &mut SourceChangeBuilder, + variant_node: &ast::Variant, +) { + if variant_node.expr().is_some() { + return; + } + + let Some(variant_def) = sema.to_def(variant_node) else { + return; + }; + let Ok(discriminant) = variant_def.eval(sema.db) else { + return; + }; + + let variant_range = variant_node.syntax().text_range(); + + builder.insert(variant_range.end(), format!(" = {discriminant}")); +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::explicit_enum_discriminant; + + #[test] + fn non_primitive_repr_non_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo, + Bar, + Baz = 42, + Quux, +} +"#, + r#" +enum TheEnum { + Foo = 0, + Bar = 1, + Baz = 42, + Quux = 43, +} +"#, + ); + } + + #[test] + fn primitive_repr_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +#[repr(u8)] +$0enum TheEnum { + Foo { x: u32 }, + Bar, + Baz(String), + Quux, +} +"#, + r#" +#[repr(u8)] +enum TheEnum { + Foo { x: u32 } = 0, + Bar = 1, + Baz(String) = 2, + Quux = 3, +} +"#, + ); + } + + #[test] + fn non_primitive_repr_data_bearing_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo, + Bar(u16), + Baz, +} +"#, + ); + } + + #[test] + fn primitive_repr_non_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +#[repr(i64)] +enum TheEnum { + Foo = 1 << 63, + Bar, + Baz$0 = 0x7fff_ffff_ffff_fffe, + Quux, +} +"#, + r#" +#[repr(i64)] +enum TheEnum { + Foo = 1 << 63, + Bar = -9223372036854775807, + Baz = 0x7fff_ffff_ffff_fffe, + Quux = 9223372036854775807, +} +"#, + ); + } + + #[test] + fn discriminants_already_explicit_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo = 0, + Bar = 4, +} +"#, + ); + } + + #[test] + fn empty_enum_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 {} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index c88cb3d5eaf05..b2ccd1fde815e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -136,6 +136,7 @@ mod handlers { mod destructure_tuple_binding; mod desugar_doc_comment; mod expand_glob_import; + mod explicit_enum_discriminant; mod extract_expressions_from_format_string; mod extract_function; mod extract_module; @@ -266,6 +267,7 @@ mod handlers { destructure_tuple_binding::destructure_tuple_binding, destructure_struct_binding::destructure_struct_binding, expand_glob_import::expand_glob_import, + explicit_enum_discriminant::explicit_enum_discriminant, extract_expressions_from_format_string::extract_expressions_from_format_string, extract_struct_from_enum_variant::extract_struct_from_enum_variant, extract_type_alias::extract_type_alias, From 5392d56c93cf5584e4d18cfb86cc314138db1c94 Mon Sep 17 00:00:00 2001 From: riverbl <94326797+riverbl@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:42:11 +0100 Subject: [PATCH 0198/1301] Update generated doctests Update generated doctests, change unit test to be different to doctest. --- .../handlers/explicit_enum_discriminant.rs | 4 ++++ .../crates/ide-assists/src/tests/generated.rs | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs index b5b7da69f7529..fafc3448a87ca 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs @@ -100,6 +100,8 @@ enum TheEnum$0 { Bar, Baz = 42, Quux, + FooBar = -5, + FooBaz, } "#, r#" @@ -108,6 +110,8 @@ enum TheEnum { Bar = 1, Baz = 42, Quux = 43, + FooBar = -5, + FooBaz = -4, } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index dce7bbf342f74..48e12a8107353 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -909,6 +909,29 @@ fn qux(bar: Bar, baz: Baz) {} ) } +#[test] +fn doctest_explicit_enum_discriminant() { + check_doc_test( + "explicit_enum_discriminant", + r#####" +enum TheEnum$0 { + Foo, + Bar, + Baz = 42, + Quux, +} +"#####, + r#####" +enum TheEnum { + Foo = 0, + Bar = 1, + Baz = 42, + Quux = 43, +} +"#####, + ) +} + #[test] fn doctest_extract_expressions_from_format_string() { check_doc_test( From d6573f9b08283a1eb732923f4ddc0899c7376e79 Mon Sep 17 00:00:00 2001 From: riverbl <94326797+riverbl@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:13:08 +0100 Subject: [PATCH 0199/1301] Rename function and remove flag argument Use less confusing name for function, duplicate logic rather than taking flag as argument. --- .../crates/hir-ty/src/consteval.rs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 3f60834e02dd2..e5c493ac56e1b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -169,23 +169,31 @@ pub fn usize_const(db: &dyn HirDatabase, value: Option, krate: CrateId) -> } pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { - try_const_usize_sign_extend(db, c, false) + match &c.data(Interner).value { + chalk_ir::ConstValue::BoundVar(_) => None, + chalk_ir::ConstValue::InferenceVar(_) => None, + chalk_ir::ConstValue::Placeholder(_) => None, + chalk_ir::ConstValue::Concrete(c) => match &c.interned { + ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, false))), + ConstScalar::UnevaluatedConst(c, subst) => { + let ec = db.const_eval(*c, subst.clone(), None).ok()?; + try_const_usize(db, &ec) + } + _ => None, + }, + } } -pub fn try_const_usize_sign_extend( - db: &dyn HirDatabase, - c: &Const, - is_signed: bool, -) -> Option { +pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option { match &c.data(Interner).value { chalk_ir::ConstValue::BoundVar(_) => None, chalk_ir::ConstValue::InferenceVar(_) => None, chalk_ir::ConstValue::Placeholder(_) => None, chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, is_signed))), + ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))), ConstScalar::UnevaluatedConst(c, subst) => { let ec = db.const_eval(*c, subst.clone(), None).ok()?; - try_const_usize_sign_extend(db, &ec, is_signed) + try_const_isize(db, &ec) } _ => None, }, @@ -287,7 +295,11 @@ pub(crate) fn const_eval_discriminant_variant( db.trait_environment_for_body(def), )?; let c = interpret_mir(db, mir_body, false, None).0?; - let c = try_const_usize_sign_extend(db, &c, is_signed).unwrap() as i128; + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, &c).unwrap() as i128 + }; Ok(c) } From 467dbcba60bb109636a31af3d298e253850422dc Mon Sep 17 00:00:00 2001 From: Kappa322 Date: Sat, 20 Jul 2024 13:05:55 +0200 Subject: [PATCH 0200/1301] Improve documentation for ::from_str_radix Two improvements to the documentation: - Document `-` as a valid character for signed integer destinations - Make the documentation even more clear that extra whitespace and non-digit characters is invalid. Many other languages, e.g. c++, are very permissive in string to integer routines and simply try to consume as much as they can, ignoring the rest. This is trying to make the transition for developers who are used to the conversion semantics in these languages a bit easier. --- library/core/src/num/mod.rs | 69 +++++++++++++++++++++------- library/core/src/num/nonzero.rs | 10 ---- library/core/tests/num/int_macros.rs | 2 + 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index e9e5324666ada..2bcfe43a55b83 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -23,6 +23,16 @@ macro_rules! unlikely { }; } +// Use this when the generated code should differ between signed and unsigned types. +macro_rules! sign_dependent_expr { + (signed ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { + $signed_case + }; + (unsigned ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { + $unsigned_case + }; +} + // All these modules are technically private and only exposed for coretests: #[cfg(not(no_fp_fmt_parse))] pub mod bignum; @@ -1410,15 +1420,25 @@ const fn from_str_radix_panic(radix: u32) { } macro_rules! from_str_radix { - ($($int_ty:ty)+) => {$( + ($signedness:ident $($int_ty:ty)+) => {$( impl $int_ty { /// Converts a string slice in a given base to an integer. /// - /// The string is expected to be an optional `+` sign - /// followed by digits. - /// Leading and trailing whitespace represent an error. - /// Digits are a subset of these characters, depending on `radix`: + /// The string is expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// also represent an error. /// + /// Digits are a subset of these characters, depending on `radix`: /// * `0-9` /// * `a-z` /// * `A-Z` @@ -1430,10 +1450,13 @@ macro_rules! from_str_radix { /// # Examples /// /// Basic usage: - /// /// ``` #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` + /// Trailing space returns error: + /// ``` + #[doc = concat!("assert!(", stringify!($int_ty), "::from_str_radix(\"1 \", 10).is_err());")] + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { @@ -1535,20 +1558,31 @@ macro_rules! from_str_radix { )+} } -from_str_radix! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 } +from_str_radix! { unsigned u8 u16 u32 u64 u128 } +from_str_radix! { signed i8 i16 i32 i64 i128 } // Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two // identical functions. macro_rules! from_str_radix_size_impl { - ($($t:ident $size:ty),*) => {$( + ($($signedness:ident $t:ident $size:ty),*) => {$( impl $size { /// Converts a string slice in a given base to an integer. /// - /// The string is expected to be an optional `+` sign - /// followed by digits. - /// Leading and trailing whitespace represent an error. - /// Digits are a subset of these characters, depending on `radix`: + /// The string is expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// also represent an error. /// + /// Digits are a subset of these characters, depending on `radix`: /// * `0-9` /// * `a-z` /// * `A-Z` @@ -1560,10 +1594,13 @@ macro_rules! from_str_radix_size_impl { /// # Examples /// /// Basic usage: - /// /// ``` #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` + /// Trailing space returns error: + /// ``` + #[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")] + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { @@ -1576,8 +1613,8 @@ macro_rules! from_str_radix_size_impl { } #[cfg(target_pointer_width = "16")] -from_str_radix_size_impl! { i16 isize, u16 usize } +from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize } #[cfg(target_pointer_width = "32")] -from_str_radix_size_impl! { i32 isize, u32 usize } +from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize } #[cfg(target_pointer_width = "64")] -from_str_radix_size_impl! { i64 isize, u64 usize } +from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 8b888f12da0b1..e5c9a7e086ac9 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1972,16 +1972,6 @@ macro_rules! nonzero_integer_signedness_dependent_methods { }; } -// Use this when the generated code should differ between signed and unsigned types. -macro_rules! sign_dependent_expr { - (signed ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { - $signed_case - }; - (unsigned ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { - $unsigned_case - }; -} - nonzero_integer! { Self = NonZeroU8, Primitive = unsigned u8, diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs index 830a96204ca03..6a26bd15a663d 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/core/tests/num/int_macros.rs @@ -244,6 +244,8 @@ macro_rules! int_module { assert_eq!($T::from_str_radix("Z", 35).ok(), None::<$T>); assert_eq!($T::from_str_radix("-9", 2).ok(), None::<$T>); + assert_eq!($T::from_str_radix("10_0", 10).ok(), None::<$T>); + assert_eq!(u32::from_str_radix("-9", 10).ok(), None::); } #[test] From 9957101f3afd0d1dedfdc9eab81b9aa95cec7771 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sat, 31 Aug 2024 15:31:31 +0300 Subject: [PATCH 0201/1301] elided_named_lifetimes: bless & add tests --- tests/ui/needless_lifetimes.stderr | 13 ++++++++++++- tests/ui/ptr_arg.stderr | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index f325d27b8c753..50f845e2d9291 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,3 +1,14 @@ +error: elided lifetime has a name + --> tests/ui/needless_lifetimes.rs:266:52 + | +LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { + | -- ^ this elided lifetime gets resolved as `'a` + | | + | lifetime `'a` declared here + | + = note: `-D elided-named-lifetimes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` + error: the following explicit lifetimes could be elided: 'a, 'b --> tests/ui/needless_lifetimes.rs:17:23 | @@ -553,5 +564,5 @@ LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { LL + fn one_input(x: &u8) -> &u8 { | -error: aborting due to 46 previous errors +error: aborting due to 47 previous errors diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 1848ef80fc495..4246453e64ca7 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -1,3 +1,12 @@ +error: elided lifetime has a name + --> tests/ui/ptr_arg.rs:295:56 + | +LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str { + | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | + = note: `-D elided-named-lifetimes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` + error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do --> tests/ui/ptr_arg.rs:13:14 | @@ -212,5 +221,5 @@ error: using a reference to `Cow` is not recommended LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` -error: aborting due to 24 previous errors +error: aborting due to 25 previous errors From e845366c82ada0466a9801363064357ad22daf12 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 22 Jul 2024 11:44:40 +0200 Subject: [PATCH 0202/1301] Add MSRV check for `saturating_sub` lints in const contexts --- clippy_config/src/msrvs.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 27 ++++++++++++++++++--- clippy_lints/src/lib.rs | 2 +- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 5b9707efd2634..db4c13fe33a29 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -35,7 +35,7 @@ msrv_aliases! { 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } 1,50,0 { BOOL_THEN, CLAMP } - 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN } + 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS } diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 04c890620396d..5a42802e6bbda 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,12 +1,16 @@ +use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use clippy_utils::{higher, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, SpanlessEq}; +use clippy_utils::{ + higher, in_constant, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, SpanlessEq, +}; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -71,13 +75,28 @@ declare_clippy_lint! { "Check if a variable is smaller than another one and still subtract from it even if smaller" } -declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]); +pub struct ImplicitSaturatingSub { + msrv: Msrv, +} + +impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]); + +impl ImplicitSaturatingSub { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if expr.span.from_expansion() { return; } + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::SATURATING_SUB_CONST) { + return; + } if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr) // Check if the conditional expression is a binary operation @@ -94,6 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { check_manual_check(cx, expr, cond_op, cond_left, cond_right, if_block, else_block); } } + + extract_msrv_attr!(LateContext); } fn check_manual_check<'tcx>( diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2ac06b360bea4..80e314dd402da 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -623,7 +623,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); store.register_late_pass(|_| Box::new(strings::StringAdd)); store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); - store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub)); + store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); From 2832faf8959932a25fda883ed3f7dfde8b2d2817 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 2 Aug 2024 11:17:14 +0200 Subject: [PATCH 0203/1301] Move MSRV check later in `implicit_saturating_sub` --- clippy_lints/src/implicit_saturating_sub.rs | 40 +++++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 5a42802e6bbda..3536309d83ca0 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -3,7 +3,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::{ - higher, in_constant, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, SpanlessEq, + higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, SpanlessEq, }; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; @@ -94,9 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if expr.span.from_expansion() { return; } - if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::SATURATING_SUB_CONST) { - return; - } if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr) // Check if the conditional expression is a binary operation @@ -110,13 +107,16 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { }) = higher::If::hir(expr) && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind { - check_manual_check(cx, expr, cond_op, cond_left, cond_right, if_block, else_block); + check_manual_check( + cx, expr, cond_op, cond_left, cond_right, if_block, else_block, &self.msrv, + ); } } extract_msrv_attr!(LateContext); } +#[allow(clippy::too_many_arguments)] fn check_manual_check<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, @@ -125,6 +125,7 @@ fn check_manual_check<'tcx>( right_hand: &Expr<'tcx>, if_block: &Expr<'tcx>, else_block: &Expr<'tcx>, + msrv: &Msrv, ) { let ty = cx.typeck_results().expr_ty(left_hand); if ty.is_numeric() && !ty.is_signed() { @@ -137,6 +138,7 @@ fn check_manual_check<'tcx>( right_hand, if_block, else_block, + msrv, ), BinOpKind::Lt | BinOpKind::Le => check_gt( cx, @@ -146,12 +148,14 @@ fn check_manual_check<'tcx>( left_hand, if_block, else_block, + msrv, ), _ => {}, } } } +#[allow(clippy::too_many_arguments)] fn check_gt( cx: &LateContext<'_>, condition_span: Span, @@ -160,11 +164,21 @@ fn check_gt( little_var: &Expr<'_>, if_block: &Expr<'_>, else_block: &Expr<'_>, + msrv: &Msrv, ) { if let Some(big_var) = Var::new(big_var) && let Some(little_var) = Var::new(little_var) { - check_subtraction(cx, condition_span, expr_span, big_var, little_var, if_block, else_block); + check_subtraction( + cx, + condition_span, + expr_span, + big_var, + little_var, + if_block, + else_block, + msrv, + ); } } @@ -182,6 +196,7 @@ impl Var { } } +#[allow(clippy::too_many_arguments)] fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, @@ -190,6 +205,7 @@ fn check_subtraction( little_var: Var, if_block: &Expr<'_>, else_block: &Expr<'_>, + msrv: &Msrv, ) { let if_block = peel_blocks(if_block); let else_block = peel_blocks(else_block); @@ -201,7 +217,16 @@ fn check_subtraction( } // If the subtraction is done in the `else` block, then we need to also revert the two // variables as it means that the check was reverted too. - check_subtraction(cx, condition_span, expr_span, little_var, big_var, else_block, if_block); + check_subtraction( + cx, + condition_span, + expr_span, + little_var, + big_var, + else_block, + if_block, + msrv, + ); return; } if is_integer_literal(else_block, 0) @@ -215,6 +240,7 @@ fn check_subtraction( // if `snippet_opt` fails, it won't try the next conditions. if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST)) { span_lint_and_sugg( cx, From 989ebae2ba5b79e259ee6267a390acd446cb5bff Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Thu, 29 Aug 2024 10:55:42 +0500 Subject: [PATCH 0204/1301] Provide more clear example for WRONG_SELF_CONVENTION --- clippy_lints/src/methods/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d7126990edb1d..e1f1df4eecba4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -441,6 +441,17 @@ declare_clippy_lint! { /// } /// } /// ``` + /// + /// Use instead: + /// ```no_run + /// # struct X; + /// impl X { + /// fn as_str(&self) -> &'static str { + /// // .. + /// # "" + /// } + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub WRONG_SELF_CONVENTION, style, From 8116b62fd2eab008dcbffc6b22f1fb2b07f6deea Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 1 Sep 2024 01:04:21 +0300 Subject: [PATCH 0205/1301] Provide an option to hide deprecated items from completion --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 6 ++++++ .../crates/rust-analyzer/src/lsp/to_proto.rs | 6 +++++- .../rust-analyzer/docs/user/generated_config.adoc | 5 +++++ src/tools/rust-analyzer/editors/code/package.json | 10 ++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c884216b3e3cd..714c835812f33 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -429,6 +429,8 @@ config_data! { completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, /// Whether to show full function/method signatures in completion docs. completion_fullFunctionSignatures_enable: bool = false, + /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. + completion_hideDeprecated: bool = false, /// Maximum number of completions to return. If `None`, the limit is infinite. completion_limit: Option = None, /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. @@ -1443,6 +1445,10 @@ impl Config { } } + pub fn completion_hide_deprecated(&self) -> bool { + *self.completion_hideDeprecated(None) + } + pub fn detached_files(&self) -> &Vec { // FIXME @alibektas : This is the only config that is confusing. If it's a proper configuration // why is it not among the others? If it's client only which I doubt it is current state should be alright diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 6dff8abb0f825..b29268f133f3a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -228,8 +228,12 @@ pub(crate) fn completion_items( line_index: &LineIndex, version: Option, tdpp: lsp_types::TextDocumentPositionParams, - items: Vec, + mut items: Vec, ) -> Vec { + if config.completion_hide_deprecated() { + items.retain(|item| !item.deprecated); + } + let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default(); let mut res = Vec::with_capacity(items.len()); for item in items { diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index e4a8c6493a883..4fcf580e75f5a 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -283,6 +283,11 @@ Whether to add parenthesis and argument snippets when completing function. -- Whether to show full function/method signatures in completion docs. -- +[[rust-analyzer.completion.hideDeprecated]]rust-analyzer.completion.hideDeprecated (default: `false`):: ++ +-- +Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. +-- [[rust-analyzer.completion.limit]]rust-analyzer.completion.limit (default: `null`):: + -- diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 98e8bbf02aa55..0b029460b3220 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1077,6 +1077,16 @@ } } }, + { + "title": "completion", + "properties": { + "rust-analyzer.completion.hideDeprecated": { + "markdownDescription": "Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.", + "default": false, + "type": "boolean" + } + } + }, { "title": "completion", "properties": { From 89a39d918f396c30fe3d0cb8f8f3cf6319736c14 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 1 Sep 2024 02:07:05 +0300 Subject: [PATCH 0206/1301] Handle attributes correctly in "Flip comma" Attributes often contain path followed by a token tree (e.g. `align(2)`, and the previous code handled them as two separate items, which led to results such as `#[repr(alignC, (2))]`. An alternative is to just make the assist unavailable in attributes, like we do in macros. But contrary to macros, attributes often have a fixed form, so this seems useful. --- .../ide-assists/src/handlers/flip_comma.rs | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs index f40f2713ad131..af2c2c759ec78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs @@ -1,4 +1,8 @@ -use syntax::{algo::non_trivia_sibling, Direction, SyntaxKind, T}; +use ide_db::base_db::SourceDatabase; +use syntax::TextSize; +use syntax::{ + algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, T, +}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -21,6 +25,8 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let comma = ctx.find_token_syntax_at_offset(T![,])?; let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; + let (mut prev_text, mut next_text) = (prev.to_string(), next.to_string()); + let (mut prev_range, mut next_range) = (prev.text_range(), next.text_range()); // Don't apply a "flip" in case of a last comma // that typically comes before punctuation @@ -34,17 +40,55 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( return None; } + if let Some(parent) = comma.parent().and_then(ast::TokenTree::cast) { + // An attribute. It often contains a path followed by a token tree (e.g. `align(2)`), so we have + // to be smarter. + let prev_start = + match comma.siblings_with_tokens(Direction::Prev).skip(1).find(|it| it.kind() == T![,]) + { + Some(it) => position_after_token(it.as_token().unwrap()), + None => position_after_token(&parent.left_delimiter_token()?), + }; + let prev_end = prev.text_range().end(); + let next_start = next.text_range().start(); + let next_end = + match comma.siblings_with_tokens(Direction::Next).skip(1).find(|it| it.kind() == T![,]) + { + Some(it) => position_before_token(it.as_token().unwrap()), + None => position_before_token(&parent.right_delimiter_token()?), + }; + prev_range = TextRange::new(prev_start, prev_end); + next_range = TextRange::new(next_start, next_end); + let file_text = ctx.db().file_text(ctx.file_id().file_id()); + prev_text = file_text[prev_range].to_owned(); + next_text = file_text[next_range].to_owned(); + } + acc.add( AssistId("flip_comma", AssistKind::RefactorRewrite), "Flip comma", comma.text_range(), |edit| { - edit.replace(prev.text_range(), next.to_string()); - edit.replace(next.text_range(), prev.to_string()); + edit.replace(prev_range, next_text); + edit.replace(next_range, prev_text); }, ) } +fn position_before_token(token: &SyntaxToken) -> TextSize { + match non_trivia_sibling(token.clone().into(), Direction::Prev) { + Some(prev_token) => prev_token.text_range().end(), + None => token.text_range().start(), + } +} + +fn position_after_token(token: &SyntaxToken) -> TextSize { + match non_trivia_sibling(token.clone().into(), Direction::Next) { + Some(prev_token) => prev_token.text_range().start(), + None => token.text_range().end(), + } +} + #[cfg(test)] mod tests { use super::*; @@ -89,4 +133,18 @@ mod tests { // See https://github.com/rust-lang/rust-analyzer/issues/7693 check_assist_not_applicable(flip_comma, r#"bar!(a,$0 b)"#); } + + #[test] + fn flip_comma_attribute() { + check_assist( + flip_comma, + r#"#[repr(align(2),$0 C)] struct Foo;"#, + r#"#[repr(C, align(2))] struct Foo;"#, + ); + check_assist( + flip_comma, + r#"#[foo(bar, baz(1 + 1),$0 qux, other)] struct Foo;"#, + r#"#[foo(bar, qux, baz(1 + 1), other)] struct Foo;"#, + ); + } } From f558be0359b41e74c09f6f171f149498d1b27cde Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 1 Sep 2024 10:02:41 +0200 Subject: [PATCH 0207/1301] minor: Downgrade cyclic deps error to warning --- .../rust-analyzer/crates/base-db/src/input.rs | 54 +++---------------- .../crates/project-model/src/workspace.rs | 2 +- 2 files changed, 8 insertions(+), 48 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 3616fa9fd8684..032e4a8e4a733 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -374,37 +374,6 @@ impl CrateGraph { self.arena.alloc(data) } - /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced - /// with the second input. - pub fn remove_and_replace( - &mut self, - id: CrateId, - replace_with: CrateId, - ) -> Result<(), CyclicDependenciesError> { - for (x, data) in self.arena.iter() { - if x == id { - continue; - } - for edge in &data.dependencies { - if edge.crate_id == id { - self.check_cycle_after_dependency(edge.crate_id, replace_with)?; - } - } - } - // if everything was ok, start to replace - for (x, data) in self.arena.iter_mut() { - if x == id { - continue; - } - for edge in &mut data.dependencies { - if edge.crate_id == id { - edge.crate_id = replace_with; - } - } - } - Ok(()) - } - pub fn add_dep( &mut self, from: CrateId, @@ -412,26 +381,17 @@ impl CrateGraph { ) -> Result<(), CyclicDependenciesError> { let _p = tracing::info_span!("add_dep").entered(); - self.check_cycle_after_dependency(from, dep.crate_id)?; - - self.arena[from].add_dep(dep); - Ok(()) - } - - /// Check if adding a dep from `from` to `to` creates a cycle. To figure - /// that out, look for a path in the *opposite* direction, from `to` to - /// `from`. - fn check_cycle_after_dependency( - &self, - from: CrateId, - to: CrateId, - ) -> Result<(), CyclicDependenciesError> { - if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) { + // Check if adding a dep from `from` to `to` creates a cycle. To figure + // that out, look for a path in the *opposite* direction, from `to` to + // `from`. + if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) { let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect(); let err = CyclicDependenciesError { path }; - assert!(err.from().0 == from && err.to().0 == to); + assert!(err.from().0 == from && err.to().0 == dep.crate_id); return Err(err); } + + self.arena[from].add_dep(dep); Ok(()) } diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 7834238acefdf..4fc9ef3d36b66 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1554,6 +1554,6 @@ fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { if let Err(err) = graph.add_dep(from, dep) { - tracing::error!("{}", err) + tracing::warn!("{}", err) } } From fc9ff795199f24b40a19e11735b1956462107d0c Mon Sep 17 00:00:00 2001 From: Yunfei Date: Mon, 29 Jul 2024 22:42:31 +0800 Subject: [PATCH 0208/1301] feat(ide-completion): extra sugar auto-completion `async fn ...` in `impl trait` for `async fn in trait` that's defined in desugar form --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 47 +++++++++++++++++++ .../src/completions/item_list/trait_impl.rs | 38 ++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 96a6e6f1f128e..74ec5227b0ec6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2207,6 +2207,53 @@ impl Function { db.function_data(self.id).is_async() } + /// Whether this function is a `fn` that returns `impl Future`. + pub fn is_desugar_async(self, db: &dyn HirDatabase) -> bool { + if self.is_async(db) || self.is_const(db) { + return false; + } + + let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false }; + + let Some(future_trait_id) = + db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait()) + else { + return false; + }; + + let Some(size_trait_id) = + db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait()) + else { + return false; + }; + + let Some(sync_trait_id) = + db.lang_item(self.ty(db).env.krate, LangItem::Sync).and_then(|t| t.as_trait()) + else { + return false; + }; + + // TODO: There's no `LangItem::Send`. How do we get the id of `Send` trait? + // let Some(send_trait_id) = db.lang_item(self.ty(db).env.krate, LangItem::Send).and_then(|t| t.as_trait()) else { + // eprint!("no future_trait_id\n"); + // return false + // }; + + let allowed_to_leaked_types = vec![size_trait_id, sync_trait_id]; + + let mut has_impl_future = false; + let mut has_types_not_allow_to_leaked = false; + for impl_trait in impl_traits { + if impl_trait.id == future_trait_id { + has_impl_future = true; + } else if !allowed_to_leaked_types.contains(&impl_trait.id) { + has_types_not_allow_to_leaked = true; + } + } + + has_impl_future && !has_types_not_allow_to_leaked + } + /// Does this function have `#[test]` attribute? pub fn is_test(self, db: &dyn HirDatabase) -> bool { db.function_data(self.id).attrs.is_test() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index fc6e1ebf05fc6..7887a87448e2f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -210,7 +210,7 @@ fn add_function_impl( ast::AssocItem::Fn(func) => func, _ => unreachable!(), }; - + // TODO: need `function_decl` that unwraps future in the return type let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro()); match ctx.config.snippet_cap { Some(cap) => { @@ -225,6 +225,42 @@ fn add_function_impl( item.add_to(acc, ctx.db); } } + + eprint!("is_desugar_async: {}", func.is_desugar_async(ctx.db)); + if func.is_desugar_async(ctx.db) { + let label = format_smolstr!( + "async fn {}({})", + fn_name.display(ctx.db), + if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } + ); + let mut item = CompletionItem::new(completion_kind, replacement_range, label); + item.lookup_by(format!("async fn {}", fn_name.display(ctx.db))) + .set_documentation(func.docs(ctx.db)) + .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + if let Some(source) = ctx.sema.source(func) { + let assoc_item = ast::AssocItem::Fn(source.value); + if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + let transformed_fn = match transformed_item { + ast::AssocItem::Fn(func) => func, + _ => unreachable!(), + }; + + let function_decl = + function_declaration(&transformed_fn, source.file_id.is_macro()); + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = format!("{function_decl} {{\n $0\n}}"); + item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); + } + None => { + let header = format!("{function_decl} {{"); + item.text_edit(TextEdit::replace(replacement_range, header)); + } + }; + item.add_to(acc, ctx.db); + } + } + } } /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. From e233c3ae22ed5b2020b8b79e97d2fe6f1da6852e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 1 Sep 2024 11:04:45 +0200 Subject: [PATCH 0209/1301] Complete desugared and resugared async fn in trait impls --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 44 +-- .../src/completions/item_list/trait_impl.rs | 302 ++++++++++++++---- .../ide-completion/src/tests/item_list.rs | 1 + .../crates/syntax/src/ast/make.rs | 2 +- .../rust-analyzer/crates/syntax/src/ted.rs | 5 + 5 files changed, 267 insertions(+), 87 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 74ec5227b0ec6..9536f12558430 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2207,51 +2207,33 @@ impl Function { db.function_data(self.id).is_async() } - /// Whether this function is a `fn` that returns `impl Future`. - pub fn is_desugar_async(self, db: &dyn HirDatabase) -> bool { - if self.is_async(db) || self.is_const(db) { - return false; + pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool { + if self.is_async(db) { + return true; } let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false }; - let Some(future_trait_id) = db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait()) else { return false; }; - - let Some(size_trait_id) = + let Some(sized_trait_id) = db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait()) else { return false; }; - let Some(sync_trait_id) = - db.lang_item(self.ty(db).env.krate, LangItem::Sync).and_then(|t| t.as_trait()) - else { - return false; - }; - - // TODO: There's no `LangItem::Send`. How do we get the id of `Send` trait? - // let Some(send_trait_id) = db.lang_item(self.ty(db).env.krate, LangItem::Send).and_then(|t| t.as_trait()) else { - // eprint!("no future_trait_id\n"); - // return false - // }; - - let allowed_to_leaked_types = vec![size_trait_id, sync_trait_id]; - let mut has_impl_future = false; - let mut has_types_not_allow_to_leaked = false; - for impl_trait in impl_traits { - if impl_trait.id == future_trait_id { - has_impl_future = true; - } else if !allowed_to_leaked_types.contains(&impl_trait.id) { - has_types_not_allow_to_leaked = true; - } - } - - has_impl_future && !has_types_not_allow_to_leaked + impl_traits + .filter(|t| { + let fut = t.id == future_trait_id; + has_impl_future |= fut; + !fut && t.id != sized_trait_id + }) + // all traits but the future trait must be auto traits + .all(|t| t.is_auto(db)) + && has_impl_future } /// Does this function have `#[test]` attribute? diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 7887a87448e2f..e93bb8db716f1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,14 +31,14 @@ //! } //! ``` -use hir::HasAttrs; +use hir::{HasAttrs, Name}; use ide_db::{ documentation::HasDocs, path_transform::PathTransform, syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind, }; use syntax::{ - ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds}, - format_smolstr, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T, + ast::{self, edit_in_place::AttrsOwnerEdit, make, HasGenericArgs, HasTypeBounds}, + format_smolstr, ted, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T, }; use text_edit::TextEdit; @@ -178,12 +178,36 @@ fn add_function_impl( func: hir::Function, impl_def: hir::Impl, ) { - let fn_name = func.name(ctx.db); + let fn_name = &func.name(ctx.db); + let sugar: &[_] = if func.is_async(ctx.db) { + &[AsyncSugaring::Async, AsyncSugaring::Desugar] + } else if func.returns_impl_future(ctx.db) { + &[AsyncSugaring::Plain, AsyncSugaring::Resugar] + } else { + &[AsyncSugaring::Plain] + }; + for &sugaring in sugar { + add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring); + } +} - let is_async = func.is_async(ctx.db); +fn add_function_impl_( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + replacement_range: TextRange, + func: hir::Function, + impl_def: hir::Impl, + fn_name: &Name, + async_sugaring: AsyncSugaring, +) { + let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring { + "async " + } else { + "" + }; let label = format_smolstr!( "{}fn {}({})", - if is_async { "async " } else { "" }, + async_, fn_name.display(ctx.db, ctx.edition), if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); @@ -195,22 +219,14 @@ fn add_function_impl( }); let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition); - item.lookup_by(format!( - "{}fn {}", - if is_async { "async " } else { "" }, - fn_name.display(ctx.db, ctx.edition) - )) - .set_documentation(func.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition))) + .set_documentation(func.docs(ctx.db)) + .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); if let Some(source) = ctx.sema.source(func) { - let assoc_item = ast::AssocItem::Fn(source.value); - if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { - let transformed_fn = match transformed_item { - ast::AssocItem::Fn(func) => func, - _ => unreachable!(), - }; - // TODO: need `function_decl` that unwraps future in the return type + if let Some(transformed_fn) = + get_transformed_fn(ctx, source.value, impl_def, async_sugaring) + { let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro()); match ctx.config.snippet_cap { Some(cap) => { @@ -225,42 +241,14 @@ fn add_function_impl( item.add_to(acc, ctx.db); } } +} - eprint!("is_desugar_async: {}", func.is_desugar_async(ctx.db)); - if func.is_desugar_async(ctx.db) { - let label = format_smolstr!( - "async fn {}({})", - fn_name.display(ctx.db), - if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } - ); - let mut item = CompletionItem::new(completion_kind, replacement_range, label); - item.lookup_by(format!("async fn {}", fn_name.display(ctx.db))) - .set_documentation(func.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); - if let Some(source) = ctx.sema.source(func) { - let assoc_item = ast::AssocItem::Fn(source.value); - if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { - let transformed_fn = match transformed_item { - ast::AssocItem::Fn(func) => func, - _ => unreachable!(), - }; - - let function_decl = - function_declaration(&transformed_fn, source.file_id.is_macro()); - match ctx.config.snippet_cap { - Some(cap) => { - let snippet = format!("{function_decl} {{\n $0\n}}"); - item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); - } - None => { - let header = format!("{function_decl} {{"); - item.text_edit(TextEdit::replace(replacement_range, header)); - } - }; - item.add_to(acc, ctx.db); - } - } - } +#[derive(Copy, Clone)] +enum AsyncSugaring { + Desugar, + Resugar, + Async, + Plain, } /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. @@ -287,6 +275,82 @@ fn get_transformed_assoc_item( Some(assoc_item) } +/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. +fn get_transformed_fn( + ctx: &CompletionContext<'_>, + fn_: ast::Fn, + impl_def: hir::Impl, + async_: AsyncSugaring, +) -> Option { + let trait_ = impl_def.trait_(ctx.db)?; + let source_scope = &ctx.sema.scope(fn_.syntax())?; + let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; + let transform = PathTransform::trait_impl( + target_scope, + source_scope, + trait_, + ctx.sema.source(impl_def)?.value, + ); + + let fn_ = fn_.clone_for_update(); + // FIXME: Paths in nested macros are not handled well. See + // `macro_generated_assoc_item2` test. + transform.apply(fn_.syntax()); + fn_.remove_attrs_and_docs(); + match async_ { + AsyncSugaring::Desugar => { + match fn_.ret_type() { + Some(ret_ty) => { + let ty = ret_ty.ty()?; + ted::replace( + ty.syntax(), + make::ty(&format!("impl Future")) + .syntax() + .clone_for_update(), + ); + } + None => ted::append_child( + fn_.param_list()?.syntax(), + make::ret_type(make::ty("impl Future")) + .syntax() + .clone_for_update(), + ), + } + fn_.async_token().unwrap().detach(); + } + AsyncSugaring::Resugar => { + let ty = fn_.ret_type()?.ty()?; + match &ty { + // best effort guessing here + ast::Type::ImplTraitType(t) => { + let output = t.type_bound_list()?.bounds().find_map(|b| match b.ty()? { + ast::Type::PathType(p) => { + let p = p.path()?.segment()?; + if p.name_ref()?.text() != "Future" { + return None; + } + match p.generic_arg_list()?.generic_args().next()? { + ast::GenericArg::AssocTypeArg(a) + if a.name_ref()?.text() == "Output" => + { + a.ty() + } + _ => None, + } + } + _ => None, + })?; + ted::replace(ty.syntax(), output.syntax()); + } + _ => (), + } + ted::prepend_child(fn_.syntax(), make::token(T![async])); + } + AsyncSugaring::Async | AsyncSugaring::Plain => (), + } + Some(fn_) +} + fn add_type_alias_impl( acc: &mut Completions, ctx: &CompletionContext<'_>, @@ -1437,6 +1501,134 @@ trait Tr { impl Tr for () { type Item = $0; } +"#, + ); + } + + #[test] + fn impl_fut() { + check_edit( + "fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + fn foo(&self) -> impl Future + Send { + $0 +} +} +"#, + ); + } + + #[test] + fn impl_fut_resugared() { + check_edit( + "async fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + async fn foo(&self) -> usize { + $0 +} +} +"#, + ); + } + + #[test] + fn async_desugared() { + check_edit( + "fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + fn foo(&self) -> impl Future { + $0 +} +} +"#, + ); + } + + #[test] + fn async_() { + check_edit( + "async fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + async fn foo(&self) -> usize { + $0 +} +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 8aad7bfc3adc9..532d4928eff99 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -313,6 +313,7 @@ impl Test for () { ct const CONST1: () = fn async fn function2() fn fn function1() + fn fn function2() ma makro!(…) macro_rules! makro md module ta type Type1 = diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index abf1a1f38207f..2eb9c1ec5a54b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -1162,7 +1162,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: LazyLock> = LazyLock::new(|| { SourceFile::parse( - "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT, + "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT, ) }); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ted.rs b/src/tools/rust-analyzer/crates/syntax/src/ted.rs index 29788d05e845f..8592df1597551 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ted.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ted.rs @@ -147,6 +147,11 @@ pub fn append_child_raw(node: &(impl Into + Clone), child: impl Elem insert_raw(position, child); } +pub fn prepend_child(node: &(impl Into + Clone), child: impl Element) { + let position = Position::first_child_of(node); + insert(position, child); +} + fn ws_before(position: &Position, new: &SyntaxElement) -> Option { let prev = match &position.repr { PositionRepr::FirstChild(_) => return None, From 61b0374cc43b75786e069c75f5fe92342fce4621 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 26 Aug 2024 10:07:11 +0200 Subject: [PATCH 0210/1301] minor: Reduce friction for updating minicore --- .../crates/ide/src/hover/tests.rs | 85 +++++++++++++------ 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index d7ae6c7508789..9b934044ac0b7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -182,13 +182,26 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { fn check_actions(ra_fixture: &str, expect: Expect) { let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); - let hover = analysis + let mut hover = analysis .hover( &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, FileRange { file_id, range: position.range_or_empty() }, ) .unwrap() .unwrap(); + // stub out ranges into minicore as they can change every now and then + hover.info.actions.iter_mut().for_each(|action| match action { + super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { + if data.nav.file_id == file_id { + return; + } + data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); + if let Some(range) = &mut data.nav.focus_range { + *range = TextRange::empty(span::TextSize::new(!0)); + } + }), + _ => (), + }); expect.assert_debug_eq(&hover.info.actions) } @@ -200,10 +213,23 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { fn check_hover_range_actions(ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); - let hover = analysis + let mut hover = analysis .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) .unwrap() .unwrap(); + // stub out ranges into minicore as they can change every now and then + hover.info.actions.iter_mut().for_each(|action| match action { + super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { + if data.nav.file_id == range.file_id { + return; + } + data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); + if let Some(range) = &mut data.nav.focus_range { + *range = TextRange::empty(span::TextSize::new(!0)); + } + }), + _ => (), + }); expect.assert_debug_eq(&hover.info.actions); } @@ -483,8 +509,13 @@ fn main() { file_id: FileId( 1, ), +<<<<<<< HEAD full_range: 633..868, focus_range: 694..700, +======= + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, +>>>>>>> 33e4a08d5b (minor: Reduce friction for updating minicore) name: "FnOnce", kind: Trait, container_name: "function", @@ -3104,26 +3135,26 @@ struct S{ f1: u32 } fn main() { let s$0t = S{ f1:0 }; } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::S", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..19, - focus_range: 7..8, - name: "S", - kind: Struct, - description: "struct S", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..19, + focus_range: 7..8, + name: "S", + kind: Struct, + description: "struct S", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -3616,8 +3647,8 @@ pub mod future { file_id: FileId( 1, ), - full_range: 21..69, - focus_range: 60..66, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Future", kind: Trait, container_name: "future", @@ -8479,8 +8510,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7802..8044, - focus_range: 7867..7873, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Future", kind: Trait, container_name: "future", @@ -8493,8 +8524,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 8674..9173, - focus_range: 8751..8759, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Iterator", kind: Trait, container_name: "iterator", From ba2577f23c0014c171235692ca4d8b6b71fbce7e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 Aug 2024 15:15:15 +0200 Subject: [PATCH 0211/1301] stabilize const_float_bits_conv --- clippy_lints/src/transmute/mod.rs | 6 +- .../src/transmute/transmute_float_to_int.rs | 3 +- .../src/transmute/transmute_int_to_float.rs | 3 +- .../src/transmute/transmute_num_to_bytes.rs | 6 - tests/ui/transmute.rs | 12 ++ tests/ui/transmute.stderr | 112 ++++++++++++++---- tests/ui/transmute_float_to_int.fixed | 28 +++-- tests/ui/transmute_float_to_int.rs | 12 +- tests/ui/transmute_float_to_int.stderr | 50 +++++++- 9 files changed, 186 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 373bf61d8ff94..a2ae36cc484ad 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -619,10 +619,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, &self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) - | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg) + | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg) | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) || transmute_undefined_repr::check(cx, e, from_ty, to_ty)) | (eager_transmute::check(cx, e, arg, from_ty, to_ty)); diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index ab3bb5e1062d3..cb46109c27e2f 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -15,10 +15,9 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, mut arg: &'tcx Expr<'_>, - const_context: bool, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => { + (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => { span_lint_and_then( cx, TRANSMUTE_FLOAT_TO_INT, diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs index d51888e30971b..e00fb90c3074e 100644 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -14,10 +14,9 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, - const_context: bool, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => { + (ty::Int(_) | ty::Uint(_), ty::Float(_)) => { span_lint_and_then( cx, TRANSMUTE_INT_TO_FLOAT, diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs index 88b0ac5a36887..362f2bb6960a2 100644 --- a/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ b/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -14,18 +14,12 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, - const_context: bool, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => { if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) { return false; } - if matches!(from_ty.kind(), ty::Float(_)) && const_context { - // TODO: Remove when const_float_bits_conv is stabilized - // rust#72447 - return false; - } span_lint_and_then( cx, diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 46629526367ec..eeea3f080b1c9 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -140,24 +140,32 @@ mod int_to_float { mod issue_5747 { const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; + //~^ ERROR: transmute from a `u16` to a `f16` const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; + //~^ ERROR: transmute from a `u32` to a `f32` const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + //~^ ERROR: transmute from a `i64` to a `f64` const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; + //~^ ERROR: transmute from a `i128` to a `f128` const fn from_bits_16(v: i16) -> f16 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `i16` to a `f16` } const fn from_bits_32(v: i32) -> f32 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `i32` to a `f32` } const fn from_bits_64(v: u64) -> f64 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `u64` to a `f64` } const fn from_bits_128(v: u128) -> f128 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `u128` to a `f128` } } } @@ -205,9 +213,13 @@ mod num_to_bytes { //~^ ERROR: transmute from a `i128` to a `[u8; 16]` let _: [u8; 2] = std::mem::transmute(0.0f16); + //~^ ERROR: transmute from a `f16` to a `[u8; 2]` let _: [u8; 4] = std::mem::transmute(0.0f32); + //~^ ERROR: transmute from a `f32` to a `[u8; 4]` let _: [u8; 8] = std::mem::transmute(0.0f64); + //~^ ERROR: transmute from a `f64` to a `[u8; 8]` let _: [u8; 16] = std::mem::transmute(0.0f128); + //~^ ERROR: transmute from a `f128` to a `[u8; 16]` } } } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 0072f62962a7d..41a10f381dc53 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -148,8 +148,56 @@ error: transmute from a `i128` to a `f128` LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` +error: transmute from a `u16` to a `f16` + --> tests/ui/transmute.rs:142:39 + | +LL | const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` + +error: transmute from a `u32` to a `f32` + --> tests/ui/transmute.rs:144:39 + | +LL | const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` + +error: transmute from a `i64` to a `f64` + --> tests/ui/transmute.rs:146:39 + | +LL | const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` + +error: transmute from a `i128` to a `f128` + --> tests/ui/transmute.rs:148:41 + | +LL | const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` + +error: transmute from a `i16` to a `f16` + --> tests/ui/transmute.rs:152:22 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(v as u16)` + +error: transmute from a `i32` to a `f32` + --> tests/ui/transmute.rs:157:22 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(v as u32)` + +error: transmute from a `u64` to a `f64` + --> tests/ui/transmute.rs:162:22 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(v)` + +error: transmute from a `u128` to a `f128` + --> tests/ui/transmute.rs:167:22 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(v)` + error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:168:30 + --> tests/ui/transmute.rs:176:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -158,97 +206,121 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:171:30 + --> tests/ui/transmute.rs:179:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:173:31 + --> tests/ui/transmute.rs:181:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:175:30 + --> tests/ui/transmute.rs:183:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:177:30 + --> tests/ui/transmute.rs:185:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:179:31 + --> tests/ui/transmute.rs:187:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:182:30 + --> tests/ui/transmute.rs:190:30 | LL | let _: [u8; 2] = std::mem::transmute(0.0f16); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:184:30 + --> tests/ui/transmute.rs:192:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:186:30 + --> tests/ui/transmute.rs:194:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:188:31 + --> tests/ui/transmute.rs:196:31 | LL | let _: [u8; 16] = std::mem::transmute(0.0f128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:194:30 + --> tests/ui/transmute.rs:202:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:196:30 + --> tests/ui/transmute.rs:204:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:198:31 + --> tests/ui/transmute.rs:206:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:200:30 + --> tests/ui/transmute.rs:208:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:202:30 + --> tests/ui/transmute.rs:210:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:204:31 + --> tests/ui/transmute.rs:212:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` +error: transmute from a `f16` to a `[u8; 2]` + --> tests/ui/transmute.rs:215:30 + | +LL | let _: [u8; 2] = std::mem::transmute(0.0f16); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` + +error: transmute from a `f32` to a `[u8; 4]` + --> tests/ui/transmute.rs:217:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` + +error: transmute from a `f64` to a `[u8; 8]` + --> tests/ui/transmute.rs:219:30 + | +LL | let _: [u8; 8] = std::mem::transmute(0.0f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` + +error: transmute from a `f128` to a `[u8; 16]` + --> tests/ui/transmute.rs:221:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0.0f128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` + error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:218:28 + --> tests/ui/transmute.rs:230:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -257,16 +329,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:221:32 + --> tests/ui/transmute.rs:233:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:223:30 + --> tests/ui/transmute.rs:235:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 42 previous errors +error: aborting due to 54 previous errors diff --git a/tests/ui/transmute_float_to_int.fixed b/tests/ui/transmute_float_to_int.fixed index 4361a7407d115..83814ca43b96d 100644 --- a/tests/ui/transmute_float_to_int.fixed +++ b/tests/ui/transmute_float_to_int.fixed @@ -1,7 +1,7 @@ #![warn(clippy::transmute_float_to_int)] #![allow(clippy::missing_transmute_annotations)] -#![feature(f128)] -#![feature(f16)] +#![feature(f128, f128_const)] +#![feature(f16, f16_const)] fn float_to_int() { let _: u32 = unsafe { 1f32.to_bits() }; @@ -20,25 +20,33 @@ fn float_to_int() { } mod issue_5747 { - const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; + const VALUE16: i16 = unsafe { 1f16.to_bits() as i16 }; + //~^ ERROR: transmute from a `f16` to a `i16` + const VALUE32: i32 = unsafe { 1f32.to_bits() as i32 }; + //~^ ERROR: transmute from a `f32` to a `i32` + const VALUE64: u64 = unsafe { 1f64.to_bits() }; + //~^ ERROR: transmute from a `f64` to a `u64` + const VALUE128: u128 = unsafe { 1f128.to_bits() }; + //~^ ERROR: transmute from a `f128` to a `u128` const fn to_bits_16(v: f16) -> u16 { - unsafe { std::mem::transmute(v) } + unsafe { v.to_bits() } + //~^ ERROR: transmute from a `f16` to a `u16` } const fn to_bits_32(v: f32) -> u32 { - unsafe { std::mem::transmute(v) } + unsafe { v.to_bits() } + //~^ ERROR: transmute from a `f32` to a `u32` } const fn to_bits_64(v: f64) -> i64 { - unsafe { std::mem::transmute(v) } + unsafe { v.to_bits() as i64 } + //~^ ERROR: transmute from a `f64` to a `i64` } const fn to_bits_128(v: f128) -> i128 { - unsafe { std::mem::transmute(v) } + unsafe { v.to_bits() as i128 } + //~^ ERROR: transmute from a `f128` to a `i128` } } diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index 363ce0bcb16d0..64d6e9172039d 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -1,7 +1,7 @@ #![warn(clippy::transmute_float_to_int)] #![allow(clippy::missing_transmute_annotations)] -#![feature(f128)] -#![feature(f16)] +#![feature(f128, f128_const)] +#![feature(f16, f16_const)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; @@ -21,24 +21,32 @@ fn float_to_int() { mod issue_5747 { const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; + //~^ ERROR: transmute from a `f16` to a `i16` const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; + //~^ ERROR: transmute from a `f32` to a `i32` const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; + //~^ ERROR: transmute from a `f64` to a `u64` const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; + //~^ ERROR: transmute from a `f128` to a `u128` const fn to_bits_16(v: f16) -> u16 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `f16` to a `u16` } const fn to_bits_32(v: f32) -> u32 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `f32` to a `u32` } const fn to_bits_64(v: f64) -> i64 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `f64` to a `i64` } const fn to_bits_128(v: f128) -> i128 { unsafe { std::mem::transmute(v) } + //~^ ERROR: transmute from a `f128` to a `i128` } } diff --git a/tests/ui/transmute_float_to_int.stderr b/tests/ui/transmute_float_to_int.stderr index 9cac75f72cdd7..0cabab58ab08c 100644 --- a/tests/ui/transmute_float_to_int.stderr +++ b/tests/ui/transmute_float_to_int.stderr @@ -37,5 +37,53 @@ error: transmute from a `f64` to a `u64` LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` -error: aborting due to 6 previous errors +error: transmute from a `f16` to a `i16` + --> tests/ui/transmute_float_to_int.rs:23:35 + | +LL | const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f16.to_bits() as i16` + +error: transmute from a `f32` to a `i32` + --> tests/ui/transmute_float_to_int.rs:25:35 + | +LL | const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` + +error: transmute from a `f64` to a `u64` + --> tests/ui/transmute_float_to_int.rs:27:35 + | +LL | const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` + +error: transmute from a `f128` to a `u128` + --> tests/ui/transmute_float_to_int.rs:29:37 + | +LL | const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f128.to_bits()` + +error: transmute from a `f16` to a `u16` + --> tests/ui/transmute_float_to_int.rs:33:18 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` + +error: transmute from a `f32` to a `u32` + --> tests/ui/transmute_float_to_int.rs:38:18 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` + +error: transmute from a `f64` to a `i64` + --> tests/ui/transmute_float_to_int.rs:43:18 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i64` + +error: transmute from a `f128` to a `i128` + --> tests/ui/transmute_float_to_int.rs:48:18 + | +LL | unsafe { std::mem::transmute(v) } + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i128` + +error: aborting due to 14 previous errors From 01b7676fe7ab67f5c56bc94790ad903f712bb5ff Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 26 Aug 2024 10:59:04 +0200 Subject: [PATCH 0212/1301] internal: Lay basic ground work for standalone mbe tests --- src/tools/rust-analyzer/Cargo.lock | 1 + .../hir-def/src/macro_expansion_tests/mbe.rs | 38 ++-- .../macro_expansion_tests/mbe/regression.rs | 32 ++-- .../hir-def/src/macro_expansion_tests/mod.rs | 6 +- .../src/macro_expansion_tests/proc_macros.rs | 12 +- .../crates/hir-expand/src/lib.rs | 1 + src/tools/rust-analyzer/crates/hir/src/lib.rs | 1 + .../rust-analyzer/crates/ide-db/src/lib.rs | 2 +- .../crates/ide/src/hover/tests.rs | 5 - src/tools/rust-analyzer/crates/mbe/Cargo.toml | 1 + src/tools/rust-analyzer/crates/mbe/src/lib.rs | 2 + .../rust-analyzer/crates/mbe/src/tests.rs | 179 ++++++++++++++++++ .../src}/insert_whitespace_into_node.rs | 2 - .../crates/syntax-bridge/src/lib.rs | 1 + src/tools/rust-analyzer/crates/vfs/src/lib.rs | 2 +- 15 files changed, 233 insertions(+), 52 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/mbe/src/tests.rs rename src/tools/rust-analyzer/crates/{ide-db/src/syntax_helpers => syntax-bridge/src}/insert_whitespace_into_node.rs (97%) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 4e9319f13aa83..8733508be746b 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1029,6 +1029,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "cov-mark", + "expect-test", "intern", "parser", "rustc-hash", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index fc1460870ceaf..85fb90fdfb69f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -389,7 +389,7 @@ m! { foo# bar } m! { Foo,# Bar } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($i:ident),*) => ($(mod $i {} )*); ($($i:ident)#*) => ($(fn $i() {} )*); @@ -404,27 +404,29 @@ fn bar() {} struct Foo; struct Bar; -"##]], +"#]], ); } #[test] fn test_match_group_pattern_with_multiple_defs() { + // FIXME: The pretty printer breaks by leaving whitespace here, +syntaxctxt is used to avoid that check( r#" macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } +// +syntaxctxt m! { foo, bar } "#, expect![[r#" macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl Bar { - fn foo() {} - fn bar() {} -} +impl#\1# Bar#\1# {#\1# + fn#\1# foo#\0#(#\1#)#\1# {#\1#}#\1# + fn#\1# bar#\0#(#\1#)#\1# {#\1#}#\1# +}#\1# "#]], ); } @@ -480,12 +482,12 @@ macro_rules! m { } m!{#abc} "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($i:ident)* #abc) => ( fn baz() { $($i ();)* } ); } fn baz() {} -"##]], +"#]], ) } @@ -1189,13 +1191,13 @@ macro_rules! m { m! { cfg(target_os = "windows") } m! { hello::world } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($m:meta) => ( #[$m] fn bar() {} ) } #[cfg(target_os = "windows")] fn bar() {} #[hello::world] fn bar() {} -"##]], +"#]], ); } @@ -1213,7 +1215,7 @@ m! { */ } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) } @@ -1221,7 +1223,7 @@ macro_rules! m { #[doc = r" MultiLines Doc "] fn bar() {} -"##]], +"#]], ); } @@ -1234,12 +1236,12 @@ macro_rules! m { } m! { #[doc = concat!("The `", "bla", "` lang item.")] } "#, - expect![[r##" + expect![[r#" macro_rules! m { (#[$m:meta]) => ( #[$m] fn bar() {} ) } #[doc = concat!("The `", "bla", "` lang item.")] fn bar() {} -"##]], +"#]], ); } @@ -1257,7 +1259,7 @@ m! { */ } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($(#[$ m:meta])+) => ( $(#[$m])+ fn bar() {} ) } @@ -1265,7 +1267,7 @@ macro_rules! m { #[doc = r" 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 "] fn bar() {} -"##]], +"#]], ); } @@ -1342,10 +1344,10 @@ fn test_tt_composite2() { macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } m! {#} "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } abs!( = > #); -"##]], +"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 485f72e92ce1c..894ef1d73e22d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -139,7 +139,7 @@ STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}} STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}} "#, - expect![[r##" + expect![[r#" macro_rules! STRUCT { ($(#[$attrs:meta])* struct $name:ident { $($field:ident: $ftype:ty,)+ @@ -194,7 +194,7 @@ impl Clone for D3DCONTENTPROTECTIONCAPS { } } } -"##]], +"#]], ); } @@ -214,7 +214,7 @@ macro_rules! int_base { } int_base!{Binary for isize as usize -> Binary} "#, - expect![[r##" + expect![[r#" macro_rules! int_base { ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] @@ -230,7 +230,7 @@ macro_rules! int_base { Binary.fmt_int(*self as usize, f) } } -"##]], +"#]], ); } @@ -318,7 +318,7 @@ impl_fn_for_zst ! { } "#, - expect![[r##" + expect![[r#" macro_rules! impl_fn_for_zst { {$( $( #[$attr: meta] )* struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = @@ -410,7 +410,7 @@ impl FnOnce<(char, )> for CharEscapeDefault { } } -"##]], +"#]], ); } @@ -511,7 +511,7 @@ cfg_if! { @__apply cfg(all(not(any(not(any(target_os = "solaris", target_os = "illumos")))))), } "#, - expect![[r##" + expect![[r#" macro_rules! cfg_if { ($(if #[cfg($($meta:meta),*)] { $($it:item)* } )else* else { $($it2:item)* }) => { @@ -534,7 +534,7 @@ __cfg_if_items! { } -"##]], +"#]], ); } @@ -618,7 +618,7 @@ RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID fn GetDataSize(&mut self) -> UINT }} "#, - expect![[r##" + expect![[r#" #[macro_export] macro_rules! RIDL { (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) @@ -639,7 +639,7 @@ impl ID3D11Asynchronous { ((*self .lpVtbl).GetDataSize)(self ) } } -"##]], +"#]], ); } @@ -676,7 +676,7 @@ quick_error ! ( ); "#, - expect![[r##" + expect![[r#" macro_rules! quick_error { (SORT [enum $name:ident $( #[$meta:meta] )*] items [$($( #[$imeta:meta] )* @@ -697,7 +697,7 @@ macro_rules! quick_error { } quick_error!(ENUMINITION[enum Wrapped#[derive(Debug)]]body[]queue[ = > One: UNIT[] = > Two: TUPLE[s: String]]); -"##]], +"#]], ) } @@ -746,7 +746,7 @@ delegate_impl ! { [G, &'a mut G, deref] pub trait Data: GraphBase {@section type type NodeWeight;} } "#, - expect![[r##" + expect![[r#" macro_rules! delegate_impl { ([$self_type:ident, $self_wrap:ty, $self_map:ident] pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { @@ -785,7 +785,7 @@ macro_rules! delegate_impl { } } impl <> Data for &'amut G where G: Data {} -"##]], +"#]], ); } @@ -959,14 +959,14 @@ macro_rules! with_std { with_std! {mod m;mod f;} "#, - expect![[r##" + expect![[r#" macro_rules! with_std { ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) } #[cfg(feature = "std")] mod m; #[cfg(feature = "std")] mod f; -"##]], +"#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 7f761192517c4..f9ccb06ce84a4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -1,6 +1,6 @@ -//! This module contains tests for macro expansion. Effectively, it covers `tt`, -//! `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a -//! wrong architecture at the first glance, but is intentional. +//! This module contains integration tests for macro expansion with name resolution. Effectively, it +//! covers `tt`, `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a wrong +//! architecture at the first glance, but is intentional. //! //! Physically, macro expansion process is intertwined with name resolution. You //! can not expand *just* the syntax. So, to be able to write integration tests diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index 6f605c0cb33db..c0178adc9a63a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -16,12 +16,12 @@ fn attribute_macro_attr_censoring() { #[attr1] #[proc_macros::identity] #[attr2] struct S; "#, - expect![[r##" + expect![[r#" #[attr1] #[proc_macros::identity] #[attr2] struct S; #[attr1] -#[attr2] struct S;"##]], +#[attr2] struct S;"#]], ); } @@ -39,7 +39,7 @@ fn derive_censoring() { #[attr2] struct S; "#, - expect![[r##" + expect![[r#" #[attr1] #[derive(Foo)] #[derive(proc_macros::DeriveIdentity)] @@ -49,7 +49,7 @@ struct S; #[attr1] #[derive(Bar)] -#[attr2] struct S;"##]], +#[attr2] struct S;"#]], ); } @@ -62,14 +62,14 @@ fn attribute_macro_syntax_completion_1() { #[proc_macros::identity_when_valid] fn foo() { bar.baz(); blub } "#, - expect![[r##" + expect![[r#" #[proc_macros::identity_when_valid] fn foo() { bar.baz(); blub } fn foo() { bar.baz(); blub -}"##]], +}"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 19c3c9c43f104..7ae2fa1e39cd0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -55,6 +55,7 @@ pub use crate::files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRea pub use mbe::{DeclarativeMacro, ValueResult}; pub use span::{HirFileId, MacroCallId, MacroFileId}; +pub use syntax_bridge::insert_whitespace_into_node; pub mod tt { pub use span::Span; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 96a6e6f1f128e..527ecf04c3e56 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -136,6 +136,7 @@ pub use { }, hygiene::{marks_rev, SyntaxContextExt}, inert_attr_macro::AttributeTemplate, + insert_whitespace_into_node, name::Name, proc_macro::{ProcMacros, ProcMacrosBuilder}, tt, ExpandResult, HirFileId, HirFileIdExt, MacroFileId, MacroFileIdExt, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 3cf29987fa14c..8a2068e9039fc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -36,7 +36,7 @@ pub mod generated { pub mod syntax_helpers { pub mod format_string; pub mod format_string_exprs; - pub mod insert_whitespace_into_node; + pub use hir::insert_whitespace_into_node; pub mod node_ext; pub use parser::LexedStr; diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 9b934044ac0b7..f4528dd754c21 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -509,13 +509,8 @@ fn main() { file_id: FileId( 1, ), -<<<<<<< HEAD - full_range: 633..868, - focus_range: 694..700, -======= full_range: 4294967295..4294967295, focus_range: 4294967295..4294967295, ->>>>>>> 33e4a08d5b (minor: Reduce friction for updating minicore) name: "FnOnce", kind: Trait, container_name: "function", diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 756d42ef573fc..413a0254a6162 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -30,6 +30,7 @@ syntax-bridge.workspace = true [dev-dependencies] test-utils.workspace = true +expect-test.workspace = true [features] in-rust-tree = ["parser/in-rust-tree", "tt/in-rust-tree", "syntax/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 88785537c7d20..f7b6e8f54b572 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -11,6 +11,8 @@ mod parser; #[cfg(test)] mod benchmark; +#[cfg(test)] +mod tests; use span::{Edition, Span, SyntaxContextId}; use syntax_bridge::to_parser_input; diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs new file mode 100644 index 0000000000000..5422c9ae644b0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -0,0 +1,179 @@ +//! Tests specific to declarative macros, aka macros by example. This covers +//! both stable `macro_rules!` macros as well as unstable `macro` macros. +// FIXME: Move more of the nameres independent tests from +// crates\hir-def\src\macro_expansion_tests\mod.rs to this +use expect_test::expect; +use span::{Edition, EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use stdx::format_to; +use syntax_bridge::insert_whitespace_into_node::insert_ws_into; +use tt::{TextRange, TextSize}; + +use crate::DeclarativeMacro; + +fn check_( + def_edition: Edition, + call_edition: Edition, + macro2: bool, + decl: &str, + arg: &str, + render_debug: bool, + expect: expect_test::Expect, + parse: parser::TopEntryPoint, +) { + let decl_tt = &syntax_bridge::parse_to_token_tree( + def_edition, + SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), def_edition), + ast_id: ErasedFileAstId::from_raw(0), + }, + SyntaxContextId::ROOT, + decl, + ) + .unwrap(); + let mac = if macro2 { + DeclarativeMacro::parse_macro2(None, decl_tt, |_| def_edition) + } else { + DeclarativeMacro::parse_macro_rules(decl_tt, |_| def_edition) + }; + let call_anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(1), call_edition), + ast_id: ErasedFileAstId::from_raw(0), + }; + let arg_tt = + syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg) + .unwrap(); + let res = mac.expand( + &arg_tt, + |_| (), + Span { + range: TextRange::up_to(TextSize::of(arg)), + anchor: call_anchor, + ctx: SyntaxContextId::ROOT, + }, + def_edition, + ); + let mut expect_res = String::new(); + if let Some(err) = res.err { + format_to!(expect_res, "{err:#?}\n\n",); + } + if render_debug { + format_to!(expect_res, "{:#?}\n\n", res.value.0); + } + let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition); + format_to!(expect_res, "{}", insert_ws_into(node.syntax_node())); + expect.assert_eq(&expect_res); +} + +fn check( + def_edition: Edition, + call_edition: Edition, + decl: &str, + arg: &str, + expect: expect_test::Expect, +) { + check_( + def_edition, + call_edition, + false, + decl, + arg, + true, + expect, + parser::TopEntryPoint::SourceFile, + ); +} + +#[test] +fn token_mapping_smoke_test() { + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +( struct $ident:ident ) => { + struct $ident { + map: ::std::collections::HashSet<()>, + } +}; +"#, + r#" +struct MyTraitMap2 +"#, + expect![[r#" + SUBTREE $$ 1:0@0..20#0 1:0@0..20#0 + IDENT struct 0:0@34..40#0 + IDENT MyTraitMap2 1:0@8..19#0 + SUBTREE {} 0:0@48..49#0 0:0@100..101#0 + IDENT map 0:0@58..61#0 + PUNCH : [alone] 0:0@61..62#0 + PUNCH : [joint] 0:0@63..64#0 + PUNCH : [alone] 0:0@64..65#0 + IDENT std 0:0@65..68#0 + PUNCH : [joint] 0:0@68..69#0 + PUNCH : [alone] 0:0@69..70#0 + IDENT collections 0:0@70..81#0 + PUNCH : [joint] 0:0@81..82#0 + PUNCH : [alone] 0:0@82..83#0 + IDENT HashSet 0:0@83..90#0 + PUNCH < [alone] 0:0@90..91#0 + SUBTREE () 0:0@91..92#0 0:0@92..93#0 + PUNCH > [joint] 0:0@93..94#0 + PUNCH , [alone] 0:0@94..95#0 + + struct MyTraitMap2 { + map: ::std::collections::HashSet<()>, + }"#]], + ); +} + +#[test] +fn token_mapping_floats() { + // Regression test for https://github.com/rust-lang/rust-analyzer/issues/12216 + // (and related issues) + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +($($tt:tt)*) => { + $($tt)* +}; +"#, + r#" +fn main() { + 1; + 1.0; + ((1,),).0.0; + let x = 1; +} +"#, + expect![[r#" + SUBTREE $$ 1:0@0..63#0 1:0@0..63#0 + IDENT fn 1:0@1..3#0 + IDENT main 1:0@4..8#0 + SUBTREE () 1:0@8..9#0 1:0@9..10#0 + SUBTREE {} 1:0@11..12#0 1:0@61..62#0 + LITERAL Integer 1 1:0@17..18#0 + PUNCH ; [alone] 1:0@18..19#0 + LITERAL Float 1.0 1:0@24..27#0 + PUNCH ; [alone] 1:0@27..28#0 + SUBTREE () 1:0@33..34#0 1:0@39..40#0 + SUBTREE () 1:0@34..35#0 1:0@37..38#0 + LITERAL Integer 1 1:0@35..36#0 + PUNCH , [alone] 1:0@36..37#0 + PUNCH , [alone] 1:0@38..39#0 + PUNCH . [alone] 1:0@40..41#0 + LITERAL Float 0.0 1:0@41..44#0 + PUNCH ; [alone] 1:0@44..45#0 + IDENT let 1:0@50..53#0 + IDENT x 1:0@54..55#0 + PUNCH = [alone] 1:0@56..57#0 + LITERAL Integer 1 1:0@58..59#0 + PUNCH ; [alone] 1:0@59..60#0 + + fn main(){ + 1; + 1.0; + ((1,),).0.0; + let x = 1; + }"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/insert_whitespace_into_node.rs similarity index 97% rename from src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs rename to src/tools/rust-analyzer/crates/syntax-bridge/src/insert_whitespace_into_node.rs index dd4a665e8eb4f..a61fca9ec520c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/insert_whitespace_into_node.rs @@ -7,8 +7,6 @@ use syntax::{ SyntaxNode, SyntaxToken, WalkEvent, T, }; -// FIXME: It would also be cool to share logic here and in the mbe tests, -// which are pretty unreadable at the moment. /// Renders a [`SyntaxNode`] with whitespace inserted between tokens that require them. pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { let mut indent = 0; diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs index 56e43e82ed27a..f23b33127fe49 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs @@ -17,6 +17,7 @@ use tt::{ token_to_literal, }; +pub mod insert_whitespace_into_node; mod to_parser_input; pub use to_parser_input::to_parser_input; // FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index bc40e03c5a246..a26444e9ea230 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -67,7 +67,7 @@ pub struct FileId(u32); // pub struct FileId(NonMaxU32); impl FileId { - pub const MAX: u32 = 0x7fff_ffff; + const MAX: u32 = 0x7fff_ffff; #[inline] pub const fn from_raw(raw: u32) -> FileId { From 193051438d7083f465f68c5fdca86c5e02b00a8f Mon Sep 17 00:00:00 2001 From: ding-young Date: Wed, 28 Aug 2024 13:24:17 +0900 Subject: [PATCH 0213/1301] update rewrite_assignment to return RewriteResult --- src/expr.rs | 70 +++++++++++++++++++++++++++++---------------------- src/items.rs | 18 +++++++------ src/macros.rs | 21 +++++++--------- src/types.rs | 5 ++-- 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 02372e7be130a..35b7bada92d4c 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -194,10 +194,10 @@ pub(crate) fn format_expr( rewrite_path(context, PathContext::Expr, qself, path, shape).ok() } ast::ExprKind::Assign(ref lhs, ref rhs, _) => { - rewrite_assignment(context, lhs, rhs, None, shape) + rewrite_assignment(context, lhs, rhs, None, shape).ok() } ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => { - rewrite_assignment(context, lhs, rhs, Some(op), shape) + rewrite_assignment(context, lhs, rhs, Some(op), shape).ok() } ast::ExprKind::Continue(ref opt_label) => { let id_str = match *opt_label { @@ -2050,15 +2050,21 @@ fn rewrite_assignment( rhs: &ast::Expr, op: Option<&ast::BinOp>, shape: Shape, -) -> Option { +) -> RewriteResult { let operator_str = match op { Some(op) => context.snippet(op.span), None => "=", }; // 1 = space between lhs and operator. - let lhs_shape = shape.sub_width(operator_str.len() + 1)?; - let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str); + let lhs_shape = shape + .sub_width(operator_str.len() + 1) + .max_width_error(shape.width, lhs.span())?; + let lhs_str = format!( + "{} {}", + lhs.rewrite_result(context, lhs_shape)?, + operator_str + ); rewrite_assign_rhs( context, @@ -2089,7 +2095,7 @@ pub(crate) fn rewrite_assign_rhs, R: Rewrite>( ex: &R, rhs_kind: &RhsAssignKind<'_>, shape: Shape, -) -> Option { +) -> RewriteResult { rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default) } @@ -2100,7 +2106,7 @@ pub(crate) fn rewrite_assign_rhs_expr( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option { +) -> RewriteResult { let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { shape.indent.width() } else { @@ -2122,7 +2128,7 @@ pub(crate) fn rewrite_assign_rhs_expr( context, ex, orig_shape, - ex.rewrite(context, orig_shape), + ex.rewrite_result(context, orig_shape), rhs_kind, rhs_tactics, has_rhs_comment, @@ -2136,10 +2142,10 @@ pub(crate) fn rewrite_assign_rhs_with, R: Rewrite>( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option { +) -> RewriteResult { let lhs = lhs.into(); let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; - Some(lhs + &rhs) + Ok(lhs + &rhs) } pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite + Spanned>( @@ -2161,8 +2167,7 @@ pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite + Spa } else { shape }; - let rhs = - rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics).unknown_error()?; + let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; if contains_comment { let rhs = rhs.trim_start(); combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend) @@ -2175,23 +2180,25 @@ fn choose_rhs( context: &RewriteContext<'_>, expr: &R, shape: Shape, - orig_rhs: Option, + orig_rhs: RewriteResult, _rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, has_rhs_comment: bool, -) -> Option { +) -> RewriteResult { match orig_rhs { - Some(ref new_str) if new_str.is_empty() => Some(String::new()), - Some(ref new_str) - if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => - { - Some(format!(" {new_str}")) + Ok(ref new_str) if new_str.is_empty() => Ok(String::new()), + Ok(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { + Ok(format!(" {new_str}")) } _ => { // Expression did not fit on the same line as the identifier. // Try splitting the line and see if that works better. - let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?; - let new_rhs = expr.rewrite(context, new_shape); + let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics) + // TODO(ding-young) Ideally, we can replace unknown_error() with max_width_error(), + // but this requires either implementing the Spanned trait for ast::GenericBounds + // or grabbing the span from the call site. + .unknown_error()?; + let new_rhs = expr.rewrite_result(context, new_shape); let new_indent_str = &shape .indent .block_indent(context.config) @@ -2199,24 +2206,27 @@ fn choose_rhs( let before_space_str = if has_rhs_comment { "" } else { " " }; match (orig_rhs, new_rhs) { - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { - Some(format!("{before_space_str}{orig_rhs}")) + Ok(format!("{before_space_str}{orig_rhs}")) } - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => { - Some(format!("{new_indent_str}{new_rhs}")) + Ok(format!("{new_indent_str}{new_rhs}")) } - (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")), - (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { + (Err(_), Ok(ref new_rhs)) => Ok(format!("{new_indent_str}{new_rhs}")), + (Err(_), Err(_)) if rhs_tactics == RhsTactics::AllowOverflow => { let shape = shape.infinite_width(); - expr.rewrite(context, shape) + expr.rewrite_result(context, shape) .map(|s| format!("{}{}", before_space_str, s)) } - (None, None) => None, - (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")), + // When both orig_rhs and new_rhs result in errors, we currently propagate + // the error from the second attempt since it is more generous with + // width constraints. This decision is somewhat arbitrary and is open to change. + (Err(_), Err(new_rhs_err)) => Err(new_rhs_err), + (Ok(orig_rhs), _) => Ok(format!("{before_space_str}{orig_rhs}")), } } } diff --git a/src/items.rs b/src/items.rs index 3894ee2cdf8c8..4ea9751e81660 100644 --- a/src/items.rs +++ b/src/items.rs @@ -137,8 +137,7 @@ impl Rewrite for ast::Local { init, &RhsAssignKind::Expr(&init.kind, init.span), nested_shape, - ) - .max_width_error(shape.width, self.span())?; + )?; if let Some(block) = else_block { let else_kw_span = init.span.between(block.span); @@ -711,7 +710,8 @@ impl<'a> FmtVisitor<'a> { shape, &RhsAssignKind::Expr(&ex.kind, ex.span), RhsTactics::AllowOverflow, - )? + ) + .ok()? } else { variant_body }; @@ -1206,7 +1206,8 @@ pub(crate) fn format_trait( shape, &RhsAssignKind::Bounds, RhsTactics::ForceNextLineWithoutIndent, - )?; + ) + .ok()?; } // Rewrite where-clause. @@ -1396,6 +1397,7 @@ pub(crate) fn format_trait_alias( shape.sub_width(1)?, ) .map(|s| s + ";") + .ok() } fn format_unit_struct( @@ -1835,7 +1837,9 @@ fn rewrite_ty( // 1 = `;` let shape = Shape::indented(indent, context.config).sub_width(1)?; - rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") + rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape) + .map(|s| s + ";") + .ok() } else { Some(format!("{result};")) } @@ -1931,8 +1935,7 @@ pub(crate) fn rewrite_struct_field( let is_prefix_empty = prefix.is_empty(); // We must use multiline. We are going to put attributes and a field on different lines. - let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape) - .unknown_error()?; + let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?; // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct. let field_str = if is_prefix_empty { field_str.trim_start() @@ -3421,6 +3424,7 @@ impl Rewrite for ast::ForeignItem { shape.sub_width(1)?, ) .map(|s| s + ";") + .ok() } ast::ForeignItemKind::TyAlias(ref ty_alias) => { let (kind, span) = (&ItemVisitorKind::ForeignItem(self), self.span); diff --git a/src/macros.rs b/src/macros.rs index 51ded8692296b..5e537002324f8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1437,18 +1437,15 @@ fn format_lazy_static( id, ty.rewrite_result(context, nested_shape)? )); - result.push_str( - &rewrite_assign_rhs( - context, - stmt, - &*expr, - &RhsAssignKind::Expr(&expr.kind, expr.span), - nested_shape - .sub_width(1) - .max_width_error(nested_shape.width, expr.span)?, - ) - .unknown_error()?, - ); + result.push_str(&rewrite_assign_rhs( + context, + stmt, + &*expr, + &RhsAssignKind::Expr(&expr.kind, expr.span), + nested_shape + .sub_width(1) + .max_width_error(nested_shape.width, expr.span)?, + )?); result.push(';'); if i != last { result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); diff --git a/src/types.rs b/src/types.rs index 76eb0ea0529cb..d1cd291d51f58 100644 --- a/src/types.rs +++ b/src/types.rs @@ -475,7 +475,7 @@ impl Rewrite for ast::WherePredicate { format!("{type_str}{colon}") }; - rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? + rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape).ok()? } ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, @@ -488,7 +488,8 @@ impl Rewrite for ast::WherePredicate { .. }) => { let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; - rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)? + rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape) + .ok()? } }; From 35b3889c77fd98b2c9ec8ab93ffd516187bdd7f6 Mon Sep 17 00:00:00 2001 From: ding-young Date: Fri, 23 Aug 2024 18:29:59 +0900 Subject: [PATCH 0214/1301] impl rewrite_result for ControlFlow, Stmt, update rewrite_index --- src/comment.rs | 7 ++-- src/expr.rs | 85 +++++++++++++++++++++++++++++-------------------- src/items.rs | 2 +- src/overflow.rs | 11 ++++--- src/stmt.rs | 30 ++++++++++++----- src/types.rs | 26 +++++++++------ 6 files changed, 101 insertions(+), 60 deletions(-) diff --git a/src/comment.rs b/src/comment.rs index 5bf3c1a725c89..c8cadf364daf8 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -1703,12 +1703,11 @@ impl<'a> Iterator for CommentCodeSlices<'a> { } /// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text -/// (if it fits in the width/offset, else return `None`), else return `new` pub(crate) fn recover_comment_removed( new: String, span: Span, context: &RewriteContext<'_>, -) -> Option { +) -> String { let snippet = context.snippet(span); if snippet != new && changed_comment_content(snippet, &new) { // We missed some comments. Warn and keep the original text. @@ -1722,9 +1721,9 @@ pub(crate) fn recover_comment_removed( )], ); } - Some(snippet.to_owned()) + snippet.to_owned() } else { - Some(new) + new } } diff --git a/src/expr.rs b/src/expr.rs index 35b7bada92d4c..8381e3ab0835e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -278,7 +278,7 @@ pub(crate) fn format_expr( ) .ok(), ast::ExprKind::Index(ref expr, ref index, _) => { - rewrite_index(&**expr, &**index, context, shape) + rewrite_index(&**expr, &**index, context, shape).ok() } ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair( &**expr, @@ -435,7 +435,7 @@ pub(crate) fn format_expr( }; expr_rw - .and_then(|expr_str| recover_comment_removed(expr_str, expr.span, context)) + .map(|expr_str| recover_comment_removed(expr_str, expr.span, context)) .and_then(|expr_str| { let attrs = outer_attributes(&expr.attrs); let attrs_str = attrs.rewrite(context, shape)?; @@ -672,6 +672,7 @@ pub(crate) fn rewrite_cond( String::from("\n") + &shape.indent.block_only().to_string(context.config); control_flow .rewrite_cond(context, shape, &alt_block_sep) + .ok() .map(|rw| rw.0) }), } @@ -896,10 +897,12 @@ impl<'a> ControlFlow<'a> { expr: &ast::Expr, shape: Shape, offset: usize, - ) -> Option { + ) -> RewriteResult { debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr); - let cond_shape = shape.offset_left(offset)?; + let cond_shape = shape + .offset_left(offset) + .max_width_error(shape.width, expr.span)?; if let Some(pat) = self.pat { let matcher = if self.matcher.is_empty() { self.matcher.to_owned() @@ -907,9 +910,10 @@ impl<'a> ControlFlow<'a> { format!("{} ", self.matcher) }; let pat_shape = cond_shape - .offset_left(matcher.len())? - .sub_width(self.connector.len())?; - let pat_string = pat.rewrite(context, pat_shape)?; + .offset_left(matcher.len()) + .and_then(|s| s.sub_width(self.connector.len())) + .max_width_error(cond_shape.width, pat.span)?; + let pat_string = pat.rewrite_result(context, pat_shape)?; let comments_lo = context .snippet_provider .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim()); @@ -923,14 +927,13 @@ impl<'a> ControlFlow<'a> { RhsTactics::Default, comments_span, true, - ) - .ok(); + ); } - let expr_rw = expr.rewrite(context, cond_shape); + let expr_rw = expr.rewrite_result(context, cond_shape); // The expression may (partially) fit on the current line. // We do not allow splitting between `if` and condition. - if self.keyword == "if" || expr_rw.is_some() { + if self.keyword == "if" || expr_rw.is_ok() { return expr_rw; } @@ -939,7 +942,7 @@ impl<'a> ControlFlow<'a> { .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config); - expr.rewrite(context, nested_shape) + expr.rewrite_result(context, nested_shape) .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw)) } @@ -948,7 +951,7 @@ impl<'a> ControlFlow<'a> { context: &RewriteContext<'_>, shape: Shape, alt_block_sep: &str, - ) -> Option<(String, usize)> { + ) -> Result<(String, usize), RewriteError> { // Do not take the rhs overhead from the upper expressions into account // when rewriting pattern. let new_width = context.budget(shape.used_width()); @@ -959,7 +962,9 @@ impl<'a> ControlFlow<'a> { let constr_shape = if self.nested_if { // We are part of an if-elseif-else chain. Our constraints are tightened. // 7 = "} else " .len() - fresh_shape.offset_left(7)? + fresh_shape + .offset_left(7) + .max_width_error(fresh_shape.width, self.span)? } else { fresh_shape }; @@ -995,7 +1000,7 @@ impl<'a> ControlFlow<'a> { if let Some(cond_str) = trial { if cond_str.len() <= context.config.single_line_if_else_max_width() { - return Some((cond_str, 0)); + return Ok((cond_str, 0)); } } } @@ -1048,7 +1053,7 @@ impl<'a> ControlFlow<'a> { label_string.len() + self.keyword.len() + pat_expr_string.len() + 2 }; - Some(( + Ok(( format!( "{}{}{}{}{}", label_string, @@ -1114,13 +1119,17 @@ pub(crate) fn rewrite_else_kw_with_comments( impl<'a> Rewrite for ControlFlow<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!("ControlFlow::rewrite {:?} {:?}", self, shape); let alt_block_sep = &shape.indent.to_string_with_newline(context.config); let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?; // If `used_width` is 0, it indicates that whole control flow is written in a single line. if used_width == 0 { - return Some(cond_str); + return Ok(cond_str); } let block_width = shape.width.saturating_sub(used_width); @@ -1138,8 +1147,7 @@ impl<'a> Rewrite for ControlFlow<'a> { let block_str = { let old_val = context.is_if_else_block.replace(self.else_block.is_some()); let result = - rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true) - .ok(); + rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true); context.is_if_else_block.replace(old_val); result? }; @@ -1165,7 +1173,7 @@ impl<'a> Rewrite for ControlFlow<'a> { true, mk_sp(else_block.span.lo(), self.span.hi()), ) - .rewrite(context, shape) + .rewrite_result(context, shape) } _ => { last_in_chain = true; @@ -1176,6 +1184,7 @@ impl<'a> Rewrite for ControlFlow<'a> { ..shape }; format_expr(else_block, ExprType::Statement, context, else_shape) + .unknown_error() } }; @@ -1190,7 +1199,7 @@ impl<'a> Rewrite for ControlFlow<'a> { result.push_str(&rewrite?); } - Some(result) + Ok(result) } } @@ -1567,8 +1576,8 @@ fn rewrite_index( index: &ast::Expr, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - let expr_str = expr.rewrite(context, shape)?; +) -> RewriteResult { + let expr_str = expr.rewrite_result(context, shape)?; let offset = last_line_width(&expr_str) + 1; let rhs_overhead = shape.rhs_overhead(context.config); @@ -1583,37 +1592,45 @@ fn rewrite_index( .and_then(|shape| shape.sub_width(1)), IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1), } - }; - let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s)); + } + .max_width_error(shape.width, index.span()); + let orig_index_rw = index_shape.and_then(|s| index.rewrite_result(context, s)); // Return if index fits in a single line. match orig_index_rw { - Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{expr_str}[{index_str}]")); + Ok(ref index_str) if !index_str.contains('\n') => { + return Ok(format!("{expr_str}[{index_str}]")); } _ => (), } // Try putting index on the next line and see if it fits in a single line. let indent = shape.indent.block_indent(context.config); - let index_shape = Shape::indented(indent, context.config).offset_left(1)?; - let index_shape = index_shape.sub_width(1 + rhs_overhead)?; - let new_index_rw = index.rewrite(context, index_shape); + let index_shape = Shape::indented(indent, context.config) + .offset_left(1) + .max_width_error(shape.width, index.span())?; + let index_shape = index_shape + .sub_width(1 + rhs_overhead) + .max_width_error(index_shape.width, index.span())?; + let new_index_rw = index.rewrite_result(context, index_shape); match (orig_index_rw, new_index_rw) { - (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!( + (_, Ok(ref new_index_str)) if !new_index_str.contains('\n') => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (None, Some(ref new_index_str)) => Some(format!( + (Err(_), Ok(ref new_index_str)) => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")), - _ => None, + (Ok(ref index_str), _) => Ok(format!("{expr_str}[{index_str}]")), + // When both orig_index_rw and new_index_rw result in errors, we currently propagate the + // error from the second attempt since it is more generous with width constraints. + // This decision is somewhat arbitrary and is open to change. + (Err(_), Err(new_index_rw_err)) => Err(new_index_rw_err), } } diff --git a/src/items.rs b/src/items.rs index 4ea9751e81660..89fb9bda9dfca 100644 --- a/src/items.rs +++ b/src/items.rs @@ -2077,7 +2077,7 @@ fn rewrite_static( true, ) .ok() - .and_then(|res| recover_comment_removed(res, static_parts.span, context)) + .map(|res| recover_comment_removed(res, static_parts.span, context)) .map(|s| if s.ends_with(';') { s } else { s + ";" }) } else { Some(format!("{prefix}{ty_str};")) diff --git a/src/overflow.rs b/src/overflow.rs index 510ad3c642e6c..dc4716a81af9b 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -19,7 +19,7 @@ use crate::lists::{ }; use crate::macros::MacroArg; use crate::patterns::{can_be_overflowed_pat, TuplePatField}; -use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; @@ -90,6 +90,10 @@ impl<'a> Rewrite for OverflowableItem<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { self.map(|item| item.rewrite(context, shape)) } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + self.map(|item| item.rewrite_result(context, shape)) + } } impl<'a> Spanned for OverflowableItem<'a> { @@ -617,7 +621,7 @@ impl<'a> Context<'a> { tactic } - fn rewrite_items(&self) -> Option<(bool, String)> { + fn rewrite_items(&self) -> Result<(bool, String), RewriteError> { let span = self.items_span(); debug!("items: {:?}", self.items); @@ -661,7 +665,6 @@ impl<'a> Context<'a> { .ends_with_newline(ends_with_newline); write_list(&list_items, &fmt) - .ok() .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str)) } @@ -718,7 +721,7 @@ impl<'a> Context<'a> { } fn rewrite(&self, shape: Shape) -> RewriteResult { - let (extendable, items_str) = self.rewrite_items().unknown_error()?; + let (extendable, items_str) = self.rewrite_items()?; // If we are using visual indent style and failed to format, retry with block indent. if !self.context.use_block_indent() diff --git a/src/stmt.rs b/src/stmt.rs index 38433433c6b5e..4f31705295eb1 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -4,7 +4,7 @@ use rustc_span::Span; use crate::comment::recover_comment_removed; use crate::config::StyleEdition; use crate::expr::{format_expr, is_simple_block, ExprType}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; use crate::spanned::Spanned; @@ -90,6 +90,14 @@ impl<'a> Stmt<'a> { impl<'a> Rewrite for Stmt<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result( + &self, + context: &RewriteContext<'_>, + shape: Shape, + ) -> crate::rewrite::RewriteResult { let expr_type = if context.config.style_edition() >= StyleEdition::Edition2024 && self.is_last_expr() { ExprType::SubExpression @@ -112,11 +120,11 @@ fn format_stmt( stmt: &ast::Stmt, expr_type: ExprType, is_last_expr: bool, -) -> Option { - skip_out_of_file_lines_range!(context, stmt.span()); +) -> RewriteResult { + skip_out_of_file_lines_range_err!(context, stmt.span()); let result = match stmt.kind { - ast::StmtKind::Let(ref local) => local.rewrite(context, shape), + ast::StmtKind::Let(ref local) => local.rewrite_result(context, shape), ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => { let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) { ";" @@ -124,10 +132,16 @@ fn format_stmt( "" }; - let shape = shape.sub_width(suffix.len())?; - format_expr(ex, expr_type, context, shape).map(|s| s + suffix) + let shape = shape + .sub_width(suffix.len()) + .max_width_error(shape.width, ex.span())?; + format_expr(ex, expr_type, context, shape) + .map(|s| s + suffix) + .unknown_error() + } + ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => { + Err(RewriteError::Unknown) } - ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => None, }; - result.and_then(|res| recover_comment_removed(res, stmt.span(), context)) + result.map(|res| recover_comment_removed(res, stmt.span(), context)) } diff --git a/src/types.rs b/src/types.rs index d1cd291d51f58..af57a5843dbe6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -480,8 +480,8 @@ impl Rewrite for ast::WherePredicate { ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, ref bounds, - .. - }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?, + span, + }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape).ok()?, ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref lhs_ty, ref rhs_ty, @@ -553,23 +553,27 @@ fn rewrite_generic_args( fn rewrite_bounded_lifetime( lt: &ast::Lifetime, bounds: &[ast::GenericBound], + span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - let result = lt.rewrite(context, shape)?; +) -> RewriteResult { + let result = lt.rewrite_result(context, shape)?; if bounds.is_empty() { - Some(result) + Ok(result) } else { let colon = type_bound_colon(context); let overhead = last_line_width(&result) + colon.len(); + let shape = shape + .sub_width(overhead) + .max_width_error(shape.width, span)?; let result = format!( "{}{}{}", result, colon, - join_bounds(context, shape.sub_width(overhead)?, bounds, true).ok()? + join_bounds(context, shape, bounds, true)? ); - Some(result) + Ok(result) } } @@ -580,8 +584,12 @@ impl Rewrite for ast::AnonConst { } impl Rewrite for ast::Lifetime { - fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option { - Some(rewrite_ident(context, self.ident).to_owned()) + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, _: Shape) -> RewriteResult { + Ok(rewrite_ident(context, self.ident).to_owned()) } } From 40909b4331a7bc481f5bcef149efa388f46222f7 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Sun, 1 Sep 2024 08:34:52 +0200 Subject: [PATCH 0215/1301] Fix `test_editions_2015_2018_2021_identical` Was comparing unit type. --- src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 8a95cc55fcbb2..d35a249a80a31 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -900,7 +900,7 @@ make_backup = false Config::default_with_style_edition(style_edition) .all_options() .to_toml() - .unwrap(); + .unwrap() }; let edition2015 = get_edition_toml(StyleEdition::Edition2015); let edition2018 = get_edition_toml(StyleEdition::Edition2018); From 0bdc5ffd685db3d1506dbf21c8df5daaf68aeaac Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 1 Sep 2024 11:23:24 -0400 Subject: [PATCH 0216/1301] Support the weak variable attribute --- Cargo.lock | 6 ++---- Cargo.toml | 3 ++- src/consts.rs | 7 ++++++- src/mono_item.rs | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 915229f7e7eef..42528419a2533 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,8 +80,7 @@ dependencies = [ [[package]] name = "gccjit" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e0ba949ebee07c5cc21f02cb48f28f2c8db7fcbc15fdc5120476a6c43b4636" +source = "git+https://github.com/rust-lang/gccjit.rs#4fbe2023250357378fb2faf6f484b34cb8f8ebc3" dependencies = [ "gccjit_sys", ] @@ -89,8 +88,7 @@ dependencies = [ [[package]] name = "gccjit_sys" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5bbf85e12c2593772329a9d4e8310271f6706e6045ce4f41b041dd34fba6603" +source = "git+https://github.com/rust-lang/gccjit.rs#4fbe2023250357378fb2faf6f484b34cb8f8ebc3" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 5caca63f63480..67a90cc34ee4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,8 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = "2.1" +#gccjit = "2.1" +gccjit = { git = "https://github.com/rust-lang/gccjit.rs" } # Local copy. #gccjit = { path = "../gccjit.rs" } diff --git a/src/consts.rs b/src/consts.rs index e5673cddc4a4f..53dbb35d711ec 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -7,6 +7,7 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs} use rustc_middle::mir::interpret::{ self, read_target_uint, ConstAllocation, ErrorHandled, Scalar as InterpScalar, }; +use rustc_middle::mir::mono::Linkage; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; @@ -256,7 +257,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { if !self.tcx.is_reachable_non_generic(def_id) { #[cfg(feature = "master")] - global.add_string_attribute(VarAttribute::Visibility(Visibility::Hidden)); + global.add_attribute(VarAttribute::Visibility(Visibility::Hidden)); } global @@ -384,6 +385,10 @@ fn check_and_apply_linkage<'gcc, 'tcx>( let global1 = cx.declare_global_with_linkage(sym, cx.type_i8(), base::global_linkage_to_gcc(linkage)); + if linkage == Linkage::ExternalWeak { + global1.add_attribute(VarAttribute::Weak); + } + // Declare an internal global `extern_with_linkage_foo` which // is initialized with the address of `foo`. If `foo` is // discarded during linking (for example, if `foo` has weak diff --git a/src/mono_item.rs b/src/mono_item.rs index e6b22d5187147..ba81dea49d542 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -37,7 +37,7 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); let global = self.define_global(symbol_name, gcc_type, is_tls, attrs.link_section); #[cfg(feature = "master")] - global.add_string_attribute(VarAttribute::Visibility(base::visibility_to_gcc(visibility))); + global.add_attribute(VarAttribute::Visibility(base::visibility_to_gcc(visibility))); // TODO(antoyo): set linkage. self.instances.borrow_mut().insert(instance, global); From 4dd288cecf8b8fc24921f45413b7609ab55a066d Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 1 Sep 2024 11:59:27 -0400 Subject: [PATCH 0217/1301] Fix tests --- build_system/src/test.rs | 3 +- libgccjit.version | 2 +- ...022-core-Disable-not-compiling-tests.patch | 30 +++++++++---------- src/consts.rs | 1 + src/intrinsic/llvm.rs | 2 ++ tests/failing-ice-tests.txt | 4 +++ tests/failing-ui-tests.txt | 26 ++++++++++++++++ 7 files changed, 51 insertions(+), 17 deletions(-) diff --git a/build_system/src/test.rs b/build_system/src/test.rs index 303a2e518cf7a..dd09de24aa34f 100644 --- a/build_system/src/test.rs +++ b/build_system/src/test.rs @@ -637,7 +637,8 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> { "https://github.com/BurntSushi/memchr", "https://github.com/dtolnay/itoa", "https://github.com/rust-lang/cfg-if", - "https://github.com/rust-lang-nursery/lazy-static.rs", + //"https://github.com/rust-lang-nursery/lazy-static.rs", // TODO: re-enable when the + //failing test is fixed upstream. //"https://github.com/marshallpierce/rust-base64", // FIXME: one test is OOM-killed. // TODO: ignore the base64 test that is OOM-killed. "https://github.com/time-rs/time", diff --git a/libgccjit.version b/libgccjit.version index 23ca7f022155e..fa2bacc2c8e72 100644 --- a/libgccjit.version +++ b/libgccjit.version @@ -1 +1 @@ -341be3b7d7ac6976cfed8ed59da3573c040d0776 +bcafd46296f7898dac02d127e441b1d838ef2afc diff --git a/patches/0022-core-Disable-not-compiling-tests.patch b/patches/0022-core-Disable-not-compiling-tests.patch index 08e2f1a762841..b2ab05691ecbf 100644 --- a/patches/0022-core-Disable-not-compiling-tests.patch +++ b/patches/0022-core-Disable-not-compiling-tests.patch @@ -1,26 +1,24 @@ -From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001 -From: bjorn3 -Date: Sun, 24 Nov 2019 15:10:23 +0100 +From 18793c6109890493ceb3ff36549849a36e3d8022 Mon Sep 17 00:00:00 2001 +From: None +Date: Sun, 1 Sep 2024 11:42:17 -0400 Subject: [PATCH] [core] Disable not compiling tests --- - library/core/tests/Cargo.toml | 8 ++++++++ - library/core/tests/num/flt2dec/mod.rs | 1 - - library/core/tests/num/int_macros.rs | 2 ++ - library/core/tests/num/uint_macros.rs | 2 ++ - library/core/tests/ptr.rs | 2 ++ - library/core/tests/slice.rs | 2 ++ - 6 files changed, 16 insertions(+), 1 deletion(-) + library/core/tests/Cargo.toml | 14 ++++++++++++++ + library/core/tests/lib.rs | 1 + + 2 files changed, 15 insertions(+) create mode 100644 library/core/tests/Cargo.toml diff --git a/library/core/tests/Cargo.toml b/library/core/tests/Cargo.toml new file mode 100644 -index 0000000..46fd999 +index 0000000..ca326ac --- /dev/null +++ b/library/core/tests/Cargo.toml -@@ -0,0 +1,12 @@ +@@ -0,0 +1,14 @@ ++[workspace] ++ +[package] -+name = "core" ++name = "coretests" +version = "0.0.0" +edition = "2021" + @@ -32,12 +30,14 @@ index 0000000..46fd999 +rand = { version = "0.8.5", default-features = false } +rand_xorshift = { version = "0.3.0", default-features = false } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs -index 42a26ae..5ac1042 100644 +index 1e336bf..5800ebb 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs -@@ -2,4 +2,5 @@ +@@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg(test)] #![cfg_attr(bootstrap, feature(offset_of_nested))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] +-- +2.46.0 diff --git a/src/consts.rs b/src/consts.rs index 53dbb35d711ec..483b2355c529f 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -386,6 +386,7 @@ fn check_and_apply_linkage<'gcc, 'tcx>( cx.declare_global_with_linkage(sym, cx.type_i8(), base::global_linkage_to_gcc(linkage)); if linkage == Linkage::ExternalWeak { + #[cfg(feature = "master")] global1.add_attribute(VarAttribute::Weak); } diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 554e57250e6d8..2287c96b41b8f 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -985,6 +985,8 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.vpdpbusds.512" => "__builtin_ia32_vpdpbusds_v16si", "llvm.x86.avx512.vpdpbusds.256" => "__builtin_ia32_vpdpbusds_v8si", "llvm.x86.avx512.vpdpbusds.128" => "__builtin_ia32_vpdpbusds_v4si", + "llvm.x86.xsave" => "__builtin_ia32_xsave", + "llvm.x86.xsaveopt" => "__builtin_ia32_xsaveopt", // NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py _ => include!("archs.rs"), diff --git a/tests/failing-ice-tests.txt b/tests/failing-ice-tests.txt index 2084f86b62e19..ff1b6f1489468 100644 --- a/tests/failing-ice-tests.txt +++ b/tests/failing-ice-tests.txt @@ -34,3 +34,7 @@ tests/ui/sepcomp/sepcomp-unwind.rs tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs tests/ui/unwind-no-uwtable.rs +tests/ui/delegation/fn-header.rs +tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +tests/ui/simd/masked-load-store.rs +tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs diff --git a/tests/failing-ui-tests.txt b/tests/failing-ui-tests.txt index 5a55bdb156e39..56b51275a53b2 100644 --- a/tests/failing-ui-tests.txt +++ b/tests/failing-ui-tests.txt @@ -95,3 +95,29 @@ tests/ui/simd/intrinsic/generic-arithmetic-pass.rs tests/ui/backtrace/backtrace.rs tests/ui/lifetimes/tail-expr-lock-poisoning.rs tests/ui/runtime/rt-explody-panic-payloads.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/function.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/print3.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/segfault.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs +tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs +tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs +tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs +tests/ui/sanitizer/cfi-sized-associated-ty.rs +tests/ui/sanitizer/cfi-can-reveal-opaques.rs From 92b8b5676682fe8cc50b4145a6bd25075454d283 Mon Sep 17 00:00:00 2001 From: Ivar Scholten Date: Sat, 31 Aug 2024 00:56:16 +0200 Subject: [PATCH 0218/1301] fix: use Result type aliases in "Wrap return type in Result" assist This commit makes the "Wrap return type in Result" assist prefer type aliases of standard library type when the are in scope, use at least one generic parameter, and have the name "Result". The last restriction was made in an attempt to avoid false assumptions about which type the user is referring to, but that might be overly strict. We could also do something like this, in order of priority: * Use the alias named "Result". * Use any alias if only a single one is in scope, otherwise: * Use the standard library type. This is easy to add if others feel differently that is appropriate, just let me know. --- .../rust-analyzer/crates/hir/src/semantics.rs | 17 +- .../handlers/wrap_return_type_in_result.rs | 293 +++++++++++++++++- 2 files changed, 294 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 763f53031e4c5..c78b59826c955 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -14,6 +14,7 @@ use hir_def::{ hir::Expr, lower::LowerCtx, nameres::MacroSubNs, + path::ModPath, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId, @@ -46,9 +47,9 @@ use crate::{ source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, - Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, - Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, - Variant, VariantDef, + ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, + ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, + TypeParam, Union, Variant, VariantDef, }; const CONTINUE_NO_BREAKS: ControlFlow = ControlFlow::Continue(()); @@ -1384,6 +1385,16 @@ impl<'db> SemanticsImpl<'db> { self.analyze(path.syntax())?.resolve_path(self.db, path) } + pub fn resolve_mod_path( + &self, + scope: &SyntaxNode, + path: &ModPath, + ) -> Option> { + let analyze = self.analyze(scope)?; + let items = analyze.resolver.resolve_module_path_in_items(self.db.upcast(), path); + Some(items.iter_items().map(|(item, _)| item.into())) + } + fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option { self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index b68ed00f77210..8f0e9b4fe09d5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -1,12 +1,14 @@ use std::iter; +use hir::HasSource; use ide_db::{ famous_defs::FamousDefs, syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, }; +use itertools::Itertools; use syntax::{ - ast::{self, make, Expr}, - match_ast, ted, AstNode, + ast::{self, make, Expr, HasGenericParams}, + match_ast, ted, AstNode, ToSmolStr, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -39,25 +41,22 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< }; let type_ref = &ret_type.ty()?; - let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); - let result_enum = + let core_result = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; - if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) { + let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); + if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == core_result) { + // The return type is already wrapped in a Result cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); return None; } - let new_result_ty = - make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); - let generic_args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast)?; - let last_genarg = generic_args.generic_args().last()?; - acc.add( AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), "Wrap return type in Result", type_ref.syntax().text_range(), |edit| { + let new_result_ty = result_type(ctx, &core_result, type_ref).clone_for_update(); let body = edit.make_mut(ast::Expr::BlockExpr(body)); let mut exprs_to_wrap = Vec::new(); @@ -81,16 +80,72 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< } let old_result_ty = edit.make_mut(type_ref.clone()); - ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); - if let Some(cap) = ctx.config.snippet_cap { - edit.add_placeholder_snippet(cap, last_genarg); + // Add a placeholder snippet at the first generic argument that doesn't equal the return type. + // This is normally the error type, but that may not be the case when we inserted a type alias. + let args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast); + let error_type_arg = args.and_then(|list| { + list.generic_args().find(|arg| match arg { + ast::GenericArg::TypeArg(_) => arg.syntax().text() != type_ref.syntax().text(), + ast::GenericArg::LifetimeArg(_) => false, + _ => true, + }) + }); + if let Some(error_type_arg) = error_type_arg { + if let Some(cap) = ctx.config.snippet_cap { + edit.add_placeholder_snippet(cap, error_type_arg); + } } }, ) } +fn result_type( + ctx: &AssistContext<'_>, + core_result: &hir::Enum, + ret_type: &ast::Type, +) -> ast::Type { + // Try to find a Result type alias in the current scope (shadowing the default). + let result_path = hir::ModPath::from_segments( + hir::PathKind::Plain, + iter::once(hir::Name::new_symbol_root(hir::sym::Result.clone())), + ); + let alias = ctx.sema.resolve_mod_path(ret_type.syntax(), &result_path).and_then(|def| { + def.filter_map(|def| match def.as_module_def()? { + hir::ModuleDef::TypeAlias(alias) => { + let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?; + (&enum_ty == core_result).then_some(alias) + } + _ => None, + }) + .find_map(|alias| { + let mut inserted_ret_type = false; + let generic_params = alias + .source(ctx.db())? + .value + .generic_param_list()? + .generic_params() + .map(|param| match param { + // Replace the very first type parameter with the functions return type. + ast::GenericParam::TypeParam(_) if !inserted_ret_type => { + inserted_ret_type = true; + ret_type.to_smolstr() + } + ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(), + _ => make::ty_placeholder().to_smolstr(), + }) + .join(", "); + + let name = alias.name(ctx.db()); + let name = name.as_str(); + Some(make::ty(&format!("{name}<{generic_params}>"))) + }) + }); + // If there is no applicable alias in scope use the default Result type. + alias.unwrap_or_else(|| make::ext::ty_result(ret_type.clone(), make::ty_placeholder())) +} + fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { match e { Expr::BreakExpr(break_expr) => { @@ -998,4 +1053,216 @@ fn foo(the_field: u32) -> Result { "#, ); } + + #[test] + fn wrap_return_type_in_local_result_type() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result2 = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result2 = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_imported_local_result_type() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_from_function_body() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +fn foo() -> i3$02 { + type Result = core::result::Result; + 0 +} +"#, + r#" +fn foo() -> Result { + type Result = core::result::Result; + Ok(0) +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_already_using_alias() { + check_assist_not_applicable( + wrap_return_type_in_result, + r#" +//- minicore: result +pub type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_multiple_generics() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + 0 +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + Ok(0) +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result, ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, ()>; + +fn foo() -> Result { + Ok(0) +} + "#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> Result<'_, i32, ${0:_}> { + Ok(0) +} + "#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result, Bar>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, Bar>; + +fn foo() -> Result { + Ok(0) +} + "#, + ); + } } From cd28176025ef5f00ed58680ac4c346c3b0c19020 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sun, 1 Sep 2024 20:58:14 -0400 Subject: [PATCH 0219/1301] Minor code simplification --- .../attrs/allow_attributes_without_reason.rs | 2 +- clippy_utils/src/sugg.rs | 38 ++++++++----------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 4ab97118df1d4..4c7e07478c129 100644 --- a/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -26,7 +26,7 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, attr.span, - format!("`{}` attribute without specifying a reason", name.as_str()), + format!("`{name}` attribute without specifying a reason"), |diag| { diag.help("try adding a reason at the end with `, reason = \"..\"`"); }, diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 0f86d89c980d7..3255c51d009ca 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -180,8 +180,10 @@ impl<'a> Sugg<'a> { ) -> Self { use rustc_ast::ast::RangeLimits; + let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0; + match expr.kind { - _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), + _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet(expr.span)), ast::ExprKind::AddrOf(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) @@ -224,46 +226,38 @@ impl<'a> Sugg<'a> { | ast::ExprKind::While(..) | ast::ExprKind::Await(..) | ast::ExprKind::Err(_) - | ast::ExprKind::Dummy => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), + | ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, - lhs.as_ref().map_or("".into(), |lhs| { - snippet_with_context(cx, lhs.span, ctxt, default, app).0 - }), - rhs.as_ref().map_or("".into(), |rhs| { - snippet_with_context(cx, rhs.span, ctxt, default, app).0 - }), + lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), + rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), ), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( AssocOp::DotDotEq, - lhs.as_ref().map_or("".into(), |lhs| { - snippet_with_context(cx, lhs.span, ctxt, default, app).0 - }), - rhs.as_ref().map_or("".into(), |rhs| { - snippet_with_context(cx, rhs.span, ctxt, default, app).0 - }), + lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), + rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), ), ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( AssocOp::Assign, - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( astbinop2assignop(op), - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::Cast(ref lhs, ref ty) | //FIXME(chenyukang), remove this after type ascription is removed from AST ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( AssocOp::As, - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, ty.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(ty.span), ), } } From 2311e96ca872abf3844fc15b81c20bb3f51809a5 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sat, 27 Jul 2024 17:09:53 -0400 Subject: [PATCH 0220/1301] wip: new syntax tree editor --- .../rust-analyzer/crates/syntax/src/lib.rs | 1 + .../crates/syntax/src/syntax_editor.rs | 333 ++++++++++++++++++ .../syntax/src/syntax_editor/edit_algo.rs | 215 +++++++++++ .../syntax/src/syntax_editor/mapping.rs | 174 +++++++++ 4 files changed, 723 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs create mode 100644 src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs create mode 100644 src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index b68374848b90c..dbbb290b4f107 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -40,6 +40,7 @@ pub mod ast; #[doc(hidden)] pub mod fuzz; pub mod hacks; +pub mod syntax_editor; pub mod ted; pub mod utils; diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs new file mode 100644 index 0000000000000..3ec5abcaeafe3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -0,0 +1,333 @@ +//! Syntax Tree editor +//! +//! Inspired by Roslyn's [`SyntaxEditor`], but is temporarily built upon mutable syntax tree editing. +//! +//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs + +use std::{ + num::NonZeroU32, + sync::atomic::{AtomicU32, Ordering}, +}; + +use rowan::TextRange; +use rustc_hash::FxHashMap; + +use crate::{SyntaxElement, SyntaxNode, SyntaxToken}; + +mod edit_algo; +mod mapping; + +pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; + +#[derive(Debug)] +pub struct SyntaxEditor { + root: SyntaxNode, + changes: Vec, + mappings: SyntaxMapping, + annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, +} + +impl SyntaxEditor { + /// Creates a syntax editor to start editing from `root` + pub fn new(root: SyntaxNode) -> Self { + Self { root, changes: vec![], mappings: SyntaxMapping::new(), annotations: vec![] } + } + + pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { + self.annotations.push((element.syntax_element(), annotation)) + } + + pub fn combine(&mut self, other: SyntaxEditor) { + todo!() + } + + pub fn delete(&mut self, element: impl Element) { + self.changes.push(Change::Replace(element.syntax_element(), None)); + } + + pub fn replace(&mut self, old: impl Element, new: impl Element) { + self.changes.push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); + } + + pub fn finish(self) -> SyntaxEdit { + edit_algo::apply_edits(self) + } +} + +pub struct SyntaxEdit { + root: SyntaxNode, + changed_elements: Vec, + annotations: FxHashMap>, +} + +impl SyntaxEdit { + pub fn root(&self) -> &SyntaxNode { + &self.root + } + + pub fn changed_elements(&self) -> &[SyntaxElement] { + self.changed_elements.as_slice() + } + + pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> Option<&[SyntaxElement]> { + self.annotations.get(&annotation).as_ref().map(|it| it.as_slice()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct SyntaxAnnotation(NonZeroU32); + +impl SyntaxAnnotation { + /// Creates a unique syntax annotation to attach data to. + pub fn new() -> Self { + static COUNTER: AtomicU32 = AtomicU32::new(1); + + // We want the id to be unique across threads, but we don't want to + // tie it to other `SeqCst` operations. + let id = COUNTER.fetch_add(1, Ordering::AcqRel); + + Self(NonZeroU32::new(id).expect("syntax annotation id overflow")) + } +} + +/// Position describing where to insert elements +#[derive(Debug)] +pub struct Position { + repr: PositionRepr, +} + +#[derive(Debug)] +enum PositionRepr { + FirstChild(SyntaxNode), + After(SyntaxElement), +} + +impl Position { + pub fn after(elem: impl Element) -> Position { + let repr = PositionRepr::After(elem.syntax_element()); + Position { repr } + } + + pub fn before(elem: impl Element) -> Position { + let elem = elem.syntax_element(); + let repr = match elem.prev_sibling_or_token() { + Some(it) => PositionRepr::After(it), + None => PositionRepr::FirstChild(elem.parent().unwrap()), + }; + Position { repr } + } + + pub fn first_child_of(node: &(impl Into + Clone)) -> Position { + let repr = PositionRepr::FirstChild(node.clone().into()); + Position { repr } + } + + pub fn last_child_of(node: &(impl Into + Clone)) -> Position { + let node = node.clone().into(); + let repr = match node.last_child_or_token() { + Some(it) => PositionRepr::After(it), + None => PositionRepr::FirstChild(node), + }; + Position { repr } + } +} + +#[derive(Debug)] +enum Change { + /// Represents both a replace single element and a delete element operation. + Replace(SyntaxElement, Option), +} + +impl Change { + fn target_range(&self) -> TextRange { + match self { + Change::Replace(target, _) => target.text_range(), + } + } + + fn target_parent(&self) -> SyntaxNode { + match self { + Change::Replace(target, _) => target.parent().unwrap(), + } + } + + fn change_kind(&self) -> ChangeKind { + match self { + Change::Replace(_, _) => ChangeKind::Replace, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum ChangeKind { + Insert, + // TODO: deal with replace spans + Replace, +} + +/// Utility trait to allow calling syntax editor functions with references or owned +/// nodes. Do not use outside of this module. +pub trait Element { + fn syntax_element(self) -> SyntaxElement; +} + +impl Element for &'_ E { + fn syntax_element(self) -> SyntaxElement { + self.clone().syntax_element() + } +} + +impl Element for SyntaxElement { + fn syntax_element(self) -> SyntaxElement { + self + } +} + +impl Element for SyntaxNode { + fn syntax_element(self) -> SyntaxElement { + self.into() + } +} + +impl Element for SyntaxToken { + fn syntax_element(self) -> SyntaxElement { + self.into() + } +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + use itertools::Itertools; + + use crate::{ + ast::{self, make, HasName}, + AstNode, + }; + + use super::*; + + fn make_ident_pat( + editor: Option<&mut SyntaxEditor>, + ref_: bool, + mut_: bool, + name: ast::Name, + ) -> ast::IdentPat { + let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update(); + + if let Some(editor) = editor { + let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone()); + mapping.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone()); + mapping.finish(editor); + } + + ast + } + + fn make_let_stmt( + editor: Option<&mut SyntaxEditor>, + pattern: ast::Pat, + ty: Option, + initializer: Option, + ) -> ast::LetStmt { + let ast = + make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()).clone_for_update(); + + if let Some(editor) = editor { + let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone()); + mapping.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone()); + if let Some(input) = ty { + mapping.map_node(input.syntax().clone(), ast.ty().unwrap().syntax().clone()); + } + if let Some(input) = initializer { + mapping + .map_node(input.syntax().clone(), ast.initializer().unwrap().syntax().clone()); + } + mapping.finish(editor); + } + + ast + } + + fn make_block_expr( + editor: Option<&mut SyntaxEditor>, + stmts: impl IntoIterator, + tail_expr: Option, + ) -> ast::BlockExpr { + let stmts = stmts.into_iter().collect_vec(); + let input = stmts.iter().map(|it| it.syntax().clone()).collect_vec(); + + let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update(); + + if let Some((editor, stmt_list)) = editor.zip(ast.stmt_list()) { + let mut mapping = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + + mapping.map_children( + input.into_iter(), + stmt_list.statements().map(|it| it.syntax().clone()), + ); + + if let Some((input, output)) = tail_expr.zip(stmt_list.tail_expr()) { + mapping.map_node(input.syntax().clone(), output.syntax().clone()); + } + + mapping.finish(editor); + } + + ast + } + + #[test] + fn it() { + let root = make::match_arm( + [make::wildcard_pat().into()], + None, + make::expr_tuple([ + make::expr_bin_op( + make::expr_literal("2").into(), + ast::BinaryOp::ArithOp(ast::ArithOp::Add), + make::expr_literal("2").into(), + ), + make::expr_literal("true").into(), + ]), + ); + + let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); + let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let name = make::name("var_name"); + let name_ref = make::name_ref("var_name").clone_for_update(); + + let placeholder_snippet = SyntaxAnnotation::new(); + editor.add_annotation(name.syntax(), placeholder_snippet); + editor.add_annotation(name_ref.syntax(), placeholder_snippet); + + let make_ident_pat = make_ident_pat(Some(&mut editor), false, false, name); + let make_let_stmt = make_let_stmt( + Some(&mut editor), + make_ident_pat.into(), + None, + Some(to_replace.clone().into()), + ); + let new_block = make_block_expr( + Some(&mut editor), + [make_let_stmt.into()], + Some(to_wrap.clone().into()), + ); + + // should die: + editor.replace(to_replace.syntax(), name_ref.syntax()); + editor.replace(to_wrap.syntax(), new_block.syntax()); + // editor.replace(to_replace.syntax(), name_ref.syntax()); + + // dbg!(&editor.mappings); + let edit = editor.finish(); + + let expect = expect![]; + expect.assert_eq(&edit.root.to_string()); + assert_eq!(edit.find_annotation(placeholder_snippet).map(|it| it.len()), Some(2)); + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs new file mode 100644 index 0000000000000..f8354c5eb67cd --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -0,0 +1,215 @@ +use std::{collections::VecDeque, ops::RangeInclusive}; + +use rowan::TextRange; + +use crate::{ + syntax_editor::{Change, ChangeKind}, + ted, SyntaxElement, SyntaxNode, SyntaxNodePtr, +}; + +use super::{SyntaxEdit, SyntaxEditor}; + +pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { + // Algorithm overview: + // + // - Sort changes by (range, type) + // - Ensures that parent edits are before child edits + // - Ensures that inserts will be guaranteed to be inserted at the right range + // - Validate changes + // - Checking for invalid changes is easy since the changes will be sorted by range + // - Fixup change targets + // - standalone change? map to original syntax tree + // - dependent change? + // - try to map to parent change (either independent or another dependent) + // - note: need to keep track of a parent change stack, since a change can be a parent of multiple changes + // - Apply changes + // - find changes to apply to real tree by applying nested changes first + // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) + // - Propagate annotations + + let SyntaxEditor { root, mut changes, mappings, annotations } = editor; + + dbg!(("initial: ", &root)); + dbg!(&changes); + + // Sort changes by range then change kind, so that we can: + // - ensure that parent edits are ordered before child edits + // - ensure that inserts will be guaranteed to be inserted at the right range + // - easily check for disjoint replace ranges + changes.sort_by(|a, b| { + a.target_range() + .start() + .cmp(&b.target_range().start()) + .then(a.change_kind().cmp(&b.change_kind())) + }); + + let disjoint_replaces_ranges = changes.iter().zip(changes.iter().skip(1)).all(|(l, r)| { + l.change_kind() == ChangeKind::Replace + && r.change_kind() == ChangeKind::Replace + && (l.target_parent() != r.target_parent() + || l.target_range().intersect(r.target_range()).is_none()) + }); + + if stdx::never!( + !disjoint_replaces_ranges, + "some replace change ranges intersect: {:?}", + changes + ) { + return SyntaxEdit { root, annotations: Default::default(), changed_elements: vec![] }; + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + struct DependentChange { + parent: u32, + child: u32, + } + + // Build change tree + let mut changed_ancestors: VecDeque = VecDeque::new(); + let mut dependent_changes = vec![]; + let mut independent_changes = vec![]; + + for (change_index, change) in changes.iter().enumerate() { + // Check if this change is dependent on another change (i.e. it's contained within another range) + if let Some(index) = changed_ancestors + .iter() + .rev() + .position(|ancestor| ancestor.affected_range().contains_range(change.target_range())) + { + // Pop off any ancestors that aren't applicable + changed_ancestors.drain((index + 1)..); + + let ancestor = &changed_ancestors[index]; + + dependent_changes.push(DependentChange { + parent: ancestor.change_index as u32, + child: change_index as u32, + }); + } else { + // This change is independent of any other change + + // Drain the changed ancestors since we're no longer in a set of dependent changes + changed_ancestors.drain(..); + + independent_changes.push(change_index as u32); + } + + // Add to changed ancestors, if applicable + match change { + Change::Replace(target, _) => { + changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) + } + } + } + + dbg!(("before: ", &changes, &dependent_changes, &independent_changes)); + + // Map change targets to the correct syntax nodes + let tree_mutator = TreeMutator::new(&root); + + for index in independent_changes { + match &mut changes[index as usize] { + Change::Replace(target, _) => { + *target = tree_mutator.make_element_mut(target); + } + } + } + + for DependentChange { parent, child } in dependent_changes.into_iter() { + let (input_ancestor, output_ancestor) = match &changes[parent as usize] { + // insert? unreachable + Change::Replace(target, Some(new_target)) => { + (to_owning_node(target), to_owning_node(new_target)) + } + Change::Replace(_, None) => continue, // silently drop outdated change + }; + + match &mut changes[child as usize] { + Change::Replace(target, _) => { + *target = mappings.upmap_child_element(target, &input_ancestor, output_ancestor) + } + } + } + + dbg!(("after: ", &changes)); + + // Apply changes + for change in changes { + match change { + Change::Replace(target, None) => ted::remove(target), + Change::Replace(target, Some(new_target)) => ted::replace(target, new_target), + } + } + + dbg!(("modified:", tree_mutator.mutable_clone)); + + todo!("draw the rest of the owl") +} + +fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { + match element { + SyntaxElement::Node(node) => node.clone(), + SyntaxElement::Token(token) => token.parent().unwrap().clone(), + } +} + +struct ChangedAncestor { + kind: ChangedAncestorKind, + change_index: usize, +} + +enum ChangedAncestorKind { + Single { node: SyntaxNode }, + Range { changed_nodes: RangeInclusive, in_parent: SyntaxNode }, +} + +impl ChangedAncestor { + fn single(element: &SyntaxElement, change_index: usize) -> Self { + let kind = match element { + SyntaxElement::Node(node) => ChangedAncestorKind::Single { node: node.clone() }, + SyntaxElement::Token(token) => { + ChangedAncestorKind::Single { node: token.parent().unwrap() } + } + }; + + Self { kind, change_index } + } + + fn affected_range(&self) -> TextRange { + match &self.kind { + ChangedAncestorKind::Single { node } => node.text_range(), + ChangedAncestorKind::Range { changed_nodes, in_parent: _ } => TextRange::new( + changed_nodes.start().text_range().start(), + changed_nodes.end().text_range().end(), + ), + } + } +} + +struct TreeMutator { + immutable: SyntaxNode, + mutable_clone: SyntaxNode, +} + +impl TreeMutator { + fn new(immutable: &SyntaxNode) -> TreeMutator { + let immutable = immutable.clone(); + let mutable_clone = immutable.clone_for_update(); + TreeMutator { immutable, mutable_clone } + } + + fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { + match element { + SyntaxElement::Node(node) => SyntaxElement::Node(self.make_syntax_mut(&node)), + SyntaxElement::Token(token) => { + let parent = self.make_syntax_mut(&token.parent().unwrap()); + parent.children_with_tokens().nth(token.index()).unwrap() + } + } + } + + fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { + let ptr = SyntaxNodePtr::new(node); + ptr.to_node(&self.mutable_clone) + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs new file mode 100644 index 0000000000000..11c7b395b37c9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -0,0 +1,174 @@ +use itertools::Itertools; +use rustc_hash::FxHashMap; + +use crate::{SyntaxElement, SyntaxNode}; + +use super::SyntaxEditor; + +#[derive(Debug, Default)] +pub struct SyntaxMapping { + // important information to keep track of: + // node -> node + // token -> token (implicit in mappings) + // input parent -> output parent (for deep lookups) + + // mappings -> parents + entry_parents: Vec, + node_mappings: FxHashMap, +} + +impl SyntaxMapping { + pub fn new() -> Self { + Self::default() + } + + pub fn upmap_child_element( + &self, + child: &SyntaxElement, + input_ancestor: &SyntaxNode, + output_ancestor: SyntaxNode, + ) -> SyntaxElement { + match child { + SyntaxElement::Node(node) => { + SyntaxElement::Node(self.upmap_child(node, input_ancestor, output_ancestor)) + } + SyntaxElement::Token(token) => { + let upmap_parent = + self.upmap_child(&token.parent().unwrap(), input_ancestor, output_ancestor); + + let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); + debug_assert!( + element.as_token().is_some_and(|it| it.kind() == token.kind()), + "token upmapping mapped to the wrong node ({token:?} -> {element:?})" + ); + + element + } + } + } + + pub fn upmap_child( + &self, + child: &SyntaxNode, + input_ancestor: &SyntaxNode, + output_ancestor: SyntaxNode, + ) -> SyntaxNode { + debug_assert!(child.ancestors().any(|ancestor| &ancestor == input_ancestor)); + + // Build a list mapping up to the first mappable ancestor + let to_first_upmap = + std::iter::successors(Some((child.index(), child.clone())), |(_, current)| { + let parent = current.parent().unwrap(); + + if &parent == input_ancestor { + return None; + } + + Some((parent.index(), parent)) + }) + .map(|(i, _)| i) + .collect::>(); + + // Progressively up-map the input ancestor until we get to the output ancestor + let to_output_ancestor = if input_ancestor != &output_ancestor { + std::iter::successors(Some((input_ancestor.index(), self.upmap_node(input_ancestor).unwrap_or_else(|| input_ancestor.clone()))), |(_, current)| { + let Some(parent) = current.parent() else { + unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}") + }; + + if &parent == &output_ancestor { + return None; + } + + if let Some(next) = self.upmap_node(&parent) { + Some((parent.index(), next)) + } else { + Some((parent.index(), parent)) + } + }).map(|(i, _)| i).collect::>() + } else { + vec![] + }; + + let to_map_down = + to_output_ancestor.into_iter().rev().chain(to_first_upmap.into_iter().rev()); + + let mut target = output_ancestor; + + for index in to_map_down { + target = target + .children_with_tokens() + .nth(index) + .and_then(|it| it.into_node()) + .expect("yep"); + } + + debug_assert_eq!(child.kind(), target.kind()); + + target + } + + pub fn upmap_node(&self, input: &SyntaxNode) -> Option { + let (parent, child_slot) = self.node_mappings.get(input)?; + + let output = self.entry_parents[*parent as usize] + .children_with_tokens() + .nth(*child_slot as usize) + .and_then(SyntaxElement::into_node) + .unwrap(); + + debug_assert_eq!(input.kind(), output.kind()); + Some(output) + } + + fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) { + let SyntaxMappingBuilder { parent_node, node_mappings } = syntax_mapping; + + let parent_entry: u32 = self.entry_parents.len() as u32; + self.entry_parents.push(parent_node); + + let node_entries = + node_mappings.into_iter().map(|(node, slot)| (node, (parent_entry, slot))); + + self.node_mappings.extend(node_entries); + } +} + +#[derive(Debug)] +pub struct SyntaxMappingBuilder { + parent_node: SyntaxNode, + node_mappings: Vec<(SyntaxNode, u32)>, +} + +impl SyntaxMappingBuilder { + pub fn new(parent_node: SyntaxNode) -> Self { + Self { parent_node, node_mappings: vec![] } + } + + pub fn map_node(&mut self, input: SyntaxNode, output: SyntaxNode) { + debug_assert_eq!(output.parent().as_ref(), Some(&self.parent_node)); + self.node_mappings.push((input, output.index() as u32)); + } + + pub fn map_children( + &mut self, + input: impl Iterator, + output: impl Iterator, + ) { + for pairs in input.zip_longest(output) { + let (input, output) = match pairs { + itertools::EitherOrBoth::Both(l, r) => (l, r), + itertools::EitherOrBoth::Left(_) => { + unreachable!("mapping more input nodes than there are output nodes") + } + itertools::EitherOrBoth::Right(_) => break, + }; + + self.map_node(input, output); + } + } + + pub fn finish(self, editor: &mut SyntaxEditor) { + editor.mappings.add_mapping(self); + } +} From 5e33650b7277543d793a9abe69b298e73b7531e7 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Sun, 1 Sep 2024 23:48:14 -0400 Subject: [PATCH 0221/1301] elaborate SyntaxEdit comments --- .../crates/syntax/src/syntax_editor.rs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 3ec5abcaeafe3..240bba7b4838b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -61,16 +61,28 @@ pub struct SyntaxEdit { } impl SyntaxEdit { + /// Root of the modified syntax tree pub fn root(&self) -> &SyntaxNode { &self.root } + /// Which syntax elements in the modified syntax tree were modified as part + /// of the edit. + /// + /// Note that for syntax nodes, only the upper-most parent of a set of + /// changes is included, not any child elements that may have been modified. pub fn changed_elements(&self) -> &[SyntaxElement] { self.changed_elements.as_slice() } - pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> Option<&[SyntaxElement]> { - self.annotations.get(&annotation).as_ref().map(|it| it.as_slice()) + /// Finds which syntax elements have been annotated with the given + /// annotation. + /// + /// Note that an annotation might not appear in the modified syntax tree if + /// the syntax elements that were annotated did not make it into the final + /// syntax tree. + pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> &[SyntaxElement] { + self.annotations.get(&annotation).as_ref().map_or(&[], |it| it.as_slice()) } } @@ -83,9 +95,8 @@ impl SyntaxAnnotation { pub fn new() -> Self { static COUNTER: AtomicU32 = AtomicU32::new(1); - // We want the id to be unique across threads, but we don't want to - // tie it to other `SeqCst` operations. - let id = COUNTER.fetch_add(1, Ordering::AcqRel); + // Only consistency within a thread matters, as SyntaxElements are !Send + let id = COUNTER.fetch_add(1, Ordering::Relaxed); Self(NonZeroU32::new(id).expect("syntax annotation id overflow")) } @@ -328,6 +339,6 @@ mod tests { let expect = expect![]; expect.assert_eq(&edit.root.to_string()); - assert_eq!(edit.find_annotation(placeholder_snippet).map(|it| it.len()), Some(2)); + assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); } } From 2094a2b117fa0a00a3dcb0a239954b84eeda031c Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 00:01:24 -0400 Subject: [PATCH 0222/1301] handle merging two syntax editors together --- .../crates/syntax/src/syntax_editor.rs | 15 ++++++-- .../syntax/src/syntax_editor/mapping.rs | 38 +++++++++++++------ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 240bba7b4838b..42373eba827d2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -37,8 +37,17 @@ impl SyntaxEditor { self.annotations.push((element.syntax_element(), annotation)) } - pub fn combine(&mut self, other: SyntaxEditor) { - todo!() + pub fn merge(&mut self, mut other: SyntaxEditor) { + debug_assert!( + self.root == other.root || other.root.ancestors().any(|node| node == self.root), + "{:?} is not in the same tree as {:?}", + other.root, + self.root + ); + + self.changes.append(&mut other.changes); + self.mappings.merge(other.mappings); + self.annotations.append(&mut other.annotations); } pub fn delete(&mut self, element: impl Element) { @@ -290,7 +299,7 @@ mod tests { } #[test] - fn it() { + fn basic_usage() { let root = make::match_arm( [make::wildcard_pat().into()], None, diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 11c7b395b37c9..8a79f7e186eda 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -14,7 +14,7 @@ pub struct SyntaxMapping { // mappings -> parents entry_parents: Vec, - node_mappings: FxHashMap, + node_mappings: FxHashMap, } impl SyntaxMapping { @@ -80,11 +80,10 @@ impl SyntaxMapping { return None; } - if let Some(next) = self.upmap_node(&parent) { - Some((parent.index(), next)) - } else { - Some((parent.index(), parent)) - } + Some((parent.index(), match self.upmap_node(&parent) { + Some(next) => next, + None => parent + })) }).map(|(i, _)| i).collect::>() } else { vec![] @@ -100,7 +99,7 @@ impl SyntaxMapping { .children_with_tokens() .nth(index) .and_then(|it| it.into_node()) - .expect("yep"); + .expect("equivalent ancestor node should be present in target tree"); } debug_assert_eq!(child.kind(), target.kind()); @@ -109,7 +108,7 @@ impl SyntaxMapping { } pub fn upmap_node(&self, input: &SyntaxNode) -> Option { - let (parent, child_slot) = self.node_mappings.get(input)?; + let MappingEntry { parent, child_slot } = self.node_mappings.get(input)?; let output = self.entry_parents[*parent as usize] .children_with_tokens() @@ -121,14 +120,25 @@ impl SyntaxMapping { Some(output) } + pub fn merge(&mut self, mut other: SyntaxMapping) { + // Remap other's entry parents to be after the current list of entry parents + let remap_base: u32 = self.entry_parents.len().try_into().unwrap(); + + self.entry_parents.append(&mut other.entry_parents); + self.node_mappings.extend(other.node_mappings.into_iter().map(|(node, entry)| { + (node, MappingEntry { parent: entry.parent + remap_base, ..entry }) + })); + } + fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) { let SyntaxMappingBuilder { parent_node, node_mappings } = syntax_mapping; - let parent_entry: u32 = self.entry_parents.len() as u32; + let parent_entry: u32 = self.entry_parents.len().try_into().unwrap(); self.entry_parents.push(parent_node); - let node_entries = - node_mappings.into_iter().map(|(node, slot)| (node, (parent_entry, slot))); + let node_entries = node_mappings + .into_iter() + .map(|(node, slot)| (node, MappingEntry { parent: parent_entry, child_slot: slot })); self.node_mappings.extend(node_entries); } @@ -172,3 +182,9 @@ impl SyntaxMappingBuilder { editor.mappings.add_mapping(self); } } + +#[derive(Debug, Clone, Copy)] +struct MappingEntry { + parent: u32, + child_slot: u32, +} From 59b42b92e7fe506a083e194aee2281525f8199c4 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Sep 2024 07:54:55 +0200 Subject: [PATCH 0223/1301] chore: Fix some typos --- src/debuginfo.rs | 4 ++-- src/lib.rs | 2 +- tools/generate_intrinsics.py | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/debuginfo.rs b/src/debuginfo.rs index 3d9ea278a6399..876386428835f 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -50,7 +50,7 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { } /// Generate the `debug_context` in an MIR Body. -/// # Souce of Origin +/// # Source of Origin /// Copied from `create_scope_map.rs` of rustc_codegen_llvm fn compute_mir_scopes<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, @@ -85,7 +85,7 @@ fn compute_mir_scopes<'gcc, 'tcx>( /// Update the `debug_context`, adding new scope to it, /// if it's not added as is denoted in `instantiated`. /// -/// # Souce of Origin +/// # Source of Origin /// Copied from `create_scope_map.rs` of rustc_codegen_llvm /// FIXME(tempdragon/?): Add Scope Support Here. fn make_mir_scope<'gcc, 'tcx>( diff --git a/src/lib.rs b/src/lib.rs index 1132b0cd2f5ab..c11a183acde52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,7 +360,7 @@ impl Deref for SyncContext { unsafe impl Send for SyncContext {} // FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". -// TODO: disable it here by returing false in CodegenBackend::supports_parallel(). +// TODO: disable it here by returning false in CodegenBackend::supports_parallel(). unsafe impl Sync for SyncContext {} impl WriteBackendMethods for GccCodegenBackend { diff --git a/tools/generate_intrinsics.py b/tools/generate_intrinsics.py index 90fb7bfad27c3..8efed3e43af85 100644 --- a/tools/generate_intrinsics.py +++ b/tools/generate_intrinsics.py @@ -45,7 +45,7 @@ def convert_to_string(content): return content -def extract_instrinsics_from_llvm(llvm_path, intrinsics): +def extract_intrinsics_from_llvm(llvm_path, intrinsics): command = ["llvm-tblgen", "llvm/IR/Intrinsics.td"] cwd = os.path.join(llvm_path, "llvm/include") print("=> Running command `{}` from `{}`".format(command, cwd)) @@ -88,7 +88,7 @@ def append_translation(json_data, p, array): append_intrinsic(array, content[1], content[3]) -def extract_instrinsics_from_llvmint(llvmint, intrinsics): +def extract_intrinsics_from_llvmint(llvmint, intrinsics): archs = [ "AMDGPU", "aarch64", @@ -152,9 +152,9 @@ def update_intrinsics(llvm_path, llvmint, llvmint2): intrinsics_llvmint = {} all_intrinsics = {} - extract_instrinsics_from_llvm(llvm_path, intrinsics_llvm) - extract_instrinsics_from_llvmint(llvmint, intrinsics_llvmint) - extract_instrinsics_from_llvmint(llvmint2, intrinsics_llvmint) + extract_intrinsics_from_llvm(llvm_path, intrinsics_llvm) + extract_intrinsics_from_llvmint(llvmint, intrinsics_llvmint) + extract_intrinsics_from_llvmint(llvmint2, intrinsics_llvmint) intrinsics = {} # We give priority to translations from LLVM over the ones from llvmint. From ddb1e41e5034ccc519873c93e7f0efd8b85e6b90 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Mon, 2 Sep 2024 18:21:19 +0800 Subject: [PATCH 0224/1301] chore: fix some comments Signed-off-by: cuishuang --- .../rust-analyzer/crates/hir/src/semantics/source_to_def.rs | 2 +- src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs | 2 +- src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs | 2 +- src/tools/rust-analyzer/crates/span/src/hygiene.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 09df639d4a810..edeb19030ac00 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -281,7 +281,7 @@ impl SourceToDefCtx<'_, '_> { let (body, source_map) = self.db.body_with_source_map(container); let src = src.cloned().map(ast::Pat::from); let pat_id = source_map.node_pat(src.as_ref())?; - // the pattern could resolve to a constant, verify that that is not the case + // the pattern could resolve to a constant, verify that this is not the case if let crate::Pat::Bind { id, .. } = body[pat_id] { Some((container, id)) } else { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index f48daba54e7b7..60070b8850f0d 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -15,7 +15,7 @@ macro_rules! define_symbols { (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => { // Ideally we would be emitting `const` here, but then we no longer have stable addresses // which is what we are relying on for equality! In the future if consts can refer to - // statics we should swap these for `const`s and have the the string literal being pointed + // statics we should swap these for `const`s and have the string literal being pointed // to be statics to refer to such that their address is stable. $( pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index b92713129df07..e9198977dea50 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -142,7 +142,7 @@ impl flags::Scip { let mut symbol_roles = Default::default(); if let Some(def) = token.definition { - // if the the range of the def and the range of the token are the same, this must be the definition. + // if the range of the def and the range of the token are the same, this must be the definition. // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988 if def.file_id == file_id && def.range == text_range { symbol_roles |= scip_types::SymbolRole::Definition as i32; diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 874480c59fbf3..cb9c092f5fcd0 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -81,7 +81,7 @@ pub struct SyntaxContextData { /// Invariant: Only [`SyntaxContextId::ROOT`] has a [`None`] outer expansion. // FIXME: The None case needs to encode the context crate id. We can encode that as the MSB of // MacroCallId is reserved anyways so we can do bit tagging here just fine. - // The bigger issue is that that will cause interning to now create completely separate chains + // The bigger issue is that this will cause interning to now create completely separate chains // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. pub outer_expn: Option, pub outer_transparency: Transparency, From f5ccde6d458507e4e17141667ffdae5abbf982a2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 2 Sep 2024 11:49:11 +0200 Subject: [PATCH 0225/1301] Simplify CompletionRelevance --- .../src/completions/item_list/trait_impl.rs | 6 +- .../crates/ide-completion/src/item.rs | 79 +++++++++++-------- .../crates/ide-completion/src/render.rs | 73 +++++++---------- .../ide-completion/src/render/function.rs | 17 ++-- 4 files changed, 85 insertions(+), 90 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index e93bb8db716f1..b5bc5533c79c8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -221,7 +221,7 @@ fn add_function_impl_( let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition); item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition))) .set_documentation(func.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); if let Some(source) = ctx.sema.source(func) { if let Some(transformed_fn) = @@ -366,7 +366,7 @@ fn add_type_alias_impl( CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label, ctx.edition); item.lookup_by(format!("type {alias_name}")) .set_documentation(type_alias.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); if let Some(source) = ctx.sema.source(type_alias) { let assoc_item = ast::AssocItem::TypeAlias(source.value); @@ -440,7 +440,7 @@ fn add_const_impl( item.lookup_by(format_smolstr!("const {const_name}")) .set_documentation(const_.docs(ctx.db)) .set_relevance(CompletionRelevance { - is_item_from_trait: true, + exact_name_match: true, ..Default::default() }); match ctx.config.snippet_cap { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index a30a115da1f1b..df30750b8fec6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -19,8 +19,10 @@ use crate::{ }; /// `CompletionItem` describes a single completion entity which expands to 1 or more entries in the -/// editor pop-up. It is basically a POD with various properties. To construct a -/// [`CompletionItem`], use [`Builder::new`] method and the [`Builder`] struct. +/// editor pop-up. +/// +/// It is basically a POD with various properties. To construct a [`CompletionItem`], +/// use [`Builder::new`] method and the [`Builder`] struct. #[derive(Clone)] #[non_exhaustive] pub struct CompletionItem { @@ -129,7 +131,8 @@ impl fmt::Debug for CompletionItem { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct CompletionRelevance { - /// This is set in cases like these: + /// This is set when the identifier being completed matches up with the name that is expected, + /// like in a function argument. /// /// ``` /// fn f(spam: String) {} @@ -139,9 +142,9 @@ pub struct CompletionRelevance { /// } /// ``` pub exact_name_match: bool, - /// See CompletionRelevanceTypeMatch doc comments for cases where this is set. + /// See [`CompletionRelevanceTypeMatch`]. pub type_match: Option, - /// This is set in cases like these: + /// Set for local variables. /// /// ``` /// fn foo(a: u32) { @@ -150,25 +153,26 @@ pub struct CompletionRelevance { /// } /// ``` pub is_local: bool, - /// This is set when trait items are completed in an impl of that trait. - pub is_item_from_trait: bool, - /// This is set for when trait items are from traits with `#[doc(notable_trait)]` - pub is_item_from_notable_trait: bool, - /// This is set when an import is suggested whose name is already imported. + /// Populated when the completion item comes from a trait (impl). + pub trait_: Option, + /// This is set when an import is suggested in a use item whose name is already imported. pub is_name_already_imported: bool, /// This is set for completions that will insert a `use` item. pub requires_import: bool, - /// Set for method completions of the `core::ops` and `core::cmp` family. - pub is_op_method: bool, /// Set for item completions that are private but in the workspace. pub is_private_editable: bool, /// Set for postfix snippet item completions pub postfix_match: Option, - /// This is set for type inference results - pub is_definite: bool, /// This is set for items that are function (associated or method) pub function: Option, } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct CompletionRelevanceTraitInfo { + /// The trait this item is from is a `#[doc(notable_trait)]` + pub notable_trait: bool, + /// Set for method completions of the `core::ops` and `core::cmp` family. + pub is_op_method: bool, +} #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum CompletionRelevanceTypeMatch { @@ -182,7 +186,7 @@ pub enum CompletionRelevanceTypeMatch { /// } /// ``` CouldUnify, - /// This is set in cases like these: + /// This is set in cases where the type matches the expected type, like: /// /// ``` /// fn f(spam: String) {} @@ -243,14 +247,11 @@ impl CompletionRelevance { exact_name_match, type_match, is_local, - is_item_from_trait, is_name_already_imported, requires_import, - is_op_method, is_private_editable, postfix_match, - is_definite, - is_item_from_notable_trait, + trait_, function, } = self; @@ -258,8 +259,17 @@ impl CompletionRelevance { if !is_private_editable { score += 1; } - // lower rank trait op methods - if !is_op_method { + + if let Some(trait_) = trait_ { + if trait_.notable_trait { + score += 1; + } + // lower rank trait op methods + if !trait_.is_op_method { + score += 10; + } + } else { + // lower rank trait op methods score += 10; } // lower rank for conflicting import names @@ -287,16 +297,6 @@ impl CompletionRelevance { if is_local { score += 1; } - if is_item_from_trait { - score += 1; - } - if is_item_from_notable_trait { - score += 1; - } - if is_definite { - score += 10; - } - score += function .map(|asf| { let mut fn_score = match asf.return_type { @@ -701,8 +701,21 @@ mod tests { // that any items in the same vec have the same score. let expected_relevance_order = vec![ vec![], - vec![Cr { is_op_method: true, is_private_editable: true, ..default }], - vec![Cr { is_op_method: true, ..default }], + vec![Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + is_private_editable: true, + ..default + }], + vec![Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + ..default + }], vec![Cr { postfix_match: Some(CompletionRelevancePostfixMatch::NonExact), ..default }], vec![Cr { is_private_editable: true, ..default }], vec![default], diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index ff5ec3a29f3e1..2bec2eb87bc4b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -249,7 +249,11 @@ pub(crate) fn render_type_inference( ty_string, ctx.edition, ); - builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); + builder.set_relevance(CompletionRelevance { + type_match: Some(CompletionRelevanceTypeMatch::Exact), + exact_name_match: true, + ..Default::default() + }); builder.build(ctx.db) } @@ -756,7 +760,7 @@ mod tests { relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet", ), - (relevance.is_op_method, "op_method"), + (relevance.trait_.map_or(false, |it| it.is_op_method), "op_method"), (relevance.requires_import, "requires_import"), ] .into_iter() @@ -1272,14 +1276,11 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, trigger_call_info: true, @@ -1300,14 +1301,11 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, trigger_call_info: true, @@ -1380,14 +1378,11 @@ fn foo() { A { the$0 } } CouldUnify, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -1431,14 +1426,11 @@ impl S { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -1558,14 +1550,11 @@ fn foo(s: S) { s.$0 } exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -1774,14 +1763,11 @@ fn f() -> i32 { Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -2492,14 +2478,11 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -2574,14 +2557,11 @@ fn foo() { Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -2624,14 +2604,11 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: false, @@ -2996,14 +2973,16 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: true, + trait_: Some( + CompletionRelevanceTraitInfo { + notable_trait: true, + is_op_method: false, + }, + ), is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -3021,14 +3000,16 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: true, + trait_: Some( + CompletionRelevanceTraitInfo { + notable_trait: true, + is_op_method: false, + }, + ), is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 74092b53f523f..29820cb2036de 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -10,7 +10,7 @@ use crate::{ context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind}, item::{ Builder, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevanceFn, - CompletionRelevanceReturnType, + CompletionRelevanceReturnType, CompletionRelevanceTraitInfo, }, render::{ compute_exact_name_match, compute_ref_match, compute_type_match, match_types, RenderContext, @@ -88,11 +88,13 @@ fn render( let ret_type = func.ret_type(db); let assoc_item = func.as_assoc_item(db); - let trait_ = assoc_item.and_then(|trait_| trait_.container_or_implemented_trait(db)); - let is_op_method = trait_.map_or(false, |trait_| completion.is_ops_trait(trait_)); - - let is_item_from_notable_trait = - trait_.map_or(false, |trait_| completion.is_doc_notable_trait(trait_)); + let trait_info = + assoc_item.and_then(|trait_| trait_.container_or_implemented_trait(db)).map(|trait_| { + CompletionRelevanceTraitInfo { + notable_trait: completion.is_doc_notable_trait(trait_), + is_op_method: completion.is_ops_trait(trait_), + } + }); let (has_dot_receiver, has_call_parens, cap) = match func_kind { FuncKind::Function(&PathCompletionCtx { @@ -129,8 +131,7 @@ fn render( }, exact_name_match: compute_exact_name_match(completion, &call), function, - is_op_method, - is_item_from_notable_trait, + trait_: trait_info, ..ctx.completion_relevance() }); From 3414a9e94fb23d13f5409d51f6a129c771af96b6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 2 Sep 2024 12:59:51 +0200 Subject: [PATCH 0226/1301] Adjust completions scoring --- .../crates/ide-completion/src/item.rs | 96 +++++++++---------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index df30750b8fec6..f8c8b12bd25af 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -242,7 +242,7 @@ impl CompletionRelevance { /// See is_relevant if you need to make some judgement about score /// in an absolute sense. pub fn score(self) -> u32 { - let mut score = 0; + let mut score = !0 / 2; let CompletionRelevance { exact_name_match, type_match, @@ -255,73 +255,69 @@ impl CompletionRelevance { function, } = self; + // only applicable for completions within use items + // lower rank for conflicting import names + if is_name_already_imported { + score -= 1; + } + // slightly prefer locals + if is_local { + score += 1; + } + // lower rank private things if !is_private_editable { score += 1; } if let Some(trait_) = trait_ { - if trait_.notable_trait { - score += 1; + // lower rank trait methods unless its notable + if !trait_.notable_trait { + score -= 5; } // lower rank trait op methods - if !trait_.is_op_method { - score += 10; + if trait_.is_op_method { + score -= 5; } - } else { - // lower rank trait op methods - score += 10; } - // lower rank for conflicting import names - if !is_name_already_imported { - score += 1; - } - // lower rank for items that don't need an import - if !requires_import { - score += 1; + // lower rank for items that need an import + if requires_import { + score -= 1; } if exact_name_match { - score += 10; + score += 20; } - score += match postfix_match { - Some(CompletionRelevancePostfixMatch::Exact) => 100, - Some(CompletionRelevancePostfixMatch::NonExact) => 0, - None => 3, + match postfix_match { + Some(CompletionRelevancePostfixMatch::Exact) => score += 100, + Some(CompletionRelevancePostfixMatch::NonExact) => score -= 5, + None => (), }; score += match type_match { - Some(CompletionRelevanceTypeMatch::Exact) => 8, - Some(CompletionRelevanceTypeMatch::CouldUnify) => 3, + Some(CompletionRelevanceTypeMatch::Exact) => 18, + Some(CompletionRelevanceTypeMatch::CouldUnify) => 5, None => 0, }; - // slightly prefer locals - if is_local { - score += 1; - } - score += function - .map(|asf| { - let mut fn_score = match asf.return_type { - CompletionRelevanceReturnType::DirectConstructor => 15, - CompletionRelevanceReturnType::Builder => 10, - CompletionRelevanceReturnType::Constructor => 5, - CompletionRelevanceReturnType::Other => 0, - }; - - // When a fn is bumped due to return type: - // Bump Constructor or Builder methods with no arguments, - // over them than with self arguments - if fn_score > 0 { - if !asf.has_params { - // bump associated functions - fn_score += 1; - } else if asf.has_self_param { - // downgrade methods (below Constructor) - fn_score = 1; - } - } + if let Some(function) = function { + let mut fn_score = match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 15, + CompletionRelevanceReturnType::Builder => 10, + CompletionRelevanceReturnType::Constructor => 5, + CompletionRelevanceReturnType::Other => 0u32, + }; + + // When a fn is bumped due to return type: + // Bump Constructor or Builder methods with no arguments, + // over them than with self arguments + if function.has_params { + // bump associated functions + fn_score = fn_score.saturating_sub(1); + } else if function.has_self_param { + // downgrade methods (below Constructor) + fn_score = fn_score.min(1); + } - fn_score - }) - .unwrap_or_default(); + score += fn_score; + }; score } From 011a1fc41f22a2b07062bb8b94656dd3acc756fa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 2 Sep 2024 16:51:18 +0200 Subject: [PATCH 0227/1301] fix: lifetime hint panic in non generic defs --- .../crates/ide/src/inlay_hints/lifetime.rs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs index 653e3a6ef1df9..2163c959b18ae 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -389,7 +389,9 @@ fn hints_( } (None, allocated_lifetimes) => on_missing_gpl(acc, allocated_lifetimes), } - ctx.lifetime_stacks.last_mut().unwrap().extend(allocated_lifetimes); + if let Some(stack) = ctx.lifetime_stacks.last_mut() { + stack.extend(allocated_lifetimes); + } Some(()) } @@ -542,6 +544,22 @@ fn fn_trait(a: &impl Fn(&()) -> &()) {} // ^^ for<'1> //^'1 // ^'1 +"#, + ); + } + + #[test] + fn hints_in_non_gen_defs() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::Always, + ..TEST_CONFIG + }, + r#" +const _: fn(&()) -> &(); + //^^ for<'0> + //^'0 + //^'0 "#, ); } From 42ed30f0c6a5f175fc6591e5c9f028b1110b9f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 2 Sep 2024 20:04:35 +0300 Subject: [PATCH 0228/1301] Avoid Option::is_none_or for a while --- src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index e5c493ac56e1b..968a828e9dfef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -287,7 +287,7 @@ pub(crate) fn const_eval_discriminant_variant( } let repr = db.enum_data(loc.parent).repr; - let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); + let is_signed = IsNoneOr::is_none_or(repr.and_then(|repr| repr.int), |int| int.is_signed()); let mir_body = db.monomorphized_mir_body( def, From 2f8ef4c8878c1505b71973275532ef38288554be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 2 Sep 2024 20:05:35 +0300 Subject: [PATCH 0229/1301] Merge some strings --- .../ide-assists/src/handlers/toggle_macro_delimiter.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index eedb2ea3b9ada..e452b5f77870c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -64,12 +64,9 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) acc.add( AssistId("toggle_macro_delimiter", AssistKind::Refactor), match token { - MacroDelims::LPar => "Replace delimiters with braces", - MacroDelims::RPar => "Replace delimiters with braces", - MacroDelims::LBra => "Replace delimiters with parentheses", - MacroDelims::RBra => "Replace delimiters with parentheses", - MacroDelims::LCur => "Replace delimiters with brackets", - MacroDelims::RCur => "Replace delimiters with brackets", + MacroDelims::LPar | MacroDelims::RPar => "Replace delimiters with braces", + MacroDelims::LBra | MacroDelims::RBra => "Replace delimiters with parentheses", + MacroDelims::LCur | MacroDelims::RCur => "Replace delimiters with brackets", }, token_tree.syntax().text_range(), |builder| { From 111c690426ac42e3f8c2db011fc74dfbb91d81d3 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 29 Aug 2024 00:12:16 +0900 Subject: [PATCH 0230/1301] feat: Implement cast typechecks --- .../crates/hir-def/src/data/adt.rs | 22 + .../crates/hir-ty/src/consteval/tests.rs | 21 +- .../hir-ty/src/consteval/tests/intrinsics.rs | 1 + .../rust-analyzer/crates/hir-ty/src/infer.rs | 32 +- .../crates/hir-ty/src/infer/cast.rs | 417 ++++++- .../crates/hir-ty/src/infer/expr.rs | 7 +- .../crates/hir-ty/src/infer/unify.rs | 30 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 1 + .../rust-analyzer/crates/hir-ty/src/mir.rs | 4 +- .../crates/hir-ty/src/mir/eval.rs | 3 +- .../crates/hir-ty/src/mir/eval/tests.rs | 3 +- .../crates/hir-ty/src/mir/lower.rs | 79 +- .../hir-ty/src/tests/method_resolution.rs | 1 + .../crates/hir-ty/src/tests/regression.rs | 2 + .../crates/hir-ty/src/tests/simple.rs | 1 + .../crates/hir/src/diagnostics.rs | 30 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../ide-assists/src/handlers/inline_call.rs | 1 + .../src/handlers/inline_local_variable.rs | 5 +- .../src/handlers/invalid_cast.rs | 1007 +++++++++++++++++ .../src/handlers/missing_unsafe.rs | 19 +- .../src/handlers/moved_out_of_ref.rs | 12 + .../crates/ide-diagnostics/src/lib.rs | 3 + .../crates/ide/src/inlay_hints/adjustment.rs | 3 + .../ide/src/syntax_highlighting/tests.rs | 1 + 25 files changed, 1614 insertions(+), 93 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index a70710e565c27..ba54451e594f7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -14,6 +14,7 @@ use triomphe::Arc; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, + hir::Expr, item_tree::{ AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId, }, @@ -317,6 +318,27 @@ impl EnumData { _ => IntegerType::Pointer(true), } } + + // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) + pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { + self.variants.iter().all(|(v, _)| { + // The condition check order is slightly modified from rustc + // to improve performance by early returning with relatively fast checks + let variant = &db.enum_variant_data(*v).variant_data; + if !variant.fields().is_empty() { + return false; + } + // The outer if condition is whether this variant has const ctor or not + if !matches!(variant.kind(), StructKind::Unit) { + let body = db.body((*v).into()); + // A variant with explicit discriminant + if body.exprs[body.body_expr] != Expr::Missing { + return false; + } + } + true + }) + } } impl EnumVariantData { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index c4cbaaa30175b..203e21302cdda 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -186,7 +186,13 @@ fn floating_point() { #[test] fn casts() { - check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12); + check_number( + r#" + //- minicore: sized + const GOAL: usize = 12 as *const i32 as usize + "#, + 12, + ); check_number( r#" //- minicore: coerce_unsized, index, slice @@ -204,7 +210,7 @@ fn casts() { r#" //- minicore: coerce_unsized, index, slice const GOAL: i16 = { - let a = &mut 5; + let a = &mut 5_i16; let z = a as *mut _; unsafe { *z } }; @@ -244,7 +250,13 @@ fn casts() { "#, 4, ); - check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12); + check_number( + r#" + //- minicore: sized + const GOAL: i32 = -12i8 as i32 + "#, + -12, + ); } #[test] @@ -1911,6 +1923,7 @@ fn function_pointer() { ); check_number( r#" + //- minicore: sized fn add2(x: u8) -> u8 { x + 2 } @@ -2422,6 +2435,7 @@ fn statics() { fn extern_weak_statics() { check_number( r#" + //- minicore: sized extern "C" { #[linkage = "extern_weak"] static __dso_handle: *mut u8; @@ -2716,6 +2730,7 @@ fn const_trait_assoc() { ); check_number( r#" + //- minicore: sized struct S(*mut T); trait MySized: Sized { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 6f294494c01eb..c5706172b20c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -311,6 +311,7 @@ fn saturating() { fn allocator() { check_number( r#" + //- minicore: sized extern "Rust" { #[rustc_allocator] fn __rust_alloc(size: usize, align: usize) -> *mut u8; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 062ea278151bb..0e68ab4bfec5c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -13,7 +13,7 @@ //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. -mod cast; +pub(crate) mod cast; pub(crate) mod closure; mod coerce; mod expr; @@ -76,7 +76,7 @@ pub use coerce::could_coerce; #[allow(unreachable_pub)] pub use unify::{could_unify, could_unify_deeply}; -use cast::CastCheck; +use cast::{CastCheck, CastError}; pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. @@ -254,6 +254,16 @@ pub enum InferenceDiagnostic { expr: ExprId, expected: Ty, }, + CastToUnsized { + expr: ExprId, + cast_ty: Ty, + }, + InvalidCast { + expr: ExprId, + error: CastError, + expr_ty: Ty, + cast_ty: Ty, + }, } /// A mismatch between an expected and an inferred type. @@ -456,6 +466,7 @@ pub struct InferenceResult { pub(crate) closure_info: FxHashMap, FnTrait)>, // FIXME: remove this field pub mutated_bindings_in_closure: FxHashSet, + pub coercion_casts: FxHashSet, } impl InferenceResult { @@ -666,7 +677,7 @@ impl<'a> InferenceContext<'a> { let InferenceContext { mut table, mut result, - deferred_cast_checks, + mut deferred_cast_checks, tuple_field_accesses_rev, .. } = self; @@ -695,6 +706,7 @@ impl<'a> InferenceContext<'a> { closure_info: _, mutated_bindings_in_closure: _, tuple_field_access_types: _, + coercion_casts, } = &mut result; table.fallback_if_possible(); @@ -702,8 +714,18 @@ impl<'a> InferenceContext<'a> { // Comment from rustc: // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - for cast in deferred_cast_checks { - cast.check(&mut table); + let mut apply_adjustments = |expr, adj| { + expr_adjustments.insert(expr, adj); + }; + let mut set_coercion_cast = |expr| { + coercion_casts.insert(expr); + }; + for cast in deferred_cast_checks.iter_mut() { + if let Err(diag) = + cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast) + { + diagnostics.push(diag); + } } // FIXME resolve obligations as well (use Guidance if necessary) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 060b5f36f292d..ae914044368ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -1,47 +1,412 @@ //! Type cast logic. Basically coercion + additional casts. -use crate::{infer::unify::InferenceTable, Interner, Ty, TyExt, TyKind}; +use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; +use hir_def::{hir::ExprId, AdtId}; +use stdx::never; + +use crate::{ + infer::unify::InferenceTable, Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, + PlaceholderIndex, QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, +}; + +#[derive(Debug)] +pub(crate) enum Int { + I, + U(UintTy), + Bool, + Char, + CEnum, + InferenceVar, +} + +#[derive(Debug)] +pub(crate) enum CastTy { + Int(Int), + Float, + FnPtr, + Ptr(Ty, Mutability), + // `DynStar` is Not supported yet in r-a +} + +impl CastTy { + pub(crate) fn from_ty(table: &mut InferenceTable<'_>, t: &Ty) -> Option { + match t.kind(Interner) { + TyKind::Scalar(Scalar::Bool) => Some(Self::Int(Int::Bool)), + TyKind::Scalar(Scalar::Char) => Some(Self::Int(Int::Char)), + TyKind::Scalar(Scalar::Int(_)) => Some(Self::Int(Int::I)), + TyKind::Scalar(Scalar::Uint(it)) => Some(Self::Int(Int::U(*it))), + TyKind::InferenceVar(_, TyVariableKind::Integer) => Some(Self::Int(Int::InferenceVar)), + TyKind::InferenceVar(_, TyVariableKind::Float) => Some(Self::Float), + TyKind::Scalar(Scalar::Float(_)) => Some(Self::Float), + TyKind::Adt(..) => { + let (AdtId::EnumId(id), _) = t.as_adt()? else { + return None; + }; + let enum_data = table.db.enum_data(id); + if enum_data.is_payload_free(table.db.upcast()) { + Some(Self::Int(Int::CEnum)) + } else { + None + } + } + TyKind::Raw(m, ty) => Some(Self::Ptr(table.resolve_ty_shallow(ty), *m)), + TyKind::Function(_) => Some(Self::FnPtr), + _ => None, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum CastError { + Unknown, + CastToBool, + CastToChar, + DifferingKinds, + SizedUnsizedCast, + IllegalCast, + IntToFatCast, + NeedDeref, + NeedViaPtr, + NeedViaThinPtr, + NeedViaInt, + NonScalar, + UnknownCastPtrKind, + UnknownExprPtrKind, +} + +impl CastError { + fn into_diagnostic(self, expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> InferenceDiagnostic { + InferenceDiagnostic::InvalidCast { expr, error: self, expr_ty, cast_ty } + } +} #[derive(Clone, Debug)] pub(super) struct CastCheck { + expr: ExprId, + source_expr: ExprId, expr_ty: Ty, cast_ty: Ty, } impl CastCheck { - pub(super) fn new(expr_ty: Ty, cast_ty: Ty) -> Self { - Self { expr_ty, cast_ty } + pub(super) fn new(expr: ExprId, source_expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> Self { + Self { expr, source_expr, expr_ty, cast_ty } + } + + pub(super) fn check( + &mut self, + table: &mut InferenceTable<'_>, + apply_adjustments: &mut F, + set_coercion_cast: &mut G, + ) -> Result<(), InferenceDiagnostic> + where + F: FnMut(ExprId, Vec), + G: FnMut(ExprId), + { + table.resolve_obligations_as_possible(); + self.expr_ty = table.resolve_ty_shallow(&self.expr_ty); + self.cast_ty = table.resolve_ty_shallow(&self.cast_ty); + + if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { + return Ok(()); + } + + if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER) + && !table.is_sized(&self.cast_ty) + { + return Err(InferenceDiagnostic::CastToUnsized { + expr: self.expr, + cast_ty: self.cast_ty.clone(), + }); + } + + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) { + apply_adjustments(self.source_expr, adj); + set_coercion_cast(self.source_expr); + return Ok(()); + } + + self.do_check(table, apply_adjustments) + .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) + } + + fn do_check( + &self, + table: &mut InferenceTable<'_>, + apply_adjustments: &mut F, + ) -> Result<(), CastError> + where + F: FnMut(ExprId, Vec), + { + let (t_from, t_cast) = + match (CastTy::from_ty(table, &self.expr_ty), CastTy::from_ty(table, &self.cast_ty)) { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { + TyKind::FnDef(..) => { + let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); + let sig = table.normalize_associated_types_in(sig); + let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) { + apply_adjustments(self.source_expr, adj); + } else { + return Err(CastError::IllegalCast); + } + + (CastTy::FnPtr, t_cast) + } + TyKind::Ref(mutbl, _, inner_ty) => { + let inner_ty = table.resolve_ty_shallow(inner_ty); + return match t_cast { + CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { + TyKind::Scalar( + Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_), + ) + | TyKind::InferenceVar( + _, + TyVariableKind::Integer | TyVariableKind::Float, + ) => Err(CastError::NeedDeref), + + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + CastTy::Ptr(t, m) => { + let t = table.resolve_ty_shallow(&t); + if !table.is_sized(&t) { + return Err(CastError::IllegalCast); + } + self.check_ref_cast( + table, + &inner_ty, + *mutbl, + &t, + m, + apply_adjustments, + ) + } + _ => Err(CastError::NonScalar), + }; + } + _ => return Err(CastError::NonScalar), + }, + _ => return Err(CastError::NonScalar), + }; + + // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym + + match (t_from, t_cast) { + (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar), + (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool), + (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), + (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar), + (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => { + Err(CastError::NeedViaInt) + } + (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..)) + | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast), + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => { + self.check_ptr_ptr_cast(table, &src, &dst) + } + (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(table, &src), + (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(table, &dst), + (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(table, &dst), + (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()), + (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()), + (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()), + (CastTy::FnPtr, CastTy::Int(_)) => Ok(()), + } } - pub(super) fn check(self, table: &mut InferenceTable<'_>) { - // FIXME: This function currently only implements the bits that influence the type - // inference. We should return the adjustments on success and report diagnostics on error. - let expr_ty = table.resolve_ty_shallow(&self.expr_ty); - let cast_ty = table.resolve_ty_shallow(&self.cast_ty); + fn check_ref_cast( + &self, + table: &mut InferenceTable<'_>, + t_expr: &Ty, + m_expr: Mutability, + t_cast: &Ty, + m_cast: Mutability, + apply_adjustments: &mut F, + ) -> Result<(), CastError> + where + F: FnMut(ExprId, Vec), + { + // Mutability order is opposite to rustc. `Mut < Not` + if m_expr <= m_cast { + if let TyKind::Array(ety, _) = t_expr.kind(Interner) { + // Coerce to a raw pointer so that we generate RawPtr in MIR. + let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) { + apply_adjustments(self.source_expr, adj); + } else { + never!( + "could not cast from reference to array to pointer to array ({:?} to {:?})", + self.expr_ty, + array_ptr_type + ); + } - if table.coerce(&expr_ty, &cast_ty).is_ok() { - return; + // This is a less strict condition than rustc's `demand_eqtype`, + // but false negative is better than false positive + if table.coerce(ety, t_cast).is_ok() { + return Ok(()); + } + } } - if check_ref_to_ptr_cast(expr_ty, cast_ty, table) { - // Note that this type of cast is actually split into a coercion to a - // pointer type and a cast: - // &[T; N] -> *[T; N] -> *T + Err(CastError::IllegalCast) + } + + fn check_ptr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + src: &Ty, + dst: &Ty, + ) -> Result<(), CastError> { + let src_kind = pointer_kind(src, table).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(dst, table).map_err(|_| CastError::Unknown)?; + + match (src_kind, dst_kind) { + (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), + (_, None) => Err(CastError::UnknownCastPtrKind), + (_, Some(PointerKind::Thin)) => Ok(()), + (None, _) => Err(CastError::UnknownExprPtrKind), + (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { + let principal = |tty: &Binders| { + tty.skip_binders().as_slice(Interner).first().and_then(|pred| { + if let WhereClause::Implemented(tr) = pred.skip_binders() { + Some(tr.trait_id) + } else { + None + } + }) + }; + match (principal(&src_tty), principal(&dst_tty)) { + (Some(src_principal), Some(dst_principal)) => { + if src_principal == dst_principal { + return Ok(()); + } + let src_principal = + table.db.trait_datum(table.trait_env.krate, src_principal); + let dst_principal = + table.db.trait_datum(table.trait_env.krate, dst_principal); + if src_principal.is_auto_trait() && dst_principal.is_auto_trait() { + Ok(()) + } else { + Err(CastError::DifferingKinds) + } + } + _ => Err(CastError::Unknown), + } + } + (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()), + (_, _) => Err(CastError::DifferingKinds), + } + } + + fn check_ptr_addr_cast( + &self, + table: &mut InferenceTable<'_>, + expr_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownExprPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + _ => Err(CastError::NeedViaThinPtr), + } + } + + fn check_addr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + cast_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast), + Some(PointerKind::Length) => Err(CastError::IntToFatCast), + Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToFatCast), } + } - // FIXME: Check other kinds of non-coercion casts and report error if any? + fn check_fptr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + cast_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + _ => Err(CastError::IllegalCast), + } } } -fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool { - let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { - return false; - }; - let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { - return false; - }; - let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { - return false; - }; - table.coerce(expr_elt_ty, cast_inner_ty).is_ok() +#[derive(PartialEq, Eq)] +enum PointerKind { + // thin pointer + Thin, + // trait object + VTable(Binders), + // slice + Length, + OfAlias, + OfParam(PlaceholderIndex), + Error, +} + +fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result, ()> { + let ty = table.resolve_ty_shallow(ty); + + if table.is_sized(&ty) { + return Ok(Some(PointerKind::Thin)); + } + + match ty.kind(Interner) { + TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)), + TyKind::Dyn(DynTy { bounds, .. }) => Ok(Some(PointerKind::VTable(bounds.clone()))), + TyKind::Adt(chalk_ir::AdtId(id), subst) => { + let AdtId::StructId(id) = *id else { + never!("`{:?}` should be sized but is not?", ty); + return Err(()); + }; + + let struct_data = table.db.struct_data(id); + if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { + let last_field_ty = + table.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); + pointer_kind(&last_field_ty, table) + } else { + Ok(Some(PointerKind::Thin)) + } + } + TyKind::Tuple(_, subst) => { + match subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) { + None => Ok(Some(PointerKind::Thin)), + Some(ty) => pointer_kind(ty, table), + } + } + TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), + TyKind::Alias(_) | TyKind::AssociatedType(..) | TyKind::OpaqueType(..) => { + Ok(Some(PointerKind::OfAlias)) + } + TyKind::Error => Ok(Some(PointerKind::Error)), + TyKind::Placeholder(idx) => Ok(Some(PointerKind::OfParam(*idx))), + TyKind::BoundVar(_) | TyKind::InferenceVar(..) => Ok(None), + TyKind::Scalar(_) + | TyKind::Array(..) + | TyKind::CoroutineWitness(..) + | TyKind::Raw(..) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::Function(_) + | TyKind::Closure(..) + | TyKind::Coroutine(..) + | TyKind::Never => { + never!("`{:?}` should be sized but is not?", ty); + Err(()) + } + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 89d92ea9af0b9..b1c793a1e38f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -610,7 +610,12 @@ impl InferenceContext<'_> { Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty.clone())); - self.deferred_cast_checks.push(CastCheck::new(expr_ty, cast_ty.clone())); + self.deferred_cast_checks.push(CastCheck::new( + tgt_expr, + *expr, + expr_ty, + cast_ty.clone(), + )); cast_ty } Expr::Ref { expr, rawness, mutability } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index c0f5ddddcbe3e..99ad678980987 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -9,6 +9,7 @@ use chalk_ir::{ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; +use hir_def::{lang_item::LangItem, AdtId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::FxHashMap; @@ -21,7 +22,7 @@ use crate::{ to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, - Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + Solution, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, }; @@ -898,6 +899,33 @@ impl<'a> InferenceTable<'a> { _ => c, } } + + /// Check if given type is `Sized` or not + pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool { + if let Some((AdtId::StructId(id), subst)) = ty.as_adt() { + let struct_data = self.db.struct_data(id); + if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { + let last_field_ty = + self.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); + // Structs can have DST as its last field and such cases are not handled + // as unsized by the chalk, so we do this manually + return self.is_sized(&last_field_ty); + } + } + let Some(sized) = self + .db + .lang_item(self.trait_env.krate, LangItem::Sized) + .and_then(|sized| sized.as_trait()) + else { + return false; + }; + let sized_pred = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(sized), + substitution: Substitution::from1(Interner, ty.clone()), + }); + let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner); + matches!(self.try_obligation(goal), Some(Solution::Unique(_))) + } } impl fmt::Debug for InferenceTable<'_> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index a316de2b48d25..79ee9e97871f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -83,6 +83,7 @@ pub use autoderef::autoderef; pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; pub use infer::{ + cast::CastError, closure::{CaptureKind, CapturedItem}, could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 22d4da0e755ae..8cd6ae1232691 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -837,7 +837,9 @@ pub enum CastKind { PointerFromExposedAddress, /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. - Pointer(PointerCast), + PtrToPtr, + /// Pointer related casts that are done by coercions. + PointerCoercion(PointerCast), /// Cast into a dyn* object. DynStar, IntToInt, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 1bb0c1886857a..89b2ecb1a63e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1475,7 +1475,7 @@ impl Evaluator<'_> { } } Rvalue::Cast(kind, operand, target_ty) => match kind { - CastKind::Pointer(cast) => match cast { + CastKind::PointerCoercion(cast) => match cast { PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => { let current_ty = self.operand_ty(operand, locals)?; if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = @@ -1506,6 +1506,7 @@ impl Evaluator<'_> { }, CastKind::DynStar => not_supported!("dyn star cast"), CastKind::IntToInt + | CastKind::PtrToPtr | CastKind::PointerExposeAddress | CastKind::PointerFromExposedAddress => { let current_ty = self.operand_ty(operand, locals)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 6825bc4862ac9..595a78da10fb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -399,7 +399,7 @@ extern "C" { fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; } -fn my_cmp(x: &[u8], y: &[u8]) -> i32 { +fn my_cmp(x: &[u8; 3], y: &[u8; 3]) -> i32 { memcmp(x as *const u8, y as *const u8, x.len()) } @@ -779,6 +779,7 @@ fn main() { fn posix_getenv() { check_pass( r#" +//- minicore: sized //- /main.rs env:foo=bar type c_char = u8; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 9e235504519e2..9cee494bbe743 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -31,7 +31,7 @@ use crate::{ display::HirDisplay, error_lifetime, generics::generics, - infer::{CaptureKind, CapturedItem, TypeMismatch}, + infer::{cast::CastTy, unify::InferenceTable, CaptureKind, CapturedItem, TypeMismatch}, inhabitedness::is_ty_uninhabited_from, layout::LayoutError, mapping::ToChalk, @@ -362,7 +362,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current, place, Rvalue::Cast( - CastKind::Pointer(*cast), + CastKind::PointerCoercion(*cast), Operand::Copy(p), last.target.clone(), ), @@ -898,14 +898,26 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; - let source_ty = self.infer[*expr].clone(); - let target_ty = self.infer[expr_id].clone(); - self.push_assignment( - current, - place, - Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, it, target_ty), - expr_id.into(), - ); + // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178) + // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313) + let rvalue = if self.infer.coercion_casts.contains(expr) { + Rvalue::Use(it) + } else { + let source_ty = self.infer[*expr].clone(); + let target_ty = self.infer[expr_id].clone(); + let cast_kind = if source_ty.as_reference().is_some() { + CastKind::PointerCoercion(PointerCast::ArrayToPointer) + } else { + let mut table = InferenceTable::new( + self.db, + self.db.trait_environment_for_body(self.owner), + ); + cast_kind(&mut table, &source_ty, &target_ty)? + }; + + Rvalue::Cast(cast_kind, it, target_ty) + }; + self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) } Expr::Ref { expr, rawness: _, mutability } => { @@ -2005,40 +2017,21 @@ impl<'ctx> MirLowerCtx<'ctx> { } } -fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { - Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) { - (TyKind::FnDef(..), TyKind::Function(_)) => CastKind::Pointer(PointerCast::ReifyFnPointer), - (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) { - (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat, - (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt, - (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, - (_, _) => CastKind::IntToInt, - }, - (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress, - (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress, - (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { - CastKind::Pointer(if a == b { - PointerCast::MutToConstPointer - } else if matches!(b.kind(Interner), TyKind::Slice(_)) - && matches!(a.kind(Interner), TyKind::Array(_, _)) - || matches!(b.kind(Interner), TyKind::Dyn(_)) - { - PointerCast::Unsize - } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { - PointerCast::ArrayToPointer - } else { - // cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like - // slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata - // will be touched) but there is no specific variant - // for it in `PointerCast` so we use `MutToConstPointer` - PointerCast::MutToConstPointer - }) - } - // Enum to int casts - (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { - CastKind::IntToInt +fn cast_kind(table: &mut InferenceTable<'_>, source_ty: &Ty, target_ty: &Ty) -> Result { + let from = CastTy::from_ty(table, source_ty); + let cast = CastTy::from_ty(table, target_ty); + Ok(match (from, cast) { + (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => { + CastKind::PointerExposeAddress } - (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"), + (Some(CastTy::Int(_)), Some(CastTy::Ptr(..))) => CastKind::PointerFromExposedAddress, + (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => CastKind::IntToInt, + (Some(CastTy::FnPtr), Some(CastTy::Ptr(..))) => CastKind::FnPtrToPtr, + (Some(CastTy::Float), Some(CastTy::Int(_))) => CastKind::FloatToInt, + (Some(CastTy::Int(_)), Some(CastTy::Float)) => CastKind::IntToFloat, + (Some(CastTy::Float), Some(CastTy::Float)) => CastKind::FloatToFloat, + (Some(CastTy::Ptr(..)), Some(CastTy::Ptr(..))) => CastKind::PtrToPtr, + _ => not_supported!("Unknown cast between {source_ty:?} and {target_ty:?}"), }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 610fc9b6b4562..74acf23b75ab6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1286,6 +1286,7 @@ fn main() { fn method_on_dyn_impl() { check_types( r#" +//- minicore: coerce_unsized trait Foo {} impl Foo for u32 {} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 98f30f0f6f749..4df10025f80fb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1907,6 +1907,7 @@ fn dont_unify_on_casts() { // #15246 check_types( r#" +//- minicore: sized fn unify(_: [bool; 1]) {} fn casted(_: *const bool) {} fn default() -> T { loop {} } @@ -1926,6 +1927,7 @@ fn test() { fn rustc_test_issue_52437() { check_types( r#" + //- minicore: sized fn main() { let x = [(); &(&'static: loop { |x| {}; }) as *const _ as usize] //^ [(); _] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 1c6fa62e30ceb..37280f81b8696 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3572,6 +3572,7 @@ fn f(t: Ark) { fn ref_to_array_to_ptr_cast() { check_types( r#" +//- minicore: sized fn default() -> T { loop {} } fn foo() { let arr = [default()]; diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index ffb972475f8f7..4d90df63b9b9f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -4,7 +4,9 @@ //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. pub use hir_ty::diagnostics::{CaseType, IncorrectCase}; -use hir_ty::{db::HirDatabase, diagnostics::BodyValidationDiagnostic, InferenceDiagnostic}; +use hir_ty::{ + db::HirDatabase, diagnostics::BodyValidationDiagnostic, CastError, InferenceDiagnostic, +}; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -50,10 +52,12 @@ macro_rules! diagnostics { diagnostics![ AwaitOutsideOfAsync, BreakOutsideOfLoop, + CastToUnsized, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, + InvalidCast, InvalidDeriveTarget, MacroDefError, MacroError, @@ -364,6 +368,20 @@ pub struct RemoveUnnecessaryElse { pub if_expr: InFile>, } +#[derive(Debug)] +pub struct CastToUnsized { + pub expr: InFile>, + pub cast_ty: Type, +} + +#[derive(Debug)] +pub struct InvalidCast { + pub expr: InFile>, + pub error: CastError, + pub expr_ty: Type, + pub cast_ty: Type, +} + impl AnyDiagnostic { pub(crate) fn body_validation_diagnostic( db: &dyn HirDatabase, @@ -620,6 +638,16 @@ impl AnyDiagnostic { }; MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into() } + InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { + let expr = expr_syntax(*expr)?; + CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.clone()) }.into() + } + InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { + let expr = expr_syntax(*expr)?; + let expr_ty = Type::new(db, def, expr_ty.clone()); + let cast_ty = Type::new(db, def, cast_ty.clone()); + InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() + } }) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6328a3c3d0d9e..81d6466c2f31d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -147,7 +147,7 @@ pub use { layout::LayoutError, mir::{MirEvalError, MirLowerError}, object_safety::{MethodViolationCode, ObjectSafetyViolation}, - FnAbi, PointerCast, Safety, + CastError, FnAbi, PointerCast, Safety, }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 5bd204dd573b9..906e9a9891a72 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -1020,6 +1020,7 @@ fn main() { check_assist( inline_call, r#" +//- minicore: sized fn foo(x: *const u32) -> u32 { x as u32 } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 6a1f7f26c92cc..b9fc075ae8382 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -333,7 +333,8 @@ fn foo() { check_assist( inline_local_variable, r" -fn bar(a: usize): usize { a } +//- minicore: sized +fn bar(a: usize) -> usize { a } fn foo() { let a$0 = bar(1) as u64; a + 1; @@ -347,7 +348,7 @@ fn foo() { bar(a); }", r" -fn bar(a: usize): usize { a } +fn bar(a: usize) -> usize { a } fn foo() { (bar(1) as u64) + 1; if (bar(1) as u64) > 10 { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs new file mode 100644 index 0000000000000..2f6f033f25c7e --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -0,0 +1,1007 @@ +use hir::{CastError, ClosureStyle, HirDisplay}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +macro_rules! format_ty { + ($ctx:expr, $fmt:literal, $($arg:expr),* $(,)?) => {{ + std::format!( + $fmt, + $( + $arg + .display($ctx.sema.db, $ctx.edition) + .with_closure_style(ClosureStyle::ClosureWithId) + ),* + ) + }} +} + +// Diagnostic: invalid-cast +// +// This diagnostic is triggered if the code contains an illegal cast +pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); + let (code, message) = match d.error { + CastError::CastToBool => ( + DiagnosticCode::RustcHardError("E0054"), + format_ty!(ctx, "cannot cast `{}` as `bool`", d.expr_ty), + ), + CastError::CastToChar => ( + DiagnosticCode::RustcHardError("E0604"), + format_ty!(ctx, "only `u8` can be cast as `char`, not {}", d.expr_ty), + ), + CastError::DifferingKinds => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: vtable kinds may not match", + d.expr_ty, + d.cast_ty + ), + ), + CastError::SizedUnsizedCast => ( + DiagnosticCode::RustcHardError("E0607"), + format_ty!( + ctx, + "cannot cast thin pointer `{}` to fat pointer `{}`", + d.expr_ty, + d.cast_ty + ), + ), + CastError::Unknown | CastError::IllegalCast => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!(ctx, "casting `{}` as `{}` is invalid", d.expr_ty, d.cast_ty), + ), + CastError::IntToFatCast => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!(ctx, "cannot cast `{}` to a fat pointer `{}`", d.expr_ty, d.cast_ty), + ), + CastError::NeedDeref => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs defererence or removal of unneeded borrow", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaPtr => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through a raw pointer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaThinPtr => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through a thin pointer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaInt => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through an integer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NonScalar => ( + DiagnosticCode::RustcHardError("E0605"), + format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty), + ), + CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => ( + DiagnosticCode::RustcHardError("E0641"), + "cannot cast to a pointer of an unknown kind".to_owned(), + ), + }; + Diagnostic::new(code, message, display_range) +} + +// Diagnostic: cast-to-unsized +// +// This diagnostic is triggered when casting to an unsized type +pub(crate) fn cast_to_unsized(ctx: &DiagnosticsContext<'_>, d: &hir::CastToUnsized) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0620"), + format_ty!(ctx, "cast to unsized type: `{}`", d.cast_ty), + display_range, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + + #[test] + fn cast_as_bool() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let u = 5 as bool; + //^^^^^^^^^ error: cannot cast `i32` as `bool` + + let t = (1 + 2) as bool; + //^^^^^^^^^^^^^^^ error: cannot cast `i32` as `bool` + + let _ = 5_u32 as bool; + //^^^^^^^^^^^^^ error: cannot cast `u32` as `bool` + + let _ = 64.0_f64 as bool; + //^^^^^^^^^^^^^^^^ error: cannot cast `f64` as `bool` + + enum IntEnum { + Zero, + One, + Two + } + let _ = IntEnum::One as bool; + //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `IntEnum` as `bool` + + fn uwu(_: u8) -> i32 { + 5 + } + + unsafe fn owo() {} + + let _ = uwu as bool; + //^^^^^^^^^^^ error: cannot cast `fn uwu(u8) -> i32` as `bool` + let _ = owo as bool; + //^^^^^^^^^^^ error: cannot cast `unsafe fn owo()` as `bool` + + let _ = uwu as fn(u8) -> i32 as bool; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot cast `fn(u8) -> i32` as `bool` + let _ = 'x' as bool; + //^^^^^^^^^^^ error: cannot cast `char` as `bool` + + let ptr = 1 as *const (); + + let _ = ptr as bool; + //^^^^^^^^^^^ error: cannot cast `*const ()` as `bool` + let v = "hello" as bool; + //^^^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first +} +"#, + ); + } + + #[test] + fn cast_pointee_projection() { + check_diagnostics( + r#" +//- minicore: sized +trait Tag<'a> { + type Type: ?Sized; +} + +trait IntoRaw: for<'a> Tag<'a> { + fn into_raw(this: *const >::Type) -> *mut >::Type; +} + +impl Tag<'a>> IntoRaw for T { + fn into_raw(this: *const >::Type) -> *mut >::Type { + this as *mut T::Type + } +} + +fn main() {} +"#, + ); + } + + #[test] + fn cast_region_to_int() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let x: isize = 3; + let _ = &x as *const isize as usize; +} +"#, + ); + } + + #[test] + fn cast_to_bare_fn() { + check_diagnostics( + r#" +//- minicore: sized +fn foo(_x: isize) { } + +fn main() { + let v: u64 = 5; + let x = foo as extern "C" fn() -> isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `fn foo(isize)` as `fn() -> isize` + let y = v as extern "Rust" fn(isize) -> (isize, isize); + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` + y(x()); +} +"#, + ); + } + + #[test] + fn cast_to_unit() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let _ = 0u32 as (); + //^^^^^^^^^^ error: non-primitive cast: `u32` as `()` +} +"#, + ); + } + + #[test] + fn cast_to_slice() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized +fn as_bytes(_: &str) -> &[u8] { + loop {} +} + +fn main() { + as_bytes("example") as [char]; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cast to unsized type: `[char]` + + let arr: &[u8] = &[0, 2, 3]; + arr as [char]; + //^^^^^^^^^^^^^ error: cast to unsized type: `[char]` +} +"#, + &["E0308"], + ); + } + + #[test] + fn cast() { + check_diagnostics( + r#" +//- minicore: sized +fn null_mut() -> *mut T { + loop {} +} + +pub fn main() { + let i: isize = 'Q' as isize; + let _u: u32 = i as u32; + + // Test that `_` is correctly inferred. + let x = &"hello"; + let mut y = x as *const _; + y = null_mut(); +} +"#, + ); + } + + #[test] + fn dyn_tail_need_normalization() { + check_diagnostics( + r#" +//- minicore: dispatch_from_dyn +trait Trait { + type Associated; +} + +impl Trait for i32 { + type Associated = i64; +} + +trait Generic {} + +type TraitObject = dyn Generic<::Associated>; + +struct Wrap(TraitObject); + +fn cast(x: *mut TraitObject) { + x as *mut Wrap; +} +"#, + ); + } + + #[test] + fn enum_to_numeric_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub enum UnitOnly { + Foo, + Bar, + Baz, +} + +pub enum Fieldless { + Tuple(), + Struct{}, + Unit, +} + +pub enum NotUnitOnlyOrFieldless { + Foo, + Bar(u8), + Baz +} + +fn main() { + let unit_only = UnitOnly::Foo; + + let _ = unit_only as isize; + let _ = unit_only as i32; + let _ = unit_only as usize; + let _ = unit_only as u32; + + + let fieldless = Fieldless::Struct{}; + + let _ = fieldless as isize; + let _ = fieldless as i32; + let _ = fieldless as usize; + let _ = fieldless as u32; + + + let not_unit_only_or_fieldless = NotUnitOnlyOrFieldless::Foo; + + let _ = not_unit_only_or_fieldless as isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `isize` + let _ = not_unit_only_or_fieldless as i32; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `i32` + let _ = not_unit_only_or_fieldless as usize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `usize` + let _ = not_unit_only_or_fieldless as u32; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `u32` +} +"#, + ); + } + + #[test] + fn fat_ptr_cast() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized +trait Foo { + fn foo(&self) {} //~ WARN method `foo` is never used +} + +struct Bar; + +impl Foo for Bar {} + +fn to_raw(_: *mut T) -> *mut () { + loop {} +} + +fn main() { + // Test we can turn a fat pointer to array back into a thin pointer. + let a: *const [i32] = &[1, 2, 3]; + let b = a as *const [i32; 2]; + + // Test conversion to an address (usize). + let a: *const [i32; 3] = &[1, 2, 3]; + let b: *const [i32] = a; + + // And conversion to a void pointer/address for trait objects too. + let a: *mut dyn Foo = &mut Bar; + let b = a as *mut () as usize; + let c = a as *const () as usize; + let d = to_raw(a) as usize; +} +"#, + &["E0308"], + ); + + check_diagnostics_with_disabled( + r#" +//- minicore: sized +trait Trait {} + +struct Box; + +impl Box { + fn new(_: T) -> Self { + loop {} + } +} + +fn as_ptr(_: &[i32]) -> *const i32 { + loop {} +} + +fn main() { + let a: &[i32] = &[1, 2, 3]; + let b: Box<[i32]> = Box::new([1, 2, 3]); + let p = a as *const [i32]; + let q = as_ptr(a); + + a as usize; + //^^^^^^^^^^ error: casting `&[i32]` as `usize` is invalid: needs casting through a raw pointer first + a as isize; + //^^^^^^^^^^ error: casting `&[i32]` as `isize` is invalid: needs casting through a raw pointer first + a as i16; + //^^^^^^^^ error: casting `&[i32]` as `i16` is invalid: needs casting through a raw pointer first + a as u32; + //^^^^^^^^ error: casting `&[i32]` as `u32` is invalid: needs casting through a raw pointer first + b as usize; + //^^^^^^^^^^ error: non-primitive cast: `Box<[i32]>` as `usize` + p as usize; + //^^^^^^^^^^ error: casting `*const [i32]` as `usize` is invalid: needs casting through a thin pointer first + q as *const [i32]; + //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]` + + let t: *mut (dyn Trait + 'static) = 0 as *mut _; + //^^^^^^^^^^^ error: cannot cast `i32` to a fat pointer `*mut _` + let mut fail: *const str = 0 as *const str; + //^^^^^^^^^^^^^^^ error: cannot cast `i32` to a fat pointer `*const str` + let mut fail2: *const str = 0isize as *const str; + //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `isize` to a fat pointer `*const str` +} + +fn foo() { + let s = 0 as *const T; + //^^^^^^^^^^^^^ error: cannot cast `i32` to a fat pointer `*const T` +} +"#, + &["E0308", "unused_variables"], + ); + } + + #[test] + fn order_dependent_cast_inference() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let x = &"hello"; + let mut y = 0 as *const _; + //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind + y = x as *const _; +} +"#, + ); + } + + #[test] + fn ptr_to_ptr_different_regions() { + check_diagnostics( + r#" +//- minicore: sized +struct Foo<'a> { a: &'a () } + +fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static> { + // This should pass because raw pointer casts can do anything they want. + v as *const Foo<'static> +} + +trait Trait {} + +fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) { + ptr as _ +} + +fn main() { + let unit = (); + let foo = Foo { a: &unit }; + let _long: *const Foo<'static> = extend_lifetime_very_very_safely(&foo); +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_add_auto() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait<'a> {} + +fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { + x as _ +} + +// (to test diagnostic list formatting) +fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) { + x as _ +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_add_super_auto() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait: Send {} +impl Trait for () {} + +fn main() { + // This is OK: `Trait` has `Send` super trait. + &() as *const dyn Trait as *const (dyn Trait + Send); +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_ok() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait<'a> {} + +fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> { + x as _ +} + +fn cast_inherent_lt<'a, 'b>(x: *mut (dyn Trait<'static> + 'a)) -> *mut (dyn Trait<'static> + 'b) { + x as _ +} + +fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) { + x as _ +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_wrap_upcast() { + check_diagnostics( + r#" +//- minicore: sized +trait Super {} +trait Sub: Super {} + +struct Wrapper(T); + +// This cast should not compile. +// Upcasting can't work here, because we are also changing the type (`Wrapper`), +// and reinterpreting would be confusing/surprising. +// See +fn cast(ptr: *const dyn Sub) -> *const Wrapper { + ptr as _ + //^^^^^^^^ error: casting `*const dyn Sub` as `*const Wrapper` is invalid: vtable kinds may not match +} +"#, + ); + } + + #[test] + fn supported_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub fn main() { + struct String; + + let f = 1_usize as *const String; + + let _ = f as isize; + let _ = f as usize; + let _ = f as i8; + let _ = f as i16; + let _ = f as i32; + let _ = f as i64; + let _ = f as u8; + let _ = f as u16; + let _ = f as u32; + let _ = f as u64; + + let _ = 1 as isize; + let _ = 1 as usize; + let _ = 1 as *const String; + let _ = 1 as i8; + let _ = 1 as i16; + let _ = 1 as i32; + let _ = 1 as i64; + let _ = 1 as u8; + let _ = 1 as u16; + let _ = 1 as u32; + let _ = 1 as u64; + let _ = 1 as f32; + let _ = 1 as f64; + + let _ = 1_usize as isize; + let _ = 1_usize as usize; + let _ = 1_usize as *const String; + let _ = 1_usize as i8; + let _ = 1_usize as i16; + let _ = 1_usize as i32; + let _ = 1_usize as i64; + let _ = 1_usize as u8; + let _ = 1_usize as u16; + let _ = 1_usize as u32; + let _ = 1_usize as u64; + let _ = 1_usize as f32; + let _ = 1_usize as f64; + + let _ = 1i8 as isize; + let _ = 1i8 as usize; + let _ = 1i8 as *const String; + let _ = 1i8 as i8; + let _ = 1i8 as i16; + let _ = 1i8 as i32; + let _ = 1i8 as i64; + let _ = 1i8 as u8; + let _ = 1i8 as u16; + let _ = 1i8 as u32; + let _ = 1i8 as u64; + let _ = 1i8 as f32; + let _ = 1i8 as f64; + + let _ = 1u8 as isize; + let _ = 1u8 as usize; + let _ = 1u8 as *const String; + let _ = 1u8 as i8; + let _ = 1u8 as i16; + let _ = 1u8 as i32; + let _ = 1u8 as i64; + let _ = 1u8 as u8; + let _ = 1u8 as u16; + let _ = 1u8 as u32; + let _ = 1u8 as u64; + let _ = 1u8 as f32; + let _ = 1u8 as f64; + + let _ = 1i16 as isize; + let _ = 1i16 as usize; + let _ = 1i16 as *const String; + let _ = 1i16 as i8; + let _ = 1i16 as i16; + let _ = 1i16 as i32; + let _ = 1i16 as i64; + let _ = 1i16 as u8; + let _ = 1i16 as u16; + let _ = 1i16 as u32; + let _ = 1i16 as u64; + let _ = 1i16 as f32; + let _ = 1i16 as f64; + + let _ = 1u16 as isize; + let _ = 1u16 as usize; + let _ = 1u16 as *const String; + let _ = 1u16 as i8; + let _ = 1u16 as i16; + let _ = 1u16 as i32; + let _ = 1u16 as i64; + let _ = 1u16 as u8; + let _ = 1u16 as u16; + let _ = 1u16 as u32; + let _ = 1u16 as u64; + let _ = 1u16 as f32; + let _ = 1u16 as f64; + + let _ = 1i32 as isize; + let _ = 1i32 as usize; + let _ = 1i32 as *const String; + let _ = 1i32 as i8; + let _ = 1i32 as i16; + let _ = 1i32 as i32; + let _ = 1i32 as i64; + let _ = 1i32 as u8; + let _ = 1i32 as u16; + let _ = 1i32 as u32; + let _ = 1i32 as u64; + let _ = 1i32 as f32; + let _ = 1i32 as f64; + + let _ = 1u32 as isize; + let _ = 1u32 as usize; + let _ = 1u32 as *const String; + let _ = 1u32 as i8; + let _ = 1u32 as i16; + let _ = 1u32 as i32; + let _ = 1u32 as i64; + let _ = 1u32 as u8; + let _ = 1u32 as u16; + let _ = 1u32 as u32; + let _ = 1u32 as u64; + let _ = 1u32 as f32; + let _ = 1u32 as f64; + + let _ = 1i64 as isize; + let _ = 1i64 as usize; + let _ = 1i64 as *const String; + let _ = 1i64 as i8; + let _ = 1i64 as i16; + let _ = 1i64 as i32; + let _ = 1i64 as i64; + let _ = 1i64 as u8; + let _ = 1i64 as u16; + let _ = 1i64 as u32; + let _ = 1i64 as u64; + let _ = 1i64 as f32; + let _ = 1i64 as f64; + + let _ = 1u64 as isize; + let _ = 1u64 as usize; + let _ = 1u64 as *const String; + let _ = 1u64 as i8; + let _ = 1u64 as i16; + let _ = 1u64 as i32; + let _ = 1u64 as i64; + let _ = 1u64 as u8; + let _ = 1u64 as u16; + let _ = 1u64 as u32; + let _ = 1u64 as u64; + let _ = 1u64 as f32; + let _ = 1u64 as f64; + + let _ = 1u64 as isize; + let _ = 1u64 as usize; + let _ = 1u64 as *const String; + let _ = 1u64 as i8; + let _ = 1u64 as i16; + let _ = 1u64 as i32; + let _ = 1u64 as i64; + let _ = 1u64 as u8; + let _ = 1u64 as u16; + let _ = 1u64 as u32; + let _ = 1u64 as u64; + let _ = 1u64 as f32; + let _ = 1u64 as f64; + + let _ = true as isize; + let _ = true as usize; + let _ = true as i8; + let _ = true as i16; + let _ = true as i32; + let _ = true as i64; + let _ = true as u8; + let _ = true as u16; + let _ = true as u32; + let _ = true as u64; + + let _ = 1f32 as isize; + let _ = 1f32 as usize; + let _ = 1f32 as i8; + let _ = 1f32 as i16; + let _ = 1f32 as i32; + let _ = 1f32 as i64; + let _ = 1f32 as u8; + let _ = 1f32 as u16; + let _ = 1f32 as u32; + let _ = 1f32 as u64; + let _ = 1f32 as f32; + let _ = 1f32 as f64; + + let _ = 1f64 as isize; + let _ = 1f64 as usize; + let _ = 1f64 as i8; + let _ = 1f64 as i16; + let _ = 1f64 as i32; + let _ = 1f64 as i64; + let _ = 1f64 as u8; + let _ = 1f64 as u16; + let _ = 1f64 as u32; + let _ = 1f64 as u64; + let _ = 1f64 as f32; + let _ = 1f64 as f64; +} +"#, + ); + } + + #[test] + fn unsized_struct_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub struct Data([u8]); + +fn foo(x: &[u8]) { + let _: *const Data = x as *const Data; + //^^^^^^^^^^^^^^^^ error: casting `&[u8]` as `*const Data` is invalid +} +"#, + ); + } + + #[test] + fn unsupported_cast() { + check_diagnostics( + r#" +//- minicore: sized +struct A; + +fn main() { + let _ = 1.0 as *const A; + //^^^^^^^^^^^^^^^ error: casting `f64` as `*const A` is invalid +} +"#, + ); + } + + #[test] + fn issue_17897() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + _ = ((), ()) as (); + //^^^^^^^^^^^^^^ error: non-primitive cast: `(_, _)` as `()` +} +"#, + ); + } + + #[test] + fn rustc_issue_10991() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let nil = (); + let _t = nil as usize; + //^^^^^^^^^^^^ error: non-primitive cast: `()` as `usize` +} +"#, + ); + } + + #[test] + fn rustc_issue_17444() { + check_diagnostics( + r#" +//- minicore: sized +enum Test { + Foo = 0 +} + +fn main() { + let _x = Test::Foo as *const isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `Test` as `*const isize` is invalid +} +"#, + ); + } + + #[test] + fn rustc_issue_43825() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let error = error; + //^^^^^ error: no such value in this scope + + 0 as f32; + 0.0 as u32; +} +"#, + ); + } + + #[test] + fn rustc_issue_84213() { + check_diagnostics( + r#" +//- minicore: sized +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + let _ = something.field; + + let _pointer_to_something = something as *const Something; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*const Something` + + let _mut_pointer_to_something = something as *mut Something; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*mut Something` +} +"#, + ); + + // Fixed + check_diagnostics( + r#" +//- minicore: sized +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + let _ = something.field; + + let _pointer_to_something = &something as *const Something; + + let _mut_pointer_to_something = &mut something as *mut Something; +} +"#, + ); + } + + #[test] + fn rustc_issue_88621() { + check_diagnostics( + r#" +//- minicore: sized +#[repr(u8)] +enum Kind2 { + Foo() = 1, + Bar{} = 2, + Baz = 3, +} + +fn main() { + let _ = Kind2::Foo() as u8; + //^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Kind2` as `u8` +} +"#, + ); + } + + #[test] + fn rustc_issue_89497() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let pointer: usize = &1_i32 as *const i32 as usize; + let _reference: &'static i32 = unsafe { pointer as *const i32 as &'static i32 }; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `*const i32` as `&i32` +} +"#, + ); + + // Fixed + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let pointer: usize = &1_i32 as *const i32 as usize; + let _reference: &'static i32 = unsafe { &*(pointer as *const i32) }; +} +"#, + ); + } + + #[test] + fn rustc_issue_106883() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized, deref +use core::ops::Deref; + +struct Foo; + +impl Deref for Foo { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &[] + } +} + +fn main() { + let _ = "foo" as bool; + //^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first + + let _ = Foo as bool; + //^^^^^^^^^^^ error: non-primitive cast: `Foo` as `bool` +} + +fn _slice(bar: &[i32]) -> bool { + bar as bool + //^^^^^^^^^^^ error: casting `&[i32]` as `bool` is invalid: needs casting through a raw pointer first +} +"#, + &["E0308"], + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 7e70a27f789bc..9a9bab091d289 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -99,8 +99,9 @@ mod tests { fn missing_unsafe_diagnostic_with_raw_ptr() { check_diagnostics( r#" +//- minicore: sized fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; unsafe { let _y = *x; } let _z = *x; } //^^💡 error: this operation is unsafe and requires an unsafe function or block @@ -112,17 +113,18 @@ fn main() { fn missing_unsafe_diagnostic_with_unsafe_call() { check_diagnostics( r#" +//- minicore: sized struct HasUnsafe; impl HasUnsafe { unsafe fn unsafe_fn(&self) { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _y = *x; } } unsafe fn unsafe_fn() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _y = *x; } @@ -250,14 +252,15 @@ fn main() { fn add_unsafe_block_when_dereferencing_a_raw_pointer() { check_fix( r#" +//- minicore: sized fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _z = *x$0; } "#, r#" fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _z = unsafe { *x }; } "#, @@ -268,8 +271,9 @@ fn main() { fn add_unsafe_block_when_calling_unsafe_function() { check_fix( r#" +//- minicore: sized unsafe fn func() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let z = *x; } fn main() { @@ -278,7 +282,7 @@ fn main() { "#, r#" unsafe fn func() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let z = *x; } fn main() { @@ -292,6 +296,7 @@ fn main() { fn add_unsafe_block_when_calling_unsafe_method() { check_fix( r#" +//- minicore: sized struct S(usize); impl S { unsafe fn func(&self) { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 06c6b0f3e4c38..a9ff06fb0ab1e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -190,4 +190,16 @@ fn foo(mut slice: &[u32]) -> usize { "#, ); } + + #[test] + fn regression_16564() { + check_diagnostics( + r#" +//- minicore: copy +fn test() { + let _x = (&(&mut (),)).0 as *const (); +} + "#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 9b50a435e4c3a..4749718f1b17c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -30,6 +30,7 @@ mod handlers { pub(crate) mod inactive_code; pub(crate) mod incoherent_impl; pub(crate) mod incorrect_case; + pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod macro_error; pub(crate) mod malformed_derive; @@ -390,6 +391,7 @@ pub fn semantic_diagnostics( for diag in diags { let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), + AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { Some(it) => it, @@ -397,6 +399,7 @@ pub fn semantic_diagnostics( } AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d), AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), + AnyDiagnostic::InvalidCast(d) => handlers::invalid_cast::invalid_cast(&ctx, &d), AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index dc390f8f67af8..c37c469dff4e1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -313,6 +313,7 @@ fn main() { //^^^^^^^^^^^^ //^^^^^^^^^^^^( //^^^^^^^^^^^^) + //^^^^ let _: fn() = || {}; //^^^^^ let _: unsafe fn() = || {}; @@ -321,6 +322,8 @@ fn main() { //^^^^^^^^^^^^^^^^^^^^^ //^^^^^^^^^^^^^^^^^^^^^( //^^^^^^^^^^^^^^^^^^^^^) + //^^^^^^^^^&raw mut $ + //^^^^^^^^^* let _: &mut [_] = &mut [0; 0]; //^^^^^^^^^^^ //^^^^^^^^^^^&mut $ diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 2070022d4183c..ad7fbb00e5c66 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -554,6 +554,7 @@ fn main() { fn test_unsafe_highlighting() { check_highlighting( r#" +//- minicore: sized macro_rules! id { ($($tt:tt)*) => { $($tt)* From b880890e598062ad671cc11d1dcf61a550293509 Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 3 Sep 2024 05:01:56 +0800 Subject: [PATCH 0231/1301] refactor: move ide_assist::utils::suggest_name to ide-db --- .../ide-assists/src/handlers/extract_variable.rs | 3 ++- .../src/handlers/generate_delegate_trait.rs | 3 ++- .../src/handlers/introduce_named_generic.rs | 3 ++- .../replace_is_method_with_if_let_method.rs | 3 ++- .../rust-analyzer/crates/ide-assists/src/utils.rs | 1 - src/tools/rust-analyzer/crates/ide-db/src/lib.rs | 1 + .../src/syntax_helpers}/suggest_name.rs | 15 +++++++-------- 7 files changed, 16 insertions(+), 13 deletions(-) rename src/tools/rust-analyzer/crates/{ide-assists/src/utils => ide-db/src/syntax_helpers}/suggest_name.rs (97%) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 5ae75bb1ff8e0..a43a4b5e1a060 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1,4 +1,5 @@ use hir::TypeInfo; +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName}, ted, NodeOrToken, @@ -6,7 +7,7 @@ use syntax::{ SyntaxNode, T, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: extract_variable // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index bf4ce5c907e80..c22d19574fb49 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -2,13 +2,14 @@ use std::ops::Not; use crate::{ assist_context::{AssistContext, Assists}, - utils::{convert_param_list_to_arg_list, suggest_name}, + utils::convert_param_list_to_arg_list, }; use either::Either; use hir::{db::HirDatabase, HasVisibility}; use ide_db::{ assists::{AssistId, GroupLabel}, path_transform::PathTransform, + syntax_helpers::suggest_name, FxHashMap, FxHashSet, }; use itertools::Itertools; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs index 543b7f7ab6329..a734a6cc2bc88 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -1,9 +1,10 @@ +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: introduce_named_generic // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 59bb0c45e1439..a856da0921537 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,9 +1,10 @@ +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, make, AstNode}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: replace_is_some_with_if_let_some // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index b8a6f3b6dbeb2..19d1ef3157d19 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -23,7 +23,6 @@ use crate::assist_context::{AssistContext, SourceChangeBuilder}; mod gen_trait_fn_body; pub(crate) mod ref_field_expr; -pub(crate) mod suggest_name; pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr { extract_trivial_expression(&block_expr) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 8a2068e9039fc..ab161f0ce5712 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -38,6 +38,7 @@ pub mod syntax_helpers { pub mod format_string_exprs; pub use hir::insert_whitespace_into_node; pub mod node_ext; + pub mod suggest_name; pub use parser::LexedStr; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs similarity index 97% rename from src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs rename to src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 3130ef0695577..14128e74438bc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -1,14 +1,16 @@ //! This module contains functions to suggest names for expressions, functions and other items use hir::Semantics; -use ide_db::{FxHashSet, RootDatabase}; use itertools::Itertools; +use rustc_hash::FxHashSet; use stdx::to_lower_snake_case; use syntax::{ ast::{self, HasName}, match_ast, AstNode, Edition, SmolStr, }; +use crate::RootDatabase; + /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"]; @@ -66,10 +68,7 @@ const USELESS_METHODS: &[&str] = &[ /// The function checks if the name conflicts with existing generic parameters. /// If so, it will try to resolve the conflict by adding a number suffix, e.g. /// `T`, `T0`, `T1`, ... -pub(crate) fn for_unique_generic_name( - name: &str, - existing_params: &ast::GenericParamList, -) -> SmolStr { +pub fn for_unique_generic_name(name: &str, existing_params: &ast::GenericParamList) -> SmolStr { let param_names = existing_params .generic_params() .map(|param| match param { @@ -101,7 +100,7 @@ pub(crate) fn for_unique_generic_name( /// /// If the name conflicts with existing generic parameters, it will try to /// resolve the conflict with `for_unique_generic_name`. -pub(crate) fn for_impl_trait_as_generic( +pub fn for_impl_trait_as_generic( ty: &ast::ImplTraitType, existing_params: &ast::GenericParamList, ) -> SmolStr { @@ -132,7 +131,7 @@ pub(crate) fn for_impl_trait_as_generic( /// /// Currently it sticks to the first name found. // FIXME: Microoptimize and return a `SmolStr` here. -pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { +pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { // `from_param` does not benefit from stripping // it need the largest context possible // so we check firstmost @@ -184,7 +183,7 @@ fn normalize(name: &str) -> Option { fn is_valid_name(name: &str) -> bool { matches!( - ide_db::syntax_helpers::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), + super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), Some((syntax::SyntaxKind::IDENT, _error)) ) } From 5f7fcbe0d4dbd34c01dc6ae16e19a0f00035ba2f Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 3 Sep 2024 05:15:21 +0800 Subject: [PATCH 0232/1301] feat: suggest name in let_stmt and fn_param --- .../crates/ide-completion/src/completions.rs | 10 ++++++++++ .../ide-completion/src/completions/pattern.rs | 13 +++++++++++++ .../crates/ide-completion/src/context.rs | 1 + .../ide-completion/src/context/analysis.rs | 13 +++++++++++++ .../ide-db/src/syntax_helpers/suggest_name.rs | 18 ++++++++++++++++-- 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index b537150608bb0..414627fbabae1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -617,6 +617,16 @@ impl Completions { } self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); } + + pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { + let item = CompletionItem::new( + CompletionItemKind::Binding, + ctx.source_range(), + SmolStr::from(name), + ctx.edition, + ); + item.add_to(self, ctx.db); + } } /// Calls the callback for each variant of the provided enum with the path to the variant. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index 60cfb7e5a8c5a..2a06fc4017557 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs @@ -1,6 +1,7 @@ //! Completes constants and paths in unqualified patterns. use hir::{db::DefDatabase, AssocItem, ScopeDef}; +use ide_db::syntax_helpers::suggest_name; use syntax::ast::Pat; use crate::{ @@ -45,6 +46,18 @@ pub(crate) fn complete_pattern( return; } + // Suggest name only in let-stmt and fn param + if pattern_ctx.should_suggest_name { + if let Some(suggested) = ctx + .expected_type + .as_ref() + .map(|ty| ty.strip_references()) + .and_then(|ty| suggest_name::for_type(&ty, ctx.db, ctx.edition)) + { + acc.suggest_name(ctx, &suggested); + } + } + let refutable = pattern_ctx.refutability == PatternRefutability::Refutable; let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index bcd9df941947f..d457ba32bf003 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -264,6 +264,7 @@ pub(crate) struct PatternContext { pub(crate) refutability: PatternRefutability, pub(crate) param_ctx: Option, pub(crate) has_type_ascription: bool, + pub(crate) should_suggest_name: bool, pub(crate) parent_pat: Option, pub(crate) ref_token: Option, pub(crate) mut_token: Option, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 292c419498d16..1f9e3edf625d1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1430,10 +1430,23 @@ fn pattern_context_for( _ => (None, None), }; + // Only suggest name in let-stmt or fn param + let should_suggest_name = matches!( + &pat, + ast::Pat::IdentPat(it) + if it.syntax() + .parent() + .map_or(false, |node| { + let kind = node.kind(); + ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind) + }) + ); + PatternContext { refutability, param_ctx, has_type_ascription, + should_suggest_name, parent_pat: pat.syntax().parent().and_then(ast::Pat::cast), mut_token, ref_token, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 14128e74438bc..6ee526a67ea56 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -60,6 +60,21 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; +/// Suggest a name for given type. +/// +/// The function will strip references first, and suggest name from the inner type. +/// +/// - If `ty` is an ADT, it will suggest the name of the ADT. +/// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type. +/// - If `ty` is a trait, it will suggest the name of the trait. +/// - If `ty` is an `impl Trait`, it will suggest the name of the first trait. +/// +/// If the suggested name conflicts with reserved keywords, it will return `None`. +pub fn for_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option { + let ty = ty.strip_references(); + name_of_type(&ty, db, edition) +} + /// Suggest a unique name for generic parameter. /// /// `existing_params` is used to check if the name conflicts with existing @@ -269,10 +284,9 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option { fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); - let ty = ty.remove_ref().unwrap_or(ty); let edition = sema.scope(expr.syntax())?.krate().edition(sema.db); - name_of_type(&ty, sema.db, edition) + for_type(&ty, sema.db, edition) } fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option { From ef491f22999bb964ac92b64935d7ffe722e9c5a8 Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 3 Sep 2024 05:16:02 +0800 Subject: [PATCH 0233/1301] tests: suggesting names in completions for let_stmt and fn_param --- .../ide-completion/src/tests/pattern.rs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs index 6a0b67e291af8..bd3e7c72bcd65 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs @@ -198,6 +198,7 @@ fn foo(a$0: Tuple) { st Unit bn Record {…} Record { field$1 }$0 bn Tuple(…) Tuple($1)$0 + bn tuple kw mut kw ref "#]], @@ -850,3 +851,75 @@ fn foo() { "#, ); } + +#[test] +fn suggest_name_for_pattern() { + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = S1; +} +"#, + ); + + check_edit( + "s1", + r#" +struct S1; + +fn foo(s$0: S1) { +} +"#, + r#" +struct S1; + +fn foo(s1: S1) { +} +"#, + ); + + // Tests for &adt + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = &S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = &S1; +} +"#, + ); + + // Do not suggest reserved keywords + check_empty( + r#" +struct Struct; + +fn foo() { + let $0 = Struct; +} +"#, + expect![[r#" + st Struct + kw mut + kw ref + "#]], + ); +} From e4bce9866e661bd875f135cc4ba027ca58c20f7a Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 18:24:47 -0400 Subject: [PATCH 0234/1301] propagate annotations to mapped elements --- .../crates/syntax/src/syntax_editor.rs | 19 ++- .../syntax/src/syntax_editor/edit_algo.rs | 50 ++++-- .../syntax/src/syntax_editor/mapping.rs | 151 +++++++++++++----- 3 files changed, 168 insertions(+), 52 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 42373eba827d2..75347aeeed909 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -75,8 +75,8 @@ impl SyntaxEdit { &self.root } - /// Which syntax elements in the modified syntax tree were modified as part - /// of the edit. + /// Which syntax elements in the modified syntax tree were inserted or + /// modified as part of the edit. /// /// Note that for syntax nodes, only the upper-most parent of a set of /// changes is included, not any child elements that may have been modified. @@ -343,11 +343,22 @@ mod tests { editor.replace(to_wrap.syntax(), new_block.syntax()); // editor.replace(to_replace.syntax(), name_ref.syntax()); - // dbg!(&editor.mappings); let edit = editor.finish(); - let expect = expect![]; + dbg!(&edit.annotations); + + let expect = expect![[r#" + _ => { + let var_name = 2 + 2; + (var_name, true) + }"#]]; expect.assert_eq(&edit.root.to_string()); + assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); + assert!(edit + .annotations + .iter() + .flat_map(|(_, elements)| elements) + .all(|element| element.ancestors().any(|it| &it == edit.root()))) } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index f8354c5eb67cd..734a26b60033d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -1,9 +1,10 @@ use std::{collections::VecDeque, ops::RangeInclusive}; use rowan::TextRange; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ - syntax_editor::{Change, ChangeKind}, + syntax_editor::{mapping::MissingMapping, Change, ChangeKind}, ted, SyntaxElement, SyntaxNode, SyntaxNodePtr, }; @@ -29,9 +30,6 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { let SyntaxEditor { root, mut changes, mappings, annotations } = editor; - dbg!(("initial: ", &root)); - dbg!(&changes); - // Sort changes by range then change kind, so that we can: // - ensure that parent edits are ordered before child edits // - ensure that inserts will be guaranteed to be inserted at the right range @@ -102,15 +100,18 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } } - dbg!(("before: ", &changes, &dependent_changes, &independent_changes)); - // Map change targets to the correct syntax nodes let tree_mutator = TreeMutator::new(&root); + let mut changed_elements = vec![]; for index in independent_changes { match &mut changes[index as usize] { - Change::Replace(target, _) => { + Change::Replace(target, new_node) => { *target = tree_mutator.make_element_mut(target); + + if let Some(new_node) = new_node { + changed_elements.push(new_node.clone()); + } } } } @@ -124,15 +125,20 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { Change::Replace(_, None) => continue, // silently drop outdated change }; + let upmap_target = |target: &SyntaxElement| { + match mappings.upmap_child_element(target, &input_ancestor, &output_ancestor) { + Ok(it) => it, + Err(MissingMapping(current)) => unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}"), + } + }; + match &mut changes[child as usize] { Change::Replace(target, _) => { - *target = mappings.upmap_child_element(target, &input_ancestor, output_ancestor) + *target = upmap_target(&target); } } } - dbg!(("after: ", &changes)); - // Apply changes for change in changes { match change { @@ -141,9 +147,29 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } } - dbg!(("modified:", tree_mutator.mutable_clone)); + // Propagate annotations + let annotations = annotations.into_iter().filter_map(|(element, annotation)| { + match mappings.upmap_element(&element, &tree_mutator.mutable_clone) { + // Needed to follow the new tree to find the resulting element + Some(Ok(mapped)) => Some((mapped, annotation)), + // Element did not need to be mapped + None => Some((element, annotation)), + // Element did not make it to the final tree + Some(Err(_)) => None, + } + }); + + let mut annotation_groups = FxHashMap::default(); - todo!("draw the rest of the owl") + for (element, annotation) in annotations { + annotation_groups.entry(annotation).or_insert(vec![]).push(element); + } + + SyntaxEdit { + root: tree_mutator.mutable_clone, + changed_elements, + annotations: annotation_groups, + } } fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 8a79f7e186eda..f14d0b347d780 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -22,19 +22,20 @@ impl SyntaxMapping { Self::default() } + /// Like [`SyntaxMapping::upmap_child`] but for syntax elements. pub fn upmap_child_element( &self, child: &SyntaxElement, input_ancestor: &SyntaxNode, - output_ancestor: SyntaxNode, - ) -> SyntaxElement { + output_ancestor: &SyntaxNode, + ) -> Result { match child { SyntaxElement::Node(node) => { - SyntaxElement::Node(self.upmap_child(node, input_ancestor, output_ancestor)) + self.upmap_child(node, input_ancestor, output_ancestor).map(SyntaxElement::Node) } SyntaxElement::Token(token) => { let upmap_parent = - self.upmap_child(&token.parent().unwrap(), input_ancestor, output_ancestor); + self.upmap_child(&token.parent().unwrap(), input_ancestor, output_ancestor)?; let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); debug_assert!( @@ -42,21 +43,26 @@ impl SyntaxMapping { "token upmapping mapped to the wrong node ({token:?} -> {element:?})" ); - element + Ok(element) } } } + /// Maps a child node of the input ancestor to the corresponding node in + /// the output ancestor. pub fn upmap_child( &self, child: &SyntaxNode, input_ancestor: &SyntaxNode, - output_ancestor: SyntaxNode, - ) -> SyntaxNode { - debug_assert!(child.ancestors().any(|ancestor| &ancestor == input_ancestor)); + output_ancestor: &SyntaxNode, + ) -> Result { + debug_assert!( + child == input_ancestor + || child.ancestors().any(|ancestor| &ancestor == input_ancestor) + ); // Build a list mapping up to the first mappable ancestor - let to_first_upmap = + let to_first_upmap = if child != input_ancestor { std::iter::successors(Some((child.index(), child.clone())), |(_, current)| { let parent = current.parent().unwrap(); @@ -67,24 +73,14 @@ impl SyntaxMapping { Some((parent.index(), parent)) }) .map(|(i, _)| i) - .collect::>(); + .collect::>() + } else { + vec![] + }; // Progressively up-map the input ancestor until we get to the output ancestor - let to_output_ancestor = if input_ancestor != &output_ancestor { - std::iter::successors(Some((input_ancestor.index(), self.upmap_node(input_ancestor).unwrap_or_else(|| input_ancestor.clone()))), |(_, current)| { - let Some(parent) = current.parent() else { - unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}") - }; - - if &parent == &output_ancestor { - return None; - } - - Some((parent.index(), match self.upmap_node(&parent) { - Some(next) => next, - None => parent - })) - }).map(|(i, _)| i).collect::>() + let to_output_ancestor = if input_ancestor != output_ancestor { + self.upmap_to_ancestor(input_ancestor, output_ancestor)? } else { vec![] }; @@ -92,7 +88,7 @@ impl SyntaxMapping { let to_map_down = to_output_ancestor.into_iter().rev().chain(to_first_upmap.into_iter().rev()); - let mut target = output_ancestor; + let mut target = output_ancestor.clone(); for index in to_map_down { target = target @@ -104,20 +100,86 @@ impl SyntaxMapping { debug_assert_eq!(child.kind(), target.kind()); - target + Ok(target) } - pub fn upmap_node(&self, input: &SyntaxNode) -> Option { - let MappingEntry { parent, child_slot } = self.node_mappings.get(input)?; + fn upmap_to_ancestor( + &self, + input_ancestor: &SyntaxNode, + output_ancestor: &SyntaxNode, + ) -> Result, MissingMapping> { + eprintln!("mapping ancestor {input_ancestor:#?} to {output_ancestor:#?}"); + let mut current = + self.upmap_node_single(input_ancestor).unwrap_or_else(|| input_ancestor.clone()); + let mut upmap_chain = vec![current.index()]; + + loop { + let Some(parent) = current.parent() else { break }; + + if &parent == output_ancestor { + return Ok(upmap_chain); + } - let output = self.entry_parents[*parent as usize] - .children_with_tokens() - .nth(*child_slot as usize) - .and_then(SyntaxElement::into_node) - .unwrap(); + current = match self.upmap_node_single(&parent) { + Some(next) => next, + None => parent, + }; + upmap_chain.push(current.index()); + } - debug_assert_eq!(input.kind(), output.kind()); - Some(output) + Err(MissingMapping(current)) + } + + pub fn upmap_element( + &self, + input: &SyntaxElement, + output_root: &SyntaxNode, + ) -> Option> { + match input { + SyntaxElement::Node(node) => { + Some(self.upmap_node(node, output_root)?.map(SyntaxElement::Node)) + } + SyntaxElement::Token(token) => { + let upmap_parent = match self.upmap_node(&token.parent().unwrap(), output_root)? { + Ok(it) => it, + Err(err) => return Some(Err(err)), + }; + + let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); + debug_assert!( + element.as_token().is_some_and(|it| it.kind() == token.kind()), + "token upmapping mapped to the wrong node ({token:?} -> {element:?})" + ); + + Some(Ok(element)) + } + } + } + + pub fn upmap_node( + &self, + input: &SyntaxNode, + output_root: &SyntaxNode, + ) -> Option> { + // Try to follow the mapping tree, if it exists + let input_mapping = self.upmap_node_single(input); + let input_ancestor = + input.ancestors().find_map(|ancestor| self.upmap_node_single(&ancestor)); + + match (input_mapping, input_ancestor) { + (Some(input_mapping), _) => { + // A mapping exists at the input, follow along the tree + Some(self.upmap_child(&input_mapping, &input_mapping, &output_root)) + } + (None, Some(input_ancestor)) => { + // A mapping exists at an ancestor, follow along the tree + Some(self.upmap_child(input, &input_ancestor, &output_root)) + } + (None, None) => { + // No mapping exists at all, is the same position in the final tree + None + } + } } pub fn merge(&mut self, mut other: SyntaxMapping) { @@ -130,6 +192,20 @@ impl SyntaxMapping { })); } + /// Follows the input one step along the syntax mapping tree + fn upmap_node_single(&self, input: &SyntaxNode) -> Option { + let MappingEntry { parent, child_slot } = self.node_mappings.get(input)?; + + let output = self.entry_parents[*parent as usize] + .children_with_tokens() + .nth(*child_slot as usize) + .and_then(SyntaxElement::into_node) + .unwrap(); + + debug_assert_eq!(input.kind(), output.kind()); + Some(output) + } + fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) { let SyntaxMappingBuilder { parent_node, node_mappings } = syntax_mapping; @@ -183,6 +259,9 @@ impl SyntaxMappingBuilder { } } +#[derive(Debug)] +pub struct MissingMapping(pub SyntaxNode); + #[derive(Debug, Clone, Copy)] struct MappingEntry { parent: u32, From 8104457a1197353f1ed276117a4603076f26e42f Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 19:11:39 -0400 Subject: [PATCH 0235/1301] support insert{_all} --- .../crates/syntax/src/syntax_editor.rs | 32 +++++++++ .../syntax/src/syntax_editor/edit_algo.rs | 67 ++++++++++++++++--- 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 75347aeeed909..4b5134249c8d6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -50,6 +50,14 @@ impl SyntaxEditor { self.annotations.append(&mut other.annotations); } + pub fn insert(&mut self, position: Position, element: impl Element) { + self.changes.push(Change::Insert(position, element.syntax_element())) + } + + pub fn insert_all(&mut self, position: Position, elements: Vec) { + self.changes.push(Change::InsertAll(position, elements)) + } + pub fn delete(&mut self, element: impl Element) { self.changes.push(Change::Replace(element.syntax_element(), None)); } @@ -117,6 +125,19 @@ pub struct Position { repr: PositionRepr, } +impl Position { + pub(crate) fn parent(&self) -> SyntaxNode { + self.place().0 + } + + pub(crate) fn place(&self) -> (SyntaxNode, usize) { + match &self.repr { + PositionRepr::FirstChild(parent) => (parent.clone(), 0), + PositionRepr::After(child) => (child.parent().unwrap(), child.index() + 1), + } + } +} + #[derive(Debug)] enum PositionRepr { FirstChild(SyntaxNode), @@ -155,6 +176,8 @@ impl Position { #[derive(Debug)] enum Change { + Insert(Position, SyntaxElement), + InsertAll(Position, Vec), /// Represents both a replace single element and a delete element operation. Replace(SyntaxElement, Option), } @@ -162,18 +185,27 @@ enum Change { impl Change { fn target_range(&self) -> TextRange { match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &target.repr { + PositionRepr::FirstChild(parent) => TextRange::at( + parent.first_child_or_token().unwrap().text_range().start(), + 0.into(), + ), + PositionRepr::After(child) => TextRange::at(child.text_range().end(), 0.into()), + }, Change::Replace(target, _) => target.text_range(), } } fn target_parent(&self) -> SyntaxNode { match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => target.parent(), Change::Replace(target, _) => target.parent().unwrap(), } } fn change_kind(&self) -> ChangeKind { match self { + Change::Insert(_, _) | Change::InsertAll(_, _) => ChangeKind::Insert, Change::Replace(_, _) => ChangeKind::Replace, } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 734a26b60033d..4f0b30ed6ad14 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -4,8 +4,8 @@ use rowan::TextRange; use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ - syntax_editor::{mapping::MissingMapping, Change, ChangeKind}, - ted, SyntaxElement, SyntaxNode, SyntaxNodePtr, + syntax_editor::{mapping::MissingMapping, Change, ChangeKind, Position, PositionRepr}, + SyntaxElement, SyntaxNode, SyntaxNodePtr, }; use super::{SyntaxEdit, SyntaxEditor}; @@ -94,6 +94,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Add to changed ancestors, if applicable match change { + Change::Insert(_, _) | Change::InsertAll(_, _) => {} Change::Replace(target, _) => { changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) } @@ -106,23 +107,46 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { for index in independent_changes { match &mut changes[index as usize] { - Change::Replace(target, new_node) => { + Change::Insert(target, _) | Change::InsertAll(target, _) => { + match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = tree_mutator.make_syntax_mut(parent); + } + PositionRepr::After(child) => { + *child = tree_mutator.make_element_mut(child); + } + }; + } + Change::Replace(target, _) => { *target = tree_mutator.make_element_mut(target); - - if let Some(new_node) = new_node { - changed_elements.push(new_node.clone()); - } } } + + // Collect changed elements + match &changes[index as usize] { + Change::Insert(_, element) => changed_elements.push(element.clone()), + Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + Change::Replace(_, Some(element)) => changed_elements.push(element.clone()), + Change::Replace(_, None) => {} + } } for DependentChange { parent, child } in dependent_changes.into_iter() { let (input_ancestor, output_ancestor) = match &changes[parent as usize] { - // insert? unreachable + // No change will depend on an insert since changes can only depend on nodes in the root tree + Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), Change::Replace(target, Some(new_target)) => { (to_owning_node(target), to_owning_node(new_target)) } - Change::Replace(_, None) => continue, // silently drop outdated change + // Silently drop outdated change + Change::Replace(_, None) => continue, + }; + + let upmap_target_node = |target: &SyntaxNode| { + match mappings.upmap_child(target, &input_ancestor, &output_ancestor) { + Ok(it) => it, + Err(MissingMapping(current)) => unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}"), + } }; let upmap_target = |target: &SyntaxElement| { @@ -133,6 +157,14 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { }; match &mut changes[child as usize] { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = upmap_target_node(parent); + } + PositionRepr::After(child) => { + *child = upmap_target(child); + } + }, Change::Replace(target, _) => { *target = upmap_target(&target); } @@ -142,8 +174,21 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Apply changes for change in changes { match change { - Change::Replace(target, None) => ted::remove(target), - Change::Replace(target, Some(new_target)) => ted::replace(target, new_target), + Change::Insert(position, element) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, vec![element]); + } + Change::InsertAll(position, elements) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, elements); + } + Change::Replace(target, None) => { + target.detach(); + } + Change::Replace(target, Some(new_target)) => { + let parent = target.parent().unwrap(); + parent.splice_children(target.index()..target.index() + 1, vec![new_target]); + } } } From f08299f9f8014ed9fb0f13f76afaebdb7cb9d2ab Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 20:45:57 -0400 Subject: [PATCH 0236/1301] fix insert ranges not being excluded from disjointness --- .../syntax/src/syntax_editor/edit_algo.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 4f0b30ed6ad14..4d6cb01c01c01 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -1,10 +1,10 @@ use std::{collections::VecDeque, ops::RangeInclusive}; use rowan::TextRange; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use crate::{ - syntax_editor::{mapping::MissingMapping, Change, ChangeKind, Position, PositionRepr}, + syntax_editor::{mapping::MissingMapping, Change, ChangeKind, PositionRepr}, SyntaxElement, SyntaxNode, SyntaxNodePtr, }; @@ -41,12 +41,17 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { .then(a.change_kind().cmp(&b.change_kind())) }); - let disjoint_replaces_ranges = changes.iter().zip(changes.iter().skip(1)).all(|(l, r)| { - l.change_kind() == ChangeKind::Replace - && r.change_kind() == ChangeKind::Replace - && (l.target_parent() != r.target_parent() + let disjoint_replaces_ranges = changes + .iter() + .zip(changes.iter().skip(1)) + .filter(|(l, r)| { + // We only care about checking for disjoint replace ranges + l.change_kind() == ChangeKind::Replace && r.change_kind() == ChangeKind::Replace + }) + .all(|(l, r)| { + (l.target_parent() != r.target_parent() || l.target_range().intersect(r.target_range()).is_none()) - }); + }); if stdx::never!( !disjoint_replaces_ranges, From f74ef3aa526f1b293f37b6574d5d24f5532d810f Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 21:34:00 -0400 Subject: [PATCH 0237/1301] properly sort changes by depth to sort between nodes that have the same start range --- .../syntax/src/syntax_editor/edit_algo.rs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 4d6cb01c01c01..2adc1f67d144c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -1,4 +1,4 @@ -use std::{collections::VecDeque, ops::RangeInclusive}; +use std::{cmp::Ordering, collections::VecDeque, ops::RangeInclusive}; use rowan::TextRange; use rustc_hash::FxHashMap; @@ -30,7 +30,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { let SyntaxEditor { root, mut changes, mappings, annotations } = editor; - // Sort changes by range then change kind, so that we can: + let mut node_depths = FxHashMap::::default(); + let mut get_node_depth = |node: SyntaxNode| { + *node_depths.entry(node).or_insert_with_key(|node| node.ancestors().count()) + }; + + // Sort changes by range, then depth, then change kind, so that we can: // - ensure that parent edits are ordered before child edits // - ensure that inserts will be guaranteed to be inserted at the right range // - easily check for disjoint replace ranges @@ -38,6 +43,16 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { a.target_range() .start() .cmp(&b.target_range().start()) + .then_with(|| { + let a_target = a.target_parent(); + let b_target = b.target_parent(); + + if a_target == b_target { + return Ordering::Equal; + } + + get_node_depth(a_target).cmp(&get_node_depth(b_target)) + }) .then(a.change_kind().cmp(&b.change_kind())) }); @@ -49,8 +64,8 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { l.change_kind() == ChangeKind::Replace && r.change_kind() == ChangeKind::Replace }) .all(|(l, r)| { - (l.target_parent() != r.target_parent() - || l.target_range().intersect(r.target_range()).is_none()) + get_node_depth(l.target_parent()) != get_node_depth(r.target_parent()) + || l.target_range().intersect(r.target_range()).is_none() }); if stdx::never!( From f03f95f36918c5d64ead3fdafc920915ac204e04 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 21:42:08 -0400 Subject: [PATCH 0238/1301] support replacing root node --- .../crates/syntax/src/syntax_editor.rs | 197 +++++++++++++++++- .../syntax/src/syntax_editor/edit_algo.rs | 16 +- .../syntax/src/syntax_editor/mapping.rs | 1 - 3 files changed, 200 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 4b5134249c8d6..77a560bfe5c45 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -51,18 +51,28 @@ impl SyntaxEditor { } pub fn insert(&mut self, position: Position, element: impl Element) { + debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); self.changes.push(Change::Insert(position, element.syntax_element())) } pub fn insert_all(&mut self, position: Position, elements: Vec) { + debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); self.changes.push(Change::InsertAll(position, elements)) } pub fn delete(&mut self, element: impl Element) { + let element = element.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&element, &self.root)); + debug_assert!( + !matches!(&element, SyntaxElement::Node(node) if node == &self.root), + "should not delete root node" + ); self.changes.push(Change::Replace(element.syntax_element(), None)); } pub fn replace(&mut self, old: impl Element, new: impl Element) { + let old = old.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); self.changes.push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); } @@ -199,7 +209,10 @@ impl Change { fn target_parent(&self) -> SyntaxNode { match self { Change::Insert(target, _) | Change::InsertAll(target, _) => target.parent(), - Change::Replace(target, _) => target.parent().unwrap(), + Change::Replace(SyntaxElement::Node(target), _) => { + target.parent().unwrap_or_else(|| target.clone()) + } + Change::Replace(SyntaxElement::Token(target), _) => target.parent().unwrap(), } } @@ -248,6 +261,15 @@ impl Element for SyntaxToken { } } +fn is_ancestor_or_self(node: &SyntaxNode, ancestor: &SyntaxNode) -> bool { + node == ancestor || node.ancestors().any(|it| &it == ancestor) +} + +fn is_ancestor_or_self_of_element(node: &SyntaxElement, ancestor: &SyntaxNode) -> bool { + matches!(node, SyntaxElement::Node(node) if node == ancestor) + || node.ancestors().any(|it| &it == ancestor) +} + #[cfg(test)] mod tests { use expect_test::expect; @@ -370,15 +392,11 @@ mod tests { Some(to_wrap.clone().into()), ); - // should die: editor.replace(to_replace.syntax(), name_ref.syntax()); editor.replace(to_wrap.syntax(), new_block.syntax()); - // editor.replace(to_replace.syntax(), name_ref.syntax()); let edit = editor.finish(); - dbg!(&edit.annotations); - let expect = expect![[r#" _ => { let var_name = 2 + 2; @@ -393,4 +411,173 @@ mod tests { .flat_map(|(_, elements)| elements) .all(|element| element.ancestors().any(|it| &it == edit.root()))) } + + #[test] + #[should_panic = "some replace change ranges intersect: [Replace(Node(TUPLE_EXPR@5..7), Some(Node(NAME_REF@0..8))), Replace(Node(TUPLE_EXPR@5..7), Some(Node(NAME_REF@0..8)))]"] + fn fail_on_non_disjoint_single_replace() { + let root = make::match_arm([make::wildcard_pat().into()], None, make::expr_tuple([])); + + let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let name_ref = make::name_ref("var_name").clone_for_update(); + + // should die, ranges are not disjoint + editor.replace(to_wrap.syntax(), name_ref.syntax()); + editor.replace(to_wrap.syntax(), name_ref.syntax()); + + let _ = editor.finish(); + } + + #[test] + fn test_insert_independent() { + let root = make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ); + + let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + editor.insert( + Position::first_child_of(root.stmt_list().unwrap().syntax()), + make_let_stmt( + None, + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ) + .syntax(), + ); + + editor.insert( + Position::after(second_let.syntax()), + make_let_stmt( + None, + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ) + .syntax(), + ); + + let edit = editor.finish(); + + let expect = expect![[r#" + let first = 1;{ + let second = 2;let third = 3; + }"#]]; + expect.assert_eq(&edit.root.to_string()); + } + + #[test] + fn test_insert_dependent() { + let root = make::block_expr( + [], + Some( + make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ) + .into(), + ), + ); + + let inner_block = + root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); + let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let new_block_expr = + make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + let third_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.insert(Position::after(second_let.syntax()), third_let.syntax()); + editor.replace(inner_block.syntax(), new_block_expr.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + { + let first = 1;{ + let second = 2;let third = 3; + } + } + }"#]]; + expect.assert_eq(&edit.root.to_string()); + } + + #[test] + fn test_replace_root_with_dependent() { + let root = make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ); + + let inner_block = root.clone(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let new_block_expr = + make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.replace(inner_block.syntax(), new_block_expr.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + let first = 1;{ + let second = 2; + } + }"#]]; + expect.assert_eq(&edit.root.to_string()); + } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 2adc1f67d144c..2c331fc1f69e3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -192,6 +192,8 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } // Apply changes + let mut root = tree_mutator.mutable_clone; + for change in changes { match change { Change::Insert(position, element) => { @@ -205,6 +207,9 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { Change::Replace(target, None) => { target.detach(); } + Change::Replace(SyntaxElement::Node(target), Some(new_target)) if &target == &root => { + root = new_target.into_node().expect("root node replacement should be a node"); + } Change::Replace(target, Some(new_target)) => { let parent = target.parent().unwrap(); parent.splice_children(target.index()..target.index() + 1, vec![new_target]); @@ -214,7 +219,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Propagate annotations let annotations = annotations.into_iter().filter_map(|(element, annotation)| { - match mappings.upmap_element(&element, &tree_mutator.mutable_clone) { + match mappings.upmap_element(&element, &root) { // Needed to follow the new tree to find the resulting element Some(Ok(mapped)) => Some((mapped, annotation)), // Element did not need to be mapped @@ -230,11 +235,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { annotation_groups.entry(annotation).or_insert(vec![]).push(element); } - SyntaxEdit { - root: tree_mutator.mutable_clone, - changed_elements, - annotations: annotation_groups, - } + SyntaxEdit { root, changed_elements, annotations: annotation_groups } } fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { @@ -278,7 +279,6 @@ impl ChangedAncestor { } struct TreeMutator { - immutable: SyntaxNode, mutable_clone: SyntaxNode, } @@ -286,7 +286,7 @@ impl TreeMutator { fn new(immutable: &SyntaxNode) -> TreeMutator { let immutable = immutable.clone(); let mutable_clone = immutable.clone_for_update(); - TreeMutator { immutable, mutable_clone } + TreeMutator { mutable_clone } } fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index f14d0b347d780..b2c677c869669 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -108,7 +108,6 @@ impl SyntaxMapping { input_ancestor: &SyntaxNode, output_ancestor: &SyntaxNode, ) -> Result, MissingMapping> { - eprintln!("mapping ancestor {input_ancestor:#?} to {output_ancestor:#?}"); let mut current = self.upmap_node_single(input_ancestor).unwrap_or_else(|| input_ancestor.clone()); let mut upmap_chain = vec![current.index()]; From f6e05a744dcf47aaa229cac246228268394263d8 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 22:27:14 -0400 Subject: [PATCH 0239/1301] handle replace_with_many and replace_all --- .../crates/syntax/src/syntax_editor.rs | 47 +++++++++++-- .../syntax/src/syntax_editor/edit_algo.rs | 66 ++++++++++++++++--- 2 files changed, 97 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 77a560bfe5c45..3a05cc480b210 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -6,6 +6,7 @@ use std::{ num::NonZeroU32, + ops::RangeInclusive, sync::atomic::{AtomicU32, Ordering}, }; @@ -76,6 +77,26 @@ impl SyntaxEditor { self.changes.push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); } + pub fn replace_with_many(&mut self, old: impl Element, new: Vec) { + let old = old.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); + debug_assert!( + !(matches!(&old, SyntaxElement::Node(node) if node == &self.root) && new.len() > 1), + "cannot replace root node with many elements" + ); + self.changes.push(Change::ReplaceWithMany(old.syntax_element(), new)); + } + + pub fn replace_all(&mut self, range: RangeInclusive, new: Vec) { + if range.start() == range.end() { + self.replace_with_many(range.start(), new); + return; + } + + debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root)); + self.changes.push(Change::ReplaceAll(range, new)) + } + pub fn finish(self) -> SyntaxEdit { edit_algo::apply_edits(self) } @@ -186,10 +207,17 @@ impl Position { #[derive(Debug)] enum Change { + /// Inserts a single element at the specified position. Insert(Position, SyntaxElement), + /// Inserts many elements in-order at the specified position. InsertAll(Position, Vec), /// Represents both a replace single element and a delete element operation. Replace(SyntaxElement, Option), + /// Replaces a single element with many elements. + ReplaceWithMany(SyntaxElement, Vec), + /// Replaces a range of elements with another list of elements. + /// Range will always have start != end. + ReplaceAll(RangeInclusive, Vec), } impl Change { @@ -202,24 +230,29 @@ impl Change { ), PositionRepr::After(child) => TextRange::at(child.text_range().end(), 0.into()), }, - Change::Replace(target, _) => target.text_range(), + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => target.text_range(), + Change::ReplaceAll(range, _) => { + range.start().text_range().cover(range.end().text_range()) + } } } fn target_parent(&self) -> SyntaxNode { match self { Change::Insert(target, _) | Change::InsertAll(target, _) => target.parent(), - Change::Replace(SyntaxElement::Node(target), _) => { - target.parent().unwrap_or_else(|| target.clone()) - } - Change::Replace(SyntaxElement::Token(target), _) => target.parent().unwrap(), + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => match target { + SyntaxElement::Node(target) => target.parent().unwrap_or_else(|| target.clone()), + SyntaxElement::Token(target) => target.parent().unwrap(), + }, + Change::ReplaceAll(target, _) => target.start().parent().unwrap(), } } fn change_kind(&self) -> ChangeKind { match self { Change::Insert(_, _) | Change::InsertAll(_, _) => ChangeKind::Insert, - Change::Replace(_, _) => ChangeKind::Replace, + Change::Replace(_, _) | Change::ReplaceWithMany(_, _) => ChangeKind::Replace, + Change::ReplaceAll(_, _) => ChangeKind::ReplaceRange, } } } @@ -227,7 +260,7 @@ impl Change { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] enum ChangeKind { Insert, - // TODO: deal with replace spans + ReplaceRange, Replace, } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 2c331fc1f69e3..3b92ac1cbd8a0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -61,7 +61,13 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { .zip(changes.iter().skip(1)) .filter(|(l, r)| { // We only care about checking for disjoint replace ranges - l.change_kind() == ChangeKind::Replace && r.change_kind() == ChangeKind::Replace + matches!( + (l.change_kind(), r.change_kind()), + ( + ChangeKind::Replace | ChangeKind::ReplaceRange, + ChangeKind::Replace | ChangeKind::ReplaceRange + ) + ) }) .all(|(l, r)| { get_node_depth(l.target_parent()) != get_node_depth(r.target_parent()) @@ -97,6 +103,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Pop off any ancestors that aren't applicable changed_ancestors.drain((index + 1)..); + // FIXME: Resolve changes that depend on a range of elements let ancestor = &changed_ancestors[index]; dependent_changes.push(DependentChange { @@ -115,9 +122,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Add to changed ancestors, if applicable match change { Change::Insert(_, _) | Change::InsertAll(_, _) => {} - Change::Replace(target, _) => { + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) } + Change::ReplaceAll(range, _) => { + changed_ancestors.push_back(ChangedAncestor::multiple(range, change_index)) + } } } @@ -137,9 +147,15 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } }; } - Change::Replace(target, _) => { + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { *target = tree_mutator.make_element_mut(target); } + Change::ReplaceAll(range, _) => { + let start = tree_mutator.make_element_mut(range.start()); + let end = tree_mutator.make_element_mut(range.end()); + + *range = start..=end; + } } // Collect changed elements @@ -148,6 +164,10 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()), Change::Replace(_, Some(element)) => changed_elements.push(element.clone()), Change::Replace(_, None) => {} + Change::ReplaceWithMany(_, elements) => { + changed_elements.extend(elements.iter().cloned()) + } + Change::ReplaceAll(_, elements) => changed_elements.extend(elements.iter().cloned()), } } @@ -160,6 +180,9 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } // Silently drop outdated change Change::Replace(_, None) => continue, + Change::ReplaceAll(_, _) | Change::ReplaceWithMany(_, _) => { + unimplemented!("cannot resolve changes that depend on replacing many elements") + } }; let upmap_target_node = |target: &SyntaxNode| { @@ -185,9 +208,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { *child = upmap_target(child); } }, - Change::Replace(target, _) => { + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { *target = upmap_target(&target); } + Change::ReplaceAll(range, _) => { + *range = upmap_target(range.start())..=upmap_target(range.end()); + } } } @@ -214,6 +240,16 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { let parent = target.parent().unwrap(); parent.splice_children(target.index()..target.index() + 1, vec![new_target]); } + Change::ReplaceWithMany(target, elements) => { + let parent = target.parent().unwrap(); + parent.splice_children(target.index()..target.index() + 1, elements); + } + Change::ReplaceAll(range, elements) => { + let start = range.start().index(); + let end = range.end().index(); + let parent = range.start().parent().unwrap(); + parent.splice_children(start..end + 1, elements); + } } } @@ -252,7 +288,7 @@ struct ChangedAncestor { enum ChangedAncestorKind { Single { node: SyntaxNode }, - Range { changed_nodes: RangeInclusive, in_parent: SyntaxNode }, + Range { _changed_elements: RangeInclusive, in_parent: SyntaxNode }, } impl ChangedAncestor { @@ -267,13 +303,25 @@ impl ChangedAncestor { Self { kind, change_index } } + fn multiple(range: &RangeInclusive, change_index: usize) -> Self { + Self { + kind: ChangedAncestorKind::Range { + _changed_elements: range.clone(), + in_parent: range.start().parent().unwrap(), + }, + change_index, + } + } + fn affected_range(&self) -> TextRange { match &self.kind { ChangedAncestorKind::Single { node } => node.text_range(), - ChangedAncestorKind::Range { changed_nodes, in_parent: _ } => TextRange::new( - changed_nodes.start().text_range().start(), - changed_nodes.end().text_range().end(), - ), + ChangedAncestorKind::Range { _changed_elements: changed_nodes, in_parent: _ } => { + TextRange::new( + changed_nodes.start().text_range().start(), + changed_nodes.end().text_range().end(), + ) + } } } } From 9d407bfd1b69801d53c9c01dc8988f62d6235fed Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Sun, 1 Sep 2024 08:36:44 +0200 Subject: [PATCH 0240/1301] Avoid allocating intermediate strings in `ItemizedBlock::trimmed_block_as_string` --- src/comment.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/comment.rs b/src/comment.rs index c8cadf364daf8..1e35395cfb31f 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -533,10 +533,11 @@ impl ItemizedBlock { /// Returns the block as a string, with each line trimmed at the start. fn trimmed_block_as_string(&self) -> String { - self.lines - .iter() - .map(|line| format!("{} ", line.trim_start())) - .collect::() + self.lines.iter().fold(String::new(), |mut acc, line| { + acc.push_str(line.trim_start()); + acc.push(' '); + acc + }) } /// Returns the block as a string under its original form. From 5d30ce68441fabbf4e2b1c789b1af2327d9dc869 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Sun, 1 Sep 2024 09:25:05 +0200 Subject: [PATCH 0241/1301] Avoid allocating intermediate strings in `determine_operation` --- src/bin/main.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 14299434bc747..3a0427d83720d 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -462,16 +462,15 @@ fn print_version() { fn determine_operation(matches: &Matches) -> Result { if matches.opt_present("h") { - let topic = matches.opt_str("h"); - if topic.is_none() { + let Some(topic) = matches.opt_str("h") else { return Ok(Operation::Help(HelpOp::None)); - } else if topic == Some("config".to_owned()) { - return Ok(Operation::Help(HelpOp::Config)); - } else if topic == Some("file-lines".to_owned()) && is_nightly() { - return Ok(Operation::Help(HelpOp::FileLines)); - } else { - return Err(OperationError::UnknownHelpTopic(topic.unwrap())); - } + }; + + return match topic.as_str() { + "config" => Ok(Operation::Help(HelpOp::Config)), + "file-lines" if is_nightly() => Ok(Operation::Help(HelpOp::FileLines)), + _ => Err(OperationError::UnknownHelpTopic(topic)), + }; } let mut free_matches = matches.free.iter(); From 4e81ca344b6ce53b11309d14bc4ea72e39db5b83 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Mon, 2 Sep 2024 22:53:54 -0400 Subject: [PATCH 0242/1301] misc fixes --- .../crates/syntax/src/syntax_editor.rs | 24 +++++-------------- .../syntax/src/syntax_editor/edit_algo.rs | 14 ++++++----- .../syntax/src/syntax_editor/mapping.rs | 8 +++++-- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 3a05cc480b210..139c6518bf7cb 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -150,6 +150,12 @@ impl SyntaxAnnotation { } } +impl Default for SyntaxAnnotation { + fn default() -> Self { + Self::new() + } +} + /// Position describing where to insert elements #[derive(Debug)] pub struct Position { @@ -445,24 +451,6 @@ mod tests { .all(|element| element.ancestors().any(|it| &it == edit.root()))) } - #[test] - #[should_panic = "some replace change ranges intersect: [Replace(Node(TUPLE_EXPR@5..7), Some(Node(NAME_REF@0..8))), Replace(Node(TUPLE_EXPR@5..7), Some(Node(NAME_REF@0..8)))]"] - fn fail_on_non_disjoint_single_replace() { - let root = make::match_arm([make::wildcard_pat().into()], None, make::expr_tuple([])); - - let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); - - let mut editor = SyntaxEditor::new(root.syntax().clone()); - - let name_ref = make::name_ref("var_name").clone_for_update(); - - // should die, ranges are not disjoint - editor.replace(to_wrap.syntax(), name_ref.syntax()); - editor.replace(to_wrap.syntax(), name_ref.syntax()); - - let _ = editor.finish(); - } - #[test] fn test_insert_independent() { let root = make::block_expr( diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 3b92ac1cbd8a0..55e8867a46cb4 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -1,3 +1,5 @@ +//! Implementation of applying changes to a syntax tree. + use std::{cmp::Ordering, collections::VecDeque, ops::RangeInclusive}; use rowan::TextRange; @@ -209,7 +211,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } }, Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { - *target = upmap_target(&target); + *target = upmap_target(target); } Change::ReplaceAll(range, _) => { *range = upmap_target(range.start())..=upmap_target(range.end()); @@ -233,7 +235,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { Change::Replace(target, None) => { target.detach(); } - Change::Replace(SyntaxElement::Node(target), Some(new_target)) if &target == &root => { + Change::Replace(SyntaxElement::Node(target), Some(new_target)) if target == root => { root = new_target.into_node().expect("root node replacement should be a node"); } Change::Replace(target, Some(new_target)) => { @@ -288,7 +290,7 @@ struct ChangedAncestor { enum ChangedAncestorKind { Single { node: SyntaxNode }, - Range { _changed_elements: RangeInclusive, in_parent: SyntaxNode }, + Range { _changed_elements: RangeInclusive, _in_parent: SyntaxNode }, } impl ChangedAncestor { @@ -307,7 +309,7 @@ impl ChangedAncestor { Self { kind: ChangedAncestorKind::Range { _changed_elements: range.clone(), - in_parent: range.start().parent().unwrap(), + _in_parent: range.start().parent().unwrap(), }, change_index, } @@ -316,7 +318,7 @@ impl ChangedAncestor { fn affected_range(&self) -> TextRange { match &self.kind { ChangedAncestorKind::Single { node } => node.text_range(), - ChangedAncestorKind::Range { _changed_elements: changed_nodes, in_parent: _ } => { + ChangedAncestorKind::Range { _changed_elements: changed_nodes, _in_parent: _ } => { TextRange::new( changed_nodes.start().text_range().start(), changed_nodes.end().text_range().end(), @@ -339,7 +341,7 @@ impl TreeMutator { fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { match element { - SyntaxElement::Node(node) => SyntaxElement::Node(self.make_syntax_mut(&node)), + SyntaxElement::Node(node) => SyntaxElement::Node(self.make_syntax_mut(node)), SyntaxElement::Token(token) => { let parent = self.make_syntax_mut(&token.parent().unwrap()); parent.children_with_tokens().nth(token.index()).unwrap() diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index b2c677c869669..9bb5e6d933822 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -1,3 +1,7 @@ +//! Maps syntax elements through disjoint syntax nodes. +//! +//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a [`SyntaxEditor`] + use itertools::Itertools; use rustc_hash::FxHashMap; @@ -168,11 +172,11 @@ impl SyntaxMapping { match (input_mapping, input_ancestor) { (Some(input_mapping), _) => { // A mapping exists at the input, follow along the tree - Some(self.upmap_child(&input_mapping, &input_mapping, &output_root)) + Some(self.upmap_child(&input_mapping, &input_mapping, output_root)) } (None, Some(input_ancestor)) => { // A mapping exists at an ancestor, follow along the tree - Some(self.upmap_child(input, &input_ancestor, &output_root)) + Some(self.upmap_child(input, &input_ancestor, output_root)) } (None, None) => { // No mapping exists at all, is the same position in the final tree From 17e5f01d2b871440f5f35b3faec1fcdc8cca6fa0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 3 Sep 2024 11:54:33 +0200 Subject: [PATCH 0243/1301] Bump smol_str --- src/tools/rust-analyzer/Cargo.lock | 22 +++++++++++++++++++--- src/tools/rust-analyzer/Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8733508be746b..6eaded6da14ab 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -96,6 +96,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "cfg_aliases 0.2.1", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -167,6 +176,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chalk-derive" version = "0.98.0" @@ -1114,7 +1129,7 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.1.1", "libc", ] @@ -1844,10 +1859,11 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol_str" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +checksum = "66eaf762c5af19db3108300515c8aa7a50efc90ff745f4c62288052ebf9fdd25" dependencies = [ + "borsh", "serde", ] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index aa7bd2dc5fe8b..e55628c8dcf4c 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -145,7 +145,7 @@ smallvec = { version = "1.10.0", features = [ "union", "const_generics", ] } -smol_str = "0.2.1" +smol_str = "0.3.1" snap = "1.1.0" text-size = "1.1.1" tracing = "0.1.40" From 39defb1c9caaa30aa4d3b55dfc35fa648258f3c4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:01:25 +0000 Subject: [PATCH 0244/1301] Rustup to rustc 1.83.0-nightly (bd53aa3bf 2024-09-02) --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 2c76d34b65d3c..0ac239c441f02 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-08-28" +channel = "nightly-2024-09-03" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" From b7ed9ad22674de27c01f3a87120c66a835503af7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:08:17 +0000 Subject: [PATCH 0245/1301] Remove test exclusion for fixed rustc test --- scripts/test_rustc_tests.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index fcfbaf053deec..046390d458d32 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -141,7 +141,6 @@ rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation war rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue rm tests/ui/backtrace/synchronized-panic-handler.rs # missing needs-unwind annotation rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables -rm -r tests/run-make/libtest-thread-limit # doesn't work with -Zpanic-abort-tests, missing needs-unwind annotation rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd From a07e54c6ea28ced847f954b5e05e948839f991f2 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Tue, 3 Sep 2024 11:20:23 -0400 Subject: [PATCH 0246/1301] bundle old root into `SyntaxEdit` result useful for `SourceChangeBuilder` so it can still perform a tree diff without having to store the old root separately --- .../crates/syntax/src/syntax_editor.rs | 25 ++++++++++++------- .../syntax/src/syntax_editor/edit_algo.rs | 17 ++++++++++--- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 139c6518bf7cb..eb114f5e5f1f7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -102,16 +102,23 @@ impl SyntaxEditor { } } +/// Represents a completed [`SyntaxEditor`] operation. pub struct SyntaxEdit { - root: SyntaxNode, + old_root: SyntaxNode, + new_root: SyntaxNode, changed_elements: Vec, annotations: FxHashMap>, } impl SyntaxEdit { - /// Root of the modified syntax tree - pub fn root(&self) -> &SyntaxNode { - &self.root + /// Root of the initial unmodified syntax tree. + pub fn old_root(&self) -> &SyntaxNode { + &self.old_root + } + + /// Root of the modified syntax tree. + pub fn new_root(&self) -> &SyntaxNode { + &self.new_root } /// Which syntax elements in the modified syntax tree were inserted or @@ -441,14 +448,14 @@ mod tests { let var_name = 2 + 2; (var_name, true) }"#]]; - expect.assert_eq(&edit.root.to_string()); + expect.assert_eq(&edit.new_root.to_string()); assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); assert!(edit .annotations .iter() .flat_map(|(_, elements)| elements) - .all(|element| element.ancestors().any(|it| &it == edit.root()))) + .all(|element| element.ancestors().any(|it| &it == edit.new_root()))) } #[test] @@ -495,7 +502,7 @@ mod tests { let first = 1;{ let second = 2;let third = 3; }"#]]; - expect.assert_eq(&edit.root.to_string()); + expect.assert_eq(&edit.new_root.to_string()); } #[test] @@ -556,7 +563,7 @@ mod tests { } } }"#]]; - expect.assert_eq(&edit.root.to_string()); + expect.assert_eq(&edit.new_root.to_string()); } #[test] @@ -599,6 +606,6 @@ mod tests { let second = 2; } }"#]]; - expect.assert_eq(&edit.root.to_string()); + expect.assert_eq(&edit.new_root.to_string()); } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 55e8867a46cb4..b769c941105b7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -81,7 +81,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { "some replace change ranges intersect: {:?}", changes ) { - return SyntaxEdit { root, annotations: Default::default(), changed_elements: vec![] }; + return SyntaxEdit { + old_root: root.clone(), + new_root: root, + annotations: Default::default(), + changed_elements: vec![], + }; } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -273,7 +278,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { annotation_groups.entry(annotation).or_insert(vec![]).push(element); } - SyntaxEdit { root, changed_elements, annotations: annotation_groups } + SyntaxEdit { + old_root: tree_mutator.immutable, + new_root: root, + changed_elements, + annotations: annotation_groups, + } } fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { @@ -329,6 +339,7 @@ impl ChangedAncestor { } struct TreeMutator { + immutable: SyntaxNode, mutable_clone: SyntaxNode, } @@ -336,7 +347,7 @@ impl TreeMutator { fn new(immutable: &SyntaxNode) -> TreeMutator { let immutable = immutable.clone(); let mutable_clone = immutable.clone_for_update(); - TreeMutator { mutable_clone } + TreeMutator { immutable, mutable_clone } } fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { From d3c9cc57d291efa09963add37b11cc52edcae19e Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 3 Sep 2024 12:03:53 -0400 Subject: [PATCH 0247/1301] Add support for missing SIMD intrinsics --- src/intrinsic/llvm.rs | 45 ++++++++++++++++++++++++++++++++++++++++++- src/intrinsic/simd.rs | 18 ++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 2287c96b41b8f..614bdbe26b808 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -182,7 +182,10 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( | "__builtin_ia32_vplzcntd_128_mask" | "__builtin_ia32_vplzcntq_512_mask" | "__builtin_ia32_vplzcntq_256_mask" - | "__builtin_ia32_vplzcntq_128_mask" => { + | "__builtin_ia32_vplzcntq_128_mask" + | "__builtin_ia32_cvtqq2pd128_mask" + | "__builtin_ia32_cvtqq2pd256_mask" + | "__builtin_ia32_cvtqq2ps256_mask" => { let mut new_args = args.to_vec(); // Remove last arg as it doesn't seem to be used in GCC and is always false. new_args.pop(); @@ -378,6 +381,23 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( ); args = vec![arg.get_address(None)].into(); } + "__builtin_ia32_cvtqq2pd512_mask" | "__builtin_ia32_cvtqq2ps512_mask" => { + let mut old_args = args.to_vec(); + let mut new_args = vec![]; + new_args.push(old_args.swap_remove(0)); + let arg2_type = gcc_func.get_param_type(1); + let vector_type = arg2_type.dyncast_vector().expect("vector type"); + let zero = builder.context.new_rvalue_zero(vector_type.get_element_type()); + let num_units = vector_type.get_num_units(); + let first_arg = + builder.context.new_rvalue_from_vector(None, arg2_type, &vec![zero; num_units]); + new_args.push(first_arg); + let arg3_type = gcc_func.get_param_type(2); + let minus_one = builder.context.new_rvalue_from_int(arg3_type, -1); + new_args.push(minus_one); + new_args.push(old_args.swap_remove(0)); + args = new_args.into(); + } _ => (), } } else { @@ -987,6 +1007,29 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.vpdpbusds.128" => "__builtin_ia32_vpdpbusds_v4si", "llvm.x86.xsave" => "__builtin_ia32_xsave", "llvm.x86.xsaveopt" => "__builtin_ia32_xsaveopt", + "llvm.x86.avx512.mask.loadu.w.512" => "__builtin_ia32_loaddquhi512_mask", + "llvm.x86.avx512.mask.loadu.b.512" => "__builtin_ia32_loaddquqi512_mask", + "llvm.x86.avx512.mask.loadu.w.256" => "__builtin_ia32_loaddquhi256_mask", + "llvm.x86.avx512.mask.loadu.b.256" => "__builtin_ia32_loaddquqi256_mask", + "llvm.x86.avx512.mask.loadu.w.128" => "__builtin_ia32_loaddquhi128_mask", + "llvm.x86.avx512.mask.loadu.b.128" => "__builtin_ia32_loaddquqi128_mask", + "llvm.x86.avx512.mask.storeu.w.512" => "__builtin_ia32_storedquhi512_mask", + "llvm.x86.avx512.mask.storeu.b.512" => "__builtin_ia32_storedquqi512_mask", + "llvm.x86.avx512.mask.storeu.w.256" => "__builtin_ia32_storedquhi256_mask", + "llvm.x86.avx512.mask.storeu.b.256" => "__builtin_ia32_storedquqi256_mask", + "llvm.x86.avx512.mask.storeu.w.128" => "__builtin_ia32_storedquhi128_mask", + "llvm.x86.avx512.mask.storeu.b.128" => "__builtin_ia32_storedquqi128_mask", + "llvm.x86.avx512.mask.expand.load.w.512" => "__builtin_ia32_expandloadhi512_mask", + "llvm.x86.avx512.mask.expand.load.w.256" => "__builtin_ia32_expandloadhi256_mask", + "llvm.x86.avx512.mask.expand.load.w.128" => "__builtin_ia32_expandloadhi128_mask", + "llvm.x86.avx512.mask.expand.load.b.512" => "__builtin_ia32_expandloadqi512_mask", + "llvm.x86.avx512.mask.expand.load.b.256" => "__builtin_ia32_expandloadqi256_mask", + "llvm.x86.avx512.mask.expand.load.b.128" => "__builtin_ia32_expandloadqi128_mask", + "llvm.x86.avx512.sitofp.round.v8f64.v8i64" => "__builtin_ia32_cvtqq2pd512_mask", + "llvm.x86.avx512.sitofp.round.v2f64.v2i64" => "__builtin_ia32_cvtqq2pd128_mask", + "llvm.x86.avx512.sitofp.round.v4f64.v4i64" => "__builtin_ia32_cvtqq2pd256_mask", + "llvm.x86.avx512.sitofp.round.v8f32.v8i64" => "__builtin_ia32_cvtqq2ps512_mask", + "llvm.x86.avx512.sitofp.round.v4f32.v4i64" => "__builtin_ia32_cvtqq2ps256_mask", // NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py _ => include!("archs.rs"), diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index d1b4a9689a93a..a0d8ff6346f3e 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -201,7 +201,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( bx.context.new_bitcast(None, shuffled, v_type) }; - if name == sym::simd_bswap || name == sym::simd_bitreverse { + if matches!(name, sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop) { require!( bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem } @@ -212,6 +212,22 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return Ok(simd_bswap(bx, args[0].immediate())); } + let simd_ctpop = |bx: &mut Builder<'a, 'gcc, 'tcx>, vector: RValue<'gcc>| -> RValue<'gcc> { + let mut vector_elements = vec![]; + let elem_ty = bx.element_type(llret_ty); + for i in 0..in_len { + let index = bx.context.new_rvalue_from_long(bx.ulong_type, i as i64); + let element = bx.extract_element(vector, index).to_rvalue(); + let result = bx.context.new_cast(None, bx.pop_count(element), elem_ty); + vector_elements.push(result); + } + bx.context.new_rvalue_from_vector(None, llret_ty, &vector_elements) + }; + + if name == sym::simd_ctpop { + return Ok(simd_ctpop(bx, args[0].immediate())); + } + // We use a different algorithm from non-vector bitreverse to take advantage of most // processors' vector shuffle units. It works like this: // 1. Generate pre-reversed low and high nibbles as a vector. From 6b0fc97c7ab25b50c5b061cbe8ec4aaa5575e9f9 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 3 Sep 2024 16:27:46 +0000 Subject: [PATCH 0248/1301] Win: Open dir for sync access in remove_dir_all --- .../std/src/sys/pal/windows/fs/remove_dir_all.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs index e7234ed8e5f56..9416049da78f8 100644 --- a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs +++ b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs @@ -71,10 +71,12 @@ unsafe fn nt_open_file( } /// Open the file `path` in the directory `parent`, requesting the given `access` rights. +/// `options` will be OR'd with `FILE_OPEN_REPARSE_POINT`. fn open_link_no_reparse( parent: &File, path: &[u16], access: u32, + options: u32, ) -> Result, WinError> { // This is implemented using the lower level `NtOpenFile` function as // unfortunately opening a file relative to a parent is not supported by @@ -96,7 +98,7 @@ fn open_link_no_reparse( ..c::OBJECT_ATTRIBUTES::default() }; let share = c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE; - let options = c::FILE_OPEN_REPARSE_POINT; + let options = c::FILE_OPEN_REPARSE_POINT | options; let result = nt_open_file(access, &object, share, options); // Retry without OBJ_DONT_REPARSE if it's not supported. @@ -128,13 +130,20 @@ fn open_link_no_reparse( } fn open_dir(parent: &File, name: &[u16]) -> Result, WinError> { - open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY) + // Open the directory for synchronous directory listing. + open_link_no_reparse( + parent, + name, + c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY, + // "_IO_NONALERT" means that a synchronous call won't be interrupted. + c::FILE_SYNCHRONOUS_IO_NONALERT, + ) } fn delete(parent: &File, name: &[u16]) -> Result<(), WinError> { // Note that the `delete` function consumes the opened file to ensure it's // dropped immediately. See module comments for why this is important. - match open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::DELETE) { + match open_link_no_reparse(parent, name, c::DELETE, 0) { Ok(Some(f)) => f.delete(), Ok(None) => Ok(()), Err(e) => Err(e), From 273b561609de64131af3d59a38e38eceb76e4a29 Mon Sep 17 00:00:00 2001 From: Soveu Date: Fri, 12 Jul 2024 12:12:58 +0200 Subject: [PATCH 0249/1301] add pointers_in_nomem_asm_block lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/pointers_in_nomem_asm_block.rs | 88 +++++++++++++++++++ tests/ui/pointers_in_nomem_asm_block.rs | 33 +++++++ tests/ui/pointers_in_nomem_asm_block.stderr | 34 +++++++ 6 files changed, 159 insertions(+) create mode 100644 clippy_lints/src/pointers_in_nomem_asm_block.rs create mode 100644 tests/ui/pointers_in_nomem_asm_block.rs create mode 100644 tests/ui/pointers_in_nomem_asm_block.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 682c3cfe0151c..fc51694ff57f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5758,6 +5758,7 @@ Released 2018-09-13 [`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false +[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index aded9e276c4a8..e478ab330e8bc 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -600,6 +600,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, + crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO, crate::precedence::PRECEDENCE_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 078f38656ece8..58ad9f645d6a5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,6 +287,7 @@ mod pass_by_ref_or_value; mod pathbuf_init_then_push; mod pattern_type_mismatch; mod permissions_set_readonly_false; +mod pointers_in_nomem_asm_block; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -935,5 +936,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); + store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/pointers_in_nomem_asm_block.rs b/clippy_lints/src/pointers_in_nomem_asm_block.rs new file mode 100644 index 0000000000000..385f634a15050 --- /dev/null +++ b/clippy_lints/src/pointers_in_nomem_asm_block.rs @@ -0,0 +1,88 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::InlineAsmOptions; +use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks if any pointer is being passed to an asm! block with `nomem` option. + /// + /// ### Why is this bad? + /// `nomem` forbids any reads or writes to memory and passing a pointer suggests + /// that either of those will happen. + /// + /// ### Example + /// ```no_run + /// fn f(p: *mut u32) { + /// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn f(p: *mut u32) { + /// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); } + /// } + /// ``` + #[clippy::version = "1.81.0"] + pub POINTERS_IN_NOMEM_ASM_BLOCK, + suspicious, + "pointers in nomem asm block" +} + +declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]); + +impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::InlineAsm(asm) = &expr.kind { + check_asm(cx, asm); + } + } +} + +fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) { + if !asm.options.contains(InlineAsmOptions::NOMEM) { + return; + } + + let spans = asm + .operands + .iter() + .filter(|(op, _span)| has_in_operand_pointer(cx, op)) + .map(|(_op, span)| *span) + .collect::>(); + + if spans.is_empty() { + return; + } + + span_lint_and_then( + cx, + POINTERS_IN_NOMEM_ASM_BLOCK, + spans, + "passing pointers to nomem asm block", + additional_notes, + ); +} + +fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool { + let asm_in_expr = match asm_op { + InlineAsmOperand::SymStatic { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::Label { .. } => return false, + InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr, + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr, + }; + + // This checks for raw ptrs, refs and function pointers - the last one + // also technically counts as reading memory. + cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr() +} + +fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) { + diag.note("`nomem` means that no memory write or read happens inside the asm! block"); + diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint"); +} diff --git a/tests/ui/pointers_in_nomem_asm_block.rs b/tests/ui/pointers_in_nomem_asm_block.rs new file mode 100644 index 0000000000000..b5abcbb3474c2 --- /dev/null +++ b/tests/ui/pointers_in_nomem_asm_block.rs @@ -0,0 +1,33 @@ +//@ needs-asm-support +#![warn(clippy::pointers_in_nomem_asm_block)] +#![crate_type = "lib"] +#![no_std] + +use core::arch::asm; + +unsafe fn nomem_bad(p: &i32) { + asm!( + "asdf {p1}, {p2}, {p3}", + p1 = in(reg) p, + //~^ ERROR: passing pointers to nomem asm block + p2 = in(reg) p as *const _ as usize, + p3 = in(reg) p, + options(nomem, nostack, preserves_flags) + ); +} + +unsafe fn nomem_good(p: &i32) { + asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32 as usize; + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); +} + +unsafe fn nomem_bad2(p: &mut i32) { + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ ERROR: passing pointers to nomem asm block +} + +unsafe fn nomem_fn(p: extern "C" fn()) { + asm!("call {p}", p = in(reg) p, options(nomem)); + //~^ ERROR: passing pointers to nomem asm block +} diff --git a/tests/ui/pointers_in_nomem_asm_block.stderr b/tests/ui/pointers_in_nomem_asm_block.stderr new file mode 100644 index 0000000000000..cabeb37344f2b --- /dev/null +++ b/tests/ui/pointers_in_nomem_asm_block.stderr @@ -0,0 +1,34 @@ +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:11:9 + | +LL | p1 = in(reg) p, + | ^^^^^^^^^^^^^^ +... +LL | p3 = in(reg) p, + | ^^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + = note: `-D clippy::pointers-in-nomem-asm-block` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]` + +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:26:22 + | +LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:31:22 + | +LL | asm!("call {p}", p = in(reg) p, options(nomem)); + | ^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + +error: aborting due to 3 previous errors + From 1681c91a356af14a2425a2017fea2fc675c8200a Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 9 Jul 2024 02:20:31 -0400 Subject: [PATCH 0250/1301] implement version-sorting algorithm for rust identifiers The algorithm is described in the [style guide] and was introduced in `r-l/rust 115046`. [style guide]: https://doc.rust-lang.org/nightly/style-guide/#sorting --- src/lib.rs | 1 + src/sort.rs | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 src/sort.rs diff --git a/src/lib.rs b/src/lib.rs index 7dc698dd92c42..1f186b87767f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,7 @@ mod rewrite; pub(crate) mod rustfmt_diff; mod shape; mod skip; +mod sort; pub(crate) mod source_file; pub(crate) mod source_map; mod spanned; diff --git a/src/sort.rs b/src/sort.rs new file mode 100644 index 0000000000000..670f664a11949 --- /dev/null +++ b/src/sort.rs @@ -0,0 +1,368 @@ +use itertools::EitherOrBoth; +use itertools::Itertools; + +/// Iterator which breaks an identifier into various [VersionChunk]s. +struct VersionChunkIter<'a> { + ident: &'a str, + start: usize, +} + +impl<'a> VersionChunkIter<'a> { + pub(crate) fn new(ident: &'a str) -> Self { + Self { ident, start: 0 } + } + + fn parse_numeric_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c.is_ascii_digit() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + let zeros = source.chars().take_while(|c| *c == '0').count(); + let value = source.parse::().ok()?; + + Some(VersionChunk::Number { + value, + zeros, + source, + }) + } + + fn parse_str_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c == '_' { + is_end_of_chunk = true; + break; + } + + if !c.is_numeric() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + Some(VersionChunk::Str(source)) + } +} + +impl<'a> Iterator for VersionChunkIter<'a> { + type Item = VersionChunk<'a>; + + fn next(&mut self) -> Option { + let mut chars = self.ident[self.start..].char_indices(); + let (_, next) = chars.next()?; + + if next == '_' { + self.start = self.start + next.len_utf8(); + return Some(VersionChunk::Underscore); + } + + if next.is_ascii_digit() { + return self.parse_numeric_chunk(chars); + } + + self.parse_str_chunk(chars) + } +} + +/// Represents a chunk in the version-sort algorithm +#[derive(Debug, PartialEq, Eq)] +enum VersionChunk<'a> { + /// A single `_` in an identifier. Underscores are sorted before all other characters. + Underscore, + /// A &str chunk in the version sort. + Str(&'a str), + /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros. + Number { + value: usize, + zeros: usize, + source: &'a str, + }, +} + +/// Determine which side of the version-sort comparison had more leading zeros. +#[derive(Debug, PartialEq, Eq)] +enum MoreLeadingZeros { + Left, + Right, + Equal, +} + +/// Compare two identifiers based on the version sorting algorithm described in [the style guide] +/// +/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/#sorting +pub(crate) fn version_sort(a: &str, b: &str) -> std::cmp::Ordering { + let iter_a = VersionChunkIter::new(a); + let iter_b = VersionChunkIter::new(b); + let mut more_leading_zeros = MoreLeadingZeros::Equal; + + for either_or_both in iter_a.zip_longest(iter_b) { + match either_or_both { + EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater, + EitherOrBoth::Right(_) => return std::cmp::Ordering::Less, + EitherOrBoth::Both(a, b) => match (a, b) { + (VersionChunk::Underscore, VersionChunk::Underscore) => { + continue; + } + (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less, + (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater, + (VersionChunk::Str(ca), VersionChunk::Str(cb)) + | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. }) + | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => { + match ca.cmp(&cb) { + std::cmp::Ordering::Equal => { + continue; + } + order @ _ => return order, + } + } + ( + VersionChunk::Number { + value: va, + zeros: lza, + .. + }, + VersionChunk::Number { + value: vb, + zeros: lzb, + .. + }, + ) => match va.cmp(&vb) { + std::cmp::Ordering::Equal => { + if lza == lzb { + continue; + } + + if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb { + more_leading_zeros = MoreLeadingZeros::Left; + } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb { + more_leading_zeros = MoreLeadingZeros::Right; + } + continue; + } + order @ _ => return order, + }, + }, + } + } + + match more_leading_zeros { + MoreLeadingZeros::Equal => std::cmp::Ordering::Equal, + MoreLeadingZeros::Left => std::cmp::Ordering::Less, + MoreLeadingZeros::Right => std::cmp::Ordering::Greater, + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_chunks() { + let mut iter = VersionChunkIter::new("x86_128"); + assert_eq!(iter.next(), Some(VersionChunk::Str("x"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 86, + zeros: 0, + source: "86" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 128, + zeros: 0, + source: "128" + }) + ); + assert_eq!(iter.next(), None); + + let mut iter = VersionChunkIter::new("w005s09t"); + assert_eq!(iter.next(), Some(VersionChunk::Str("w"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 5, + zeros: 2, + source: "005" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Str("s"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 9, + zeros: 1, + source: "09" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Str("t"))); + assert_eq!(iter.next(), None); + + let mut iter = VersionChunkIter::new("ZY_WX"); + assert_eq!(iter.next(), Some(VersionChunk::Str("ZY"))); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!(iter.next(), Some(VersionChunk::Str("WX"))); + + let mut iter = VersionChunkIter::new("_v1"); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!(iter.next(), Some(VersionChunk::Str("v"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 1, + zeros: 0, + source: "1" + }) + ); + + let mut iter = VersionChunkIter::new("_1v"); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 1, + zeros: 0, + source: "1" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Str("v"))); + + let mut iter = VersionChunkIter::new("v009"); + assert_eq!(iter.next(), Some(VersionChunk::Str("v"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 9, + zeros: 2, + source: "009" + }) + ); + } + + #[test] + fn test_version_sort() { + let mut input = vec!["", "b", "a"]; + let expected = vec!["", "a", "b"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["x7x", "xxx"]; + let expected = vec!["x7x", "xxx"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["applesauce", "apple"]; + let expected = vec!["apple", "applesauce"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["aaaaa", "aaa_a"]; + let expected = vec!["aaa_a", "aaaaa"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["AAAAA", "AAA1A", "BBBBB", "BB_BB", "C3CCC"]; + let expected = vec!["AAA1A", "AAAAA", "BB_BB", "BBBBB", "C3CCC"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["1_000_000", "1_010_001"]; + let expected = vec!["1_000_000", "1_010_001"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec![ + "5", "50", "500", "5_000", "5_005", "5_050", "5_500", "50_000", "50_005", "50_050", + "50_500", + ]; + let expected = vec![ + "5", "5_000", "5_005", "5_050", "5_500", "50", "50_000", "50_005", "50_050", "50_500", + "500", + ]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["X86_64", "x86_64", "X86_128", "x86_128"]; + let expected = vec!["X86_64", "X86_128", "x86_64", "x86_128"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["__", "_"]; + let expected = vec!["_", "__"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["foo_", "foo"]; + let expected = vec!["foo", "foo_"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["A", "AA", "B", "a", "aA", "aa", "b"]; + let expected = vec!["A", "AA", "B", "a", "aA", "aa", "b"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec![ + "x86_128", "usize", "uz", "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", + "v1", "v009", "x87", "zyxw", "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", + "ZYXW", "v09", "v9", "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", + "ua", "x86_64", "ZYXW_", "a1", "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", + "u256", + ]; + let expected = vec![ + "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", "ZYXW", "ZYXW_", "a1", + "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", "u256", "ua", "usize", "uz", + "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", "v1", "v009", "v09", "v9", + "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", "x86_64", "x86_128", + "x87", "zyxw", + ]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected) + } +} From 296417260c6a53fca85a0adf1dfd6a9b3760768f Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 9 Jul 2024 02:28:11 -0400 Subject: [PATCH 0251/1301] use version-sort when reordering imports using `style_edition=2024` --- src/imports.rs | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index 94b62ef7aa030..868314354b048 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -21,6 +21,7 @@ use crate::lists::{ }; use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; +use crate::sort::version_sort; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{is_same_visibility, mk_sp, rewrite_ident}; @@ -923,7 +924,7 @@ impl Ord for UseSegment { | (Crate(ref a), Crate(ref b)) => match (a, b) { (Some(sa), Some(sb)) => { if self.style_edition >= StyleEdition::Edition2024 { - sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + version_sort(sa.trim_start_matches("r#"), sb.trim_start_matches("r#")) } else { a.cmp(b) } @@ -937,20 +938,26 @@ impl Ord for UseSegment { } else { (pia.as_str(), pib.as_str()) }; - // snake_case < CamelCase < UPPER_SNAKE_CASE - if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { - return Ordering::Greater; - } - if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { - return Ordering::Less; - } - if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { - return Ordering::Greater; - } - if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { - return Ordering::Less; - } - let ident_ord = ia.cmp(ib); + + let ident_ord = if self.style_edition >= StyleEdition::Edition2024 { + version_sort(ia, ib) + } else { + // snake_case < CamelCase < UPPER_SNAKE_CASE + if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { + return Ordering::Greater; + } + if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { + return Ordering::Less; + } + if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { + return Ordering::Greater; + } + if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { + return Ordering::Less; + } + ia.cmp(ib) + }; + if ident_ord != Ordering::Equal { return ident_ord; } @@ -959,8 +966,7 @@ impl Ord for UseSegment { (Some(_), None) => Ordering::Greater, (Some(aas), Some(abs)) => { if self.style_edition >= StyleEdition::Edition2024 { - aas.trim_start_matches("r#") - .cmp(abs.trim_start_matches("r#")) + version_sort(aas.trim_start_matches("r#"), abs.trim_start_matches("r#")) } else { aas.cmp(abs) } From ae59ee755afdbc086c10ce8d693f363a40e3216e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 16 Aug 2024 14:34:06 -0400 Subject: [PATCH 0252/1301] sort rustfmt imports The version-sort algorithm makes changes to the sort order of imports, and we're updating them here for the self tests. --- config_proc_macro/src/utils.rs | 2 +- src/attr.rs | 12 ++++++------ src/bin/main.rs | 8 ++++---- src/chains.rs | 4 ++-- src/closures.rs | 6 +++--- src/comment.rs | 4 ++-- src/config/file_lines.rs | 2 +- src/config/options.rs | 4 ++-- src/emitter/checkstyle.rs | 2 +- src/emitter/json.rs | 2 +- src/emitter/modified_lines.rs | 2 +- src/expr.rs | 18 +++++++++--------- src/formatting.rs | 2 +- src/git-rustfmt/main.rs | 2 +- src/imports.rs | 6 +++--- src/items.rs | 17 ++++++++--------- src/lib.rs | 4 ++-- src/lists.rs | 2 +- src/macros.rs | 18 +++++++++--------- src/matches.rs | 10 +++++----- src/missed_spans.rs | 4 ++-- src/modules.rs | 2 +- src/overflow.rs | 8 ++++---- src/pairs.rs | 2 +- src/parse/macros/asm.rs | 2 +- src/parse/macros/cfg_if.rs | 2 +- src/parse/macros/mod.rs | 4 ++-- src/parse/parser.rs | 6 +++--- src/parse/session.rs | 5 +++-- src/patterns.rs | 14 +++++++------- src/reorder.rs | 6 +++--- src/rewrite.rs | 2 +- src/rustfmt_diff.rs | 2 +- src/source_file.rs | 2 +- src/spanned.rs | 2 +- src/stmt.rs | 2 +- src/string.rs | 2 +- src/test/configuration_snippet.rs | 4 ++-- src/test/mod.rs | 6 +++--- src/types.rs | 12 ++++++------ src/utils.rs | 4 ++-- src/vertical.rs | 2 +- src/visitor.rs | 12 ++++++------ 43 files changed, 117 insertions(+), 117 deletions(-) diff --git a/config_proc_macro/src/utils.rs b/config_proc_macro/src/utils.rs index f5cba87b07b67..1f5b5cdb604f0 100644 --- a/config_proc_macro/src/utils.rs +++ b/config_proc_macro/src/utils.rs @@ -1,5 +1,5 @@ use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; +use quote::{ToTokens, quote}; pub fn fold_quote(input: impl Iterator, f: F) -> TokenStream where diff --git a/src/attr.rs b/src/attr.rs index 802dfc0485c26..ac57858ef7e95 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -1,20 +1,20 @@ //! Format attributes and meta items. -use rustc_ast::ast; use rustc_ast::HasAttrs; -use rustc_span::{symbol::sym, Span}; +use rustc_ast::ast; +use rustc_span::{Span, symbol::sym}; use self::doc_comment::DocCommentFormatter; -use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle}; -use crate::config::lists::*; +use crate::comment::{CommentStyle, contains_comment, rewrite_doc_comment}; use crate::config::IndentStyle; +use crate::config::lists::*; use crate::expr::rewrite_literal; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; use crate::overflow; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; -use crate::types::{rewrite_path, PathContext}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{count_newlines, mk_sp}; mod doc_comment; diff --git a/src/bin/main.rs b/src/bin/main.rs index 3a0427d83720d..0d593c6c4168b 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,6 +1,6 @@ #![feature(rustc_private)] -use anyhow::{format_err, Result}; +use anyhow::{Result, format_err}; use io::Error as IoError; use thiserror::Error; @@ -11,15 +11,15 @@ use tracing_subscriber::EnvFilter; use std::collections::HashMap; use std::env; use std::fs::File; -use std::io::{self, stdout, Read, Write}; +use std::io::{self, Read, Write, stdout}; use std::path::{Path, PathBuf}; use std::str::FromStr; use getopts::{Matches, Options}; use crate::rustfmt::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, - FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, + CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, + FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, load_config, }; const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug"; diff --git a/src/chains.rs b/src/chains.rs index a3f7005542575..297eb268d30cd 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -59,9 +59,9 @@ use std::borrow::Cow; use std::cmp::min; use rustc_ast::{ast, ptr}; -use rustc_span::{symbol, BytePos, Span}; +use rustc_span::{BytePos, Span, symbol}; -use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar}; +use crate::comment::{CharClasses, FullCodeCharKind, RichChar, rewrite_comment}; use crate::config::{IndentStyle, StyleEdition}; use crate::expr::rewrite_call; use crate::lists::extract_pre_comment; diff --git a/src/closures.rs b/src/closures.rs index a628e3da97996..95d2bc5a00b9a 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -3,17 +3,17 @@ use rustc_span::Span; use thin_vec::thin_vec; use crate::attr::get_attrs_from_stmt; -use crate::config::lists::*; use crate::config::StyleEdition; +use crate::config::lists::*; use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond}; use crate::items::{span_hi_for_param, span_lo_for_param}; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; use crate::overflow::OverflowableItem; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::types::rewrite_bound_params; -use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; +use crate::utils::{NodeIdExt, last_line_width, left_most_sub_expr, stmt_expr}; // This module is pretty messy because of the rules around closures and blocks: // FIXME - the below is probably no longer true in full. diff --git a/src/comment.rs b/src/comment.rs index 1e35395cfb31f..b565712b326e6 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -2,13 +2,13 @@ use std::{borrow::Cow, iter}; -use itertools::{multipeek, MultiPeek}; +use itertools::{MultiPeek, multipeek}; use rustc_span::Span; use crate::config::Config; use crate::rewrite::{RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; -use crate::string::{rewrite_string, StringFormat}; +use crate::string::{StringFormat, rewrite_string}; use crate::utils::{ count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, trimmed_last_line_width, unicode_str_width, diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index bc5a28b74237f..7f32765195da9 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -7,7 +7,7 @@ use std::{cmp, fmt, iter, str}; use rustc_data_structures::sync::Lrc; use rustc_span::SourceFile; -use serde::{ser, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, ser}; use serde_json as json; use thiserror::Error; diff --git a/src/config/options.rs b/src/config/options.rs index b9e79b9a7de59..45496fd4e48a7 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -1,6 +1,6 @@ #![allow(unused_imports)] -use std::collections::{hash_set, HashSet}; +use std::collections::{HashSet, hash_set}; use std::fmt; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -11,10 +11,10 @@ use serde::de::{SeqAccess, Visitor}; use serde::ser::SerializeSeq; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use crate::config::Config; use crate::config::file_lines::FileLines; use crate::config::lists::*; use crate::config::macro_names::MacroSelectors; -use crate::config::Config; #[config_type] pub enum NewlineStyle { diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index 9385ae59a06b0..c320c16bd1d33 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -1,6 +1,6 @@ use self::xml::XmlEscaped; use super::*; -use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff}; mod xml; diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 084f565804c97..a99626f783d4d 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -1,5 +1,5 @@ use super::*; -use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff}; use serde::Serialize; use serde_json::to_string as to_json_string; diff --git a/src/emitter/modified_lines.rs b/src/emitter/modified_lines.rs index 81f0a31b97483..0d5124bc33ee8 100644 --- a/src/emitter/modified_lines.rs +++ b/src/emitter/modified_lines.rs @@ -1,5 +1,5 @@ use super::*; -use crate::rustfmt_diff::{make_diff, ModifiedLines}; +use crate::rustfmt_diff::{ModifiedLines, make_diff}; #[derive(Debug, Default)] pub(crate) struct ModifiedLinesEmitter; diff --git a/src/expr.rs b/src/expr.rs index 8381e3ab0835e..5bd87d00b1d57 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3,32 +3,32 @@ use std::cmp::min; use itertools::Itertools; use rustc_ast::token::{Delimiter, Lit, LitKind}; -use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind}; +use rustc_ast::{ForLoopKind, MatchKind, ast, ptr, token}; use rustc_span::{BytePos, Span}; use crate::chains::rewrite_chain; use crate::closures; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, - rewrite_missing_comment, CharClasses, FindUncommented, + CharClasses, FindUncommented, combine_strs_with_missing_comments, contains_comment, + recover_comment_removed, rewrite_comment, rewrite_missing_comment, }; use crate::config::lists::*; use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, StyleEdition}; use crate::lists::{ - definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, - struct_lit_tactic, write_list, ListFormatting, Separator, + ListFormatting, Separator, definitive_tactic, itemize_list, shape_for_tactic, + struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list, }; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::matches::rewrite_match; use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; -use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; +use crate::pairs::{PairParts, rewrite_all_pairs, rewrite_pair}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt; -use crate::string::{rewrite_string, StringFormat}; -use crate::types::{rewrite_path, PathContext}; +use crate::string::{StringFormat, rewrite_string}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{ colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with, inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, diff --git a/src/formatting.rs b/src/formatting.rs index 7f442230b12be..1315c9c43a1e6 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -16,7 +16,7 @@ use crate::parse::parser::{DirectoryOwnership, Parser, ParserError}; use crate::parse::session::ParseSess; use crate::utils::{contains_skip, count_newlines}; use crate::visitor::FmtVisitor; -use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; +use crate::{ErrorKind, FormatReport, Input, Session, modules, source_file}; mod generated; mod newline_style; diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index b5a71588e9e4e..a487ede92844b 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -16,7 +16,7 @@ use rustfmt_nightly as rustfmt; use tracing_subscriber::EnvFilter; use crate::rustfmt::{ - load_config, CliOptions, FormatReportFormatterBuilder, Input, Session, Version, + CliOptions, FormatReportFormatterBuilder, Input, Session, Version, load_config, }; fn prune_files(files: Vec<&str>) -> Vec<&str> { diff --git a/src/imports.rs b/src/imports.rs index 868314354b048..b741dd9b5da54 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -8,16 +8,16 @@ use itertools::Itertools; use rustc_ast::ast::{self, UseTreeKind}; use rustc_span::{ + BytePos, DUMMY_SP, Span, symbol::{self, sym}, - BytePos, Span, DUMMY_SP, }; use crate::comment::combine_strs_with_missing_comments; -use crate::config::lists::*; use crate::config::ImportGranularity; +use crate::config::lists::*; use crate::config::{Edition, IndentStyle, StyleEdition}; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; diff --git a/src/items.rs b/src/items.rs index 89fb9bda9dfca..ddcbfac129956 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1,28 +1,27 @@ // Formatting top-level items - functions, structs, enums, traits, impls. use std::borrow::Cow; -use std::cmp::{max, min, Ordering}; +use std::cmp::{Ordering, max, min}; use regex::Regex; use rustc_ast::visit; use rustc_ast::{ast, ptr}; -use rustc_span::{symbol, BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, DUMMY_SP, Span, symbol}; use crate::attr::filter_inline_attrs; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, is_last_comment_block, + FindUncommented, combine_strs_with_missing_comments, contains_comment, is_last_comment_block, recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment, - FindUncommented, }; use crate::config::lists::*; use crate::config::{BraceStyle, Config, IndentStyle, StyleEdition}; use crate::expr::{ - is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, - rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block, - RhsAssignKind, RhsTactics, + RhsAssignKind, RhsTactics, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, + rewrite_assign_rhs_with, rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, + rewrite_let_else_block, }; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::overflow; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; diff --git a/src/lib.rs b/src/lib.rs index 1f186b87767f1..7d707c341cb53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,8 +47,8 @@ use crate::shape::Indent; use crate::utils::indent_next_line; pub use crate::config::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, - Range, StyleEdition, Verbosity, Version, + CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range, + StyleEdition, Verbosity, Version, load_config, }; pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; diff --git a/src/lists.rs b/src/lists.rs index f9e722130cd12..415144a8abc05 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -5,7 +5,7 @@ use std::iter::Peekable; use rustc_span::BytePos; -use crate::comment::{find_comment_end, rewrite_comment, FindUncommented}; +use crate::comment::{FindUncommented, find_comment_end, rewrite_comment}; use crate::config::lists::*; use crate::config::{Config, IndentStyle}; use crate::rewrite::{RewriteContext, RewriteError, RewriteResult}; diff --git a/src/macros.rs b/src/macros.rs index 5e537002324f8..2085ed35324ae 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -10,27 +10,27 @@ // and those with brackets will be formatted as array literals. use std::collections::HashMap; -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{ + BytePos, DUMMY_SP, Span, Symbol, symbol::{self, kw}, - BytePos, Span, Symbol, DUMMY_SP, }; use crate::comment::{ - contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, + CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment, }; -use crate::config::lists::*; use crate::config::StyleEdition; -use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; -use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::config::lists::*; +use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs}; +use crate::lists::{ListFormatting, itemize_list, write_list}; use crate::overflow; use crate::parse::macros::lazy_static::parse_lazy_static; -use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; +use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args}; use crate::rewrite::{ MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult, }; @@ -38,8 +38,8 @@ use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{ - filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, - remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt, + NodeIdExt, filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, + remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, }; use crate::visitor::FmtVisitor; diff --git a/src/matches.rs b/src/matches.rs index 0a6fada5cb300..8de92eb55383e 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -2,17 +2,17 @@ use std::iter::repeat; -use rustc_ast::{ast, ptr, MatchKind}; +use rustc_ast::{MatchKind, ast, ptr}; use rustc_span::{BytePos, Span}; -use crate::comment::{combine_strs_with_missing_comments, rewrite_comment, FindUncommented}; +use crate::comment::{FindUncommented, combine_strs_with_missing_comments, rewrite_comment}; use crate::config::lists::*; use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, StyleEdition}; use crate::expr::{ - format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond, - ExprType, RhsTactics, + ExprType, RhsTactics, format_expr, is_empty_block, is_simple_block, is_unsafe_block, + prefer_next_line, rewrite_cond, }; -use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::lists::{ListFormatting, itemize_list, write_list}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 4d947328bf763..4ef54c90d4664 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -1,9 +1,9 @@ use rustc_span::{BytePos, Pos, Span}; -use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices}; -use crate::config::file_lines::FileLines; +use crate::comment::{CodeCharKind, CommentCodeSlices, is_last_comment_block, rewrite_comment}; use crate::config::FileName; use crate::config::StyleEdition; +use crate::config::file_lines::FileLines; use crate::coverage::transform_missing_snippet; use crate::shape::{Indent, Shape}; use crate::source_map::LineRangeUtils; diff --git a/src/modules.rs b/src/modules.rs index 0590f28ee0521..493b04f16c6f4 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -4,8 +4,8 @@ use std::path::{Path, PathBuf}; use rustc_ast::ast; use rustc_ast::visit::Visitor; -use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{self, Symbol, sym}; use thin_vec::ThinVec; use thiserror::Error; diff --git a/src/overflow.rs b/src/overflow.rs index dc4716a81af9b..8ce11897d265a 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -9,21 +9,21 @@ use rustc_span::Span; use crate::closures; use crate::config::StyleEdition; -use crate::config::{lists::*, Config}; +use crate::config::{Config, lists::*}; use crate::expr::{ can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr, rewrite_cond, }; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; use crate::macros::MacroArg; -use crate::patterns::{can_be_overflowed_pat, TuplePatField}; +use crate::patterns::{TuplePatField, can_be_overflowed_pat}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; -use crate::types::{can_be_overflowed_type, SegmentParam}; +use crate::types::{SegmentParam, can_be_overflowed_type}; use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp}; /// A list of `format!`-like macros, that take a long format string and a list of arguments to diff --git a/src/pairs.rs b/src/pairs.rs index 5a7d9c8aaa45f..9c51298416b0d 100644 --- a/src/pairs.rs +++ b/src/pairs.rs @@ -1,8 +1,8 @@ use rustc_ast::ast; use rustc_span::Span; -use crate::config::lists::*; use crate::config::IndentStyle; +use crate::config::lists::*; use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::spanned::Spanned; diff --git a/src/parse/macros/asm.rs b/src/parse/macros/asm.rs index 6373d0251f896..5ee083da5399d 100644 --- a/src/parse/macros/asm.rs +++ b/src/parse/macros/asm.rs @@ -1,5 +1,5 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; +use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args}; use crate::rewrite::RewriteContext; diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs index b91d203d53118..ec771f96a3f1a 100644 --- a/src/parse/macros/cfg_if.rs +++ b/src/parse/macros/cfg_if.rs @@ -1,4 +1,4 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::ast; use rustc_ast::token::{Delimiter, TokenKind}; diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 8d5f7f90958f1..7271e73db8dd4 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -1,11 +1,11 @@ use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; -use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::MACRO_ARGUMENTS; +use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::{self, kw}; use rustc_span::Symbol; +use rustc_span::symbol::{self, kw}; use crate::macros::MacroArg; use crate::rewrite::RewriteContext; diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 6051241309d19..28b4c2b612f1d 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -1,4 +1,4 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use std::path::{Path, PathBuf}; use rustc_ast::token::TokenKind; @@ -6,11 +6,11 @@ use rustc_ast::{ast, attr, ptr}; use rustc_errors::Diag; use rustc_parse::parser::Parser as RawParser; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use thin_vec::ThinVec; -use crate::parse::session::ParseSess; use crate::Input; +use crate::parse::session::ParseSess; pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess; diff --git a/src/parse/session.rs b/src/parse/session.rs index 05cf467167c8d..4160e02b8f8b3 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -2,13 +2,14 @@ use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; -use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter, SilentEmitter}; +use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination}; use rustc_errors::translation::Translate; use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ + BytePos, Span, source_map::{FilePathMapping, SourceMap}, - symbol, BytePos, Span, + symbol, }; use crate::config::file_lines::LineRange; diff --git a/src/patterns.rs b/src/patterns.rs index 9a4f6528ce8e9..6fe2d4a8520e3 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -2,22 +2,22 @@ use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd, use rustc_ast::ptr; use rustc_span::{BytePos, Span}; -use crate::comment::{combine_strs_with_missing_comments, FindUncommented}; -use crate::config::lists::*; +use crate::comment::{FindUncommented, combine_strs_with_missing_comments}; use crate::config::StyleEdition; +use crate::config::lists::*; use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field}; use crate::lists::{ - definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, - struct_lit_tactic, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, shape_for_tactic, + struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list, }; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::overflow; -use crate::pairs::{rewrite_pair, PairParts}; +use crate::pairs::{PairParts, rewrite_pair}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; -use crate::types::{rewrite_path, PathContext}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident}; /// Returns `true` if the given pattern is "short". diff --git a/src/reorder.rs b/src/reorder.rs index 80d1c73899497..8a31e0ac816e3 100644 --- a/src/reorder.rs +++ b/src/reorder.rs @@ -9,12 +9,12 @@ use std::cmp::Ordering; use rustc_ast::{ast, attr}; -use rustc_span::{symbol::sym, Span}; +use rustc_span::{Span, symbol::sym}; use crate::config::{Config, GroupImportsTactic}; -use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree}; +use crate::imports::{UseSegmentKind, UseTree, normalize_use_trees_with_granularity}; use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; -use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; +use crate::lists::{ListFormatting, ListItem, itemize_list, write_list}; use crate::rewrite::{RewriteContext, RewriteErrorExt}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; diff --git a/src/rewrite.rs b/src/rewrite.rs index 8cefda1657f9d..830207097970d 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -7,12 +7,12 @@ use rustc_ast::ptr; use rustc_span::Span; use thiserror::Error; +use crate::FormatReport; use crate::config::{Config, IndentStyle}; use crate::parse::session::ParseSess; use crate::shape::Shape; use crate::skip::SkipContext; use crate::visitor::SnippetProvider; -use crate::FormatReport; pub(crate) type RewriteResult = Result; pub(crate) trait Rewrite { diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs index c98834521852c..4624683fa05b1 100644 --- a/src/rustfmt_diff.rs +++ b/src/rustfmt_diff.rs @@ -282,7 +282,7 @@ where #[cfg(test)] mod test { use super::DiffLine::*; - use super::{make_diff, Mismatch}; + use super::{Mismatch, make_diff}; use super::{ModifiedChunk, ModifiedLines}; #[test] diff --git a/src/source_file.rs b/src/source_file.rs index 5eea8021b329a..73f8ecb552977 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -2,10 +2,10 @@ use std::fs; use std::io::{self, Write}; use std::path::Path; +use crate::NewlineStyle; use crate::config::FileName; use crate::emitter::{self, Emitter}; use crate::parse::session::ParseSess; -use crate::NewlineStyle; #[cfg(test)] use crate::config::Config; diff --git a/src/spanned.rs b/src/spanned.rs index 030495e3ff8f3..b4424e476ee6b 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -1,7 +1,7 @@ use std::cmp::max; use rustc_ast::{ast, ptr}; -use rustc_span::{source_map, Span}; +use rustc_span::{Span, source_map}; use crate::macros::MacroArg; use crate::patterns::RangeOperand; diff --git a/src/stmt.rs b/src/stmt.rs index 4f31705295eb1..426bf89fc1622 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -3,7 +3,7 @@ use rustc_span::Span; use crate::comment::recover_comment_removed; use crate::config::StyleEdition; -use crate::expr::{format_expr, is_simple_block, ExprType}; +use crate::expr::{ExprType, format_expr, is_simple_block}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; diff --git a/src/string.rs b/src/string.rs index cb666fff69514..41fddeb517ec6 100644 --- a/src/string.rs +++ b/src/string.rs @@ -375,7 +375,7 @@ fn graphemes_width(graphemes: &[&str]) -> usize { #[cfg(test)] mod test { - use super::{break_string, detect_url, rewrite_string, SnippetState, StringFormat}; + use super::{SnippetState, StringFormat, break_string, detect_url, rewrite_string}; use crate::config::Config; use crate::shape::{Indent, Shape}; use unicode_segmentation::UnicodeSegmentation; diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index e4a390ada6631..5b93703de0b80 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -4,9 +4,9 @@ use std::io::{BufRead, BufReader, Write}; use std::iter::Enumerate; use std::path::{Path, PathBuf}; -use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE}; +use super::{DIFF_CONTEXT_SIZE, print_mismatches, write_message}; use crate::config::{Config, EmitMode, Verbosity}; -use crate::rustfmt_diff::{make_diff, Mismatch}; +use crate::rustfmt_diff::{Mismatch, make_diff}; use crate::{Input, Session}; const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md"; diff --git a/src/test/mod.rs b/src/test/mod.rs index 4d22f64f3526c..9dc05c846dedb 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -11,11 +11,11 @@ use std::thread; use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle}; use crate::formatting::{ReportedErrors, SourceFile}; -use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter}; +use crate::rustfmt_diff::{DiffLine, Mismatch, ModifiedChunk, OutputWriter, make_diff, print_diff}; use crate::source_file; use crate::{ - is_nightly_channel, Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, - StyleEdition, Version, + Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, StyleEdition, Version, + is_nightly_channel, }; use rustfmt_config_proc_macro::nightly_only_test; diff --git a/src/types.rs b/src/types.rs index af57a5843dbe6..384a69e23b222 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,21 +2,21 @@ use std::ops::Deref; use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; use rustc_ast::ptr; -use rustc_span::{symbol::kw, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, symbol::kw}; use crate::comment::{combine_strs_with_missing_comments, contains_comment}; use crate::config::lists::*; use crate::config::{IndentStyle, StyleEdition, TypeDensity}; use crate::expr::{ - format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, - RhsAssignKind, + ExprType, RhsAssignKind, format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, + rewrite_unary_prefix, }; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::overflow; -use crate::pairs::{rewrite_pair, PairParts}; +use crate::pairs::{PairParts, rewrite_pair}; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; diff --git a/src/utils.rs b/src/utils.rs index 57f2f1779603d..be21e89f760e9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,10 +6,10 @@ use rustc_ast::ast::{ }; use rustc_ast::ptr; use rustc_ast_pretty::pprust; -use rustc_span::{sym, symbol, BytePos, LocalExpnId, Span, Symbol, SyntaxContext}; +use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol}; use unicode_width::UnicodeWidthStr; -use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses}; +use crate::comment::{CharClasses, FullCodeCharKind, LineClasses, filter_normal_code}; use crate::config::{Config, StyleEdition}; use crate::rewrite::RewriteContext; use crate::shape::{Indent, Shape}; diff --git a/src/vertical.rs b/src/vertical.rs index 80c46da4620c7..1ec239c382100 100644 --- a/src/vertical.rs +++ b/src/vertical.rs @@ -11,7 +11,7 @@ use crate::config::lists::*; use crate::expr::rewrite_field; use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix}; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; use crate::rewrite::{Rewrite, RewriteContext, RewriteResult}; use crate::shape::{Indent, Shape}; diff --git a/src/visitor.rs b/src/visitor.rs index 9859100a038ca..afb54c8e2bcd7 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -3,22 +3,22 @@ use std::rc::Rc; use rustc_ast::{ast, token::Delimiter, visit}; use rustc_data_structures::sync::Lrc; -use rustc_span::{symbol, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, symbol}; use crate::attr::*; -use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; +use crate::comment::{CodeCharKind, CommentCodeSlices, contains_comment, rewrite_comment}; use crate::config::{BraceStyle, Config, MacroSelector, StyleEdition}; use crate::coverage::transform_missing_snippet; use crate::items::{ - format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, - rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, + FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, format_impl, format_trait, + format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, rewrite_type_alias, }; -use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; +use crate::macros::{MacroPosition, macro_style, rewrite_macro, rewrite_macro_def}; use crate::modules::Module; use crate::parse::session::ParseSess; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; -use crate::skip::{is_skip_attr, SkipContext}; +use crate::skip::{SkipContext, is_skip_attr}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt::Stmt; From 06e16449d28bf4ff2c56e49b158b4f1cead2b993 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 16 Aug 2024 15:27:57 -0400 Subject: [PATCH 0253/1301] Add test cases for issue 4381 This was one of the original issues filed against rustfmt that discussed using a natural sort order instead of a lexicographical order. At the time it was closed, but now that we're supporting version-sorting in `style_edition=2024` I thought it would be good to add a test case. --- tests/source/issue-4381/style_edition_2015.rs | 3 +++ tests/target/issue-4381/style_edition_2015.rs | 3 +++ tests/target/issue-4381/style_edition_2024.rs | 3 +++ 3 files changed, 9 insertions(+) create mode 100644 tests/source/issue-4381/style_edition_2015.rs create mode 100644 tests/target/issue-4381/style_edition_2015.rs create mode 100644 tests/target/issue-4381/style_edition_2024.rs diff --git a/tests/source/issue-4381/style_edition_2015.rs b/tests/source/issue-4381/style_edition_2015.rs new file mode 100644 index 0000000000000..bb4c9feae1d56 --- /dev/null +++ b/tests/source/issue-4381/style_edition_2015.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2015 + +use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64}; diff --git a/tests/target/issue-4381/style_edition_2015.rs b/tests/target/issue-4381/style_edition_2015.rs new file mode 100644 index 0000000000000..1fc1613083eb1 --- /dev/null +++ b/tests/target/issue-4381/style_edition_2015.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2015 + +use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8}; diff --git a/tests/target/issue-4381/style_edition_2024.rs b/tests/target/issue-4381/style_edition_2024.rs new file mode 100644 index 0000000000000..df85636ad573d --- /dev/null +++ b/tests/target/issue-4381/style_edition_2024.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2024 + +use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64}; From e064b729b9cff869e3c03b28cc2fd28f25b928dd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Sep 2024 21:53:06 +0200 Subject: [PATCH 0254/1301] Add missing intrinsic translation for `llvm.x86.xsave64` --- src/intrinsic/llvm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 614bdbe26b808..3b9855ddc1894 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -802,6 +802,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.mask.cmp.b.128" => "__builtin_ia32_cmpb128_mask", "llvm.x86.xrstor" => "__builtin_ia32_xrstor", "llvm.x86.xsavec" => "__builtin_ia32_xsavec", + "llvm.x86.xsave64" => "__builtin_ia32_xsave64", "llvm.x86.addcarry.32" => "__builtin_ia32_addcarryx_u32", "llvm.x86.subborrow.32" => "__builtin_ia32_sbb_u32", "llvm.x86.avx512.mask.compress.store.w.512" => "__builtin_ia32_compressstoreuhi512_mask", From 197df44af97026e6c04da77a73c3121387f9816f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Sep 2024 21:58:18 +0200 Subject: [PATCH 0255/1301] Regenerate intrinsics --- src/intrinsic/archs.rs | 364 ++++++++++++++++++++++++++--------------- 1 file changed, 234 insertions(+), 130 deletions(-) diff --git a/src/intrinsic/archs.rs b/src/intrinsic/archs.rs index f750093378989..b8d1cde1d5dda 100644 --- a/src/intrinsic/archs.rs +++ b/src/intrinsic/archs.rs @@ -31,8 +31,11 @@ match name { "llvm.AMDGPU.trig.preop.v2f64" => "__builtin_amdgpu_trig_preop", "llvm.AMDGPU.trig.preop.v4f32" => "__builtin_amdgpu_trig_preop", // aarch64 + "llvm.aarch64.chkfeat" => "__builtin_arm_chkfeat", "llvm.aarch64.dmb" => "__builtin_arm_dmb", "llvm.aarch64.dsb" => "__builtin_arm_dsb", + "llvm.aarch64.gcspopm" => "__builtin_arm_gcspopm", + "llvm.aarch64.gcsss" => "__builtin_arm_gcsss", "llvm.aarch64.isb" => "__builtin_arm_isb", "llvm.aarch64.prefetch" => "__builtin_arm_prefetch", "llvm.aarch64.sve.aesd" => "__builtin_sve_svaesd_u8", @@ -80,7 +83,6 @@ match name { "llvm.amdgcn.dot4.f32.fp8.fp8" => "__builtin_amdgcn_dot4_f32_fp8_fp8", "llvm.amdgcn.ds.add.gs.reg.rtn" => "__builtin_amdgcn_ds_add_gs_reg_rtn", "llvm.amdgcn.ds.bpermute" => "__builtin_amdgcn_ds_bpermute", - "llvm.amdgcn.ds.fadd.v2bf16" => "__builtin_amdgcn_ds_atomic_fadd_v2bf16", "llvm.amdgcn.ds.gws.barrier" => "__builtin_amdgcn_ds_gws_barrier", "llvm.amdgcn.ds.gws.init" => "__builtin_amdgcn_ds_gws_init", "llvm.amdgcn.ds.gws.sema.br" => "__builtin_amdgcn_ds_gws_sema_br", @@ -96,6 +98,7 @@ match name { "llvm.amdgcn.fdot2.f16.f16" => "__builtin_amdgcn_fdot2_f16_f16", "llvm.amdgcn.fdot2.f32.bf16" => "__builtin_amdgcn_fdot2_f32_bf16", "llvm.amdgcn.fmul.legacy" => "__builtin_amdgcn_fmul_legacy", + "llvm.amdgcn.global.load.lds" => "__builtin_amdgcn_global_load_lds", "llvm.amdgcn.groupstaticsize" => "__builtin_amdgcn_groupstaticsize", "llvm.amdgcn.iglp.opt" => "__builtin_amdgcn_iglp_opt", "llvm.amdgcn.implicit.buffer.ptr" => "__builtin_amdgcn_implicit_buffer_ptr", @@ -154,16 +157,11 @@ match name { "llvm.amdgcn.mqsad.u32.u8" => "__builtin_amdgcn_mqsad_u32_u8", "llvm.amdgcn.msad.u8" => "__builtin_amdgcn_msad_u8", "llvm.amdgcn.perm" => "__builtin_amdgcn_perm", - "llvm.amdgcn.permlane16" => "__builtin_amdgcn_permlane16", "llvm.amdgcn.permlane16.var" => "__builtin_amdgcn_permlane16_var", - "llvm.amdgcn.permlane64" => "__builtin_amdgcn_permlane64", - "llvm.amdgcn.permlanex16" => "__builtin_amdgcn_permlanex16", "llvm.amdgcn.permlanex16.var" => "__builtin_amdgcn_permlanex16_var", "llvm.amdgcn.qsad.pk.u16.u8" => "__builtin_amdgcn_qsad_pk_u16_u8", "llvm.amdgcn.queue.ptr" => "__builtin_amdgcn_queue_ptr", "llvm.amdgcn.rcp.legacy" => "__builtin_amdgcn_rcp_legacy", - "llvm.amdgcn.readfirstlane" => "__builtin_amdgcn_readfirstlane", - "llvm.amdgcn.readlane" => "__builtin_amdgcn_readlane", "llvm.amdgcn.rsq.legacy" => "__builtin_amdgcn_rsq_legacy", "llvm.amdgcn.s.barrier" => "__builtin_amdgcn_s_barrier", "llvm.amdgcn.s.barrier.init" => "__builtin_amdgcn_s_barrier_init", @@ -192,6 +190,8 @@ match name { "llvm.amdgcn.s.setreg" => "__builtin_amdgcn_s_setreg", "llvm.amdgcn.s.sleep" => "__builtin_amdgcn_s_sleep", "llvm.amdgcn.s.sleep.var" => "__builtin_amdgcn_s_sleep_var", + "llvm.amdgcn.s.ttracedata" => "__builtin_amdgcn_s_ttracedata", + "llvm.amdgcn.s.ttracedata.imm" => "__builtin_amdgcn_s_ttracedata_imm", "llvm.amdgcn.s.wait.event.export.ready" => "__builtin_amdgcn_s_wait_event_export_ready", "llvm.amdgcn.s.waitcnt" => "__builtin_amdgcn_s_waitcnt", "llvm.amdgcn.s.wakeup.barrier" => "__builtin_amdgcn_s_wakeup_barrier", @@ -227,7 +227,6 @@ match name { "llvm.amdgcn.workgroup.id.x" => "__builtin_amdgcn_workgroup_id_x", "llvm.amdgcn.workgroup.id.y" => "__builtin_amdgcn_workgroup_id_y", "llvm.amdgcn.workgroup.id.z" => "__builtin_amdgcn_workgroup_id_z", - "llvm.amdgcn.writelane" => "__builtin_amdgcn_writelane", // arm "llvm.arm.cdp" => "__builtin_arm_cdp", "llvm.arm.cdp2" => "__builtin_arm_cdp2", @@ -4536,10 +4535,18 @@ match name { "llvm.nvvm.div.rz.d" => "__nvvm_div_rz_d", "llvm.nvvm.div.rz.f" => "__nvvm_div_rz_f", "llvm.nvvm.div.rz.ftz.f" => "__nvvm_div_rz_ftz_f", + "llvm.nvvm.e4m3x2.to.f16x2.rn" => "__nvvm_e4m3x2_to_f16x2_rn", + "llvm.nvvm.e4m3x2.to.f16x2.rn.relu" => "__nvvm_e4m3x2_to_f16x2_rn_relu", + "llvm.nvvm.e5m2x2.to.f16x2.rn" => "__nvvm_e5m2x2_to_f16x2_rn", + "llvm.nvvm.e5m2x2.to.f16x2.rn.relu" => "__nvvm_e5m2x2_to_f16x2_rn_relu", "llvm.nvvm.ex2.approx.d" => "__nvvm_ex2_approx_d", "llvm.nvvm.ex2.approx.f" => "__nvvm_ex2_approx_f", "llvm.nvvm.ex2.approx.ftz.f" => "__nvvm_ex2_approx_ftz_f", "llvm.nvvm.exit" => "__nvvm_exit", + "llvm.nvvm.f16x2.to.e4m3x2.rn" => "__nvvm_f16x2_to_e4m3x2_rn", + "llvm.nvvm.f16x2.to.e4m3x2.rn.relu" => "__nvvm_f16x2_to_e4m3x2_rn_relu", + "llvm.nvvm.f16x2.to.e5m2x2.rn" => "__nvvm_f16x2_to_e5m2x2_rn", + "llvm.nvvm.f16x2.to.e5m2x2.rn.relu" => "__nvvm_f16x2_to_e5m2x2_rn_relu", "llvm.nvvm.f2bf16.rn" => "__nvvm_f2bf16_rn", "llvm.nvvm.f2bf16.rn.relu" => "__nvvm_f2bf16_rn_relu", "llvm.nvvm.f2bf16.rz" => "__nvvm_f2bf16_rz", @@ -4582,6 +4589,10 @@ match name { "llvm.nvvm.fabs.d" => "__nvvm_fabs_d", "llvm.nvvm.fabs.f" => "__nvvm_fabs_f", "llvm.nvvm.fabs.ftz.f" => "__nvvm_fabs_ftz_f", + "llvm.nvvm.ff.to.e4m3x2.rn" => "__nvvm_ff_to_e4m3x2_rn", + "llvm.nvvm.ff.to.e4m3x2.rn.relu" => "__nvvm_ff_to_e4m3x2_rn_relu", + "llvm.nvvm.ff.to.e5m2x2.rn" => "__nvvm_ff_to_e5m2x2_rn", + "llvm.nvvm.ff.to.e5m2x2.rn.relu" => "__nvvm_ff_to_e5m2x2_rn_relu", "llvm.nvvm.ff2bf16x2.rn" => "__nvvm_ff2bf16x2_rn", "llvm.nvvm.ff2bf16x2.rn.relu" => "__nvvm_ff2bf16x2_rn_relu", "llvm.nvvm.ff2bf16x2.rz" => "__nvvm_ff2bf16x2_rz", @@ -4866,6 +4877,7 @@ match name { "llvm.nvvm.round.ftz.f" => "__nvvm_round_ftz_f", "llvm.nvvm.rsqrt.approx.d" => "__nvvm_rsqrt_approx_d", "llvm.nvvm.rsqrt.approx.f" => "__nvvm_rsqrt_approx_f", + "llvm.nvvm.rsqrt.approx.ftz.d" => "__nvvm_rsqrt_approx_ftz_d", "llvm.nvvm.rsqrt.approx.ftz.f" => "__nvvm_rsqrt_approx_ftz_f", "llvm.nvvm.sad.i" => "__nvvm_sad_i", "llvm.nvvm.sad.ll" => "__nvvm_sad_ll", @@ -5164,6 +5176,8 @@ match name { // ppc "llvm.ppc.addex" => "__builtin_ppc_addex", "llvm.ppc.addf128.round.to.odd" => "__builtin_addf128_round_to_odd", + "llvm.ppc.addg6s" => "__builtin_addg6s", + "llvm.ppc.addg6sd" => "__builtin_ppc_addg6s", "llvm.ppc.altivec.crypto.vcipher" => "__builtin_altivec_crypto_vcipher", "llvm.ppc.altivec.crypto.vcipherlast" => "__builtin_altivec_crypto_vcipherlast", "llvm.ppc.altivec.crypto.vncipher" => "__builtin_altivec_crypto_vncipher", @@ -5461,6 +5475,10 @@ match name { "llvm.ppc.bcdsub" => "__builtin_ppc_bcdsub", "llvm.ppc.bcdsub.p" => "__builtin_ppc_bcdsub_p", "llvm.ppc.bpermd" => "__builtin_bpermd", + "llvm.ppc.cbcdtd" => "__builtin_cbcdtd", + "llvm.ppc.cbcdtdd" => "__builtin_ppc_cbcdtd", + "llvm.ppc.cdtbcd" => "__builtin_cdtbcd", + "llvm.ppc.cdtbcdd" => "__builtin_ppc_cdtbcd", "llvm.ppc.cfuged" => "__builtin_cfuged", "llvm.ppc.cmpeqb" => "__builtin_ppc_cmpeqb", "llvm.ppc.cmprb" => "__builtin_ppc_cmprb", @@ -5627,7 +5645,6 @@ match name { "llvm.ppc.qpx.qvstfs" => "__builtin_qpx_qvstfs", "llvm.ppc.qpx.qvstfsa" => "__builtin_qpx_qvstfsa", "llvm.ppc.readflm" => "__builtin_readflm", - "llvm.ppc.rldimi" => "__builtin_ppc_rldimi", "llvm.ppc.rlwimi" => "__builtin_ppc_rlwimi", "llvm.ppc.rlwnm" => "__builtin_ppc_rlwnm", "llvm.ppc.scalar.extract.expq" => "__builtin_vsx_scalar_extract_expq", @@ -7210,29 +7227,6 @@ match name { "llvm.ve.vl.xorm.MMM" => "__builtin_ve_vl_xorm_MMM", "llvm.ve.vl.xorm.mmm" => "__builtin_ve_vl_xorm_mmm", // x86 - "llvm.x86.3dnow.pavgusb" => "__builtin_ia32_pavgusb", - "llvm.x86.3dnow.pf2id" => "__builtin_ia32_pf2id", - "llvm.x86.3dnow.pfacc" => "__builtin_ia32_pfacc", - "llvm.x86.3dnow.pfadd" => "__builtin_ia32_pfadd", - "llvm.x86.3dnow.pfcmpeq" => "__builtin_ia32_pfcmpeq", - "llvm.x86.3dnow.pfcmpge" => "__builtin_ia32_pfcmpge", - "llvm.x86.3dnow.pfcmpgt" => "__builtin_ia32_pfcmpgt", - "llvm.x86.3dnow.pfmax" => "__builtin_ia32_pfmax", - "llvm.x86.3dnow.pfmin" => "__builtin_ia32_pfmin", - "llvm.x86.3dnow.pfmul" => "__builtin_ia32_pfmul", - "llvm.x86.3dnow.pfrcp" => "__builtin_ia32_pfrcp", - "llvm.x86.3dnow.pfrcpit1" => "__builtin_ia32_pfrcpit1", - "llvm.x86.3dnow.pfrcpit2" => "__builtin_ia32_pfrcpit2", - "llvm.x86.3dnow.pfrsqit1" => "__builtin_ia32_pfrsqit1", - "llvm.x86.3dnow.pfrsqrt" => "__builtin_ia32_pfrsqrt", - "llvm.x86.3dnow.pfsub" => "__builtin_ia32_pfsub", - "llvm.x86.3dnow.pfsubr" => "__builtin_ia32_pfsubr", - "llvm.x86.3dnow.pi2fd" => "__builtin_ia32_pi2fd", - "llvm.x86.3dnow.pmulhrw" => "__builtin_ia32_pmulhrw", - "llvm.x86.3dnowa.pf2iw" => "__builtin_ia32_pf2iw", - "llvm.x86.3dnowa.pfnacc" => "__builtin_ia32_pfnacc", - "llvm.x86.3dnowa.pfpnacc" => "__builtin_ia32_pfpnacc", - "llvm.x86.3dnowa.pi2fw" => "__builtin_ia32_pi2fw", "llvm.x86.aadd32" => "__builtin_ia32_aadd32", "llvm.x86.aadd64" => "__builtin_ia32_aadd64", "llvm.x86.aand32" => "__builtin_ia32_aand32", @@ -7334,6 +7328,207 @@ match name { "llvm.x86.avx.vtestz.ps.256" => "__builtin_ia32_vtestzps256", "llvm.x86.avx.vzeroall" => "__builtin_ia32_vzeroall", "llvm.x86.avx.vzeroupper" => "__builtin_ia32_vzeroupper", + "llvm.x86.avx10.mask.vcvt2ps2phx.128" => "__builtin_ia32_vcvt2ps2phx128_mask", + "llvm.x86.avx10.mask.vcvt2ps2phx.256" => "__builtin_ia32_vcvt2ps2phx256_mask", + "llvm.x86.avx10.mask.vcvt2ps2phx.512" => "__builtin_ia32_vcvt2ps2phx512_mask", + "llvm.x86.avx10.mask.vcvtbiasph2bf8128" => "__builtin_ia32_vcvtbiasph2bf8_128_mask", + "llvm.x86.avx10.mask.vcvtbiasph2bf8256" => "__builtin_ia32_vcvtbiasph2bf8_256_mask", + "llvm.x86.avx10.mask.vcvtbiasph2bf8512" => "__builtin_ia32_vcvtbiasph2bf8_512_mask", + "llvm.x86.avx10.mask.vcvtbiasph2bf8s128" => "__builtin_ia32_vcvtbiasph2bf8s_128_mask", + "llvm.x86.avx10.mask.vcvtbiasph2bf8s256" => "__builtin_ia32_vcvtbiasph2bf8s_256_mask", + "llvm.x86.avx10.mask.vcvtbiasph2bf8s512" => "__builtin_ia32_vcvtbiasph2bf8s_512_mask", + "llvm.x86.avx10.mask.vcvtbiasph2hf8128" => "__builtin_ia32_vcvtbiasph2hf8_128_mask", + "llvm.x86.avx10.mask.vcvtbiasph2hf8256" => "__builtin_ia32_vcvtbiasph2hf8_256_mask", + "llvm.x86.avx10.mask.vcvtbiasph2hf8512" => "__builtin_ia32_vcvtbiasph2hf8_512_mask", + "llvm.x86.avx10.mask.vcvtbiasph2hf8s128" => "__builtin_ia32_vcvtbiasph2hf8s_128_mask", + "llvm.x86.avx10.mask.vcvtbiasph2hf8s256" => "__builtin_ia32_vcvtbiasph2hf8s_256_mask", + "llvm.x86.avx10.mask.vcvtbiasph2hf8s512" => "__builtin_ia32_vcvtbiasph2hf8s_512_mask", + "llvm.x86.avx10.mask.vcvthf82ph128" => "__builtin_ia32_vcvthf8_2ph128_mask", + "llvm.x86.avx10.mask.vcvthf82ph256" => "__builtin_ia32_vcvthf8_2ph256_mask", + "llvm.x86.avx10.mask.vcvthf82ph512" => "__builtin_ia32_vcvthf8_2ph512_mask", + "llvm.x86.avx10.mask.vcvtneph2bf8128" => "__builtin_ia32_vcvtneph2bf8_128_mask", + "llvm.x86.avx10.mask.vcvtneph2bf8256" => "__builtin_ia32_vcvtneph2bf8_256_mask", + "llvm.x86.avx10.mask.vcvtneph2bf8512" => "__builtin_ia32_vcvtneph2bf8_512_mask", + "llvm.x86.avx10.mask.vcvtneph2bf8s128" => "__builtin_ia32_vcvtneph2bf8s_128_mask", + "llvm.x86.avx10.mask.vcvtneph2bf8s256" => "__builtin_ia32_vcvtneph2bf8s_256_mask", + "llvm.x86.avx10.mask.vcvtneph2bf8s512" => "__builtin_ia32_vcvtneph2bf8s_512_mask", + "llvm.x86.avx10.mask.vcvtneph2hf8128" => "__builtin_ia32_vcvtneph2hf8_128_mask", + "llvm.x86.avx10.mask.vcvtneph2hf8256" => "__builtin_ia32_vcvtneph2hf8_256_mask", + "llvm.x86.avx10.mask.vcvtneph2hf8512" => "__builtin_ia32_vcvtneph2hf8_512_mask", + "llvm.x86.avx10.mask.vcvtneph2hf8s128" => "__builtin_ia32_vcvtneph2hf8s_128_mask", + "llvm.x86.avx10.mask.vcvtneph2hf8s256" => "__builtin_ia32_vcvtneph2hf8s_256_mask", + "llvm.x86.avx10.mask.vcvtneph2hf8s512" => "__builtin_ia32_vcvtneph2hf8s_512_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2dq256" => "__builtin_ia32_vcvtpd2dq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ph256" => "__builtin_ia32_vcvtpd2ph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ps256" => "__builtin_ia32_vcvtpd2ps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2qq256" => "__builtin_ia32_vcvtpd2qq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2udq256" => "__builtin_ia32_vcvtpd2udq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2uqq256" => "__builtin_ia32_vcvtpd2uqq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2dq256" => "__builtin_ia32_vcvtph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvtph2ibs128" => "__builtin_ia32_vcvtph2ibs128_mask", + "llvm.x86.avx10.mask.vcvtph2ibs256" => "__builtin_ia32_vcvtph2ibs256_mask", + "llvm.x86.avx10.mask.vcvtph2ibs512" => "__builtin_ia32_vcvtph2ibs512_mask", + "llvm.x86.avx10.mask.vcvtph2iubs128" => "__builtin_ia32_vcvtph2iubs128_mask", + "llvm.x86.avx10.mask.vcvtph2iubs256" => "__builtin_ia32_vcvtph2iubs256_mask", + "llvm.x86.avx10.mask.vcvtph2iubs512" => "__builtin_ia32_vcvtph2iubs512_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2pd256" => "__builtin_ia32_vcvtph2pd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2psx256" => "__builtin_ia32_vcvtph2psx256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2qq256" => "__builtin_ia32_vcvtph2qq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2udq256" => "__builtin_ia32_vcvtph2udq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uqq256" => "__builtin_ia32_vcvtph2uqq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uw256" => "__builtin_ia32_vcvtph2uw256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2w256" => "__builtin_ia32_vcvtph2w256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2dq256" => "__builtin_ia32_vcvtps2dq256_round_mask", + "llvm.x86.avx10.mask.vcvtps2ibs128" => "__builtin_ia32_vcvtps2ibs128_mask", + "llvm.x86.avx10.mask.vcvtps2ibs256" => "__builtin_ia32_vcvtps2ibs256_mask", + "llvm.x86.avx10.mask.vcvtps2ibs512" => "__builtin_ia32_vcvtps2ibs512_mask", + "llvm.x86.avx10.mask.vcvtps2iubs128" => "__builtin_ia32_vcvtps2iubs128_mask", + "llvm.x86.avx10.mask.vcvtps2iubs256" => "__builtin_ia32_vcvtps2iubs256_mask", + "llvm.x86.avx10.mask.vcvtps2iubs512" => "__builtin_ia32_vcvtps2iubs512_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2pd256" => "__builtin_ia32_vcvtps2pd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2ph256" => "__builtin_ia32_vcvtps2ph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2phx256" => "__builtin_ia32_vcvtps2phx256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2qq256" => "__builtin_ia32_vcvtps2qq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2udq256" => "__builtin_ia32_vcvtps2udq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2uqq256" => "__builtin_ia32_vcvtps2uqq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2dq256" => "__builtin_ia32_vcvttpd2dq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2qq256" => "__builtin_ia32_vcvttpd2qq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2udq256" => "__builtin_ia32_vcvttpd2udq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2uqq256" => "__builtin_ia32_vcvttpd2uqq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2dq256" => "__builtin_ia32_vcvttph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttph2ibs128" => "__builtin_ia32_vcvttph2ibs128_mask", + "llvm.x86.avx10.mask.vcvttph2ibs256" => "__builtin_ia32_vcvttph2ibs256_mask", + "llvm.x86.avx10.mask.vcvttph2ibs512" => "__builtin_ia32_vcvttph2ibs512_mask", + "llvm.x86.avx10.mask.vcvttph2iubs128" => "__builtin_ia32_vcvttph2iubs128_mask", + "llvm.x86.avx10.mask.vcvttph2iubs256" => "__builtin_ia32_vcvttph2iubs256_mask", + "llvm.x86.avx10.mask.vcvttph2iubs512" => "__builtin_ia32_vcvttph2iubs512_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2qq256" => "__builtin_ia32_vcvttph2qq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2udq256" => "__builtin_ia32_vcvttph2udq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uqq256" => "__builtin_ia32_vcvttph2uqq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uw256" => "__builtin_ia32_vcvttph2uw256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2w256" => "__builtin_ia32_vcvttph2w256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2dq256" => "__builtin_ia32_vcvttps2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttps2ibs128" => "__builtin_ia32_vcvttps2ibs128_mask", + "llvm.x86.avx10.mask.vcvttps2ibs256" => "__builtin_ia32_vcvttps2ibs256_mask", + "llvm.x86.avx10.mask.vcvttps2ibs512" => "__builtin_ia32_vcvttps2ibs512_mask", + "llvm.x86.avx10.mask.vcvttps2iubs128" => "__builtin_ia32_vcvttps2iubs128_mask", + "llvm.x86.avx10.mask.vcvttps2iubs256" => "__builtin_ia32_vcvttps2iubs256_mask", + "llvm.x86.avx10.mask.vcvttps2iubs512" => "__builtin_ia32_vcvttps2iubs512_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2qq256" => "__builtin_ia32_vcvttps2qq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2udq256" => "__builtin_ia32_vcvttps2udq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2uqq256" => "__builtin_ia32_vcvttps2uqq256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_mask3", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmulcph256" => "__builtin_ia32_vfcmulcph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_mask3", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmulcph256" => "__builtin_ia32_vfmulcph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexppd256" => "__builtin_ia32_vgetexppd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpph256" => "__builtin_ia32_vgetexpph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpps256" => "__builtin_ia32_vgetexpps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantpd256" => "__builtin_ia32_vgetmantpd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantph256" => "__builtin_ia32_vgetmantph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantps256" => "__builtin_ia32_vgetmantps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd.round" => "__builtin_ia32_vminmaxpd512_round_mask", + "llvm.x86.avx10.mask.vminmaxpd128" => "__builtin_ia32_vminmaxpd128_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd256.round" => "__builtin_ia32_vminmaxpd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph.round" => "__builtin_ia32_vminmaxph512_round_mask", + "llvm.x86.avx10.mask.vminmaxph128" => "__builtin_ia32_vminmaxph128_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph256.round" => "__builtin_ia32_vminmaxph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps.round" => "__builtin_ia32_vminmaxps512_round_mask", + "llvm.x86.avx10.mask.vminmaxps128" => "__builtin_ia32_vminmaxps128_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps256.round" => "__builtin_ia32_vminmaxps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsd.round" => "__builtin_ia32_vminmaxsd_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsh.round" => "__builtin_ia32_vminmaxsh_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxss.round" => "__builtin_ia32_vminmaxss_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangepd256" => "__builtin_ia32_vrangepd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangeps256" => "__builtin_ia32_vrangeps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreducepd256" => "__builtin_ia32_vreducepd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceph256" => "__builtin_ia32_vreduceph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceps256" => "__builtin_ia32_vreduceps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscalepd256" => "__builtin_ia32_vrndscalepd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleph256" => "__builtin_ia32_vrndscaleph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleps256" => "__builtin_ia32_vrndscaleps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefpd256" => "__builtin_ia32_vscalefpd256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefph256" => "__builtin_ia32_vscalefph256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefps256" => "__builtin_ia32_vscalefps256_round_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_maskz", + // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_maskz", + // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_maskz", + // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_maskz", + "llvm.x86.avx10.vaddpd256" => "__builtin_ia32_vaddpd256_round", + "llvm.x86.avx10.vaddph256" => "__builtin_ia32_vaddph256_round", + "llvm.x86.avx10.vaddps256" => "__builtin_ia32_vaddps256_round", + "llvm.x86.avx10.vcvtne2ph2bf8128" => "__builtin_ia32_vcvtne2ph2bf8_128", + "llvm.x86.avx10.vcvtne2ph2bf8256" => "__builtin_ia32_vcvtne2ph2bf8_256", + "llvm.x86.avx10.vcvtne2ph2bf8512" => "__builtin_ia32_vcvtne2ph2bf8_512", + "llvm.x86.avx10.vcvtne2ph2bf8s128" => "__builtin_ia32_vcvtne2ph2bf8s_128", + "llvm.x86.avx10.vcvtne2ph2bf8s256" => "__builtin_ia32_vcvtne2ph2bf8s_256", + "llvm.x86.avx10.vcvtne2ph2bf8s512" => "__builtin_ia32_vcvtne2ph2bf8s_512", + "llvm.x86.avx10.vcvtne2ph2hf8128" => "__builtin_ia32_vcvtne2ph2hf8_128", + "llvm.x86.avx10.vcvtne2ph2hf8256" => "__builtin_ia32_vcvtne2ph2hf8_256", + "llvm.x86.avx10.vcvtne2ph2hf8512" => "__builtin_ia32_vcvtne2ph2hf8_512", + "llvm.x86.avx10.vcvtne2ph2hf8s128" => "__builtin_ia32_vcvtne2ph2hf8s_128", + "llvm.x86.avx10.vcvtne2ph2hf8s256" => "__builtin_ia32_vcvtne2ph2hf8s_256", + "llvm.x86.avx10.vcvtne2ph2hf8s512" => "__builtin_ia32_vcvtne2ph2hf8s_512", + "llvm.x86.avx10.vcvtnebf162ibs128" => "__builtin_ia32_vcvtnebf162ibs128", + "llvm.x86.avx10.vcvtnebf162ibs256" => "__builtin_ia32_vcvtnebf162ibs256", + "llvm.x86.avx10.vcvtnebf162ibs512" => "__builtin_ia32_vcvtnebf162ibs512", + "llvm.x86.avx10.vcvtnebf162iubs128" => "__builtin_ia32_vcvtnebf162iubs128", + "llvm.x86.avx10.vcvtnebf162iubs256" => "__builtin_ia32_vcvtnebf162iubs256", + "llvm.x86.avx10.vcvtnebf162iubs512" => "__builtin_ia32_vcvtnebf162iubs512", + "llvm.x86.avx10.vcvttnebf162ibs128" => "__builtin_ia32_vcvttnebf162ibs128", + "llvm.x86.avx10.vcvttnebf162ibs256" => "__builtin_ia32_vcvttnebf162ibs256", + "llvm.x86.avx10.vcvttnebf162ibs512" => "__builtin_ia32_vcvttnebf162ibs512", + "llvm.x86.avx10.vcvttnebf162iubs128" => "__builtin_ia32_vcvttnebf162iubs128", + "llvm.x86.avx10.vcvttnebf162iubs256" => "__builtin_ia32_vcvttnebf162iubs256", + "llvm.x86.avx10.vcvttnebf162iubs512" => "__builtin_ia32_vcvttnebf162iubs512", + "llvm.x86.avx10.vdivpd256" => "__builtin_ia32_vdivpd256_round", + "llvm.x86.avx10.vdivph256" => "__builtin_ia32_vdivph256_round", + "llvm.x86.avx10.vdivps256" => "__builtin_ia32_vdivps256_round", + "llvm.x86.avx10.vdpphps.128" => "__builtin_ia32_vdpphps128", + "llvm.x86.avx10.vdpphps.256" => "__builtin_ia32_vdpphps256", + "llvm.x86.avx10.vdpphps.512" => "__builtin_ia32_vdpphps512", + "llvm.x86.avx10.vfmaddsubpd256" => "__builtin_ia32_vfmaddsubpd256_round", + "llvm.x86.avx10.vfmaddsubph256" => "__builtin_ia32_vfmaddsubph256_round", + "llvm.x86.avx10.vfmaddsubps256" => "__builtin_ia32_vfmaddsubps256_round", + "llvm.x86.avx10.vmaxpd256" => "__builtin_ia32_vmaxpd256_round", + "llvm.x86.avx10.vmaxph256" => "__builtin_ia32_vmaxph256_round", + "llvm.x86.avx10.vmaxps256" => "__builtin_ia32_vmaxps256_round", + "llvm.x86.avx10.vminmaxnepbf16128" => "__builtin_ia32_vminmaxnepbf16128", + "llvm.x86.avx10.vminmaxnepbf16256" => "__builtin_ia32_vminmaxnepbf16256", + "llvm.x86.avx10.vminmaxnepbf16512" => "__builtin_ia32_vminmaxnepbf16512", + "llvm.x86.avx10.vminmaxpd128" => "__builtin_ia32_vminmaxpd128", + "llvm.x86.avx10.vminmaxpd256" => "__builtin_ia32_vminmaxpd256", + "llvm.x86.avx10.vminmaxph128" => "__builtin_ia32_vminmaxph128", + "llvm.x86.avx10.vminmaxph256" => "__builtin_ia32_vminmaxph256", + "llvm.x86.avx10.vminmaxps128" => "__builtin_ia32_vminmaxps128", + "llvm.x86.avx10.vminmaxps256" => "__builtin_ia32_vminmaxps256", + "llvm.x86.avx10.vminpd256" => "__builtin_ia32_vminpd256_round", + "llvm.x86.avx10.vminph256" => "__builtin_ia32_vminph256_round", + "llvm.x86.avx10.vminps256" => "__builtin_ia32_vminps256_round", + "llvm.x86.avx10.vmpsadbw.512" => "__builtin_ia32_mpsadbw512", + "llvm.x86.avx10.vmulpd256" => "__builtin_ia32_vmulpd256_round", + "llvm.x86.avx10.vmulph256" => "__builtin_ia32_vmulph256_round", + "llvm.x86.avx10.vmulps256" => "__builtin_ia32_vmulps256_round", + "llvm.x86.avx10.vpdpbssd.512" => "__builtin_ia32_vpdpbssd512", + "llvm.x86.avx10.vpdpbssds.512" => "__builtin_ia32_vpdpbssds512", + "llvm.x86.avx10.vpdpbsud.512" => "__builtin_ia32_vpdpbsud512", + "llvm.x86.avx10.vpdpbsuds.512" => "__builtin_ia32_vpdpbsuds512", + "llvm.x86.avx10.vpdpbuud.512" => "__builtin_ia32_vpdpbuud512", + "llvm.x86.avx10.vpdpbuuds.512" => "__builtin_ia32_vpdpbuuds512", + "llvm.x86.avx10.vpdpwsud.512" => "__builtin_ia32_vpdpwsud512", + "llvm.x86.avx10.vpdpwsuds.512" => "__builtin_ia32_vpdpwsuds512", + "llvm.x86.avx10.vpdpwusd.512" => "__builtin_ia32_vpdpwusd512", + "llvm.x86.avx10.vpdpwusds.512" => "__builtin_ia32_vpdpwusds512", + "llvm.x86.avx10.vpdpwuud.512" => "__builtin_ia32_vpdpwuud512", + "llvm.x86.avx10.vpdpwuuds.512" => "__builtin_ia32_vpdpwuuds512", + "llvm.x86.avx10.vsqrtpd256" => "__builtin_ia32_vsqrtpd256_round", + "llvm.x86.avx10.vsqrtph256" => "__builtin_ia32_vsqrtph256_round", + "llvm.x86.avx10.vsqrtps256" => "__builtin_ia32_vsqrtps256_round", + "llvm.x86.avx10.vsubpd256" => "__builtin_ia32_vsubpd256_round", + "llvm.x86.avx10.vsubph256" => "__builtin_ia32_vsubph256_round", + "llvm.x86.avx10.vsubps256" => "__builtin_ia32_vsubps256_round", "llvm.x86.avx2.gather.d.d" => "__builtin_ia32_gatherd_d", "llvm.x86.avx2.gather.d.d.256" => "__builtin_ia32_gatherd_d256", "llvm.x86.avx2.gather.d.pd" => "__builtin_ia32_gatherd_pd", @@ -8738,10 +8933,10 @@ match name { "llvm.x86.avx512.rcp14.ss" => "__builtin_ia32_rcp14ss_mask", "llvm.x86.avx512.rcp28.pd" => "__builtin_ia32_rcp28pd_mask", "llvm.x86.avx512.rcp28.ps" => "__builtin_ia32_rcp28ps_mask", - // [INVALID CONVERSION]: "llvm.x86.avx512.rcp28.sd" => "__builtin_ia32_rcp28sd_round_mask", - // [DUPLICATE]: "llvm.x86.avx512.rcp28.sd" => "__builtin_ia32_rcp28sd_mask", - // [INVALID CONVERSION]: "llvm.x86.avx512.rcp28.ss" => "__builtin_ia32_rcp28ss_round_mask", - // [DUPLICATE]: "llvm.x86.avx512.rcp28.ss" => "__builtin_ia32_rcp28ss_mask", + "llvm.x86.avx512.rcp28.sd" => "__builtin_ia32_rcp28sd_mask", + // [DUPLICATE]: "llvm.x86.avx512.rcp28.sd" => "__builtin_ia32_rcp28sd_round_mask", + "llvm.x86.avx512.rcp28.ss" => "__builtin_ia32_rcp28ss_mask", + // [DUPLICATE]: "llvm.x86.avx512.rcp28.ss" => "__builtin_ia32_rcp28ss_round_mask", "llvm.x86.avx512.rndscale.sd" => "__builtin_ia32_rndscalesd", "llvm.x86.avx512.rndscale.ss" => "__builtin_ia32_rndscaless", "llvm.x86.avx512.rsqrt14.pd.128" => "__builtin_ia32_rsqrt14pd128_mask", @@ -8754,10 +8949,10 @@ match name { "llvm.x86.avx512.rsqrt14.ss" => "__builtin_ia32_rsqrt14ss_mask", "llvm.x86.avx512.rsqrt28.pd" => "__builtin_ia32_rsqrt28pd_mask", "llvm.x86.avx512.rsqrt28.ps" => "__builtin_ia32_rsqrt28ps_mask", - // [INVALID CONVERSION]: "llvm.x86.avx512.rsqrt28.sd" => "__builtin_ia32_rsqrt28sd_round_mask", - // [DUPLICATE]: "llvm.x86.avx512.rsqrt28.sd" => "__builtin_ia32_rsqrt28sd_mask", - // [INVALID CONVERSION]: "llvm.x86.avx512.rsqrt28.ss" => "__builtin_ia32_rsqrt28ss_round_mask", - // [DUPLICATE]: "llvm.x86.avx512.rsqrt28.ss" => "__builtin_ia32_rsqrt28ss_mask", + "llvm.x86.avx512.rsqrt28.sd" => "__builtin_ia32_rsqrt28sd_mask", + // [DUPLICATE]: "llvm.x86.avx512.rsqrt28.sd" => "__builtin_ia32_rsqrt28sd_round_mask", + "llvm.x86.avx512.rsqrt28.ss" => "__builtin_ia32_rsqrt28ss_mask", + // [DUPLICATE]: "llvm.x86.avx512.rsqrt28.ss" => "__builtin_ia32_rsqrt28ss_round_mask", "llvm.x86.avx512.scatter.dpd.512" => "__builtin_ia32_scattersiv8df", "llvm.x86.avx512.scatter.dpi.512" => "__builtin_ia32_scattersiv16si", "llvm.x86.avx512.scatter.dpq.512" => "__builtin_ia32_scattersiv8di", @@ -9082,75 +9277,6 @@ match name { "llvm.x86.lwpval64" => "__builtin_ia32_lwpval64", "llvm.x86.mmx.emms" => "__builtin_ia32_emms", "llvm.x86.mmx.femms" => "__builtin_ia32_femms", - "llvm.x86.mmx.maskmovq" => "__builtin_ia32_maskmovq", - "llvm.x86.mmx.movnt.dq" => "__builtin_ia32_movntq", - "llvm.x86.mmx.packssdw" => "__builtin_ia32_packssdw", - "llvm.x86.mmx.packsswb" => "__builtin_ia32_packsswb", - "llvm.x86.mmx.packuswb" => "__builtin_ia32_packuswb", - "llvm.x86.mmx.padd.b" => "__builtin_ia32_paddb", - "llvm.x86.mmx.padd.d" => "__builtin_ia32_paddd", - "llvm.x86.mmx.padd.q" => "__builtin_ia32_paddq", - "llvm.x86.mmx.padd.w" => "__builtin_ia32_paddw", - "llvm.x86.mmx.padds.b" => "__builtin_ia32_paddsb", - "llvm.x86.mmx.padds.w" => "__builtin_ia32_paddsw", - "llvm.x86.mmx.paddus.b" => "__builtin_ia32_paddusb", - "llvm.x86.mmx.paddus.w" => "__builtin_ia32_paddusw", - "llvm.x86.mmx.palignr.b" => "__builtin_ia32_palignr", - "llvm.x86.mmx.pand" => "__builtin_ia32_pand", - "llvm.x86.mmx.pandn" => "__builtin_ia32_pandn", - "llvm.x86.mmx.pavg.b" => "__builtin_ia32_pavgb", - "llvm.x86.mmx.pavg.w" => "__builtin_ia32_pavgw", - "llvm.x86.mmx.pcmpeq.b" => "__builtin_ia32_pcmpeqb", - "llvm.x86.mmx.pcmpeq.d" => "__builtin_ia32_pcmpeqd", - "llvm.x86.mmx.pcmpeq.w" => "__builtin_ia32_pcmpeqw", - "llvm.x86.mmx.pcmpgt.b" => "__builtin_ia32_pcmpgtb", - "llvm.x86.mmx.pcmpgt.d" => "__builtin_ia32_pcmpgtd", - "llvm.x86.mmx.pcmpgt.w" => "__builtin_ia32_pcmpgtw", - "llvm.x86.mmx.pextr.w" => "__builtin_ia32_vec_ext_v4hi", - "llvm.x86.mmx.pinsr.w" => "__builtin_ia32_vec_set_v4hi", - "llvm.x86.mmx.pmadd.wd" => "__builtin_ia32_pmaddwd", - "llvm.x86.mmx.pmaxs.w" => "__builtin_ia32_pmaxsw", - "llvm.x86.mmx.pmaxu.b" => "__builtin_ia32_pmaxub", - "llvm.x86.mmx.pmins.w" => "__builtin_ia32_pminsw", - "llvm.x86.mmx.pminu.b" => "__builtin_ia32_pminub", - "llvm.x86.mmx.pmovmskb" => "__builtin_ia32_pmovmskb", - "llvm.x86.mmx.pmulh.w" => "__builtin_ia32_pmulhw", - "llvm.x86.mmx.pmulhu.w" => "__builtin_ia32_pmulhuw", - "llvm.x86.mmx.pmull.w" => "__builtin_ia32_pmullw", - "llvm.x86.mmx.pmulu.dq" => "__builtin_ia32_pmuludq", - "llvm.x86.mmx.por" => "__builtin_ia32_por", - "llvm.x86.mmx.psad.bw" => "__builtin_ia32_psadbw", - "llvm.x86.mmx.psll.d" => "__builtin_ia32_pslld", - "llvm.x86.mmx.psll.q" => "__builtin_ia32_psllq", - "llvm.x86.mmx.psll.w" => "__builtin_ia32_psllw", - "llvm.x86.mmx.pslli.d" => "__builtin_ia32_pslldi", - "llvm.x86.mmx.pslli.q" => "__builtin_ia32_psllqi", - "llvm.x86.mmx.pslli.w" => "__builtin_ia32_psllwi", - "llvm.x86.mmx.psra.d" => "__builtin_ia32_psrad", - "llvm.x86.mmx.psra.w" => "__builtin_ia32_psraw", - "llvm.x86.mmx.psrai.d" => "__builtin_ia32_psradi", - "llvm.x86.mmx.psrai.w" => "__builtin_ia32_psrawi", - "llvm.x86.mmx.psrl.d" => "__builtin_ia32_psrld", - "llvm.x86.mmx.psrl.q" => "__builtin_ia32_psrlq", - "llvm.x86.mmx.psrl.w" => "__builtin_ia32_psrlw", - "llvm.x86.mmx.psrli.d" => "__builtin_ia32_psrldi", - "llvm.x86.mmx.psrli.q" => "__builtin_ia32_psrlqi", - "llvm.x86.mmx.psrli.w" => "__builtin_ia32_psrlwi", - "llvm.x86.mmx.psub.b" => "__builtin_ia32_psubb", - "llvm.x86.mmx.psub.d" => "__builtin_ia32_psubd", - "llvm.x86.mmx.psub.q" => "__builtin_ia32_psubq", - "llvm.x86.mmx.psub.w" => "__builtin_ia32_psubw", - "llvm.x86.mmx.psubs.b" => "__builtin_ia32_psubsb", - "llvm.x86.mmx.psubs.w" => "__builtin_ia32_psubsw", - "llvm.x86.mmx.psubus.b" => "__builtin_ia32_psubusb", - "llvm.x86.mmx.psubus.w" => "__builtin_ia32_psubusw", - "llvm.x86.mmx.punpckhbw" => "__builtin_ia32_punpckhbw", - "llvm.x86.mmx.punpckhdq" => "__builtin_ia32_punpckhdq", - "llvm.x86.mmx.punpckhwd" => "__builtin_ia32_punpckhwd", - "llvm.x86.mmx.punpcklbw" => "__builtin_ia32_punpcklbw", - "llvm.x86.mmx.punpckldq" => "__builtin_ia32_punpckldq", - "llvm.x86.mmx.punpcklwd" => "__builtin_ia32_punpcklwd", - "llvm.x86.mmx.pxor" => "__builtin_ia32_pxor", "llvm.x86.monitorx" => "__builtin_ia32_monitorx", "llvm.x86.movdir64b" => "__builtin_ia32_movdir64b", "llvm.x86.mwaitx" => "__builtin_ia32_mwaitx", @@ -9193,16 +9319,10 @@ match name { "llvm.x86.sse.comile.ss" => "__builtin_ia32_comile", "llvm.x86.sse.comilt.ss" => "__builtin_ia32_comilt", "llvm.x86.sse.comineq.ss" => "__builtin_ia32_comineq", - "llvm.x86.sse.cvtpd2pi" => "__builtin_ia32_cvtpd2pi", - "llvm.x86.sse.cvtpi2pd" => "__builtin_ia32_cvtpi2pd", - "llvm.x86.sse.cvtpi2ps" => "__builtin_ia32_cvtpi2ps", - "llvm.x86.sse.cvtps2pi" => "__builtin_ia32_cvtps2pi", "llvm.x86.sse.cvtsi2ss" => "__builtin_ia32_cvtsi2ss", "llvm.x86.sse.cvtsi642ss" => "__builtin_ia32_cvtsi642ss", "llvm.x86.sse.cvtss2si" => "__builtin_ia32_cvtss2si", "llvm.x86.sse.cvtss2si64" => "__builtin_ia32_cvtss2si64", - "llvm.x86.sse.cvttpd2pi" => "__builtin_ia32_cvttpd2pi", - "llvm.x86.sse.cvttps2pi" => "__builtin_ia32_cvttps2pi", "llvm.x86.sse.cvttss2si" => "__builtin_ia32_cvttss2si", "llvm.x86.sse.cvttss2si64" => "__builtin_ia32_cvttss2si64", "llvm.x86.sse.div.ss" => "__builtin_ia32_divss", @@ -9212,7 +9332,6 @@ match name { "llvm.x86.sse.min.ss" => "__builtin_ia32_minss", "llvm.x86.sse.movmsk.ps" => "__builtin_ia32_movmskps", "llvm.x86.sse.mul.ss" => "__builtin_ia32_mulss", - "llvm.x86.sse.pshuf.w" => "__builtin_ia32_pshufw", "llvm.x86.sse.rcp.ps" => "__builtin_ia32_rcpps", "llvm.x86.sse.rcp.ss" => "__builtin_ia32_rcpss", "llvm.x86.sse.rsqrt.ps" => "__builtin_ia32_rsqrtps", @@ -9398,35 +9517,20 @@ match name { "llvm.x86.sse4a.insertqi" => "__builtin_ia32_insertqi", "llvm.x86.sse4a.movnt.sd" => "__builtin_ia32_movntsd", "llvm.x86.sse4a.movnt.ss" => "__builtin_ia32_movntss", - "llvm.x86.ssse3.pabs.b" => "__builtin_ia32_pabsb", "llvm.x86.ssse3.pabs.b.128" => "__builtin_ia32_pabsb128", - "llvm.x86.ssse3.pabs.d" => "__builtin_ia32_pabsd", "llvm.x86.ssse3.pabs.d.128" => "__builtin_ia32_pabsd128", - "llvm.x86.ssse3.pabs.w" => "__builtin_ia32_pabsw", "llvm.x86.ssse3.pabs.w.128" => "__builtin_ia32_pabsw128", - "llvm.x86.ssse3.phadd.d" => "__builtin_ia32_phaddd", "llvm.x86.ssse3.phadd.d.128" => "__builtin_ia32_phaddd128", - "llvm.x86.ssse3.phadd.sw" => "__builtin_ia32_phaddsw", "llvm.x86.ssse3.phadd.sw.128" => "__builtin_ia32_phaddsw128", - "llvm.x86.ssse3.phadd.w" => "__builtin_ia32_phaddw", "llvm.x86.ssse3.phadd.w.128" => "__builtin_ia32_phaddw128", - "llvm.x86.ssse3.phsub.d" => "__builtin_ia32_phsubd", "llvm.x86.ssse3.phsub.d.128" => "__builtin_ia32_phsubd128", - "llvm.x86.ssse3.phsub.sw" => "__builtin_ia32_phsubsw", "llvm.x86.ssse3.phsub.sw.128" => "__builtin_ia32_phsubsw128", - "llvm.x86.ssse3.phsub.w" => "__builtin_ia32_phsubw", "llvm.x86.ssse3.phsub.w.128" => "__builtin_ia32_phsubw128", - "llvm.x86.ssse3.pmadd.ub.sw" => "__builtin_ia32_pmaddubsw", "llvm.x86.ssse3.pmadd.ub.sw.128" => "__builtin_ia32_pmaddubsw128", - "llvm.x86.ssse3.pmul.hr.sw" => "__builtin_ia32_pmulhrsw", "llvm.x86.ssse3.pmul.hr.sw.128" => "__builtin_ia32_pmulhrsw128", - "llvm.x86.ssse3.pshuf.b" => "__builtin_ia32_pshufb", "llvm.x86.ssse3.pshuf.b.128" => "__builtin_ia32_pshufb128", - "llvm.x86.ssse3.psign.b" => "__builtin_ia32_psignb", "llvm.x86.ssse3.psign.b.128" => "__builtin_ia32_psignb128", - "llvm.x86.ssse3.psign.d" => "__builtin_ia32_psignd", "llvm.x86.ssse3.psign.d.128" => "__builtin_ia32_psignd128", - "llvm.x86.ssse3.psign.w" => "__builtin_ia32_psignw", "llvm.x86.ssse3.psign.w.128" => "__builtin_ia32_psignw128", "llvm.x86.sttilecfg" => "__builtin_ia32_tile_storeconfig", "llvm.x86.stui" => "__builtin_ia32_stui", From 2e7d2562805b79647d192a1511f2d616607c565a Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 3 Sep 2024 16:25:28 -0400 Subject: [PATCH 0256/1301] Add more SIMD intrinsics --- src/intrinsic/llvm.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 3b9855ddc1894..2d7bb8a2d5481 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -185,7 +185,10 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( | "__builtin_ia32_vplzcntq_128_mask" | "__builtin_ia32_cvtqq2pd128_mask" | "__builtin_ia32_cvtqq2pd256_mask" - | "__builtin_ia32_cvtqq2ps256_mask" => { + | "__builtin_ia32_cvtqq2ps256_mask" + | "__builtin_ia32_cvtuqq2pd128_mask" + | "__builtin_ia32_cvtuqq2pd256_mask" + | "__builtin_ia32_cvtuqq2ps256_mask" => { let mut new_args = args.to_vec(); // Remove last arg as it doesn't seem to be used in GCC and is always false. new_args.pop(); @@ -381,7 +384,8 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( ); args = vec![arg.get_address(None)].into(); } - "__builtin_ia32_cvtqq2pd512_mask" | "__builtin_ia32_cvtqq2ps512_mask" => { + "__builtin_ia32_cvtqq2pd512_mask" | "__builtin_ia32_cvtqq2ps512_mask" | "__builtin_ia32_cvtuqq2pd512_mask" | + "__builtin_ia32_cvtuqq2ps512_mask" => { let mut old_args = args.to_vec(); let mut new_args = vec![]; new_args.push(old_args.swap_remove(0)); @@ -493,6 +497,11 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let new_args = args.to_vec(); args = vec![new_args[1], new_args[0], new_args[2]].into(); } + "__builtin_ia32_rangesd128_mask_round" | "__builtin_ia32_rangess128_mask_round" + | "__builtin_ia32_reducesd_mask_round" => { + let new_args = args.to_vec(); + args = vec![new_args[0], new_args[1], new_args[4], new_args[2], new_args[3], new_args[5]].into(); + } _ => (), } } @@ -1031,6 +1040,14 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.sitofp.round.v4f64.v4i64" => "__builtin_ia32_cvtqq2pd256_mask", "llvm.x86.avx512.sitofp.round.v8f32.v8i64" => "__builtin_ia32_cvtqq2ps512_mask", "llvm.x86.avx512.sitofp.round.v4f32.v4i64" => "__builtin_ia32_cvtqq2ps256_mask", + "llvm.x86.avx512.uitofp.round.v8f64.v8u64" => "__builtin_ia32_cvtuqq2pd512_mask", + "llvm.x86.avx512.uitofp.round.v2f64.v2u64" => "__builtin_ia32_cvtuqq2pd128_mask", + "llvm.x86.avx512.uitofp.round.v4f64.v4u64" => "__builtin_ia32_cvtuqq2pd256_mask", + "llvm.x86.avx512.uitofp.round.v8f32.v8u64" => "__builtin_ia32_cvtuqq2ps512_mask", + "llvm.x86.avx512.uitofp.round.v4f32.v4u64" => "__builtin_ia32_cvtuqq2ps256_mask", + "llvm.x86.avx512.mask.reduce.pd.512" => "__builtin_ia32_reducepd512_mask_round", + "llvm.x86.avx512.mask.reduce.ps.512" => "__builtin_ia32_reduceps512_mask_round", + "llvm.x86.avx512.mask.reduce.sd" => "__builtin_ia32_reducesd_mask_round", // NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py _ => include!("archs.rs"), From 139eea948d2f4d6f816f1ac548527566814b37de Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Sep 2024 00:34:34 +0200 Subject: [PATCH 0257/1301] fmt --- src/intrinsic/llvm.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 2d7bb8a2d5481..981603747c547 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -384,8 +384,10 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( ); args = vec![arg.get_address(None)].into(); } - "__builtin_ia32_cvtqq2pd512_mask" | "__builtin_ia32_cvtqq2ps512_mask" | "__builtin_ia32_cvtuqq2pd512_mask" | - "__builtin_ia32_cvtuqq2ps512_mask" => { + "__builtin_ia32_cvtqq2pd512_mask" + | "__builtin_ia32_cvtqq2ps512_mask" + | "__builtin_ia32_cvtuqq2pd512_mask" + | "__builtin_ia32_cvtuqq2ps512_mask" => { let mut old_args = args.to_vec(); let mut new_args = vec![]; new_args.push(old_args.swap_remove(0)); @@ -497,10 +499,19 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let new_args = args.to_vec(); args = vec![new_args[1], new_args[0], new_args[2]].into(); } - "__builtin_ia32_rangesd128_mask_round" | "__builtin_ia32_rangess128_mask_round" - | "__builtin_ia32_reducesd_mask_round" => { + "__builtin_ia32_rangesd128_mask_round" + | "__builtin_ia32_rangess128_mask_round" + | "__builtin_ia32_reducesd_mask_round" => { let new_args = args.to_vec(); - args = vec![new_args[0], new_args[1], new_args[4], new_args[2], new_args[3], new_args[5]].into(); + args = vec![ + new_args[0], + new_args[1], + new_args[4], + new_args[2], + new_args[3], + new_args[5], + ] + .into(); } _ => (), } From 244ac087349bd57a626202ce37a82f90a94d4037 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Sep 2024 00:35:04 +0200 Subject: [PATCH 0258/1301] Add missing intrinsic translation for `llvm.x86.xrstor64` --- src/intrinsic/llvm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 981603747c547..d3e50afe53b2a 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -821,6 +821,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.mask.cmp.b.256" => "__builtin_ia32_cmpb256_mask", "llvm.x86.avx512.mask.cmp.b.128" => "__builtin_ia32_cmpb128_mask", "llvm.x86.xrstor" => "__builtin_ia32_xrstor", + "llvm.x86.xrstor64" => "__builtin_ia32_xrstor64", "llvm.x86.xsavec" => "__builtin_ia32_xsavec", "llvm.x86.xsave64" => "__builtin_ia32_xsave64", "llvm.x86.addcarry.32" => "__builtin_ia32_addcarryx_u32", From 39e191077bb075a1b4f6facc9b75e5270cb507e4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Sep 2024 00:42:12 +0200 Subject: [PATCH 0259/1301] Add missing intrinsic translation for `llvm.x86.xsaveopt64` --- src/intrinsic/llvm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index d3e50afe53b2a..286aa6dab75c4 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -1029,6 +1029,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.vpdpbusds.128" => "__builtin_ia32_vpdpbusds_v4si", "llvm.x86.xsave" => "__builtin_ia32_xsave", "llvm.x86.xsaveopt" => "__builtin_ia32_xsaveopt", + "llvm.x86.xsaveopt64" => "__builtin_ia32_xsaveopt64", "llvm.x86.avx512.mask.loadu.w.512" => "__builtin_ia32_loaddquhi512_mask", "llvm.x86.avx512.mask.loadu.b.512" => "__builtin_ia32_loaddquqi512_mask", "llvm.x86.avx512.mask.loadu.w.256" => "__builtin_ia32_loaddquhi256_mask", From 1a70f40d7fa8cafa37037652ba6ea8b7d9938b7a Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Sun, 1 Sep 2024 13:13:14 +0200 Subject: [PATCH 0260/1301] Avoid allocation in `ChainItemKind::is_tup_field_access` --- src/chains.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chains.rs b/src/chains.rs index 297eb268d30cd..693aaef997904 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -209,7 +209,7 @@ impl ChainItemKind { fn is_tup_field_access(expr: &ast::Expr) -> bool { match expr.kind { ast::ExprKind::Field(_, ref field) => { - field.name.to_string().chars().all(|c| c.is_digit(10)) + field.name.as_str().chars().all(|c| c.is_digit(10)) } _ => false, } From 623dc09fa3a1d12f5a83ed763f4defa1c7e84c67 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 3 Sep 2024 21:09:49 -0400 Subject: [PATCH 0261/1301] Add support for more SIMD intrinsics --- src/intrinsic/llvm.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 286aa6dab75c4..ba57e425b0fca 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -501,7 +501,8 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( } "__builtin_ia32_rangesd128_mask_round" | "__builtin_ia32_rangess128_mask_round" - | "__builtin_ia32_reducesd_mask_round" => { + | "__builtin_ia32_reducesd_mask_round" + | "__builtin_ia32_reducess_mask_round" => { let new_args = args.to_vec(); args = vec![ new_args[0], @@ -1061,6 +1062,21 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.mask.reduce.pd.512" => "__builtin_ia32_reducepd512_mask_round", "llvm.x86.avx512.mask.reduce.ps.512" => "__builtin_ia32_reduceps512_mask_round", "llvm.x86.avx512.mask.reduce.sd" => "__builtin_ia32_reducesd_mask_round", + "llvm.x86.avx512.mask.reduce.ss" => "__builtin_ia32_reducess_mask_round", + "llvm.x86.avx512.mask.loadu.d.256" => "__builtin_ia32_loaddqusi256_mask", + "llvm.x86.avx512.mask.loadu.q.256" => "__builtin_ia32_loaddqudi256_mask", + "llvm.x86.avx512.mask.loadu.ps.256" => "__builtin_ia32_loadups256_mask", + "llvm.x86.avx512.mask.loadu.pd.256" => "__builtin_ia32_loadupd256_mask", + "llvm.x86.avx512.mask.loadu.d.128" => "__builtin_ia32_loaddqusi128_mask", + "llvm.x86.avx512.mask.loadu.q.128" => "__builtin_ia32_loaddqudi128_mask", + "llvm.x86.avx512.mask.loadu.ps.128" => "__builtin_ia32_loadups128_mask", + "llvm.x86.avx512.mask.loadu.pd.128" => "__builtin_ia32_loadupd128_mask", + "llvm.x86.avx512.mask.load.d.512" => "__builtin_ia32_movdqa32load512_mask", + "llvm.x86.avx512.mask.load.q.512" => "__builtin_ia32_movdqa64load512_mask", + "llvm.x86.avx512.mask.load.ps.512" => "__builtin_ia32_loadaps512_mask", + "llvm.x86.avx512.mask.load.pd.512" => "__builtin_ia32_loadapd512_mask", + "llvm.x86.avx512.mask.load.d.256" => "__builtin_ia32_movdqa32load256_mask", + "llvm.x86.avx512.mask.load.q.256" => "__builtin_ia32_movdqa64load256_mask", // NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py _ => include!("archs.rs"), From 01b1bfd18aa97c42bdee359260561b27e351e9a6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 4 Sep 2024 11:32:59 +0200 Subject: [PATCH 0262/1301] Add edition dependent keyword highlighting tests --- .../test_data/highlight_keywords_2015.html | 74 +++++++++++++++++++ .../test_data/highlight_keywords_2018.html | 74 +++++++++++++++++++ .../test_data/highlight_keywords_2021.html | 74 +++++++++++++++++++ .../test_data/highlight_keywords_2024.html | 74 +++++++++++++++++++ .../ide/src/syntax_highlighting/tests.rs | 31 ++++++-- .../crates/parser/src/edition.rs | 6 ++ 6 files changed, 326 insertions(+), 7 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html new file mode 100644 index 0000000000000..a790b385786d8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html new file mode 100644 index 0000000000000..6dac066bfaa19 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html new file mode 100644 index 0000000000000..6dac066bfaa19 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html new file mode 100644 index 0000000000000..4ccc40799088a --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index ad7fbb00e5c66..c06ea155fb86b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -2,6 +2,7 @@ use std::time::Instant; use expect_test::{expect_file, ExpectFile}; use ide_db::SymbolKind; +use span::Edition; use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear}; use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange}; @@ -383,8 +384,10 @@ where #[test] fn test_keyword_highlighting() { - check_highlighting( - r#" + for edition in Edition::iter() { + check_highlighting( + &(format!("//- /main.rs crate:main edition:{edition}") + + r#" extern crate self; use crate; @@ -396,13 +399,27 @@ mod __ { macro_rules! void { ($($tt:tt)*) => {} } -void!(Self); + struct __ where Self:; fn __(_: Self) {} -"#, - expect_file!["./test_data/highlight_keywords.html"], - false, - ); +void!(Self); + +// edition dependent +void!(try async await gen); +// edition and context dependent +void!(dyn); +// builtin custom syntax +void!(builtin offset_of format_args asm); +// contextual +void!(macro_rules, union, default, raw, auto, yeet); +// reserved +void!(abstract become box do final macro override priv typeof unsized virtual yield); +void!('static 'self 'unsafe) +"#), + expect_file![format!("./test_data/highlight_keywords_{edition}.html")], + false, + ); + } } #[test] diff --git a/src/tools/rust-analyzer/crates/parser/src/edition.rs b/src/tools/rust-analyzer/crates/parser/src/edition.rs index be0a2c794e58c..702b16252d4e7 100644 --- a/src/tools/rust-analyzer/crates/parser/src/edition.rs +++ b/src/tools/rust-analyzer/crates/parser/src/edition.rs @@ -30,6 +30,12 @@ impl Edition { pub fn at_least_2018(self) -> bool { self >= Edition::Edition2018 } + + pub fn iter() -> impl Iterator { + [Edition::Edition2015, Edition::Edition2018, Edition::Edition2021, Edition::Edition2024] + .iter() + .copied() + } } #[derive(Debug)] From 1e7d99ff4392e10c5d6195d624ba3de846198c88 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 4 Sep 2024 11:48:03 +0200 Subject: [PATCH 0263/1301] fix: Fix lowering of for loops dropping the `loop` block --- .../crates/hir-def/src/body/lower.rs | 31 +++++++++++----- .../crates/hir-def/src/body/tests.rs | 35 +++++++++++++++++++ .../crates/hir-ty/src/tests/macros.rs | 2 ++ .../crates/hir-ty/src/tests/never_type.rs | 3 ++ .../crates/hir-ty/src/tests/patterns.rs | 1 + .../crates/hir-ty/src/tests/regression.rs | 2 ++ 6 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index abf7895829206..145afc1b610c2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -737,7 +737,7 @@ impl ExprCollector<'_> { /// `try { ; }` into `': { ; ::std::ops::Try::from_output(()) }` /// and save the `` to use it as a break target for desugaring of the `?` operator. fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { - let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else { + let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else { return self.collect_block(e); }; let label = self @@ -840,10 +840,10 @@ impl ExprCollector<'_> { fn collect_for_loop(&mut self, syntax_ptr: AstPtr, e: ast::ForExpr) -> ExprId { let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| { Some(( - LangItem::IntoIterIntoIter.path(self.db, self.krate)?, - LangItem::IteratorNext.path(self.db, self.krate)?, - LangItem::OptionSome.path(self.db, self.krate)?, - LangItem::OptionNone.path(self.db, self.krate)?, + self.lang_path(LangItem::IntoIterIntoIter)?, + self.lang_path(LangItem::IteratorNext)?, + self.lang_path(LangItem::OptionSome)?, + self.lang_path(LangItem::OptionNone)?, )) })() else { // Some of the needed lang items are missing, so we can't desugar @@ -896,6 +896,15 @@ impl ExprCollector<'_> { Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr, ); + let loop_inner = self.alloc_expr( + Expr::Block { + id: None, + statements: Box::default(), + tail: Some(loop_inner), + label: None, + }, + syntax_ptr, + ); let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); @@ -923,10 +932,10 @@ impl ExprCollector<'_> { fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| { Some(( - LangItem::TryTraitBranch.path(self.db, self.krate)?, - LangItem::ControlFlowContinue.path(self.db, self.krate)?, - LangItem::ControlFlowBreak.path(self.db, self.krate)?, - LangItem::TryTraitFromResidual.path(self.db, self.krate)?, + self.lang_path(LangItem::TryTraitBranch)?, + self.lang_path(LangItem::ControlFlowContinue)?, + self.lang_path(LangItem::ControlFlowBreak)?, + self.lang_path(LangItem::TryTraitFromResidual)?, )) })() else { // Some of the needed lang items are missing, so we can't desugar @@ -2053,6 +2062,10 @@ impl ExprCollector<'_> { }) } // endregion: format + + fn lang_path(&self, lang: LangItem) -> Option { + lang.path(self.db, self.krate) + } } fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs index 38fc01d8fca5f..dd3e79c874d85 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs @@ -142,6 +142,41 @@ mod m { ); } +#[test] +fn desugar_for_loop() { + let (db, body, def) = lower( + r#" +//- minicore: iterator +fn main() { + for ident in 0..10 { + foo(); + bar() + } +} +"#, + ); + + expect![[r#" + fn main() -> () { + match builtin#lang(into_iter)( + (0) ..(10) , + ) { + mut 11 => loop { + match builtin#lang(next)( + &mut 11, + ) { + builtin#lang(None) => break, + builtin#lang(Some)(ident) => { + foo(); + bar() + }, + } + }, + } + }"#]] + .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) +} + #[test] fn desugar_builtin_format_args() { let (db, body, def) = lower( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5454a496ba8c1..5a6161f94289f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -206,6 +206,7 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () + 100..119 'for _ ...!() {}': () 104..105 '_': IntoIterator::Item 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize @@ -299,6 +300,7 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () + 114..133 'for _ ...!() {}': () 118..119 '_': IntoIterator::Item 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 0ccbcf63e2b67..5c63cd00f97d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -371,6 +371,7 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () + 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} 162..172 '{ break; }': () @@ -387,6 +388,7 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () + 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} 248..250 '{}': () @@ -402,6 +404,7 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () + 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} 326..337 '{ return; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 57866acc06374..51c27f8714ac5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -57,6 +57,7 @@ fn infer_pattern() { 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () + 101..151 'for (e... }': () 105..111 '(e, f)': ({unknown}, {unknown}) 106..107 'e': {unknown} 109..110 'f': {unknown} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 4df10025f80fb..db289f430eafb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -275,6 +275,7 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () + 32..320 'for co... }': () 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () @@ -1244,6 +1245,7 @@ fn test() { 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () + 16..66 'for _ ... }': () 20..21 '_': IntoIterator::Item<()> 25..39 '{ let x = 0; }': () 31..32 'x': i32 From 7222f2de387c5bf23799b5d9c4e124a75690ed18 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 1 Sep 2024 13:43:05 +0200 Subject: [PATCH 0264/1301] Parse builtin#asm expressions --- src/tools/rust-analyzer/.typos.toml | 1 + src/tools/rust-analyzer/Cargo.lock | 1 + src/tools/rust-analyzer/Cargo.toml | 1 + .../crates/hir-def/src/body/lower.rs | 7 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 5 +- .../crates/hir-ty/src/infer/closure.rs | 4 +- .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir-ty/src/infer/mutability.rs | 7 +- .../parser/src/grammar/expressions/atom.rs | 171 ++++++- .../rust-analyzer/crates/parser/src/parser.rs | 8 + .../parser/src/syntax_kind/generated.rs | 77 +++- .../parser/test_data/generated/runner.rs | 2 + .../test_data/parser/inline/ok/asm_expr.rast | 77 ++++ .../test_data/parser/inline/ok/asm_expr.rs | 10 + .../parser/inline/ok/builtin_expr.rast | 2 +- .../parser/inline/ok/builtin_expr.rs | 2 +- .../rust-analyzer/crates/syntax/rust.ungram | 27 +- .../crates/syntax/src/ast/generated/nodes.rs | 425 +++++++++++++++++- src/tools/rust-analyzer/xtask/Cargo.toml | 1 + .../xtask/src/codegen/grammar.rs | 40 +- .../xtask/src/codegen/grammar/ast_src.rs | 26 +- 21 files changed, 865 insertions(+), 31 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index e7e764ce03517..febfb233bd979 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -15,6 +15,7 @@ extend-ignore-re = [ '"flate2"', "raison d'être", "inout", + "INOUT", "optin" ] diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 6eaded6da14ab..85ef3a6ba92ca 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2624,6 +2624,7 @@ version = "0.1.0" dependencies = [ "anyhow", "directories", + "either", "flate2", "itertools", "proc-macro2", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index e55628c8dcf4c..9e972e12118da 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -185,6 +185,7 @@ style = { level = "warn", priority = -1 } suspicious = { level = "warn", priority = -1 } ## allow following lints +too_long_first_doc_paragraph = "allow" # subjective single_match = "allow" # () makes a fine error in most cases diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 145afc1b610c2..f2eb43beb1f18 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -694,8 +694,11 @@ impl ExprCollector<'_> { } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), ast::Expr::AsmExpr(e) => { - let e = self.collect_expr_opt(e.expr()); - self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr) + let template = e.template().map(|it| self.collect_expr(it)).collect(); + self.alloc_expr( + Expr::InlineAsm(InlineAsm { template, operands: Box::default() }), + syntax_ptr, + ) } ast::Expr::OffsetOfExpr(e) => { let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 86fd092603ffd..8f537672b58d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -307,7 +307,8 @@ pub struct OffsetOf { #[derive(Debug, Clone, PartialEq, Eq)] pub struct InlineAsm { - pub e: ExprId, + pub template: Box<[ExprId]>, + pub operands: Box<[()]>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -372,7 +373,7 @@ impl Expr { match self { Expr::Missing => {} Expr::Path(_) | Expr::OffsetOf(_) => {} - Expr::InlineAsm(it) => f(it.e), + Expr::InlineAsm(it) => it.template.iter().copied().for_each(f), Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 36327d1d49c0a..67a3d2434d24e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -666,7 +666,9 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { Expr::OffsetOf(_) => (), - Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e), + Expr::InlineAsm(e) => { + e.template.iter().for_each(|it| self.walk_expr_without_adjust(*it)) + } Expr::If { condition, then_branch, else_branch } => { self.consume_expr(*condition); self.consume_expr(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index b1c793a1e38f6..6b725d690d1a7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -925,7 +925,7 @@ impl InferenceContext<'_> { } Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), Expr::InlineAsm(it) => { - self.infer_expr_no_expect(it.e); + it.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr)); self.result.standard_types.unit.clone() } }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 7fed5f0203bad..e1b460d072d3f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -39,7 +39,10 @@ impl InferenceContext<'_> { fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { match &self.body[tgt_expr] { Expr::Missing => (), - Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not), + Expr::InlineAsm(e) => e + .template + .iter() + .for_each(|&expr| self.infer_mut_expr_without_adjust(expr, Mutability::Not)), Expr::OffsetOf(_) => (), &Expr::If { condition, then_branch, else_branch } => { self.infer_mut_expr(condition, Mutability::Not); @@ -129,7 +132,7 @@ impl InferenceContext<'_> { target, }) = base_adjustments { - // For assignee exprs `IndexMut` obiligations are already applied + // For assignee exprs `IndexMut` obligations are already applied if !is_assignee_expr { if let TyKind::Ref(_, _, ty) = target.kind(Interner) { base_ty = Some(ty.clone()); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index a678c1f3a70e6..57f1e6e9f031f 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // test builtin_expr // fn foo() { -// builtin#asm(0); +// builtin#asm(""); // builtin#format_args("", 0, 1, a = 2 + 3, a + b); // builtin#offset_of(Foo, bar.baz.0); // } @@ -297,18 +297,175 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option { p.expect(T![')']); Some(m.complete(p, FORMAT_ARGS_EXPR)) } else if p.at_contextual_kw(T![asm]) { - p.bump_remap(T![asm]); - p.expect(T!['(']); - // FIXME: We just put expression here so highlighting kind of keeps working - expr(p); - p.expect(T![')']); - Some(m.complete(p, ASM_EXPR)) + parse_asm_expr(p, m) } else { m.abandon(p); None } } +// test asm_expr +// fn foo() { +// builtin#asm( +// "mov {tmp}, {x}", +// "shl {tmp}, 1", +// "shl {x}, 2", +// "add {x}, {tmp}", +// x = inout(reg) x, +// tmp = out(reg) _, +// ); +// } +fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { + p.bump_remap(T![asm]); + p.expect(T!['(']); + if expr(p).is_none() { + p.err_and_bump("expected asm template"); + } + let mut allow_templates = true; + while !p.at(EOF) && !p.at(T![')']) { + p.expect(T![,]); + // accept trailing commas + if p.at(T![')']) { + break; + } + + // Parse clobber_abi + if p.eat_contextual_kw(T![clobber_abi]) { + parse_clobber_abi(p); + allow_templates = false; + continue; + } + + // Parse options + if p.eat_contextual_kw(T![options]) { + parse_options(p); + allow_templates = false; + continue; + } + + // Parse operand names + if p.at(T![ident]) && p.nth_at(1, T![=]) { + name(p); + p.bump(T![=]); + allow_templates = false; + true + } else { + false + }; + + let op = p.start(); + if p.eat(T![in]) { + parse_reg(p); + expr(p); + op.complete(p, ASM_REG_OPERAND); + } else if p.eat_contextual_kw(T![out]) { + parse_reg(p); + expr(p); + op.complete(p, ASM_REG_OPERAND); + } else if p.eat_contextual_kw(T![lateout]) { + parse_reg(p); + expr(p); + op.complete(p, ASM_REG_OPERAND); + } else if p.eat_contextual_kw(T![inout]) { + parse_reg(p); + expr(p); + if p.eat(T![=>]) { + expr(p); + } + op.complete(p, ASM_REG_OPERAND); + } else if p.eat_contextual_kw(T![inlateout]) { + parse_reg(p); + expr(p); + if p.eat(T![=>]) { + expr(p); + } + op.complete(p, ASM_REG_OPERAND); + } else if p.eat_contextual_kw(T![label]) { + block_expr(p); + op.complete(p, ASM_LABEL); + } else if p.eat(T![const]) { + expr(p); + op.complete(p, ASM_CONST); + } else if p.eat_contextual_kw(T![sym]) { + expr(p); + op.complete(p, ASM_SYM); + } else if allow_templates { + op.abandon(p); + if expr(p).is_none() { + p.err_and_bump("expected asm template"); + } + continue; + } else { + op.abandon(p); + p.err_and_bump("expected asm operand"); + if p.at(T!['}']) { + break; + } + continue; + }; + allow_templates = false; + } + p.expect(T![')']); + Some(m.complete(p, ASM_EXPR)) +} + +fn parse_options(p: &mut Parser<'_>) { + p.expect(T!['(']); + + while !p.eat(T![')']) && !p.at(EOF) { + const OPTIONS: &[SyntaxKind] = &[ + T![pure], + T![nomem], + T![readonly], + T![preserves_flags], + T![noreturn], + T![nostack], + T![may_unwind], + T![att_syntax], + T![raw], + ]; + + if !OPTIONS.iter().any(|&syntax| p.eat(syntax)) { + p.err_and_bump("expected asm option"); + continue; + } + + // Allow trailing commas + if p.eat(T![')']) { + break; + } + p.expect(T![,]); + } +} + +fn parse_clobber_abi(p: &mut Parser<'_>) { + p.expect(T!['(']); + + while !p.eat(T![')']) && !p.at(EOF) { + if !p.expect(T![string]) { + break; + } + + // Allow trailing commas + if p.eat(T![')']) { + break; + } + p.expect(T![,]); + } +} + +fn parse_reg(p: &mut Parser<'_>) { + p.expect(T!['(']); + if p.at(T![ident]) { + name_ref(p) + } else if p.at(T![string]) { + p.bump_any() + } else { + p.err_and_bump("expected register name"); + } + p.expect(T![')']); +} + // test array_expr // fn foo() { // []; diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 7d3eb5de25f9a..f6b3783d1cace 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs @@ -131,6 +131,14 @@ impl<'t> Parser<'t> { true } + pub(crate) fn eat_contextual_kw(&mut self, kind: SyntaxKind) -> bool { + if !self.at_contextual_kw(kind) { + return false; + } + self.bump_remap(kind); + true + } + fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { self.inp.kind(self.pos + n) == k1 && self.inp.kind(self.pos + n + 1) == k2 diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 00f212487ae68..ee3adac158f41 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -111,16 +111,32 @@ pub enum SyntaxKind { YIELD_KW, ASM_KW, ASYNC_KW, + ATT_SYNTAX_KW, AUTO_KW, AWAIT_KW, BUILTIN_KW, + CLOBBER_ABI_KW, DEFAULT_KW, DYN_KW, FORMAT_ARGS_KW, GEN_KW, + INLATEOUT_KW, + INOUT_KW, + LABEL_KW, + LATEOUT_KW, MACRO_RULES_KW, + MAY_UNWIND_KW, + NOMEM_KW, + NORETURN_KW, + NOSTACK_KW, OFFSET_OF_KW, + OPTIONS_KW, + OUT_KW, + PRESERVES_FLAGS_KW, + PURE_KW, RAW_KW, + READONLY_KW, + SYM_KW, TRY_KW, UNION_KW, YEET_KW, @@ -146,7 +162,18 @@ pub enum SyntaxKind { ARG_LIST, ARRAY_EXPR, ARRAY_TYPE, + ASM_CLOBBER_ABI, + ASM_CONST, + ASM_DIR_SPEC, ASM_EXPR, + ASM_LABEL, + ASM_OPERAND, + ASM_OPERAND_EXPR, + ASM_OPTION, + ASM_OPTIONS, + ASM_REG_OPERAND, + ASM_REG_SPEC, + ASM_SYM, ASSOC_ITEM, ASSOC_ITEM_LIST, ASSOC_TYPE_ARG, @@ -364,14 +391,30 @@ impl SyntaxKind { pub fn is_contextual_keyword(self, edition: Edition) -> bool { match self { ASM_KW => true, + ATT_SYNTAX_KW => true, AUTO_KW => true, BUILTIN_KW => true, + CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, + INLATEOUT_KW => true, + INOUT_KW => true, + LABEL_KW => true, + LATEOUT_KW => true, MACRO_RULES_KW => true, + MAY_UNWIND_KW => true, + NOMEM_KW => true, + NORETURN_KW => true, + NOSTACK_KW => true, OFFSET_OF_KW => true, + OPTIONS_KW => true, + OUT_KW => true, + PRESERVES_FLAGS_KW => true, + PURE_KW => true, RAW_KW => true, + READONLY_KW => true, + SYM_KW => true, UNION_KW => true, YEET_KW => true, _ => false, @@ -435,14 +478,30 @@ impl SyntaxKind { GEN_KW if Edition::Edition2024 <= edition => true, TRY_KW if Edition::Edition2018 <= edition => true, ASM_KW => true, + ATT_SYNTAX_KW => true, AUTO_KW => true, BUILTIN_KW => true, + CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, + INLATEOUT_KW => true, + INOUT_KW => true, + LABEL_KW => true, + LATEOUT_KW => true, MACRO_RULES_KW => true, + MAY_UNWIND_KW => true, + NOMEM_KW => true, + NORETURN_KW => true, + NOSTACK_KW => true, OFFSET_OF_KW => true, + OPTIONS_KW => true, + OUT_KW => true, + PRESERVES_FLAGS_KW => true, + PURE_KW => true, RAW_KW => true, + READONLY_KW => true, + SYM_KW => true, UNION_KW => true, YEET_KW => true, _ => false, @@ -580,14 +639,30 @@ impl SyntaxKind { pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option { let kw = match ident { "asm" => ASM_KW, + "att_syntax" => ATT_SYNTAX_KW, "auto" => AUTO_KW, "builtin" => BUILTIN_KW, + "clobber_abi" => CLOBBER_ABI_KW, "default" => DEFAULT_KW, "dyn" if edition < Edition::Edition2018 => DYN_KW, "format_args" => FORMAT_ARGS_KW, + "inlateout" => INLATEOUT_KW, + "inout" => INOUT_KW, + "label" => LABEL_KW, + "lateout" => LATEOUT_KW, "macro_rules" => MACRO_RULES_KW, + "may_unwind" => MAY_UNWIND_KW, + "nomem" => NOMEM_KW, + "noreturn" => NORETURN_KW, + "nostack" => NOSTACK_KW, "offset_of" => OFFSET_OF_KW, + "options" => OPTIONS_KW, + "out" => OUT_KW, + "preserves_flags" => PRESERVES_FLAGS_KW, + "pure" => PURE_KW, "raw" => RAW_KW, + "readonly" => READONLY_KW, + "sym" => SYM_KW, "union" => UNION_KW, "yeet" => YEET_KW, _ => return None, @@ -630,4 +705,4 @@ impl SyntaxKind { } } #[macro_export] -macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } +macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 9ce5a2ae748fd..164d0f36f1be2 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -19,6 +19,8 @@ mod ok { #[test] fn as_precedence() { run_and_expect_no_errors("test_data/parser/inline/ok/as_precedence.rs"); } #[test] + fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); } + #[test] fn assoc_const_eq() { run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast new file mode 100644 index 0000000000000..f4d53fa9ae657 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast @@ -0,0 +1,77 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + ASM_EXPR + BUILTIN_KW "builtin" + POUND "#" + ASM_KW "asm" + L_PAREN "(" + WHITESPACE "\n " + LITERAL + STRING "\"mov {tmp}, {x}\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"shl {tmp}, 1\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"shl {x}, 2\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"add {x}, {tmp}\"" + COMMA "," + WHITESPACE "\n " + NAME + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_REG_OPERAND + INOUT_KW "inout" + L_PAREN "(" + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE "\n " + NAME + IDENT "tmp" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_REG_OPERAND + OUT_KW "out" + L_PAREN "(" + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + UNDERSCORE_EXPR + UNDERSCORE "_" + COMMA "," + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs new file mode 100644 index 0000000000000..0906cd3e7197d --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs @@ -0,0 +1,10 @@ +fn foo() { + builtin#asm( + "mov {tmp}, {x}", + "shl {tmp}, 1", + "shl {x}, 2", + "add {x}, {tmp}", + x = inout(reg) x, + tmp = out(reg) _, + ); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 361900b6d3e4b..19a84ac540961 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast @@ -19,7 +19,7 @@ SOURCE_FILE ASM_KW "asm" L_PAREN "(" LITERAL - INT_NUMBER "0" + STRING "\"\"" R_PAREN ")" SEMICOLON ";" WHITESPACE "\n " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs index 14431b0210ea8..920d0f794f242 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs @@ -1,5 +1,5 @@ fn foo() { - builtin#asm(0); + builtin#asm(""); builtin#format_args("", 0, 1, a = 2 + 3, a + b); builtin#offset_of(Foo, bar.baz.0); } diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 43375ce6ae051..4d780ba28f310 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -391,8 +391,33 @@ Expr = OffsetOfExpr = Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' +// asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")" +// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" +// format_string := STRING_LITERAL / RAW_STRING_LITERAL AsmExpr = - Attr* 'builtin' '#' 'asm' '(' Expr ')' + Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmOperand (',' AsmOperand)*)? ','? ')' + +// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_" +AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)? +// dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout" +AsmDirSpec = 'in' | 'out' | 'lateout' | 'inout' | 'inlateout' +// reg_spec := / "\"" "\"" +AsmRegSpec = '@string' | NameRef +// reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr +AsmRegOperand = (Name '=')? AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr +// clobber_abi := "clobber_abi(" *("," ) [","] ")" +AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')' +// option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" +AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind' +// options := "options(" option *("," option) [","] ")" +AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')' +// operand := reg_operand / clobber_abi / options +AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel +AsmLabel = 'label' BlockExpr +AsmSym = 'sym' Expr +AsmConst = 'const' Expr + + FormatArgsExpr = Attr* 'builtin' '#' 'format_args' '(' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index c9b39e9922c92..e5e1115e05a7b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -64,6 +64,53 @@ impl ArrayType { pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmClobberAbi { + pub(crate) syntax: SyntaxNode, +} +impl AsmClobberAbi { + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn clobber_abi_token(&self) -> Option { + support::token(&self.syntax, T![clobber_abi]) + } + #[inline] + pub fn string_token(&self) -> Option { support::token(&self.syntax, T![string]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmConst { + pub(crate) syntax: SyntaxNode, +} +impl AsmConst { + #[inline] + pub fn expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmDirSpec { + pub(crate) syntax: SyntaxNode, +} +impl AsmDirSpec { + #[inline] + pub fn in_token(&self) -> Option { support::token(&self.syntax, T![in]) } + #[inline] + pub fn inlateout_token(&self) -> Option { + support::token(&self.syntax, T![inlateout]) + } + #[inline] + pub fn inout_token(&self) -> Option { support::token(&self.syntax, T![inout]) } + #[inline] + pub fn lateout_token(&self) -> Option { support::token(&self.syntax, T![lateout]) } + #[inline] + pub fn out_token(&self) -> Option { support::token(&self.syntax, T![out]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AsmExpr { pub(crate) syntax: SyntaxNode, @@ -71,7 +118,9 @@ pub struct AsmExpr { impl ast::HasAttrs for AsmExpr {} impl AsmExpr { #[inline] - pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn asm_operands(&self) -> AstChildren { support::children(&self.syntax) } + #[inline] + pub fn template(&self) -> AstChildren { support::children(&self.syntax) } #[inline] pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } #[inline] @@ -79,11 +128,133 @@ impl AsmExpr { #[inline] pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] pub fn asm_token(&self) -> Option { support::token(&self.syntax, T![asm]) } #[inline] pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmLabel { + pub(crate) syntax: SyntaxNode, +} +impl AsmLabel { + #[inline] + pub fn block_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn label_token(&self) -> Option { support::token(&self.syntax, T![label]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOperandExpr { + pub(crate) syntax: SyntaxNode, +} +impl AsmOperandExpr { + #[inline] + pub fn in_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn out_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn fat_arrow_token(&self) -> Option { support::token(&self.syntax, T![=>]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOption { + pub(crate) syntax: SyntaxNode, +} +impl AsmOption { + #[inline] + pub fn att_syntax_token(&self) -> Option { + support::token(&self.syntax, T![att_syntax]) + } + #[inline] + pub fn may_unwind_token(&self) -> Option { + support::token(&self.syntax, T![may_unwind]) + } + #[inline] + pub fn nomem_token(&self) -> Option { support::token(&self.syntax, T![nomem]) } + #[inline] + pub fn noreturn_token(&self) -> Option { + support::token(&self.syntax, T![noreturn]) + } + #[inline] + pub fn nostack_token(&self) -> Option { support::token(&self.syntax, T![nostack]) } + #[inline] + pub fn preserves_flags_token(&self) -> Option { + support::token(&self.syntax, T![preserves_flags]) + } + #[inline] + pub fn pure_token(&self) -> Option { support::token(&self.syntax, T![pure]) } + #[inline] + pub fn raw_token(&self) -> Option { support::token(&self.syntax, T![raw]) } + #[inline] + pub fn readonly_token(&self) -> Option { + support::token(&self.syntax, T![readonly]) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOptions { + pub(crate) syntax: SyntaxNode, +} +impl AsmOptions { + #[inline] + pub fn asm_option(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_options(&self) -> AstChildren { support::children(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn options_token(&self) -> Option { support::token(&self.syntax, T![options]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmRegOperand { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasName for AsmRegOperand {} +impl AsmRegOperand { + #[inline] + pub fn asm_dir_spec(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_operand_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_reg_spec(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmRegSpec { + pub(crate) syntax: SyntaxNode, +} +impl AsmRegSpec { + #[inline] + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn string_token(&self) -> Option { support::token(&self.syntax, T![string]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmSym { + pub(crate) syntax: SyntaxNode, +} +impl AsmSym { + #[inline] + pub fn expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn sym_token(&self) -> Option { support::token(&self.syntax, T![sym]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AssocItemList { pub(crate) syntax: SyntaxNode, @@ -2051,6 +2222,14 @@ impl ast::HasGenericParams for Adt {} impl ast::HasName for Adt {} impl ast::HasVisibility for Adt {} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AsmOperand { + AsmClobberAbi(AsmClobberAbi), + AsmLabel(AsmLabel), + AsmOptions(AsmOptions), + AsmRegOperand(AsmRegOperand), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AssocItem { Const(Const), @@ -2316,6 +2495,48 @@ impl AstNode for ArrayType { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for AsmClobberAbi { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmConst { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmDirSpec { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for AsmExpr { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR } @@ -2330,6 +2551,104 @@ impl AstNode for AsmExpr { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for AsmLabel { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmOperandExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmOptions { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmRegOperand { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmRegSpec { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmSym { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for AssocItemList { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST } @@ -4268,6 +4587,48 @@ impl AstNode for Adt { } } } +impl From for AsmOperand { + #[inline] + fn from(node: AsmClobberAbi) -> AsmOperand { AsmOperand::AsmClobberAbi(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmOptions) -> AsmOperand { AsmOperand::AsmOptions(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) } +} +impl AstNode for AsmOperand { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, ASM_CLOBBER_ABI | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + ASM_CLOBBER_ABI => AsmOperand::AsmClobberAbi(AsmClobberAbi { syntax }), + ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }), + ASM_OPTIONS => AsmOperand::AsmOptions(AsmOptions { syntax }), + ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AsmOperand::AsmClobberAbi(it) => &it.syntax, + AsmOperand::AsmLabel(it) => &it.syntax, + AsmOperand::AsmOptions(it) => &it.syntax, + AsmOperand::AsmRegOperand(it) => &it.syntax, + } + } +} impl From for AssocItem { #[inline] fn from(node: Const) -> AssocItem { AssocItem::Const(node) } @@ -5803,7 +6164,8 @@ impl AstNode for AnyHasName { fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - CONST + ASM_REG_OPERAND + | CONST | CONST_PARAM | ENUM | FN @@ -5832,6 +6194,10 @@ impl AstNode for AnyHasName { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl From for AnyHasName { + #[inline] + fn from(node: AsmRegOperand) -> AnyHasName { AnyHasName { syntax: node.syntax } } +} impl From for AnyHasName { #[inline] fn from(node: Const) -> AnyHasName { AnyHasName { syntax: node.syntax } } @@ -6072,6 +6438,11 @@ impl std::fmt::Display for Adt { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmOperand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AssocItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -6142,11 +6513,61 @@ impl std::fmt::Display for ArrayType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmClobberAbi { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmConst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmDirSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AsmExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmLabel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOperandExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOption { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmRegOperand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmRegSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmSym { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AssocItemList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index 192de8694721d..4bc1821ee5eab 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/xtask/Cargo.toml @@ -19,6 +19,7 @@ stdx.workspace = true proc-macro2 = "1.0.47" quote = "1.0.20" ungrammar = "1.16.1" +either.workspace = true itertools.workspace = true # Avoid adding more dependencies to this crate diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index 39e06f9642db1..e7534582f2b42 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs @@ -11,9 +11,11 @@ use std::{ fs, }; +use either::Either; use itertools::Itertools; use proc_macro2::{Punct, Spacing}; use quote::{format_ident, quote}; +use stdx::panic_context; use ungrammar::{Grammar, Rule}; use crate::{ @@ -462,6 +464,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String { let tokens = grammar.tokens.iter().map(|name| format_ident!("{}", name)).collect::>(); + // FIXME: This generates enum kinds? let nodes = grammar.nodes.iter().map(|name| format_ident!("{}", name)).collect::>(); let ast = quote! { @@ -711,6 +714,7 @@ fn lower(grammar: &Grammar) -> AstSrc { for &node in &nodes { let name = grammar[node].name.clone(); let rule = &grammar[node].rule; + let _g = panic_context::enter(name.clone()); match lower_enum(grammar, rule) { Some(variants) => { let enum_src = AstEnumSrc { doc: Vec::new(), name, traits: Vec::new(), variants }; @@ -838,11 +842,16 @@ fn lower_separated_list( Rule::Seq(it) => it, _ => return false, }; - let (node, repeat, trailing_sep) = match rule.as_slice() { + + let (nt, repeat, trailing_sep) = match rule.as_slice() { [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => { - (node, repeat, Some(trailing_sep)) + (Either::Left(node), repeat, Some(trailing_sep)) + } + [Rule::Node(node), Rule::Rep(repeat)] => (Either::Left(node), repeat, None), + [Rule::Token(token), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => { + (Either::Right(token), repeat, Some(trailing_sep)) } - [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None), + [Rule::Token(token), Rule::Rep(repeat)] => (Either::Right(token), repeat, None), _ => return false, }; let repeat = match &**repeat { @@ -851,15 +860,28 @@ fn lower_separated_list( }; if !matches!( repeat.as_slice(), - [comma, Rule::Node(n)] - if trailing_sep.map_or(true, |it| comma == &**it) && n == node + [comma, nt_] + if trailing_sep.map_or(true, |it| comma == &**it) && match (nt, nt_) { + (Either::Left(node), Rule::Node(nt_)) => node == nt_, + (Either::Right(token), Rule::Token(nt_)) => token == nt_, + _ => false, + } ) { return false; } - let ty = grammar[*node].name.clone(); - let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty))); - let field = Field::Node { name, ty, cardinality: Cardinality::Many }; - acc.push(field); + match nt { + Either::Right(token) => { + let name = clean_token_name(&grammar[*token].name); + let field = Field::Token(name); + acc.push(field); + } + Either::Left(node) => { + let ty = grammar[*node].name.clone(); + let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty))); + let field = Field::Node { name, ty, cardinality: Cardinality::Many }; + acc.push(field); + } + } true } diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs index 34151bd958765..f1a96e0c6a2d4 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs @@ -113,7 +113,31 @@ const RESERVED: &[&str] = &[ const CONTEXTUAL_KEYWORDS: &[&str] = &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet"]; // keywords we use for special macro expansions -const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &["builtin", "offset_of", "format_args", "asm"]; +const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ + "asm", + "att_syntax", + "builtin", + "clobber_abi", + "format_args", + // "in", + "inlateout", + "inout", + "label", + "lateout", + "may_unwind", + "nomem", + "noreturn", + "nostack", + "offset_of", + "options", + "out", + "preserves_flags", + "pure", + // "raw", + "readonly", + "sym", +]; + // keywords that are keywords depending on the edition const EDITION_DEPENDENT_KEYWORDS: &[(&str, Edition)] = &[ ("try", Edition::Edition2018), From 0444056aa3ddd97e02478b7c41d130db74583d16 Mon Sep 17 00:00:00 2001 From: beetrees Date: Thu, 25 Jul 2024 22:15:47 +0100 Subject: [PATCH 0265/1301] Remove macOS 10.10 dynamic linker bug workaround --- compiler/rustc_codegen_llvm/src/consts.rs | 52 ------------------- library/std/src/fs.rs | 13 +++-- .../issues/issue-44056-macos-tls-align.rs | 27 ---------- 3 files changed, 6 insertions(+), 86 deletions(-) delete mode 100644 tests/codegen/issues/issue-44056-macos-tls-align.rs diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 80aa2018c81b5..0e50e32913c7b 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -440,58 +440,6 @@ impl<'ll> CodegenCx<'ll, '_> { if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { llvm::set_thread_local_mode(g, self.tls_model); - - // Do not allow LLVM to change the alignment of a TLS on macOS. - // - // By default a global's alignment can be freely increased. - // This allows LLVM to generate more performant instructions - // e.g., using load-aligned into a SIMD register. - // - // However, on macOS 10.10 or below, the dynamic linker does not - // respect any alignment given on the TLS (radar 24221680). - // This will violate the alignment assumption, and causing segfault at runtime. - // - // This bug is very easy to trigger. In `println!` and `panic!`, - // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS, - // which the values would be `mem::replace`d on initialization. - // The implementation of `mem::replace` will use SIMD - // whenever the size is 32 bytes or higher. LLVM notices SIMD is used - // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary, - // which macOS's dyld disregarded and causing crashes - // (see issues #51794, #51758, #50867, #48866 and #44056). - // - // To workaround the bug, we trick LLVM into not increasing - // the global's alignment by explicitly assigning a section to it - // (equivalent to automatically generating a `#[link_section]` attribute). - // See the comment in the `GlobalValue::canIncreaseAlignment()` function - // of `lib/IR/Globals.cpp` for why this works. - // - // When the alignment is not increased, the optimized `mem::replace` - // will use load-unaligned instructions instead, and thus avoiding the crash. - // - // We could remove this hack whenever we decide to drop macOS 10.10 support. - if self.tcx.sess.target.is_like_osx { - // The `inspect` method is okay here because we checked for provenance, and - // because we are doing this access to inspect the final interpreter state - // (not as part of the interpreter execution). - // - // FIXME: This check requires that the (arbitrary) value of undefined bytes - // happens to be zero. Instead, we should only check the value of defined bytes - // and set all undefined bytes to zero if this allocation is headed for the - // BSS. - let all_bytes_are_zero = alloc.provenance().ptrs().is_empty() - && alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()) - .iter() - .all(|&byte| byte == 0); - - let sect_name = if all_bytes_are_zero { - c"__DATA,__thread_bss" - } else { - c"__DATA,__thread_data" - }; - llvm::LLVMSetSection(g, sect_name.as_ptr()); - } } // Wasm statics with custom link sections get special treatment as they diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 536d0d1b356a9..25540410cc7eb 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2471,16 +2471,15 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// # Platform-specific behavior /// /// This function currently corresponds to `openat`, `fdopendir`, `unlinkat` and `lstat` functions -/// on Unix (except for macOS before version 10.10 and REDOX) and the `CreateFileW`, -/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile` functions on -/// Windows. Note that, this [may change in the future][changes]. +/// on Unix (except for REDOX) and the `CreateFileW`, `GetFileInformationByHandleEx`, +/// `SetFileInformationByHandle`, and `NtCreateFile` functions on Windows. Note that, this +/// [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior /// -/// On macOS before version 10.10 and REDOX, as well as when running in Miri for any target, this -/// function is not protected against time-of-check to time-of-use (TOCTOU) race conditions, and -/// should not be used in security-sensitive code on those platforms. All other platforms are -/// protected. +/// On REDOX, as well as when running in Miri for any target, this function is not protected against +/// time-of-check to time-of-use (TOCTOU) race conditions, and should not be used in +/// security-sensitive code on those platforms. All other platforms are protected. /// /// # Errors /// diff --git a/tests/codegen/issues/issue-44056-macos-tls-align.rs b/tests/codegen/issues/issue-44056-macos-tls-align.rs deleted file mode 100644 index 972b8490d18f9..0000000000000 --- a/tests/codegen/issues/issue-44056-macos-tls-align.rs +++ /dev/null @@ -1,27 +0,0 @@ -// -//@ only-apple -//@ compile-flags: -O - -#![crate_type = "rlib"] -#![feature(thread_local)] - -// local_unnamed_addr does not appear when std is built with debug assertions. -// CHECK: @STATIC_VAR_1 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4 -#[no_mangle] -#[thread_local] -static mut STATIC_VAR_1: [u32; 8] = [0; 8]; - -// CHECK: @STATIC_VAR_2 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4 -#[no_mangle] -#[thread_local] -static mut STATIC_VAR_2: [u32; 8] = [4; 8]; - -#[no_mangle] -pub unsafe fn f(x: &mut [u32; 8]) { - std::mem::swap(x, &mut STATIC_VAR_1) -} - -#[no_mangle] -pub unsafe fn g(x: &mut [u32; 8]) { - std::mem::swap(x, &mut STATIC_VAR_2) -} From c207badc2dd6abb1f416c4e09968184a69ff0484 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Sep 2024 15:19:29 +0200 Subject: [PATCH 0266/1301] Add missing intrinsics translation for `llvm.x86.xsavec64` and fix more intrinsic calls --- src/intrinsic/llvm.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index ba57e425b0fca..89d5d582e4c71 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -361,7 +361,14 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let minus_one = builder.context.new_rvalue_from_int(arg4_type, -1); args = vec![new_args[1], new_args[0], new_args[2], minus_one].into(); } - "__builtin_ia32_xrstor" | "__builtin_ia32_xsavec" => { + "__builtin_ia32_xrstor" + | "__builtin_ia32_xrstor64" + | "__builtin_ia32_xsavec" + | "__builtin_ia32_xsavec64" + | "__builtin_ia32_xsave" + | "__builtin_ia32_xsave64" + | "__builtin_ia32_xsaveopt" + | "__builtin_ia32_xsaveopt64" => { let new_args = args.to_vec(); let thirty_two = builder.context.new_rvalue_from_int(new_args[1].get_type(), 32); let arg2 = new_args[1] << thirty_two | new_args[2]; @@ -824,7 +831,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.xrstor" => "__builtin_ia32_xrstor", "llvm.x86.xrstor64" => "__builtin_ia32_xrstor64", "llvm.x86.xsavec" => "__builtin_ia32_xsavec", - "llvm.x86.xsave64" => "__builtin_ia32_xsave64", + "llvm.x86.xsavec64" => "__builtin_ia32_xsavec64", "llvm.x86.addcarry.32" => "__builtin_ia32_addcarryx_u32", "llvm.x86.subborrow.32" => "__builtin_ia32_sbb_u32", "llvm.x86.avx512.mask.compress.store.w.512" => "__builtin_ia32_compressstoreuhi512_mask", @@ -1029,6 +1036,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.vpdpbusds.256" => "__builtin_ia32_vpdpbusds_v8si", "llvm.x86.avx512.vpdpbusds.128" => "__builtin_ia32_vpdpbusds_v4si", "llvm.x86.xsave" => "__builtin_ia32_xsave", + "llvm.x86.xsave64" => "__builtin_ia32_xsave64", "llvm.x86.xsaveopt" => "__builtin_ia32_xsaveopt", "llvm.x86.xsaveopt64" => "__builtin_ia32_xsaveopt64", "llvm.x86.avx512.mask.loadu.w.512" => "__builtin_ia32_loaddquhi512_mask", From 38514bade22c673cadc244c0f264917104998294 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Wed, 4 Sep 2024 08:52:57 -0400 Subject: [PATCH 0267/1301] assist: ensure replace_qualified_name_with_use applies to the first path segment --- .../replace_qualified_name_with_use.rs | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 65330b34c4ad2..1101c20f1b785 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -29,7 +29,7 @@ pub(crate) fn replace_qualified_name_with_use( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let original_path: ast::Path = ctx.find_node_at_offset()?; + let mut original_path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements if original_path.syntax().ancestors().find_map(ast::UseTree::cast).is_some() { cov_mark::hit!(not_applicable_in_use); @@ -37,8 +37,7 @@ pub(crate) fn replace_qualified_name_with_use( } if original_path.qualifier().is_none() { - cov_mark::hit!(dont_import_trivial_paths); - return None; + original_path = original_path.parent_path()?; } // only offer replacement for non assoc items @@ -236,12 +235,6 @@ fs::Path ); } - #[test] - fn dont_import_trivial_paths() { - cov_mark::check!(dont_import_trivial_paths); - check_assist_not_applicable(replace_qualified_name_with_use, r"impl foo$0 for () {}"); - } - #[test] fn test_replace_not_applicable_in_use() { cov_mark::check!(not_applicable_in_use); @@ -271,6 +264,29 @@ fn main() { ); } + #[test] + fn assist_runs_on_first_segment() { + check_assist( + replace_qualified_name_with_use, + r" +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + $0std::fmt::Debug; + let x: std::fmt::Debug = std::fmt::Debug; +} + ", + r" +use std::fmt; + +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + fmt::Debug; + let x: fmt::Debug = fmt::Debug; +} + ", + ); + } + #[test] fn does_not_replace_in_submodules() { check_assist( From cdb442362b464472f1ede46f5b2763811978c2bf Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 4 Sep 2024 18:17:31 +0200 Subject: [PATCH 0268/1301] Bump actions/download-artifact from 3 to 4 --- .github/workflows/clippy_bors.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 2aa13313fa518..026771e6fcf42 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -162,7 +162,7 @@ jobs: find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf - name: Upload Binaries - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: binaries path: target/debug @@ -202,7 +202,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: binaries path: target/debug From f2d4198d6ef65134fe5bc277e362df90d237a41d Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Wed, 4 Sep 2024 18:58:44 +0200 Subject: [PATCH 0269/1301] Drop bubble_sort While using it results in slightly slammer binaries, it's not deemed worth it to add yet another sort algorithm to the standard library. select_nth_unstable has bigger binary-size problems. --- library/core/src/slice/sort/mod.rs | 1 - library/core/src/slice/sort/select.rs | 41 +---------------------- library/core/src/slice/sort/shared/mod.rs | 2 ++ 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/library/core/src/slice/sort/mod.rs b/library/core/src/slice/sort/mod.rs index 81b99a4364b2e..79852708b81ea 100644 --- a/library/core/src/slice/sort/mod.rs +++ b/library/core/src/slice/sort/mod.rs @@ -5,5 +5,4 @@ pub mod stable; pub mod unstable; pub(crate) mod select; -#[cfg(not(feature = "optimize_for_size"))] pub(crate) mod shared; diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index 7bda54e792528..3358c03d30a9b 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -9,7 +9,6 @@ use crate::mem::{self, SizedTypeProperties}; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; -#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; use crate::slice::sort::unstable::quicksort::partition; @@ -176,13 +175,7 @@ fn median_of_medians bool>(mut v: &mut [T], is_less: &mut loop { if v.len() <= INSERTION_SORT_THRESHOLD { if v.len() >= 2 { - cfg_if! { - if #[cfg(feature = "optimize_for_size")] { - bubble_sort(v, is_less); - } else { - insertion_sort_shift_left(v, 1, is_less); - } - } + insertion_sort_shift_left(v, 1, is_less); } return; @@ -314,35 +307,3 @@ fn median_idx bool>( } b } - -// It's possible to re-use the insertion sort in the smallsort module, but with optimize_for_size it -// would clutter that module with cfg statements and make it generally harder to read and develop. -// So to decouple things and simplify it, we use an even smaller bubble sort. -#[cfg(feature = "optimize_for_size")] -fn bubble_sort bool>(v: &mut [T], is_less: &mut F) { - use crate::ptr; - - let mut n = v.len(); - - let v_base = v.as_mut_ptr(); - - while n > 1 { - let loop_n = n; - n = 0; - for i in 1..loop_n { - // SAFETY: The loop construction implies that `i` and `i - 1` will always be in-bounds. - unsafe { - // Even if `is_less` erroneously always returns true, we are guaranteed that `n` - // reduces by one each out loop iteration, because `1..n` is exclusive. This - // guarantees a bounded run-time should `Ord` be implemented incorrectly. - let v_i = v_base.add(i); - let v_i_minus_one = v_base.add(i - 1); - - if is_less(&*v_i, &*v_i_minus_one) { - ptr::swap_nonoverlapping(v_i, v_i_minus_one, 1); - n = i; - } - } - } - } -} diff --git a/library/core/src/slice/sort/shared/mod.rs b/library/core/src/slice/sort/shared/mod.rs index ad1171bfc6a0a..e0f8d475a2e30 100644 --- a/library/core/src/slice/sort/shared/mod.rs +++ b/library/core/src/slice/sort/shared/mod.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "optimize_for_size", allow(dead_code))] + use crate::marker::Freeze; pub(crate) mod pivot; From adb0e27acd065354b6635c3c5a66dcbbc2e9d9c5 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Wed, 4 Sep 2024 19:37:49 +0200 Subject: [PATCH 0270/1301] Shrink heapsort further by combining sift_down loops --- .../core/src/slice/sort/unstable/heapsort.rs | 36 +++++++++---------- library/core/src/slice/sort/unstable/mod.rs | 5 +-- .../core/src/slice/sort/unstable/quicksort.rs | 7 ++-- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/library/core/src/slice/sort/unstable/heapsort.rs b/library/core/src/slice/sort/unstable/heapsort.rs index 27e2ad588ea09..35bfe2959cd78 100644 --- a/library/core/src/slice/sort/unstable/heapsort.rs +++ b/library/core/src/slice/sort/unstable/heapsort.rs @@ -1,46 +1,46 @@ //! This module contains a branchless heapsort as fallback for unstable quicksort. -use crate::{intrinsics, ptr}; +use crate::{cmp, intrinsics, ptr}; /// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. /// /// Never inline this, it sits the main hot-loop in `recurse` and is meant as unlikely algorithmic /// fallback. -/// -/// SAFETY: The caller has to guarantee that `v.len()` >= 2. #[inline(never)] -pub(crate) unsafe fn heapsort(v: &mut [T], is_less: &mut F) +pub(crate) fn heapsort(v: &mut [T], is_less: &mut F) where F: FnMut(&T, &T) -> bool, { - // SAFETY: See function safety. - unsafe { - intrinsics::assume(v.len() >= 2); - - // Build the heap in linear time. - for i in (0..v.len() / 2).rev() { - sift_down(v, i, is_less); - } + let len = v.len(); - // Pop maximal elements from the heap. - for i in (1..v.len()).rev() { + for i in (0..len + len / 2).rev() { + let sift_idx = if i >= len { + i - len + } else { v.swap(0, i); - sift_down(&mut v[..i], 0, is_less); + 0 + }; + + // SAFETY: The above calculation ensures that `sift_idx` is either 0 or + // `(len..(len + (len / 2))) - len`, which simplifies to `0..(len / 2)`. + // This guarantees the required `sift_idx <= len`. + unsafe { + sift_down(&mut v[..cmp::min(i, len)], sift_idx, is_less); } } } // This binary heap respects the invariant `parent >= child`. // -// SAFETY: The caller has to guarantee that node < `v.len()`. -#[inline(never)] +// SAFETY: The caller has to guarantee that `node <= v.len()`. +#[inline(always)] unsafe fn sift_down(v: &mut [T], mut node: usize, is_less: &mut F) where F: FnMut(&T, &T) -> bool, { // SAFETY: See function safety. unsafe { - intrinsics::assume(node < v.len()); + intrinsics::assume(node <= v.len()); } let len = v.len(); diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 130be21ee3fe8..953c27ab6f417 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -32,10 +32,7 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { cfg_if! { if #[cfg(feature = "optimize_for_size")] { - // SAFETY: We checked that `len >= 2`. - unsafe { - heapsort::heapsort(v, is_less); - } + heapsort::heapsort(v, is_less); } else { // More advanced sorting methods than insertion sort are faster if called in // a hot loop for small inputs, but for general-purpose code the small diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index 9c59ccdb70005..4feef5deeb0fb 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -5,6 +5,8 @@ use crate::mem::{self, ManuallyDrop}; use crate::slice::sort::shared::pivot::choose_pivot; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; +#[cfg(not(feature = "optimize_for_size"))] +use crate::slice::sort::unstable::heapsort; use crate::{intrinsics, ptr}; /// Sorts `v` recursively. @@ -31,10 +33,7 @@ pub(crate) fn quicksort<'a, T, F>( // If too many bad pivot choices were made, simply fall back to heapsort in order to // guarantee `O(N x log(N))` worst-case. if limit == 0 { - // SAFETY: We assume the `small_sort` threshold is at least 1. - unsafe { - crate::slice::sort::unstable::heapsort::heapsort(v, is_less); - } + heapsort::heapsort(v, is_less); return; } From 7d2e6ebcbeb2ca161b4b0f8a88bd41bf4c1381f5 Mon Sep 17 00:00:00 2001 From: Lukas Lueg Date: Wed, 4 Sep 2024 19:37:59 +0200 Subject: [PATCH 0271/1301] Expand missing_transmute_annotations docs Fixes #13339 --- clippy_lints/src/transmute/mod.rs | 32 +++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 373bf61d8ff94..6e62668fd5272 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -527,24 +527,44 @@ declare_clippy_lint! { /// Checks if transmute calls have all generics specified. /// /// ### Why is this bad? - /// If not set, some unexpected output type could be retrieved instead of the expected one, - /// potentially leading to invalid code. + /// If not, one or more unexpected types could be used during `transmute()`, potentially leading + /// to Undefined Behavior or other problems. /// - /// This is particularly dangerous in case a seemingly innocent/unrelated change can cause type - /// inference to start inferring a different type. E.g. the transmute is the tail expression of - /// an `if` branch, and a different branches type changes, causing the transmute to silently - /// have a different type, instead of a proper error. + /// This is particularly dangerous in case a seemingly innocent/unrelated change causes type + /// inference to result in a different type. For example, if `transmute()` is the tail + /// expression of an `if`-branch, and the `else`-branch type changes, the compiler may silently + /// infer a different type to be returned by `transmute()`. That is because the compiler is + /// free to change the inference of a type as long as that inference is technically correct, + /// regardless of the programmer's unknown expectation. + /// + /// Both type-parameters, the input- and the output-type, to any `transmute()` should + /// be given explicitly: Setting the input-type explicitly avoids confusion about what the + /// argument's type actually is. Setting the output-type explicitly avoids type-inference + /// to infer a technically correct yet unexpected type. /// /// ### Example /// ```no_run /// # unsafe { + /// // Avoid "naked" calls to `transmute()`! /// let x: i32 = std::mem::transmute([1u16, 2u16]); + /// + /// // `first_answers` is intended to transmute a slice of bool to a slice of u8. + /// // But the programmer forgot to index the first element of the outer slice, + /// // so we are actually transmuting from "pointers to slices" instead of + /// // transmuting from "a slice of bool", causing a nonsensical result. + /// let the_answers: &[&[bool]] = &[&[true, false, true]]; + /// let first_answers: &[u8] = std::mem::transmute(the_answers); /// # } /// ``` /// Use instead: /// ```no_run /// # unsafe { /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + /// + /// // The explicit type parameters on `transmute()` makes the intention clear, + /// // and cause a type-error if the actual types don't match our expectation. + /// let the_answers: &[&[bool]] = &[&[true, false, true]]; + /// let first_answers: &[u8] = std::mem::transmute::<&[bool], &[u8]>(the_answers[0]); /// # } /// ``` #[clippy::version = "1.79.0"] From a0e4303ba2ed087a85e0218a4049d8cc73d7f404 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Wed, 4 Sep 2024 19:53:56 +0200 Subject: [PATCH 0272/1301] Select tiny sorts for 16-bit platforms Also skips stack alloc in stable sort if 16-bit target platform. --- library/core/src/slice/sort/stable/mod.rs | 31 +++++++++++++-------- library/core/src/slice/sort/unstable/mod.rs | 2 +- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index 3472401c4dcf8..00eb3785e0f25 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -40,20 +40,27 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less } cfg_if! { - if #[cfg(feature = "optimize_for_size")] { + if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { let alloc_len = len / 2; - // For small inputs 4KiB of stack storage suffices, which allows us to avoid - // calling the (de-)allocator. Benchmarks showed this was quite beneficial. - let mut stack_buf = AlignedStorage::::new(); - let stack_scratch = stack_buf.as_uninit_slice_mut(); - let mut heap_buf; - let scratch = if stack_scratch.len() >= alloc_len { - stack_scratch - } else { - heap_buf = BufT::with_capacity(alloc_len); - heap_buf.as_uninit_slice_mut() - }; + cfg_if! { + if #[cfg(target_pointer_width = "16")] { + let heap_buf = BufT::with_capacity(alloc_len); + let scratch = heap_buf.as_uninit_slice_mut(); + } else { + // For small inputs 4KiB of stack storage suffices, which allows us to avoid + // calling the (de-)allocator. Benchmarks showed this was quite beneficial. + let mut stack_buf = AlignedStorage::::new(); + let stack_scratch = stack_buf.as_uninit_slice_mut(); + let mut heap_buf; + let scratch = if stack_scratch.len() >= alloc_len { + stack_scratch + } else { + heap_buf = BufT::with_capacity(alloc_len); + heap_buf.as_uninit_slice_mut() + }; + } + } tiny::mergesort(v, scratch, is_less); } else { diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 953c27ab6f417..8bbd85443d478 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -31,7 +31,7 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { } cfg_if! { - if #[cfg(feature = "optimize_for_size")] { + if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { heapsort::heapsort(v, is_less); } else { // More advanced sorting methods than insertion sort are faster if called in From 54391983486d5271c378821c8e6a8f018dfdd14d Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Wed, 4 Sep 2024 19:54:46 +0200 Subject: [PATCH 0273/1301] Use non-overlapping swap for inner heapsort loop This regresses binary-size slightly for normal builds, but the important release_lto_thin_opt_level_s config sees a small improvement in binary-size and a larger types such as string and 1k see 2-3% run-time improvements with this change. --- library/core/src/slice/sort/unstable/heapsort.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/core/src/slice/sort/unstable/heapsort.rs b/library/core/src/slice/sort/unstable/heapsort.rs index 35bfe2959cd78..85231779d031f 100644 --- a/library/core/src/slice/sort/unstable/heapsort.rs +++ b/library/core/src/slice/sort/unstable/heapsort.rs @@ -69,9 +69,7 @@ where break; } - // Swap `node` with the greater child, move one step down, and continue sifting. This - // could be ptr::swap_nonoverlapping but that adds a significant amount of binary-size. - ptr::swap(v_base.add(node), v_base.add(child)); + ptr::swap_nonoverlapping(v_base.add(node), v_base.add(child), 1); } node = child; From f7f550561e801fa1adb74377fd95a151820d8d0e Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 4 Sep 2024 21:22:28 +0000 Subject: [PATCH 0274/1301] Only lint `manual_non_exhaustive` for exported types --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/manual_non_exhaustive.rs | 182 ++++++++----------- tests/ui/manual_non_exhaustive_enum.rs | 24 ++- tests/ui/manual_non_exhaustive_enum.stderr | 22 ++- tests/ui/manual_non_exhaustive_struct.rs | 50 ++--- tests/ui/manual_non_exhaustive_struct.stderr | 64 ++++--- 6 files changed, 166 insertions(+), 179 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 078f38656ece8..747119e114795 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -633,8 +633,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))); store.register_late_pass(move |_| Box::new(matches::Matches::new(conf))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(conf))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(conf))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf))); store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))); store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))); diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6b1d90483ccd1..01f1d9c3beba2 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -2,16 +2,16 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_doc_hidden; -use clippy_utils::source::SpanRangeExt; -use rustc_ast::ast::{self, VisibilityKind}; +use clippy_utils::source::snippet_indent; +use itertools::Itertools; use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{self as hir, Expr, ExprKind, QPath}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -62,29 +62,13 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[expect(clippy::module_name_repetitions)] -pub struct ManualNonExhaustiveStruct { +pub struct ManualNonExhaustive { msrv: Msrv, -} - -impl ManualNonExhaustiveStruct { - pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: conf.msrv.clone(), - } - } -} - -impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); - -#[expect(clippy::module_name_repetitions)] -pub struct ManualNonExhaustiveEnum { - msrv: Msrv, - constructed_enum_variants: FxHashSet<(DefId, DefId)>, + constructed_enum_variants: FxHashSet, potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, } -impl ManualNonExhaustiveEnum { +impl ManualNonExhaustive { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv.clone(), @@ -94,96 +78,78 @@ impl ManualNonExhaustiveEnum { } } -impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); - -impl EarlyLintPass for ManualNonExhaustiveStruct { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let ast::ItemKind::Struct(variant_data, _) = &item.kind - && let (fields, delimiter) = match variant_data { - ast::VariantData::Struct { fields, .. } => (&**fields, '{'), - ast::VariantData::Tuple(fields, _) => (&**fields, '('), - ast::VariantData::Unit(_) => return, - } - && fields.len() > 1 - && self.msrv.meets(msrvs::NON_EXHAUSTIVE) - { - let mut iter = fields.iter().filter_map(|f| match f.vis.kind { - VisibilityKind::Public => None, - VisibilityKind::Inherited => Some(Ok(f)), - VisibilityKind::Restricted { .. } => Some(Err(())), - }); - if let Some(Ok(field)) = iter.next() - && iter.next().is_none() - && field.ty.kind.is_unit() - { - span_lint_and_then( - cx, - MANUAL_NON_EXHAUSTIVE, - item.span, - "this seems like a manual implementation of the non-exhaustive pattern", - |diag| { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) - && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) - && let Some(snippet) = header_span.get_source_text(cx) - { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); - } - diag.span_help(field.span, "remove this field"); - }, - ); - } - } - } +impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); - extract_msrv_attr!(EarlyContext); -} - -impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) || !cx.effective_visibilities.is_exported(item.owner_id.def_id) { return; } - if let hir::ItemKind::Enum(def, _) = &item.kind - && def.variants.len() > 1 - { - let mut iter = def.variants.iter().filter_map(|v| { - (matches!(v.data, hir::VariantData::Unit(_, _)) - && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)) - && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)) - .then_some((v.def_id, v.span)) - }); - if let Some((id, span)) = iter.next() - && iter.next().is_none() - { - self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); - } + match item.kind { + ItemKind::Enum(def, _) if def.variants.len() > 1 => { + let iter = def.variants.iter().filter_map(|v| { + (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) + .then_some((v.def_id, v.span)) + }); + if let Ok((id, span)) = iter.exactly_one() + && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + { + self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); + } + }, + ItemKind::Struct(variant_data, _) => { + let fields = variant_data.fields(); + let private_fields = fields + .iter() + .filter(|field| !cx.effective_visibilities.is_exported(field.def_id)); + if fields.len() > 1 + && let Ok(field) = private_fields.exactly_one() + && let TyKind::Tup([]) = field.ty.kind + { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if let Some(non_exhaustive) = + attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + { + diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive"); + } else { + let indent = snippet_indent(cx, item.span).unwrap_or_default(); + diag.span_suggestion_verbose( + item.span.shrink_to_lo(), + "use the `#[non_exhaustive]` attribute instead", + format!("#[non_exhaustive]\n{indent}"), + Applicability::MaybeIncorrect, + ); + } + diag.span_help(field.span, "remove this field"); + }, + ); + } + }, + _ => {}, } } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind - && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), ctor_id) = p.res + && let Some(local_ctor) = ctor_id.as_local() { - let variant_id = cx.tcx.parent(id); - let enum_id = cx.tcx.parent(variant_id); - - self.constructed_enum_variants.insert((enum_id, variant_id)); + let variant_id = cx.tcx.local_parent(local_ctor); + self.constructed_enum_variants.insert(variant_id); } } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for &(enum_id, _, enum_span, variant_span) in - self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| { - !self - .constructed_enum_variants - .contains(&(enum_id.to_def_id(), variant_id.to_def_id())) - }) + for &(enum_id, _, enum_span, variant_span) in self + .potential_enums + .iter() + .filter(|(_, variant_id, _, _)| !self.constructed_enum_variants.contains(variant_id)) { let hir_id = cx.tcx.local_def_id_to_hir_id(enum_id); span_lint_hir_and_then( @@ -193,15 +159,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let header_span = cx.sess().source_map().span_until_char(enum_span, '{'); - if let Some(snippet) = header_span.get_source_text(cx) { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); - } + let indent = snippet_indent(cx, enum_span).unwrap_or_default(); + diag.span_suggestion_verbose( + enum_span.shrink_to_lo(), + "use the `#[non_exhaustive]` attribute instead", + format!("#[non_exhaustive]\n{indent}"), + Applicability::MaybeIncorrect, + ); diag.span_help(variant_span, "remove this variant"); }, ); diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs index 31c3cc8013722..ffe2bb9246737 100644 --- a/tests/ui/manual_non_exhaustive_enum.rs +++ b/tests/ui/manual_non_exhaustive_enum.rs @@ -1,8 +1,8 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] //@no-rustfix -enum E { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub enum E { + //~^ manual_non_exhaustive A, B, #[doc(hidden)] @@ -11,7 +11,7 @@ enum E { // if the user explicitly marks as nonexhaustive we shouldn't warn them #[non_exhaustive] -enum Ep { +pub enum Ep { A, B, #[doc(hidden)] @@ -19,14 +19,14 @@ enum Ep { } // marker variant does not have doc hidden attribute, should be ignored -enum NoDocHidden { +pub enum NoDocHidden { A, B, _C, } // name of variant with doc hidden does not start with underscore -enum NoUnderscore { +pub enum NoUnderscore { A, B, #[doc(hidden)] @@ -34,7 +34,7 @@ enum NoUnderscore { } // variant with doc hidden is not unit, should be ignored -enum NotUnit { +pub enum NotUnit { A, B, #[doc(hidden)] @@ -42,13 +42,13 @@ enum NotUnit { } // variant with doc hidden is the only one, should be ignored -enum OnlyMarker { +pub enum OnlyMarker { #[doc(hidden)] _A, } // variant with multiple markers, should be ignored -enum MultipleMarkers { +pub enum MultipleMarkers { A, #[doc(hidden)] _B, @@ -58,13 +58,13 @@ enum MultipleMarkers { // already non_exhaustive and no markers, should be ignored #[non_exhaustive] -enum NonExhaustive { +pub enum NonExhaustive { A, B, } // marked is used, don't lint -enum UsedHidden { +pub enum UsedHidden { #[doc(hidden)] _A, B, @@ -77,11 +77,9 @@ fn foo(x: &mut UsedHidden) { } #[expect(clippy::manual_non_exhaustive)] -enum ExpectLint { +pub enum ExpectLint { A, B, #[doc(hidden)] _C, } - -fn main() {} diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr index dc669568dd2d4..0a9ac157f8502 100644 --- a/tests/ui/manual_non_exhaustive_enum.stderr +++ b/tests/ui/manual_non_exhaustive_enum.stderr @@ -1,11 +1,7 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_enum.rs:4:1 | -LL | enum E { - | ^----- - | | - | _help: add the attribute: `#[non_exhaustive] enum E` - | | +LL | / pub enum E { LL | | LL | | A, LL | | B, @@ -21,15 +17,16 @@ LL | _C, | ^^ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` +help: use the `#[non_exhaustive]` attribute instead + | +LL + #[non_exhaustive] +LL | pub enum E { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_enum.rs:29:1 | -LL | enum NoUnderscore { - | ^---------------- - | | - | _help: add the attribute: `#[non_exhaustive] enum NoUnderscore` - | | +LL | / pub enum NoUnderscore { LL | | A, LL | | B, LL | | #[doc(hidden)] @@ -42,6 +39,11 @@ help: remove this variant | LL | C, | ^ +help: use the `#[non_exhaustive]` attribute instead + | +LL + #[non_exhaustive] +LL | pub enum NoUnderscore { + | error: aborting due to 2 previous errors diff --git a/tests/ui/manual_non_exhaustive_struct.rs b/tests/ui/manual_non_exhaustive_struct.rs index 4b2803ccc4a79..31dd50281fa1d 100644 --- a/tests/ui/manual_non_exhaustive_struct.rs +++ b/tests/ui/manual_non_exhaustive_struct.rs @@ -1,9 +1,9 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] //@no-rustfix -mod structs { - struct S { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub mod structs { + pub struct S { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, _c: (), @@ -11,68 +11,76 @@ mod structs { // user forgot to remove the private field #[non_exhaustive] - struct Sp { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern + pub struct Sp { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, _c: (), } // some other fields are private, should be ignored - struct PrivateFields { + pub struct PrivateFields { a: i32, pub b: i32, _c: (), } - // private field name does not start with underscore, should be ignored - struct NoUnderscore { + pub struct NoUnderscore { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, c: (), } // private field is not unit type, should be ignored - struct NotUnit { + pub struct NotUnit { pub a: i32, pub b: i32, _c: i32, } // private field is the only field, should be ignored - struct OnlyMarker { + pub struct OnlyMarker { _a: (), } // already non exhaustive and no private fields, should be ignored #[non_exhaustive] - struct NonExhaustive { + pub struct NonExhaustive { pub a: i32, pub b: i32, } } -mod tuple_structs { - struct T(pub i32, pub i32, ()); - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub mod tuple_structs { + pub struct T(pub i32, pub i32, ()); + //~^ manual_non_exhaustive // user forgot to remove the private field #[non_exhaustive] - struct Tp(pub i32, pub i32, ()); - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern + pub struct Tp(pub i32, pub i32, ()); + //~^ manual_non_exhaustive // some other fields are private, should be ignored - struct PrivateFields(pub i32, i32, ()); + pub struct PrivateFields(pub i32, i32, ()); // private field is not unit type, should be ignored - struct NotUnit(pub i32, pub i32, i32); + pub struct NotUnit(pub i32, pub i32, i32); // private field is the only field, should be ignored - struct OnlyMarker(()); + pub struct OnlyMarker(()); // already non exhaustive and no private fields, should be ignored #[non_exhaustive] - struct NonExhaustive(pub i32, pub i32); + pub struct NonExhaustive(pub i32, pub i32); } -fn main() {} +mod private { + // Don't lint structs that are not actually public as `#[non_exhaustive]` only applies to + // external crates. The manual pattern can still be used to get module local non exhaustiveness + pub struct NotPublic { + pub a: i32, + pub b: i32, + _c: (), + } +} diff --git a/tests/ui/manual_non_exhaustive_struct.stderr b/tests/ui/manual_non_exhaustive_struct.stderr index 1cab812988a34..81de1e4f27190 100644 --- a/tests/ui/manual_non_exhaustive_struct.stderr +++ b/tests/ui/manual_non_exhaustive_struct.stderr @@ -1,11 +1,7 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:5:5 | -LL | struct S { - | ^------- - | | - | _____help: add the attribute: `#[non_exhaustive] struct S` - | | +LL | / pub struct S { LL | | LL | | pub a: i32, LL | | pub b: i32, @@ -20,11 +16,16 @@ LL | _c: (), | ^^^^^^ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct S { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:14:5 | -LL | / struct Sp { +LL | / pub struct Sp { LL | | LL | | pub a: i32, LL | | pub b: i32, @@ -32,6 +33,11 @@ LL | | _c: (), LL | | } | |_____^ | +note: the struct is already non-exhaustive + --> tests/ui/manual_non_exhaustive_struct.rs:13:5 + | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ help: remove this field --> tests/ui/manual_non_exhaustive_struct.rs:18:9 | @@ -39,13 +45,10 @@ LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> tests/ui/manual_non_exhaustive_struct.rs:29:5 + --> tests/ui/manual_non_exhaustive_struct.rs:28:5 | -LL | struct NoUnderscore { - | ^------------------ - | | - | _____help: add the attribute: `#[non_exhaustive] struct NoUnderscore` - | | +LL | / pub struct NoUnderscore { +LL | | LL | | pub a: i32, LL | | pub b: i32, LL | | c: (), @@ -57,32 +60,45 @@ help: remove this field | LL | c: (), | ^^^^^ +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct NoUnderscore { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:56:5 | -LL | struct T(pub i32, pub i32, ()); - | --------^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: add the attribute: `#[non_exhaustive] struct T` +LL | pub struct T(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove this field - --> tests/ui/manual_non_exhaustive_struct.rs:56:32 + --> tests/ui/manual_non_exhaustive_struct.rs:56:36 + | +LL | pub struct T(pub i32, pub i32, ()); + | ^^ +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct T(pub i32, pub i32, ()); | -LL | struct T(pub i32, pub i32, ()); - | ^^ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:61:5 | -LL | struct Tp(pub i32, pub i32, ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub struct Tp(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the struct is already non-exhaustive + --> tests/ui/manual_non_exhaustive_struct.rs:60:5 | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ help: remove this field - --> tests/ui/manual_non_exhaustive_struct.rs:61:33 + --> tests/ui/manual_non_exhaustive_struct.rs:61:37 | -LL | struct Tp(pub i32, pub i32, ()); - | ^^ +LL | pub struct Tp(pub i32, pub i32, ()); + | ^^ error: aborting due to 5 previous errors From 263937090528baf51d703a364a91a2b2a647e848 Mon Sep 17 00:00:00 2001 From: ding-young Date: Thu, 29 Aug 2024 01:25:09 +0900 Subject: [PATCH 0275/1301] impl rewrite_result for TraitAliasBounds, WherePredicate --- src/items.rs | 67 ++++++++++++++++++++++++++++++---------------------- src/types.rs | 19 +++++++++------ 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/items.rs b/src/items.rs index ddcbfac129956..39fb358401eda 100644 --- a/src/items.rs +++ b/src/items.rs @@ -840,7 +840,8 @@ pub(crate) fn format_impl( where_span_end, self_ty.span.hi(), option, - )?; + ) + .ok()?; // If there is no where-clause, we may have missing comments between the trait name and // the opening brace. @@ -1231,7 +1232,8 @@ pub(crate) fn format_trait( None, pos_before_where, option, - )?; + ) + .ok()?; // If the where-clause cannot fit on the same line, // put the where-clause on a new line if !where_clause_str.contains('\n') @@ -1336,7 +1338,11 @@ pub(crate) struct TraitAliasBounds<'a> { impl<'a> Rewrite for TraitAliasBounds<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let generic_bounds_str = self.generic_bounds.rewrite_result(context, shape)?; let mut option = WhereClauseOption::new(true, WhereClauseSpace::None); option.allow_single_line(); @@ -1365,7 +1371,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { shape.indent.to_string_with_newline(context.config) }; - Some(format!("{generic_bounds_str}{space}{where_str}")) + Ok(format!("{generic_bounds_str}{space}{where_str}")) } } @@ -1623,7 +1629,8 @@ fn format_tuple_struct( None, body_hi, option, - )? + ) + .ok()? } None => "".to_owned(), }; @@ -1792,7 +1799,8 @@ fn rewrite_ty( None, generics.span.hi(), option, - )?; + ) + .ok()?; result.push_str(&where_clause_str); if let Some(ty) = rhs { @@ -2663,7 +2671,8 @@ fn rewrite_fn_base( Some(span.hi()), pos_before_where, option, - )?; + ) + .ok()?; // If there are neither where-clause nor return type, we may be missing comments between // params and `{`. if where_clause_str.is_empty() { @@ -2939,7 +2948,7 @@ fn rewrite_where_clause_rfc_style( span_end: Option, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option { +) -> RewriteResult { let (where_keyword, allow_single_line) = rewrite_where_keyword( context, predicates, @@ -2953,8 +2962,9 @@ fn rewrite_where_clause_rfc_style( let clause_shape = shape .block() .with_max_width(context.config) - .block_left(context.config.tab_spaces())? - .sub_width(1)?; + .block_left(context.config.tab_spaces()) + .and_then(|s| s.sub_width(1)) + .max_width_error(shape.width, where_span)?; let force_single_line = context.config.where_single_line() && predicates.len() == 1 && !where_clause_option.veto_single_line; @@ -2979,7 +2989,7 @@ fn rewrite_where_clause_rfc_style( clause_shape.indent.to_string_with_newline(context.config) }; - Some(format!("{where_keyword}{clause_sep}{preds_str}")) + Ok(format!("{where_keyword}{clause_sep}{preds_str}")) } /// Rewrite `where` and comment around it. @@ -2990,12 +3000,13 @@ fn rewrite_where_keyword( shape: Shape, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option<(String, bool)> { +) -> Result<(String, bool), RewriteError> { let block_shape = shape.block().with_max_width(context.config); // 1 = `,` let clause_shape = block_shape - .block_left(context.config.tab_spaces())? - .sub_width(1)?; + .block_left(context.config.tab_spaces()) + .and_then(|s| s.sub_width(1)) + .max_width_error(block_shape.width, where_span)?; let comment_separator = |comment: &str, shape: Shape| { if comment.is_empty() { @@ -3026,7 +3037,7 @@ fn rewrite_where_keyword( && comment_before.is_empty() && comment_after.is_empty(); - Some((result, allow_single_line)) + Ok((result, allow_single_line)) } /// Rewrite bounds on a where clause. @@ -3038,7 +3049,7 @@ fn rewrite_bounds_on_where_clause( span_end: Option, where_clause_option: WhereClauseOption, force_single_line: bool, -) -> Option { +) -> RewriteResult { let span_start = predicates[0].span().lo(); // If we don't have the start of the next span, then use the end of the // predicates, but that means we miss comments. @@ -3077,7 +3088,7 @@ fn rewrite_bounds_on_where_clause( .tactic(shape_tactic) .trailing_separator(comma_tactic) .preserve_newline(preserve_newline); - write_list(&items.collect::>(), &fmt).ok() + write_list(&items.collect::>(), &fmt) } fn rewrite_where_clause( @@ -3091,9 +3102,9 @@ fn rewrite_where_clause( span_end: Option, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option { +) -> RewriteResult { if predicates.is_empty() { - return Some(String::new()); + return Ok(String::new()); } if context.config.indent_style() == IndentStyle::Block { @@ -3153,7 +3164,7 @@ fn rewrite_where_clause( .trailing_separator(comma_tactic) .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) .preserve_newline(true); - let preds_str = write_list(&item_vec, &fmt).ok()?; + let preds_str = write_list(&item_vec, &fmt)?; let end_length = if terminator == "{" { // If the brace is on the next line we don't need to count it otherwise it needs two @@ -3171,13 +3182,13 @@ fn rewrite_where_clause( || preds_str.contains('\n') || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width { - Some(format!( + Ok(format!( "\n{}where {}", (shape.indent + extra_indent).to_string(context.config), preds_str )) } else { - Some(format!(" where {preds_str}")) + Ok(format!(" where {preds_str}")) } } @@ -3198,15 +3209,14 @@ fn rewrite_comments_before_after_where( span_before_where: Span, span_after_where: Span, shape: Shape, -) -> Option<(String, String)> { - let before_comment = rewrite_missing_comment(span_before_where, shape, context).ok()?; +) -> Result<(String, String), RewriteError> { + let before_comment = rewrite_missing_comment(span_before_where, shape, context)?; let after_comment = rewrite_missing_comment( span_after_where, shape.block_indent(context.config.tab_spaces()), context, - ) - .ok()?; - Some((before_comment, after_comment)) + )?; + Ok((before_comment, after_comment)) } fn format_header( @@ -3288,7 +3298,8 @@ fn format_generics( Some(span.hi()), span_end_before_where, option, - )?; + ) + .ok()?; result.push_str(&where_clause_str); ( brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine, diff --git a/src/types.rs b/src/types.rs index 384a69e23b222..5477942f82e72 100644 --- a/src/types.rs +++ b/src/types.rs @@ -457,6 +457,10 @@ fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveL impl Rewrite for ast::WherePredicate { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { // FIXME: dead spans? let result = match *self { ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { @@ -465,7 +469,7 @@ impl Rewrite for ast::WherePredicate { ref bounds, .. }) => { - let type_str = bounded_ty.rewrite(context, shape)?; + let type_str = bounded_ty.rewrite_result(context, shape)?; let colon = type_bound_colon(context).trim_end(); let lhs = if let Some(binder_str) = rewrite_bound_params(context, shape, bound_generic_params) @@ -475,25 +479,26 @@ impl Rewrite for ast::WherePredicate { format!("{type_str}{colon}") }; - rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape).ok()? + rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? } ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, ref bounds, span, - }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape).ok()?, + }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?, ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { - let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; - rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape) - .ok()? + let lhs_ty_str = lhs_ty + .rewrite_result(context, shape) + .map(|lhs| lhs + " =")?; + rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)? } }; - Some(result) + Ok(result) } } From b40c3cc3888229080dff8a59e607cffbcfed82b7 Mon Sep 17 00:00:00 2001 From: ding-young Date: Sun, 1 Sep 2024 15:44:17 +0900 Subject: [PATCH 0276/1301] impl rewrite_result for ForeignItem --- src/items.rs | 118 +++++++++++++++++++++++++++---------------------- src/visitor.rs | 16 ++++--- 2 files changed, 76 insertions(+), 58 deletions(-) diff --git a/src/items.rs b/src/items.rs index 39fb358401eda..1f25827e9198c 100644 --- a/src/items.rs +++ b/src/items.rs @@ -433,7 +433,7 @@ impl<'a> FmtVisitor<'a> { let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause); let (result, _, force_newline_brace) = - rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?; + rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style).ok()?; // 2 = ` {` if self.config.brace_style() == BraceStyle::AlwaysNextLine @@ -454,7 +454,7 @@ impl<'a> FmtVisitor<'a> { vis: &ast::Visibility, generics: &ast::Generics, span: Span, - ) -> Option { + ) -> RewriteResult { // Drop semicolon or it will be interpreted as comment. let span = mk_sp(span.lo(), span.hi() - BytePos(1)); let context = self.get_context(); @@ -476,7 +476,7 @@ impl<'a> FmtVisitor<'a> { // Re-attach semicolon result.push(';'); - Some(result) + Ok(result) } pub(crate) fn single_line_fn( @@ -977,7 +977,7 @@ fn format_impl_ref_and_type( 0, )? }; - let generics_str = rewrite_generics(context, "impl", generics, shape)?; + let generics_str = rewrite_generics(context, "impl", generics, shape).ok()?; result.push_str(&generics_str); result.push_str(format_constness_right(constness)); @@ -1185,7 +1185,7 @@ pub(crate) fn format_trait( let shape = Shape::indented(offset, context.config).offset_left(result.len())?; let generics_str = - rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; + rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape).ok()?; result.push_str(&generics_str); // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. @@ -1386,7 +1386,7 @@ pub(crate) fn format_trait_alias( let alias = rewrite_ident(context, ident); // 6 = "trait ", 2 = " =" let g_shape = shape.offset_left(6)?.sub_width(2)?; - let generics_str = rewrite_generics(context, alias, generics, g_shape)?; + let generics_str = rewrite_generics(context, alias, generics, g_shape).ok()?; let vis_str = format_visibility(context, vis); let lhs = format!("{vis_str}trait {generics_str} ="); // 1 = ";" @@ -1613,7 +1613,7 @@ fn format_tuple_struct( Some(generics) => { let budget = context.budget(last_line_width(&header_str)); let shape = Shape::legacy(budget, offset); - let generics_str = rewrite_generics(context, "", generics, shape)?; + let generics_str = rewrite_generics(context, "", generics, shape).ok()?; result.push_str(&generics_str); let where_budget = context.budget(last_line_width(&result)); @@ -1700,7 +1700,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( indent: Indent, visitor_kind: &ItemVisitorKind<'b>, span: Span, -) -> Option { +) -> RewriteResult { use ItemVisitorKind::*; let ast::TyAlias { @@ -1738,8 +1738,8 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( rewrite_ty(rw_info, Some(bounds), ty_opt, vis) }?; match defaultness { - ast::Defaultness::Default(..) => Some(format!("default {result}")), - _ => Some(result), + ast::Defaultness::Default(..) => Ok(format!("default {result}")), + _ => Ok(result), } } } @@ -1750,7 +1750,7 @@ fn rewrite_ty( generic_bounds_opt: Option<&ast::GenericBounds>, rhs: Option<&R>, vis: &ast::Visibility, -) -> Option { +) -> RewriteResult { let mut result = String::with_capacity(128); let TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span) = *rw_info; let (before_where_predicates, after_where_predicates) = generics @@ -1758,7 +1758,7 @@ fn rewrite_ty( .predicates .split_at(where_clauses.split); if !after_where_predicates.is_empty() { - return None; + return Err(RewriteError::Unknown); } result.push_str(&format!("{}type ", format_visibility(context, vis))); let ident_str = rewrite_ident(context, ident); @@ -1767,9 +1767,11 @@ fn rewrite_ty( result.push_str(ident_str) } else { // 2 = `= ` - let g_shape = Shape::indented(indent, context.config) - .offset_left(result.len())? - .sub_width(2)?; + let g_shape = Shape::indented(indent, context.config); + let g_shape = g_shape + .offset_left(result.len()) + .and_then(|s| s.sub_width(2)) + .max_width_error(g_shape.width, span)?; let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?; result.push_str(&generics_str); } @@ -1777,8 +1779,13 @@ fn rewrite_ty( if let Some(bounds) = generic_bounds_opt { if !bounds.is_empty() { // 2 = `: ` - let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?; - let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?; + let shape = Shape::indented(indent, context.config); + let shape = shape + .offset_left(result.len() + 2) + .max_width_error(shape.width, span)?; + let type_bounds = bounds + .rewrite_result(context, shape) + .map(|s| format!(": {}", s))?; result.push_str(&type_bounds); } } @@ -1799,8 +1806,7 @@ fn rewrite_ty( None, generics.span.hi(), option, - ) - .ok()?; + )?; result.push_str(&where_clause_str); if let Some(ty) = rhs { @@ -1820,13 +1826,20 @@ fn rewrite_ty( let lhs = match comment_span { Some(comment_span) - if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) => + if contains_comment( + context + .snippet_provider + .span_to_snippet(comment_span) + .unknown_error()?, + ) => { let comment_shape = if has_where { Shape::indented(indent, context.config) } else { - Shape::indented(indent, context.config) - .block_left(context.config.tab_spaces())? + let shape = Shape::indented(indent, context.config); + shape + .block_left(context.config.tab_spaces()) + .max_width_error(shape.width, span)? }; combine_strs_with_missing_comments( @@ -1836,19 +1849,17 @@ fn rewrite_ty( comment_span, comment_shape, true, - ) - .ok()? + )? } _ => format!("{result}="), }; // 1 = `;` - let shape = Shape::indented(indent, context.config).sub_width(1)?; - rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape) - .map(|s| s + ";") - .ok() + let shape = Shape::indented(indent, context.config); + let shape = shape.sub_width(1).max_width_error(shape.width, span)?; + rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") } else { - Some(format!("{result};")) + Ok(format!("{result};")) } } @@ -2392,7 +2403,7 @@ fn rewrite_fn_base( fn_sig: &FnSig<'_>, span: Span, fn_brace_style: FnBraceStyle, -) -> Option<(String, bool, bool)> { +) -> Result<(String, bool, bool), RewriteError> { let mut force_new_line_for_brace = false; let where_clause = &fn_sig.generics.where_clause; @@ -2436,7 +2447,7 @@ fn rewrite_fn_base( // return type later anyway. let ret_str = fd .output - .rewrite(context, Shape::indented(indent, context.config))?; + .rewrite_result(context, Shape::indented(indent, context.config))?; let multi_line_ret_str = ret_str.contains('\n'); let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() }; @@ -2449,7 +2460,7 @@ fn rewrite_fn_base( ret_str_len, fn_brace_style, multi_line_ret_str, - )?; + ); debug!( "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}", @@ -2611,7 +2622,7 @@ fn rewrite_fn_base( if multi_line_ret_str || ret_should_indent { // Now that we know the proper indent and width, we need to // re-layout the return type. - let ret_str = fd.output.rewrite(context, ret_shape)?; + let ret_str = fd.output.rewrite_result(context, ret_shape)?; result.push_str(&ret_str); } else { result.push_str(&ret_str); @@ -2671,8 +2682,7 @@ fn rewrite_fn_base( Some(span.hi()), pos_before_where, option, - ) - .ok()?; + )?; // If there are neither where-clause nor return type, we may be missing comments between // params and `{`. if where_clause_str.is_empty() { @@ -2699,7 +2709,7 @@ fn rewrite_fn_base( force_new_line_for_brace |= ends_with_comment; force_new_line_for_brace |= is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty(); - Some((result, ends_with_comment, force_new_line_for_brace)) + Ok((result, ends_with_comment, force_new_line_for_brace)) } /// Kind of spaces to put before `where`. @@ -2770,7 +2780,7 @@ fn rewrite_params( param_indent: Indent, span: Span, variadic: bool, -) -> Option { +) -> RewriteResult { if params.is_empty() { let comment = context .snippet(mk_sp( @@ -2779,7 +2789,7 @@ fn rewrite_params( span.hi() - BytePos(1), )) .trim(); - return Some(comment.to_owned()); + return Ok(comment.to_owned()); } let param_items: Vec<_> = itemize_list( context.snippet_provider, @@ -2829,7 +2839,7 @@ fn rewrite_params( .trailing_separator(trailing_separator) .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) .preserve_newline(true); - write_list(¶m_items, &fmt).ok() + write_list(¶m_items, &fmt) } fn compute_budgets_for_params( @@ -2839,7 +2849,7 @@ fn compute_budgets_for_params( ret_str_len: usize, fn_brace_style: FnBraceStyle, force_vertical_layout: bool, -) -> Option<(usize, usize, Indent)> { +) -> (usize, usize, Indent) { debug!( "compute_budgets_for_params {} {:?}, {}, {:?}", result.len(), @@ -2876,7 +2886,7 @@ fn compute_budgets_for_params( } }; - return Some((one_line_budget, multi_line_budget, indent)); + return (one_line_budget, multi_line_budget, indent); } } @@ -2888,7 +2898,7 @@ fn compute_budgets_for_params( // Account for `)` and possibly ` {`. IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 }, }; - Some((0, context.budget(used_space), new_indent)) + (0, context.budget(used_space), new_indent) } fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle { @@ -2913,16 +2923,16 @@ fn rewrite_generics( ident: &str, generics: &ast::Generics, shape: Shape, -) -> Option { +) -> RewriteResult { // FIXME: convert bounds to where-clauses where they get too big or if // there is a where-clause at all. if generics.params.is_empty() { - return Some(ident.to_owned()); + return Ok(ident.to_owned()); } let params = generics.params.iter(); - overflow::rewrite_with_angle_brackets(context, ident, params, shape, generics.span).ok() + overflow::rewrite_with_angle_brackets(context, ident, params, shape, generics.span) } fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) -> Option { @@ -3272,7 +3282,7 @@ fn format_generics( used_width: usize, ) -> Option { let shape = Shape::legacy(context.budget(used_width + offset.width()), offset); - let mut result = rewrite_generics(context, "", generics, shape)?; + let mut result = rewrite_generics(context, "", generics, shape).ok()?; // If the generics are not parameterized then generics.span.hi() == 0, // so we use span.lo(), which is the position after `struct Foo`. @@ -3366,7 +3376,11 @@ fn format_generics( impl Rewrite for ast::ForeignItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - let attrs_str = self.attrs.rewrite(context, shape)?; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let attrs_str = self.attrs.rewrite_result(context, shape)?; // Drop semicolon or it will be interpreted as comment. // FIXME: this may be a faulty span from libsyntax. let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1)); @@ -3399,7 +3413,7 @@ impl Rewrite for ast::ForeignItem { defaultness, Some(&inner_attrs), ); - Some(visitor.buffer.to_owned()) + Ok(visitor.buffer.to_owned()) } else { rewrite_fn_base( context, @@ -3431,17 +3445,18 @@ impl Rewrite for ast::ForeignItem { prefix, &static_foreign_item.ty, &RhsAssignKind::Ty, - shape.sub_width(1)?, + shape + .sub_width(1) + .max_width_error(shape.width, static_foreign_item.ty.span)?, ) .map(|s| s + ";") - .ok() } ast::ForeignItemKind::TyAlias(ref ty_alias) => { let (kind, span) = (&ItemVisitorKind::ForeignItem(self), self.span); rewrite_type_alias(ty_alias, context, shape.indent, kind, span) } ast::ForeignItemKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Item).ok() + rewrite_macro(mac, None, context, shape, MacroPosition::Item) } }?; @@ -3458,7 +3473,6 @@ impl Rewrite for ast::ForeignItem { shape, false, ) - .ok() } } diff --git a/src/visitor.rs b/src/visitor.rs index afb54c8e2bcd7..99416f8550362 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -561,9 +561,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ) } else { let indent = self.block_indent; - let rewrite = self.rewrite_required_fn( - indent, item.ident, sig, &item.vis, generics, item.span, - ); + let rewrite = self + .rewrite_required_fn( + indent, item.ident, sig, &item.vis, generics, item.span, + ) + .ok(); self.push_rewrite(item.span, rewrite); } } @@ -610,7 +612,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.block_indent, visitor_kind, span, - ); + ) + .ok(); self.push_rewrite(span, rewrite); } @@ -656,8 +659,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ); } else { let indent = self.block_indent; - let rewrite = - self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span); + let rewrite = self + .rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span) + .ok(); self.push_rewrite(ai.span, rewrite); } } From d720a7ef77f76670d2275952e81e9ae6b2556638 Mon Sep 17 00:00:00 2001 From: ding-young Date: Sun, 25 Aug 2024 15:42:46 +0900 Subject: [PATCH 0277/1301] impl rewrite_result for ast::Expr --- src/expr.rs | 174 +++++++++++++++++++++++-------------------------- src/matches.rs | 23 ++++--- src/stmt.rs | 4 +- src/types.rs | 4 ++ src/utils.rs | 8 --- 5 files changed, 98 insertions(+), 115 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 5bd87d00b1d57..32bfc8b4722a4 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -39,6 +39,10 @@ use crate::visitor::FmtVisitor; impl Rewrite for ast::Expr { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { format_expr(self, ExprType::SubExpression, context, shape) } } @@ -58,14 +62,14 @@ pub(crate) fn format_expr( expr_type: ExprType, context: &RewriteContext<'_>, shape: Shape, -) -> Option { - skip_out_of_file_lines_range!(context, expr.span); +) -> RewriteResult { + skip_out_of_file_lines_range_err!(context, expr.span); if contains_skip(&*expr.attrs) { - return Some(context.snippet(expr.span()).to_owned()); + return Ok(context.snippet(expr.span()).to_owned()); } let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) { - shape.sub_width(1)? + shape.sub_width(1).max_width_error(shape.width, expr.span)? } else { shape }; @@ -79,41 +83,38 @@ pub(crate) fn format_expr( shape, choose_separator_tactic(context, expr.span), None, - ) - .ok(), + ), ast::ExprKind::Lit(token_lit) => { if let Ok(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { - Some(expr_rw) + Ok(expr_rw) } else { if let LitKind::StrRaw(_) = token_lit.kind { - Some(context.snippet(expr.span).trim().into()) + Ok(context.snippet(expr.span).trim().into()) } else { - None + Err(RewriteError::Unknown) } } } ast::ExprKind::Call(ref callee, ref args) => { let inner_span = mk_sp(callee.span.hi(), expr.span.hi()); - let callee_str = callee.rewrite(context, shape)?; - rewrite_call(context, &callee_str, args, inner_span, shape).ok() + let callee_str = callee.rewrite_result(context, shape)?; + rewrite_call(context, &callee_str, args, inner_span, shape) } - ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span).ok(), + ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span), ast::ExprKind::Binary(op, ref lhs, ref rhs) => { // FIXME: format comments between operands and operator - rewrite_all_pairs(expr, shape, context) - .or_else(|_| { - rewrite_pair( - &**lhs, - &**rhs, - PairParts::infix(&format!(" {} ", context.snippet(op.span))), - context, - shape, - context.config.binop_separator(), - ) - }) - .ok() + rewrite_all_pairs(expr, shape, context).or_else(|_| { + rewrite_pair( + &**lhs, + &**rhs, + PairParts::infix(&format!(" {} ", context.snippet(op.span))), + context, + shape, + context.config.binop_separator(), + ) + }) } - ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape).ok(), + ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape), ast::ExprKind::Struct(ref struct_expr) => { let ast::StructExpr { qself, @@ -131,19 +132,17 @@ pub(crate) fn format_expr( expr.span, shape, ) - .ok() } ast::ExprKind::Tup(ref items) => { - rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1).ok() - } - ast::ExprKind::Let(ref pat, ref expr, _span, _) => { - rewrite_let(context, shape, pat, expr).ok() + rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1) } + ast::ExprKind::Let(ref pat, ref expr, _span, _) => rewrite_let(context, shape, pat, expr), ast::ExprKind::If(..) | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Loop(..) | ast::ExprKind::While(..) => to_control_flow(expr, expr_type) - .and_then(|control_flow| control_flow.rewrite(context, shape)), + .unknown_error() + .and_then(|control_flow| control_flow.rewrite_result(context, shape)), ast::ExprKind::ConstBlock(ref anon_const) => { let rewrite = match anon_const.value.kind { ast::ExprKind::Block(ref block, opt_label) => { @@ -151,24 +150,24 @@ pub(crate) fn format_expr( // not the `ast::Block` node we're about to rewrite. To prevent dropping inner // attributes call `rewrite_block` directly. // See https://github.com/rust-lang/rustfmt/issues/6158 - rewrite_block(block, Some(&expr.attrs), opt_label, context, shape).ok()? + rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)? } - _ => anon_const.rewrite(context, shape)?, + _ => anon_const.rewrite_result(context, shape)?, }; - Some(format!("const {}", rewrite)) + Ok(format!("const {}", rewrite)) } ast::ExprKind::Block(ref block, opt_label) => { match expr_type { ExprType::Statement => { if is_unsafe_block(block) { - rewrite_block(block, Some(&expr.attrs), opt_label, context, shape).ok() - } else if let rw @ Some(_) = + rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) + } else if let Some(rw) = rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape) { // Rewrite block without trying to put it in a single line. - rw + Ok(rw) } else { - let prefix = block_prefix(context, block, shape).ok()?; + let prefix = block_prefix(context, block, shape)?; rewrite_block_with_visitor( context, @@ -179,32 +178,31 @@ pub(crate) fn format_expr( shape, true, ) - .ok() } } ExprType::SubExpression => { - rewrite_block(block, Some(&expr.attrs), opt_label, context, shape).ok() + rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) } } } ast::ExprKind::Match(ref cond, ref arms, kind) => { - rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs, kind).ok() + rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs, kind) } ast::ExprKind::Path(ref qself, ref path) => { - rewrite_path(context, PathContext::Expr, qself, path, shape).ok() + rewrite_path(context, PathContext::Expr, qself, path, shape) } ast::ExprKind::Assign(ref lhs, ref rhs, _) => { - rewrite_assignment(context, lhs, rhs, None, shape).ok() + rewrite_assignment(context, lhs, rhs, None, shape) } ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => { - rewrite_assignment(context, lhs, rhs, Some(op), shape).ok() + rewrite_assignment(context, lhs, rhs, Some(op), shape) } ast::ExprKind::Continue(ref opt_label) => { let id_str = match *opt_label { Some(label) => format!(" {}", label.ident), None => String::new(), }; - Some(format!("continue{id_str}")) + Ok(format!("continue{id_str}")) } ast::ExprKind::Break(ref opt_label, ref opt_expr) => { let id_str = match *opt_label { @@ -213,16 +211,16 @@ pub(crate) fn format_expr( }; if let Some(ref expr) = *opt_expr { - rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape).ok() + rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape) } else { - Some(format!("break{id_str}")) + Ok(format!("break{id_str}")) } } ast::ExprKind::Yield(ref opt_expr) => { if let Some(ref expr) = *opt_expr { - rewrite_unary_prefix(context, "yield ", &**expr, shape).ok() + rewrite_unary_prefix(context, "yield ", &**expr, shape) } else { - Some("yield".to_string()) + Ok("yield".to_string()) } } ast::ExprKind::Closure(ref cl) => closures::rewrite_closure( @@ -236,37 +234,32 @@ pub(crate) fn format_expr( expr.span, context, shape, - ) - .ok(), + ), ast::ExprKind::Try(..) | ast::ExprKind::Field(..) | ast::ExprKind::MethodCall(..) - | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape).ok(), + | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape), ast::ExprKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Expression) - .or_else(|_| { - wrap_str( - context.snippet(expr.span).to_owned(), - context.config.max_width(), - shape, - ) - .max_width_error(shape.width, expr.span) - }) - .ok() + rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|_| { + wrap_str( + context.snippet(expr.span).to_owned(), + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, expr.span) + }) } - ast::ExprKind::Ret(None) => Some("return".to_owned()), + ast::ExprKind::Ret(None) => Ok("return".to_owned()), ast::ExprKind::Ret(Some(ref expr)) => { - rewrite_unary_prefix(context, "return ", &**expr, shape).ok() - } - ast::ExprKind::Become(ref expr) => { - rewrite_unary_prefix(context, "become ", &**expr, shape).ok() + rewrite_unary_prefix(context, "return ", &**expr, shape) } - ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()), + ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape), + ast::ExprKind::Yeet(None) => Ok("do yeet".to_owned()), ast::ExprKind::Yeet(Some(ref expr)) => { - rewrite_unary_prefix(context, "do yeet ", &**expr, shape).ok() + rewrite_unary_prefix(context, "do yeet ", &**expr, shape) } ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => { - rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape).ok() + rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape) } ast::ExprKind::Cast(ref expr, ref ty) => rewrite_pair( &**expr, @@ -275,10 +268,9 @@ pub(crate) fn format_expr( context, shape, SeparatorPlace::Front, - ) - .ok(), + ), ast::ExprKind::Index(ref expr, ref index, _) => { - rewrite_index(&**expr, &**index, context, shape).ok() + rewrite_index(&**expr, &**index, context, shape) } ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair( &**expr, @@ -287,8 +279,7 @@ pub(crate) fn format_expr( context, shape, SeparatorPlace::Back, - ) - .ok(), + ), ast::ExprKind::Range(ref lhs, ref rhs, limits) => { let delim = match limits { ast::RangeLimits::HalfOpen => "..", @@ -341,7 +332,6 @@ pub(crate) fn format_expr( shape, context.config.binop_separator(), ) - .ok() } (None, Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { @@ -349,7 +339,7 @@ pub(crate) fn format_expr( } else { default_sp_delim(None, Some(rhs)) }; - rewrite_unary_prefix(context, &sp_delim, &*rhs, shape).ok() + rewrite_unary_prefix(context, &sp_delim, &*rhs, shape) } (Some(lhs), None) => { let sp_delim = if context.config.spaces_around_ranges() { @@ -357,25 +347,25 @@ pub(crate) fn format_expr( } else { default_sp_delim(Some(lhs), None) }; - rewrite_unary_suffix(context, &sp_delim, &*lhs, shape).ok() + rewrite_unary_suffix(context, &sp_delim, &*lhs, shape) } - (None, None) => Some(delim.to_owned()), + (None, None) => Ok(delim.to_owned()), } } // We do not format these expressions yet, but they should still // satisfy our width restrictions. // Style Guide RFC for InlineAsm variant pending // https://github.com/rust-dev-tools/fmt-rfcs/issues/152 - ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), + ast::ExprKind::InlineAsm(..) => Ok(context.snippet(expr.span).to_owned()), ast::ExprKind::TryBlock(ref block) => { if let rw @ Ok(_) = rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape) { - rw.ok() + rw } else { // 9 = `try ` let budget = shape.width.saturating_sub(9); - Some(format!( + Ok(format!( "{}{}", "try ", rewrite_block( @@ -384,8 +374,7 @@ pub(crate) fn format_expr( None, context, Shape::legacy(budget, shape.indent) - ) - .ok()? + )? )) } } @@ -403,11 +392,11 @@ pub(crate) fn format_expr( None, shape, ) { - rw.ok() + rw } else { // 6 = `async ` let budget = shape.width.saturating_sub(6); - Some(format!( + Ok(format!( "{kind} {mover}{}", rewrite_block( block, @@ -415,12 +404,11 @@ pub(crate) fn format_expr( None, context, Shape::legacy(budget, shape.indent) - ) - .ok()? + )? )) } } - ast::ExprKind::Underscore => Some("_".to_owned()), + ast::ExprKind::Underscore => Ok("_".to_owned()), ast::ExprKind::FormatArgs(..) | ast::ExprKind::Type(..) | ast::ExprKind::IncludedBytes(..) @@ -429,22 +417,21 @@ pub(crate) fn format_expr( // rustfmt tries to parse macro arguments when formatting macros, so it's not totally // impossible for rustfmt to come across one of these nodes when formatting a file. // Also, rustfmt might get passed the output from `-Zunpretty=expanded`. - None + Err(RewriteError::Unknown) } - ast::ExprKind::Err(_) | ast::ExprKind::Dummy => None, + ast::ExprKind::Err(_) | ast::ExprKind::Dummy => Err(RewriteError::Unknown), }; expr_rw .map(|expr_str| recover_comment_removed(expr_str, expr.span, context)) .and_then(|expr_str| { let attrs = outer_attributes(&expr.attrs); - let attrs_str = attrs.rewrite(context, shape)?; + let attrs_str = attrs.rewrite_result(context, shape)?; let span = mk_sp( attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()), expr.span.lo(), ); combine_strs_with_missing_comments(context, &attrs_str, &expr_str, span, shape, false) - .ok() }) } @@ -1184,7 +1171,6 @@ impl<'a> Rewrite for ControlFlow<'a> { ..shape }; format_expr(else_block, ExprType::Statement, context, else_shape) - .unknown_error() } }; diff --git a/src/matches.rs b/src/matches.rs index 8de92eb55383e..ee631fa1b4706 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -516,7 +516,7 @@ fn rewrite_match_body( .offset_left(extra_offset(pats_str, shape) + 4) .and_then(|shape| shape.sub_width(comma.len())); let orig_body = if forbid_same_line || !arrow_comment.is_empty() { - None + Err(RewriteError::Unknown) } else if let Some(body_shape) = orig_body_shape { let rewrite = nop_block_collapse( format_expr(body, ExprType::Statement, context, body_shape), @@ -524,7 +524,7 @@ fn rewrite_match_body( ); match rewrite { - Some(ref body_str) + Ok(ref body_str) if is_block || (!body_str.contains('\n') && unicode_str_width(body_str) <= body_shape.width) => @@ -534,7 +534,7 @@ fn rewrite_match_body( _ => rewrite, } } else { - None + Err(RewriteError::Unknown) }; let orig_budget = orig_body_shape.map_or(0, |shape| shape.width); @@ -545,20 +545,23 @@ fn rewrite_match_body( next_line_body_shape.width, ); match (orig_body, next_line_body) { - (Some(ref orig_str), Some(ref next_line_str)) + (Ok(ref orig_str), Ok(ref next_line_str)) if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) => { combine_next_line_body(next_line_str) } - (Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => { + (Ok(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => { combine_orig_body(orig_str) } - (Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => { + (Ok(ref orig_str), Ok(ref next_line_str)) if orig_str.contains('\n') => { combine_next_line_body(next_line_str) } - (None, Some(ref next_line_str)) => combine_next_line_body(next_line_str), - (None, None) => Err(RewriteError::Unknown), - (Some(ref orig_str), _) => combine_orig_body(orig_str), + (Err(_), Ok(ref next_line_str)) => combine_next_line_body(next_line_str), + // When both orig_body and next_line_body result in errors, we currently propagate the + // error from the second attempt since it is more generous with width constraints. + // This decision is somewhat arbitrary and is open to change. + (Err(_), Err(next_line_err)) => Err(next_line_err), + (Ok(ref orig_str), _) => combine_orig_body(orig_str), } } @@ -605,7 +608,7 @@ fn rewrite_guard( } } -fn nop_block_collapse(block_str: Option, budget: usize) -> Option { +fn nop_block_collapse(block_str: RewriteResult, budget: usize) -> RewriteResult { debug!("nop_block_collapse {:?} {}", block_str, budget); block_str.map(|block_str| { if block_str.starts_with('{') diff --git a/src/stmt.rs b/src/stmt.rs index 426bf89fc1622..2788159018d0d 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -135,9 +135,7 @@ fn format_stmt( let shape = shape .sub_width(suffix.len()) .max_width_error(shape.width, ex.span())?; - format_expr(ex, expr_type, context, shape) - .map(|s| s + suffix) - .unknown_error() + format_expr(ex, expr_type, context, shape).map(|s| s + suffix) } ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => { Err(RewriteError::Unknown) diff --git a/src/types.rs b/src/types.rs index 5477942f82e72..1aa3f60f86880 100644 --- a/src/types.rs +++ b/src/types.rs @@ -584,6 +584,10 @@ fn rewrite_bounded_lifetime( impl Rewrite for ast::AnonConst { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { format_expr(&self.value, ExprType::SubExpression, context, shape) } } diff --git a/src/utils.rs b/src/utils.rs index be21e89f760e9..d1cfc6acc49bc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -367,14 +367,6 @@ macro_rules! out_of_file_lines_range { }; } -macro_rules! skip_out_of_file_lines_range { - ($self:ident, $span:expr) => { - if out_of_file_lines_range!($self, $span) { - return None; - } - }; -} - macro_rules! skip_out_of_file_lines_range_err { ($self:ident, $span:expr) => { if out_of_file_lines_range!($self, $span) { From b41f6ab525dbab21a73de3dc844b87a98b36190b Mon Sep 17 00:00:00 2001 From: coekjan Date: Thu, 5 Sep 2024 13:50:44 +0800 Subject: [PATCH 0278/1301] fix: Fix `inline_const_as_literal` error when the number >= 10 --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +++++++++++ .../src/handlers/inline_const_as_literal.rs | 7 +++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 81d6466c2f31d..410e05fb1d1eb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2553,6 +2553,17 @@ impl Const { Type::from_value_def(db, self.id) } + /// Evaluate the constant and return the result as a string. + /// + /// This function is intended for IDE assistance, different from [`Const::render_eval`]. + pub fn eval(self, db: &dyn HirDatabase, edition: Edition) -> Result { + let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?; + Ok(format!("{}", c.display(db, edition))) + } + + /// Evaluate the constant and return the result as a string, with more detailed information. + /// + /// This function is intended for user-facing display. pub fn render_eval( self, db: &dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs index f1c2acdd3ed91..6b504a918b403 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs @@ -53,10 +53,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_> | ast::Expr::BinExpr(_) | ast::Expr::CallExpr(_) => { let edition = ctx.sema.scope(variable.syntax())?.krate().edition(ctx.db()); - match konst.render_eval(ctx.sema.db, edition) { - Ok(result) => result, - Err(_) => return None, - } + konst.eval(ctx.sema.db, edition).ok()? } _ => return None, }; @@ -127,12 +124,14 @@ mod tests { ("u64", "0", NUMBER), ("u128", "0", NUMBER), ("usize", "0", NUMBER), + ("usize", "16", NUMBER), ("i8", "0", NUMBER), ("i16", "0", NUMBER), ("i32", "0", NUMBER), ("i64", "0", NUMBER), ("i128", "0", NUMBER), ("isize", "0", NUMBER), + ("isize", "16", NUMBER), ("bool", "false", BOOL), ("&str", "\"str\"", STR), ("char", "'c'", CHAR), From 1328b52e9150c1ef6193528f137e31bd89223cde Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 1 Sep 2024 15:17:52 +0200 Subject: [PATCH 0279/1301] Lower asm expressions --- .../crates/hir-def/src/body/lower.rs | 15 +- .../crates/hir-def/src/body/lower/asm.rs | 230 ++++++++++++++++++ .../rust-analyzer/crates/hir-def/src/hir.rs | 132 +++++++++- .../macro_expansion_tests/builtin_fn_macro.rs | 6 +- .../crates/hir-expand/src/builtin/fn_macro.rs | 34 +-- .../crates/hir-ty/src/infer/closure.rs | 23 +- .../crates/hir-ty/src/infer/expr.rs | 67 ++++- .../crates/hir-ty/src/infer/mutability.rs | 27 +- .../crates/hir-ty/src/tests/macros.rs | 99 +++++++- .../rust-analyzer/crates/hir/src/semantics.rs | 1 - .../test_data/highlight_strings.html | 8 +- .../parser/src/grammar/expressions/atom.rs | 38 ++- .../rust-analyzer/crates/syntax/rust.ungram | 8 +- .../crates/syntax/src/ast/generated/nodes.rs | 21 +- 14 files changed, 612 insertions(+), 97 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index f2eb43beb1f18..dfcb8b81204d2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1,6 +1,8 @@ //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` //! representation. +mod asm; + use std::mem; use base_db::CrateId; @@ -35,8 +37,8 @@ use crate::{ FormatPlaceholder, FormatSign, FormatTrait, }, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, + Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat, + PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -693,13 +695,7 @@ impl ExprCollector<'_> { } } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), - ast::Expr::AsmExpr(e) => { - let template = e.template().map(|it| self.collect_expr(it)).collect(); - self.alloc_expr( - Expr::InlineAsm(InlineAsm { template, operands: Box::default() }), - syntax_ptr, - ) - } + ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr), ast::Expr::OffsetOfExpr(e) => { let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); let fields = e.fields().map(|it| it.as_name()).collect(); @@ -2064,6 +2060,7 @@ impl ExprCollector<'_> { is_assignee_expr: false, }) } + // endregion: format fn lang_path(&self, lang: LangItem) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs new file mode 100644 index 0000000000000..dafa3a859debd --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -0,0 +1,230 @@ +use hir_expand::name::Name; +use intern::Symbol; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::{ + ast::{self, HasName, IsString}, + AstNode, AstPtr, AstToken, T, +}; +use tt::{TextRange, TextSize}; + +use crate::{ + body::lower::{ExprCollector, FxIndexSet}, + hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass}, +}; + +impl ExprCollector<'_> { + pub(super) fn lower_inline_asm( + &mut self, + asm: ast::AsmExpr, + syntax_ptr: AstPtr, + ) -> ExprId { + let mut clobber_abis = FxIndexSet::default(); + let mut operands = vec![]; + let mut options = AsmOptions::empty(); + + let mut named_pos: FxHashMap = Default::default(); + let mut named_args: FxHashMap = Default::default(); + let mut reg_args: FxHashSet = Default::default(); + for operand in asm.asm_operands() { + let slot = operands.len(); + let mut lower_reg = |reg: Option| { + let reg = reg?; + if let Some(string) = reg.string_token() { + reg_args.insert(slot); + Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text()))) + } else { + reg.name_ref().map(|name_ref| { + InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text())) + }) + } + }; + + let op = match operand { + ast::AsmOperand::AsmClobberAbi(clobber_abi) => { + if let Some(abi_name) = clobber_abi.string_token() { + clobber_abis.insert(Symbol::intern(abi_name.text())); + } + continue; + } + ast::AsmOperand::AsmOptions(opt) => { + opt.asm_options().for_each(|opt| { + options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) { + T![att_syntax] => AsmOptions::ATT_SYNTAX, + T![may_unwind] => AsmOptions::MAY_UNWIND, + T![nomem] => AsmOptions::NOMEM, + T![noreturn] => AsmOptions::NORETURN, + T![nostack] => AsmOptions::NOSTACK, + T![preserves_flags] => AsmOptions::PRESERVES_FLAGS, + T![pure] => AsmOptions::PURE, + T![raw] => AsmOptions::RAW, + T![readonly] => AsmOptions::READONLY, + _ => return, + } + }); + continue; + } + ast::AsmOperand::AsmRegOperand(op) => { + let Some(dir_spec) = op.asm_dir_spec() else { + continue; + }; + let Some(reg) = lower_reg(op.asm_reg_spec()) else { + continue; + }; + if let Some(name) = op.name() { + let sym = Symbol::intern(&name.text()); + named_args.insert(sym.clone(), slot); + named_pos.insert(slot, sym); + } + if dir_spec.in_token().is_some() { + let expr = self + .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr())); + AsmOperand::In { reg, expr } + } else if dir_spec.out_token().is_some() { + let expr = self + .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr())); + AsmOperand::Out { reg, expr: Some(expr), late: false } + } else if dir_spec.lateout_token().is_some() { + let expr = self + .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr())); + AsmOperand::Out { reg, expr: Some(expr), late: true } + } else if dir_spec.inout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); + match out_expr { + Some(out_expr) => AsmOperand::SplitInOut { + reg, + in_expr, + out_expr: Some(out_expr), + late: false, + }, + None => AsmOperand::InOut { reg, expr: in_expr, late: false }, + } + } else if dir_spec.inlateout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); + match out_expr { + Some(out_expr) => AsmOperand::SplitInOut { + reg, + in_expr, + out_expr: Some(out_expr), + late: false, + }, + None => AsmOperand::InOut { reg, expr: in_expr, late: false }, + } + } else { + continue; + } + } + ast::AsmOperand::AsmLabel(l) => { + AsmOperand::Label(self.collect_block_opt(l.block_expr())) + } + ast::AsmOperand::AsmConst(c) => AsmOperand::Const(self.collect_expr_opt(c.expr())), + ast::AsmOperand::AsmSym(s) => { + let Some(path) = s.path().and_then(|p| self.expander.parse_path(self.db, p)) + else { + continue; + }; + AsmOperand::Sym(path) + } + }; + operands.push(op); + } + + let mut mappings = vec![]; + let mut curarg = 0; + if !options.contains(AsmOptions::RAW) { + // Don't treat raw asm as a format string. + asm.template() + .filter_map(|it| Some((it.clone(), self.expand_macros_to_string(it)?))) + .for_each(|(expr, (s, is_direct_literal))| { + let Ok(text) = s.value() else { + return; + }; + let template_snippet = match expr { + ast::Expr::Literal(literal) => match literal.kind() { + ast::LiteralKind::String(s) => Some(s.text().to_owned()), + _ => None, + }, + _ => None, + }; + let str_style = match s.quote_offsets() { + Some(offsets) => { + let raw = usize::from(offsets.quotes.0.len()) - 1; + // subtract 1 for the `r` prefix + (raw != 0).then(|| raw - 1) + } + None => None, + }; + + let mut parser = rustc_parse_format::Parser::new( + &text, + str_style, + template_snippet, + false, + rustc_parse_format::ParseMode::InlineAsm, + ); + parser.curarg = curarg; + + let mut unverified_pieces = Vec::new(); + while let Some(piece) = parser.next() { + if !parser.errors.is_empty() { + break; + } else { + unverified_pieces.push(piece); + } + } + + curarg = parser.curarg; + + let to_span = |inner_span: rustc_parse_format::InnerSpan| { + is_direct_literal.then(|| { + TextRange::new( + inner_span.start.try_into().unwrap(), + inner_span.end.try_into().unwrap(), + ) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1) + }) + }; + for piece in unverified_pieces { + match piece { + rustc_parse_format::Piece::String(_) => {} + rustc_parse_format::Piece::NextArgument(arg) => { + // let span = arg_spans.next(); + + let _operand_idx = match arg.position { + rustc_parse_format::ArgumentIs(idx) + | rustc_parse_format::ArgumentImplicitlyIs(idx) => { + if idx >= operands.len() + || named_pos.contains_key(&idx) + || reg_args.contains(&idx) + { + None + } else { + Some(idx) + } + } + rustc_parse_format::ArgumentNamed(name) => { + let name = Symbol::intern(name); + if let Some(position_span) = to_span(arg.position_span) { + mappings.push(( + position_span, + Name::new_symbol_root(name.clone()), + )); + } + named_args.get(&name).copied() + } + }; + } + } + } + }) + }; + let idx = self.alloc_expr( + Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }), + syntax_ptr, + ); + self.source_map.format_args_template_map.insert(idx, mappings); + idx + } +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 8f537672b58d4..7d2c573ebfed4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -307,8 +307,120 @@ pub struct OffsetOf { #[derive(Debug, Clone, PartialEq, Eq)] pub struct InlineAsm { - pub template: Box<[ExprId]>, - pub operands: Box<[()]>, + pub operands: Box<[AsmOperand]>, + pub options: AsmOptions, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct AsmOptions(u16); +bitflags::bitflags! { + impl AsmOptions: u16 { + const PURE = 1 << 0; + const NOMEM = 1 << 1; + const READONLY = 1 << 2; + const PRESERVES_FLAGS = 1 << 3; + const NORETURN = 1 << 4; + const NOSTACK = 1 << 5; + const ATT_SYNTAX = 1 << 6; + const RAW = 1 << 7; + const MAY_UNWIND = 1 << 8; + } +} + +impl AsmOptions { + pub const COUNT: usize = Self::all().bits().count_ones() as usize; + + pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW); + pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN); + + pub fn human_readable_names(&self) -> Vec<&'static str> { + let mut options = vec![]; + + if self.contains(AsmOptions::PURE) { + options.push("pure"); + } + if self.contains(AsmOptions::NOMEM) { + options.push("nomem"); + } + if self.contains(AsmOptions::READONLY) { + options.push("readonly"); + } + if self.contains(AsmOptions::PRESERVES_FLAGS) { + options.push("preserves_flags"); + } + if self.contains(AsmOptions::NORETURN) { + options.push("noreturn"); + } + if self.contains(AsmOptions::NOSTACK) { + options.push("nostack"); + } + if self.contains(AsmOptions::ATT_SYNTAX) { + options.push("att_syntax"); + } + if self.contains(AsmOptions::RAW) { + options.push("raw"); + } + if self.contains(AsmOptions::MAY_UNWIND) { + options.push("may_unwind"); + } + + options + } +} + +impl std::fmt::Debug for AsmOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bitflags::parser::to_writer(self, f) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum AsmOperand { + In { + reg: InlineAsmRegOrRegClass, + expr: ExprId, + }, + Out { + reg: InlineAsmRegOrRegClass, + expr: Option, + late: bool, + }, + InOut { + reg: InlineAsmRegOrRegClass, + expr: ExprId, + late: bool, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + in_expr: ExprId, + out_expr: Option, + late: bool, + }, + Label(ExprId), + Const(ExprId), + Sym(Path), +} + +impl AsmOperand { + pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> { + match self { + Self::In { reg, .. } + | Self::Out { reg, .. } + | Self::InOut { reg, .. } + | Self::SplitInOut { reg, .. } => Some(reg), + Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None, + } + } + + pub fn is_clobber(&self) -> bool { + matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmRegOrRegClass { + Reg(Symbol), + RegClass(Symbol), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -373,7 +485,21 @@ impl Expr { match self { Expr::Missing => {} Expr::Path(_) | Expr::OffsetOf(_) => {} - Expr::InlineAsm(it) => it.template.iter().copied().for_each(f), + Expr::InlineAsm(it) => it.operands.iter().for_each(|op| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => f(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + f(*in_expr); + if let Some(out_expr) = out_expr { + f(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index b6dbba12cd6d6..37ae1ab39b494 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -50,11 +50,7 @@ fn main() { let i: u64 = 3; let o: u64; unsafe { - builtin #asm ( { - $crate::format_args!("mov {0}, {1}"); - $crate::format_args!("add {0}, 5"); - } - ); + builtin #asm ("mov {0}, {1}", "add {0}, 5", out (reg)o, in (reg)i, ); } } "##]], diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 795d9b14df961..15fed45caf02f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -119,9 +119,8 @@ register_builtin! { (module_path, ModulePath) => module_path_expand, (assert, Assert) => assert_expand, (stringify, Stringify) => stringify_expand, - (llvm_asm, LlvmAsm) => asm_expand, (asm, Asm) => asm_expand, - (global_asm, GlobalAsm) => global_asm_expand, + (global_asm, GlobalAsm) => asm_expand, (cfg, Cfg) => cfg_expand, (core_panic, CorePanic) => panic_expand, (std_panic, StdPanic) => panic_expand, @@ -324,40 +323,15 @@ fn asm_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - // We expand all assembly snippets to `format_args!` invocations to get format syntax - // highlighting for them. - let mut literals = Vec::new(); - for tt in tt.token_trees.chunks(2) { - match tt { - [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))] - | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] => - { - let dollar_krate = dollar_crate(span); - literals.push(quote!(span=>#dollar_krate::format_args!(#lit);)); - } - _ => break, - } - } - + let mut tt = tt.clone(); + tt.delimiter.kind = tt::DelimiterKind::Parenthesis; let pound = mk_pound(span); let expanded = quote! {span => - builtin #pound asm ( - {##literals} - ) + builtin #pound asm #tt }; ExpandResult::ok(expanded) } -fn global_asm_expand( - _db: &dyn ExpandDatabase, - _id: MacroCallId, - _tt: &tt::Subtree, - span: Span, -) -> ExpandResult { - // Expand to nothing (at item-level) - ExpandResult::ok(quote! {span =>}) -} - fn cfg_expand( db: &dyn ExpandDatabase, id: MacroCallId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 67a3d2434d24e..3ad330c1a0b5d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -10,7 +10,10 @@ use chalk_ir::{ use either::Either; use hir_def::{ data::adt::VariantData, - hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp}, + hir::{ + Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, + UnaryOp, + }, lang_item::LangItem, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, @@ -666,9 +669,21 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { Expr::OffsetOf(_) => (), - Expr::InlineAsm(e) => { - e.template.iter().for_each(|it| self.walk_expr_without_adjust(*it)) - } + Expr::InlineAsm(e) => e.operands.iter().for_each(|op| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.walk_expr_without_adjust(*in_expr); + if let Some(out_expr) = out_expr { + self.walk_expr_without_adjust(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), Expr::If { condition, then_branch, else_branch } => { self.consume_expr(*condition); self.consume_expr(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 6b725d690d1a7..e6eaf2f446d43 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin use either::Either; use hir_def::{ hir::{ - ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, + ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId, + Literal, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs, Path}, @@ -41,9 +42,9 @@ use crate::{ primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig, - FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, - TyExt, TyKind, + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer, + FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, + TyBuilder, TyExt, TyKind, }; use super::{ @@ -924,9 +925,61 @@ impl InferenceContext<'_> { expected } Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), - Expr::InlineAsm(it) => { - it.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr)); - self.result.standard_types.unit.clone() + Expr::InlineAsm(asm) => { + let mut check_expr_asm_operand = |expr, is_input: bool| { + let ty = self.infer_expr_no_expect(expr); + + // If this is an input value, we require its type to be fully resolved + // at this point. This allows us to provide helpful coercions which help + // pass the type candidate list in a later pass. + // + // We don't require output types to be resolved at this point, which + // allows them to be inferred based on how they are used later in the + // function. + if is_input { + let ty = self.resolve_ty_shallow(&ty); + match ty.kind(Interner) { + TyKind::FnDef(def, parameters) => { + let fnptr_ty = TyKind::Function( + CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(), + ) + .intern(Interner); + _ = self.coerce(Some(expr), &ty, &fnptr_ty); + } + TyKind::Ref(mutbl, _, base_ty) => { + let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); + _ = self.coerce(Some(expr), &ty, &ptr_ty); + } + _ => {} + } + } + }; + + let diverge = asm.options.contains(AsmOptions::NORETURN); + asm.operands.iter().for_each(|operand| match *operand { + AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true), + AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => { + check_expr_asm_operand(expr, false) + } + AsmOperand::Out { expr: None, .. } => (), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + check_expr_asm_operand(in_expr, true); + if let Some(out_expr) = out_expr { + check_expr_asm_operand(out_expr, false); + } + } + // FIXME + AsmOperand::Label(_) => (), + // FIXME + AsmOperand::Const(_) => (), + // FIXME + AsmOperand::Sym(_) => (), + }); + if diverge { + self.result.standard_types.never.clone() + } else { + self.result.standard_types.unit.clone() + } } }; // use a new type variable if we got unknown here diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index e1b460d072d3f..e841c6630822a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -3,7 +3,9 @@ use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ - hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp}, + hir::{ + Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp, + }, lang_item::LangItem, }; use hir_expand::name::Name; @@ -39,10 +41,25 @@ impl InferenceContext<'_> { fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { match &self.body[tgt_expr] { Expr::Missing => (), - Expr::InlineAsm(e) => e - .template - .iter() - .for_each(|&expr| self.infer_mut_expr_without_adjust(expr, Mutability::Not)), + Expr::InlineAsm(e) => { + e.operands.iter().for_each(|op| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => { + self.infer_mut_expr_without_adjust(*expr, Mutability::Not) + } + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not); + if let Some(out_expr) = out_expr { + self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Label(_) + | AsmOperand::Sym(_) + | AsmOperand::Const(_) => (), + }); + } Expr::OffsetOf(_) => (), &Expr::If { condition, then_branch, else_branch } => { self.infer_mut_expr(condition, Mutability::Not); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5a6161f94289f..83aceec981a5a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -1,7 +1,7 @@ use expect_test::expect; use test_utils::{bench, bench_fixture, skip_slow_tests}; -use crate::tests::check_infer_with_mismatches; +use crate::tests::{check_infer_with_mismatches, check_no_mismatches}; use super::{check_infer, check_types}; @@ -1406,3 +1406,100 @@ fn foo(t: Tensor) { "#, ); } + +#[test] +fn asm_unit() { + check_no_mismatches( + r#" +//- minicore: asm +fn unit() { + asm!("") +} +"#, + ); +} + +#[test] +fn asm_no_return() { + check_no_mismatches( + r#" +//- minicore: asm +fn unit() -> ! { + asm!("", options(noreturn)) +} +"#, + ); +} + +#[test] +fn asm_things() { + check_infer( + r#" +//- minicore: asm, concat +fn main() { + unsafe { + let foo = 1; + let mut o = 0; + asm!( + "%input = OpLoad _ {0}", + concat!("%result = ", bar, " _ %input"), + "OpStore {1} %result", + in(reg) &foo, + in(reg) &mut o, + ); + o + + let thread_id: usize; + asm!(" + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] + ", out(reg) thread_id, options(pure, readonly, nostack)); + + static UNMAP_BASE: usize; + const MEM_RELEASE: usize; + static VirtualFree: usize; + const OffPtr: usize; + const OffFn: usize; + asm!(" + push {free_type} + push {free_size} + push {base} + + mov eax, fs:[30h] + mov eax, [eax+8h] + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax + + push eax + + jmp {virtual_free} + ", + off_ptr = const OffPtr, + off_fn = const OffFn, + + free_size = const 0, + free_type = const MEM_RELEASE, + + virtual_free = sym VirtualFree, + + base = sym UNMAP_BASE, + options(noreturn), + ); + } +} +"#, + expect![[r#" + !0..122 'builti...muto,)': () + !0..190 'builti...tack))': () + !0..449 'builti...urn),)': ! + 10..1254 '{ ... } }': () + 16..1252 'unsafe... }': () + 37..40 'foo': i32 + 43..44 '1': i32 + 58..63 'mut o': i32 + 66..67 '0': i32 + 281..282 'o': i32 + 296..305 'thread_id': usize + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index c78b59826c955..4e6887295c8a3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -368,7 +368,6 @@ impl<'db> SemanticsImpl<'db> { | BuiltinFnLikeExpander::File | BuiltinFnLikeExpander::ModulePath | BuiltinFnLikeExpander::Asm - | BuiltinFnLikeExpander::LlvmAsm | BuiltinFnLikeExpander::GlobalAsm | BuiltinFnLikeExpander::LogSyntax | BuiltinFnLikeExpander::TraceMacros diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index cb47fc68bc1b6..9376ef65a4e6a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -166,10 +166,10 @@ let i: u64 = 3; let o: u64; asm!( - "mov {0}, {1}", - "add {0}, 5", - out(reg) o, - in(reg) i, + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, ); const CONSTANT: () = (): diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 57f1e6e9f031f..f98ca5f403225 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -329,9 +329,11 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { break; } + let op = p.start(); // Parse clobber_abi if p.eat_contextual_kw(T![clobber_abi]) { parse_clobber_abi(p); + op.complete(p, ASM_CLOBBER_ABI); allow_templates = false; continue; } @@ -339,6 +341,7 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { // Parse options if p.eat_contextual_kw(T![options]) { parse_options(p); + op.complete(p, ASM_OPTIONS); allow_templates = false; continue; } @@ -353,27 +356,14 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { false }; - let op = p.start(); - if p.eat(T![in]) { - parse_reg(p); - expr(p); - op.complete(p, ASM_REG_OPERAND); - } else if p.eat_contextual_kw(T![out]) { - parse_reg(p); - expr(p); - op.complete(p, ASM_REG_OPERAND); - } else if p.eat_contextual_kw(T![lateout]) { + let dir_spec = p.start(); + if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) { + dir_spec.complete(p, ASM_DIR_SPEC); parse_reg(p); expr(p); op.complete(p, ASM_REG_OPERAND); - } else if p.eat_contextual_kw(T![inout]) { - parse_reg(p); - expr(p); - if p.eat(T![=>]) { - expr(p); - } - op.complete(p, ASM_REG_OPERAND); - } else if p.eat_contextual_kw(T![inlateout]) { + } else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) { + dir_spec.complete(p, ASM_DIR_SPEC); parse_reg(p); expr(p); if p.eat(T![=>]) { @@ -381,21 +371,26 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { } op.complete(p, ASM_REG_OPERAND); } else if p.eat_contextual_kw(T![label]) { + dir_spec.abandon(p); block_expr(p); op.complete(p, ASM_LABEL); } else if p.eat(T![const]) { + dir_spec.abandon(p); expr(p); op.complete(p, ASM_CONST); } else if p.eat_contextual_kw(T![sym]) { - expr(p); + dir_spec.abandon(p); + paths::type_path(p); op.complete(p, ASM_SYM); } else if allow_templates { + dir_spec.abandon(p); op.abandon(p); if expr(p).is_none() { p.err_and_bump("expected asm template"); } continue; } else { + dir_spec.abandon(p); op.abandon(p); p.err_and_bump("expected asm operand"); if p.at(T!['}']) { @@ -424,11 +419,12 @@ fn parse_options(p: &mut Parser<'_>) { T![att_syntax], T![raw], ]; - - if !OPTIONS.iter().any(|&syntax| p.eat(syntax)) { + let m = p.start(); + if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) { p.err_and_bump("expected asm option"); continue; } + m.complete(p, ASM_OPTION); // Allow trailing commas if p.eat(T![')']) { diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 4d780ba28f310..5dace66c32d51 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -411,13 +411,11 @@ AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')' AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind' // options := "options(" option *("," option) [","] ")" AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')' -// operand := reg_operand / clobber_abi / options -AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel AsmLabel = 'label' BlockExpr -AsmSym = 'sym' Expr +AsmSym = 'sym' Path AsmConst = 'const' Expr - - +// operand := reg_operand / clobber_abi / options +AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel | AsmSym | AsmConst FormatArgsExpr = Attr* 'builtin' '#' 'format_args' '(' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index e5e1115e05a7b..01d47c34bbdf6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -250,7 +250,7 @@ pub struct AsmSym { } impl AsmSym { #[inline] - pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn path(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn sym_token(&self) -> Option { support::token(&self.syntax, T![sym]) } } @@ -2225,9 +2225,11 @@ impl ast::HasVisibility for Adt {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AsmOperand { AsmClobberAbi(AsmClobberAbi), + AsmConst(AsmConst), AsmLabel(AsmLabel), AsmOptions(AsmOptions), AsmRegOperand(AsmRegOperand), + AsmSym(AsmSym), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -4591,6 +4593,10 @@ impl From for AsmOperand { #[inline] fn from(node: AsmClobberAbi) -> AsmOperand { AsmOperand::AsmClobberAbi(node) } } +impl From for AsmOperand { + #[inline] + fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) } +} impl From for AsmOperand { #[inline] fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) } @@ -4603,18 +4609,27 @@ impl From for AsmOperand { #[inline] fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) } } +impl From for AsmOperand { + #[inline] + fn from(node: AsmSym) -> AsmOperand { AsmOperand::AsmSym(node) } +} impl AstNode for AsmOperand { #[inline] fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, ASM_CLOBBER_ABI | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND) + matches!( + kind, + ASM_CLOBBER_ABI | ASM_CONST | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND | ASM_SYM + ) } #[inline] fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { ASM_CLOBBER_ABI => AsmOperand::AsmClobberAbi(AsmClobberAbi { syntax }), + ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }), ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }), ASM_OPTIONS => AsmOperand::AsmOptions(AsmOptions { syntax }), ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }), + ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }), _ => return None, }; Some(res) @@ -4623,9 +4638,11 @@ impl AstNode for AsmOperand { fn syntax(&self) -> &SyntaxNode { match self { AsmOperand::AsmClobberAbi(it) => &it.syntax, + AsmOperand::AsmConst(it) => &it.syntax, AsmOperand::AsmLabel(it) => &it.syntax, AsmOperand::AsmOptions(it) => &it.syntax, AsmOperand::AsmRegOperand(it) => &it.syntax, + AsmOperand::AsmSym(it) => &it.syntax, } } } From 44e3b7d205da25c4f7538b439bd6990416910d2e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 10:23:00 +0200 Subject: [PATCH 0280/1301] Add Definition kind for asm register classes --- .../crates/hir-ty/src/tests/macros.rs | 20 ++-- .../crates/ide-completion/src/item.rs | 1 + .../rust-analyzer/crates/ide-db/src/defs.rs | 11 ++- .../rust-analyzer/crates/ide-db/src/lib.rs | 1 + .../rust-analyzer/crates/ide-db/src/rename.rs | 3 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 6 +- .../rust-analyzer/crates/ide/src/moniker.rs | 4 +- .../crates/ide/src/navigation_target.rs | 1 + .../ide/src/syntax_highlighting/highlight.rs | 3 + .../ide/src/syntax_highlighting/inject.rs | 1 + .../ide/src/syntax_highlighting/tags.rs | 1 + .../test_data/highlight_asm.html | 98 +++++++++++++++++++ .../test_data/highlight_strings.html | 2 +- .../ide/src/syntax_highlighting/tests.rs | 64 +++++++++++- .../crates/rust-analyzer/src/lsp/to_proto.rs | 3 + .../crates/test-utils/src/minicore.rs | 23 +++-- 16 files changed, 214 insertions(+), 28 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 83aceec981a5a..10ab6d3ff8939 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -1413,7 +1413,7 @@ fn asm_unit() { r#" //- minicore: asm fn unit() { - asm!("") + core::arch::asm!("") } "#, ); @@ -1425,7 +1425,7 @@ fn asm_no_return() { r#" //- minicore: asm fn unit() -> ! { - asm!("", options(noreturn)) + core::arch::asm!("", options(noreturn)) } "#, ); @@ -1440,7 +1440,7 @@ fn main() { unsafe { let foo = 1; let mut o = 0; - asm!( + core::arch::asm!( "%input = OpLoad _ {0}", concat!("%result = ", bar, " _ %input"), "OpStore {1} %result", @@ -1450,7 +1450,7 @@ fn main() { o let thread_id: usize; - asm!(" + core::arch::asm!(" mov {0}, gs:[0x30] mov {0}, [{0}+0x48] ", out(reg) thread_id, options(pure, readonly, nostack)); @@ -1460,7 +1460,7 @@ fn main() { static VirtualFree: usize; const OffPtr: usize; const OffFn: usize; - asm!(" + core::arch::asm!(" push {free_type} push {free_size} push {base} @@ -1490,16 +1490,16 @@ fn main() { "#, expect![[r#" !0..122 'builti...muto,)': () - !0..190 'builti...tack))': () + !0..136 'builti...tack))': () !0..449 'builti...urn),)': ! - 10..1254 '{ ... } }': () - 16..1252 'unsafe... }': () + 10..1236 '{ ... } }': () + 16..1234 'unsafe... }': () 37..40 'foo': i32 43..44 '1': i32 58..63 'mut o': i32 66..67 '0': i32 - 281..282 'o': i32 - 296..305 'thread_id': usize + 293..294 'o': i32 + 308..317 'thread_id': usize "#]], ) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index f8c8b12bd25af..8c97ebd550032 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -360,6 +360,7 @@ impl CompletionItemKind { SymbolKind::Field => "fd", SymbolKind::Function => "fn", SymbolKind::Impl => "im", + SymbolKind::InlineAsmRegOrRegClass => "ar", SymbolKind::Label => "lb", SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 5d4b1999088a0..3d372bab27471 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -50,6 +50,7 @@ pub enum Definition { BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), ExternCrateDecl(ExternCrateDecl), + InlineAsmRegOrRegClass(()), } impl Definition { @@ -87,7 +88,8 @@ impl Definition { | Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::TupleField(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, }; Some(module) } @@ -121,7 +123,8 @@ impl Definition { | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) - | Definition::DeriveHelper(_) => return None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, }; Some(vis) } @@ -150,6 +153,7 @@ impl Definition { Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), Definition::ExternCrateDecl(it) => return it.alias_or_name(db), + Definition::InlineAsmRegOrRegClass(_) => return None, // FIXME }; Some(name) } @@ -212,6 +216,7 @@ impl Definition { Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, Definition::TupleField(_) => None, + Definition::InlineAsmRegOrRegClass(_) => None, }; docs.or_else(|| { @@ -268,6 +273,8 @@ impl Definition { Definition::DeriveHelper(it) => { format!("derive_helper {}", it.name(db).display(db, edition)) } + // FIXME + Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(), } } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index ab161f0ce5712..3435757ad3182 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -224,6 +224,7 @@ pub enum SymbolKind { Function, Method, Impl, + InlineAsmRegOrRegClass, Label, LifetimeParam, Local, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 262eefeec00e4..3a1a9aba8551c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -205,7 +205,8 @@ impl Definition { | Definition::BuiltinAttr(_) | Definition::SelfType(_) | Definition::ToolModule(_) - | Definition::TupleField(_) => return None, + | Definition::TupleField(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 925ae620231d2..aa632ab12f742 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -219,7 +219,8 @@ pub(crate) fn resolve_doc_path_for_def( | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) - | Definition::DeriveHelper(_) => None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) => None, } .map(Definition::from) } @@ -672,7 +673,8 @@ fn filename_and_frag_for_def( | Definition::BuiltinAttr(_) | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) - | Definition::DeriveHelper(_) => return None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, }; Some((def, res, None)) diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 4be1b570981f6..69c94bc357838 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -228,6 +228,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Definition::BuiltinAttr(..) => Attribute, Definition::ToolModule(..) => Module, Definition::ExternCrateDecl(..) => Module, + Definition::InlineAsmRegOrRegClass(..) => Module, } } @@ -320,7 +321,8 @@ pub(crate) fn def_to_moniker( | Definition::DeriveHelper(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, Definition::Local(local) => { if !local.is_param(db) { diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 9ace9fda62b97..cf1412560ddf2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -242,6 +242,7 @@ impl TryToNav for Definition { | Definition::BuiltinType(_) | Definition::TupleField(_) | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index eeba9cf35c997..2894cb9cf6dce 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -534,6 +534,9 @@ pub(super) fn highlight_def( Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)), + Definition::InlineAsmRegOrRegClass(_) => { + Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass)) + } }; let def_crate = def.krate(db); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index bc1ec530076ce..e2b32c17d66a7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -315,6 +315,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr, Definition::ToolModule(_) => SymbolKind::ToolModule, Definition::DeriveHelper(_) => SymbolKind::DeriveHelper, + Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass, }; HlTag::Symbol(symbol) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index e329023606a1b..3b5d1af0ac72a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -146,6 +146,7 @@ impl HlTag { SymbolKind::Field => "field", SymbolKind::Function => "function", SymbolKind::Impl => "self_type", + SymbolKind::InlineAsmRegOrRegClass => "reg", SymbolKind::Label => "label", SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html new file mode 100644 index 0000000000000..1280d78cc815e --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -0,0 +1,98 @@ + + +
fn main() {
+    unsafe {
+        let foo = 1;
+        let mut o = 0;
+        core::arch::asm!(
+            "%input = OpLoad _ {0}",
+            concat!("%result = ", bar, " _ %input"),
+            "OpStore {1} %result",
+            in(reg) &foo,
+            in(reg) &mut o,
+        );
+        o
+
+        let thread_id: usize;
+        core::arch::asm!("
+            mov {0}, gs:[0x30]
+            mov {0}, [{0}+0x48]
+        ", out(reg) thread_id, options(pure, readonly, nostack));
+
+        static UNMAP_BASE: usize;
+        const MEM_RELEASE: usize;
+        static VirtualFree: usize;
+        const OffPtr: usize;
+        const OffFn: usize;
+        core::arch::asm!("
+            push {free_type}
+            push {free_size}
+            push {base}
+
+            mov eax, fs:[30h]
+            mov eax, [eax+8h]
+            add eax, {off_fn}
+            mov [eax-{off_fn}+{off_ptr}], eax
+
+            push eax
+
+            jmp {virtual_free}
+            ",
+            off_ptr = const OffPtr,
+            off_fn  = const OffFn,
+
+            free_size = const 0,
+            free_type = const MEM_RELEASE,
+
+            virtual_free = sym VirtualFree,
+
+            base = sym UNMAP_BASE,
+            options(noreturn),
+        );
+    }
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 9376ef65a4e6a..ced522e7ebb21 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -165,7 +165,7 @@ toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( + core::arch::asm!( "mov {0}, {1}", "add {0}, 5", out(reg) o, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index c06ea155fb86b..dbe8de8f91ffe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -549,7 +549,7 @@ fn main() { toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( + core::arch::asm!( "mov {0}, {1}", "add {0}, 5", out(reg) o, @@ -1275,3 +1275,65 @@ fn f<'de, T: Deserialize<'de>>() { ); let _ = analysis.highlight(HL_CONFIG, file_id).unwrap(); } + +#[test] +fn test_asm_highlighting() { + check_highlighting( + r#" +//- minicore: asm, concat +fn main() { + unsafe { + let foo = 1; + let mut o = 0; + core::arch::asm!( + "%input = OpLoad _ {0}", + concat!("%result = ", bar, " _ %input"), + "OpStore {1} %result", + in(reg) &foo, + in(reg) &mut o, + ); + o + + let thread_id: usize; + core::arch::asm!(" + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] + ", out(reg) thread_id, options(pure, readonly, nostack)); + + static UNMAP_BASE: usize; + const MEM_RELEASE: usize; + static VirtualFree: usize; + const OffPtr: usize; + const OffFn: usize; + core::arch::asm!(" + push {free_type} + push {free_size} + push {base} + + mov eax, fs:[30h] + mov eax, [eax+8h] + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax + + push eax + + jmp {virtual_free} + ", + off_ptr = const OffPtr, + off_fn = const OffFn, + + free_size = const 0, + free_type = const MEM_RELEASE, + + virtual_free = sym VirtualFree, + + base = sym UNMAP_BASE, + options(noreturn), + ); + } +} +"#, + expect_file!["./test_data/highlight_asm.html"], + false, + ); +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index b29268f133f3a..4902c9f88c143 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -80,6 +80,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { | SymbolKind::ValueParam | SymbolKind::Label => lsp_types::SymbolKind::VARIABLE, SymbolKind::Union => lsp_types::SymbolKind::STRUCT, + SymbolKind::InlineAsmRegOrRegClass => lsp_types::SymbolKind::VARIABLE, } } @@ -159,6 +160,7 @@ pub(crate) fn completion_item_kind( SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER, SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE, + SymbolKind::InlineAsmRegOrRegClass => lsp_types::CompletionItemKind::KEYWORD, }, } } @@ -702,6 +704,7 @@ fn semantic_token_type_and_modifiers( SymbolKind::ProcMacro => types::PROC_MACRO, SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE, SymbolKind::ToolModule => types::TOOL_MODULE, + SymbolKind::InlineAsmRegOrRegClass => types::KEYWORD, }, HlTag::AttributeBracket => types::ATTRIBUTE_BRACKET, HlTag::BoolLiteral => types::BOOLEAN, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 4b7e23388c7e9..fec270a556045 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1475,6 +1475,19 @@ mod panicking { } // endregion:panic +// region:asm +mod arch { + #[rustc_builtin_macro] + pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ + } + #[rustc_builtin_macro] + pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ + } +} +// endregion:asm + #[macro_use] mod macros { // region:panic @@ -1487,16 +1500,6 @@ mod macros { } // endregion:panic - // region:asm - #[macro_export] - #[rustc_builtin_macro] - macro_rules! asm { - ($($arg:tt)*) => { - /* compiler built-in */ - }; - } - // endregion:asm - // region:assert #[macro_export] #[rustc_builtin_macro] From 304f54e023cf5784163a319b22316950c71c58f2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 10:53:07 +0200 Subject: [PATCH 0281/1301] Add Definition kind for asm register operand --- .../rust-analyzer/crates/ide-db/src/defs.rs | 17 ++++++--- .../rust-analyzer/crates/ide-db/src/rename.rs | 2 ++ .../rust-analyzer/crates/ide/src/doc_links.rs | 6 ++-- .../rust-analyzer/crates/ide/src/moniker.rs | 5 +-- .../crates/ide/src/navigation_target.rs | 2 ++ .../ide/src/syntax_highlighting/highlight.rs | 1 + .../ide/src/syntax_highlighting/inject.rs | 1 + .../test_data/highlight_asm.html | 9 +++-- .../test_data/highlight_strings.html | 4 +-- .../ide/src/syntax_highlighting/tests.rs | 3 +- .../parser/src/grammar/expressions/atom.rs | 8 +++-- .../test_data/parser/inline/ok/asm_expr.rast | 36 ++++++++++--------- 12 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 3d372bab27471..469f83644fc5f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -51,6 +51,7 @@ pub enum Definition { ToolModule(ToolModule), ExternCrateDecl(ExternCrateDecl), InlineAsmRegOrRegClass(()), + InlineAsmRegOperand(()), } impl Definition { @@ -89,7 +90,8 @@ impl Definition { | Definition::BuiltinLifetime(_) | Definition::TupleField(_) | Definition::ToolModule(_) - | Definition::InlineAsmRegOrRegClass(_) => return None, + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmRegOperand(_) => return None, }; Some(module) } @@ -124,7 +126,8 @@ impl Definition { | Definition::GenericParam(_) | Definition::Label(_) | Definition::DeriveHelper(_) - | Definition::InlineAsmRegOrRegClass(_) => return None, + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmRegOperand(_) => return None, }; Some(vis) } @@ -153,7 +156,9 @@ impl Definition { Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), Definition::ExternCrateDecl(it) => return it.alias_or_name(db), - Definition::InlineAsmRegOrRegClass(_) => return None, // FIXME + Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmRegOperand(_) => { + return None + } // FIXME }; Some(name) } @@ -216,7 +221,7 @@ impl Definition { Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, Definition::TupleField(_) => None, - Definition::InlineAsmRegOrRegClass(_) => None, + Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmRegOperand(_) => None, }; docs.or_else(|| { @@ -275,6 +280,7 @@ impl Definition { } // FIXME Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(), + Definition::InlineAsmRegOperand(_) => "inline_asm_reg_operand".to_owned(), } } } @@ -706,6 +712,9 @@ impl NameRefClass { NameRefClass::ExternCrateShorthand { krate, decl: extern_crate } }) }, + ast::AsmRegSpec(_) => { + Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()))) + }, _ => None } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 3a1a9aba8551c..e8299a4a46347 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -207,6 +207,8 @@ impl Definition { | Definition::ToolModule(_) | Definition::TupleField(_) | Definition::InlineAsmRegOrRegClass(_) => return None, + // FIXME: + Definition::InlineAsmRegOperand(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index aa632ab12f742..184486ec0a1e7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -220,7 +220,8 @@ pub(crate) fn resolve_doc_path_for_def( | Definition::GenericParam(_) | Definition::Label(_) | Definition::DeriveHelper(_) - | Definition::InlineAsmRegOrRegClass(_) => None, + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmRegOperand(_) => None, } .map(Definition::from) } @@ -674,7 +675,8 @@ fn filename_and_frag_for_def( | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) | Definition::DeriveHelper(_) - | Definition::InlineAsmRegOrRegClass(_) => return None, + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmRegOperand(_) => return None, }; Some((def, res, None)) diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 69c94bc357838..1f996f716d0eb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -223,7 +223,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Variable } } - Definition::Label(..) => Variable, // For lack of a better variant + Definition::Label(..) | Definition::InlineAsmRegOperand(_) => Variable, // For lack of a better variant Definition::DeriveHelper(..) => Attribute, Definition::BuiltinAttr(..) => Attribute, Definition::ToolModule(..) => Module, @@ -322,7 +322,8 @@ pub(crate) fn def_to_moniker( | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) | Definition::ToolModule(_) - | Definition::InlineAsmRegOrRegClass(_) => return None, + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmRegOperand(_) => return None, Definition::Local(local) => { if !local.is_param(db) { diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index cf1412560ddf2..eb28b78e60ca5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -244,6 +244,8 @@ impl TryToNav for Definition { | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, + // FIXME + Definition::InlineAsmRegOperand(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 2894cb9cf6dce..fe1d0b452aede 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -537,6 +537,7 @@ pub(super) fn highlight_def( Definition::InlineAsmRegOrRegClass(_) => { Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass)) } + Definition::InlineAsmRegOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)), }; let def_crate = def.krate(db); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index e2b32c17d66a7..bff52943afdf5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -316,6 +316,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::ToolModule(_) => SymbolKind::ToolModule, Definition::DeriveHelper(_) => SymbolKind::DeriveHelper, Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass, + Definition::InlineAsmRegOperand(_) => SymbolKind::Local, }; HlTag::Symbol(symbol) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index 1280d78cc815e..9cedf012f7fde 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -51,18 +51,17 @@ let mut o = 0; core::arch::asm!( "%input = OpLoad _ {0}", - concat!("%result = ", bar, " _ %input"), + concat!("%result = ", "bar", " _ %input"), "OpStore {1} %result", - in(reg) &foo, - in(reg) &mut o, + in(reg) &foo, + in(reg) &mut o, ); - o let thread_id: usize; core::arch::asm!(" mov {0}, gs:[0x30] mov {0}, [{0}+0x48] - ", out(reg) thread_id, options(pure, readonly, nostack)); + ", out(reg) thread_id, options(pure, readonly, nostack)); static UNMAP_BASE: usize; const MEM_RELEASE: usize; diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index ced522e7ebb21..b387808b297cd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -168,8 +168,8 @@ core::arch::asm!( "mov {0}, {1}", "add {0}, 5", - out(reg) o, - in(reg) i, + out(reg) o, + in(reg) i, ); const CONSTANT: () = (): diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index dbe8de8f91ffe..f47b2115bfc68 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -1287,12 +1287,11 @@ fn main() { let mut o = 0; core::arch::asm!( "%input = OpLoad _ {0}", - concat!("%result = ", bar, " _ %input"), + concat!("%result = ", "bar", " _ %input"), "OpStore {1} %result", in(reg) &foo, in(reg) &mut o, ); - o let thread_id: usize; core::arch::asm!(" diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index f98ca5f403225..d6ccf38e7436e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -453,9 +453,13 @@ fn parse_clobber_abi(p: &mut Parser<'_>) { fn parse_reg(p: &mut Parser<'_>) { p.expect(T!['(']); if p.at(T![ident]) { - name_ref(p) + let m = p.start(); + name_ref(p); + m.complete(p, ASM_REG_SPEC); } else if p.at(T![string]) { - p.bump_any() + let m = p.start(); + p.bump_any(); + m.complete(p, ASM_REG_SPEC); } else { p.err_and_bump("expected register name"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast index f4d53fa9ae657..2360135db7cfe 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast @@ -35,16 +35,18 @@ SOURCE_FILE STRING "\"add {x}, {tmp}\"" COMMA "," WHITESPACE "\n " - NAME - IDENT "x" - WHITESPACE " " - EQ "=" - WHITESPACE " " ASM_REG_OPERAND - INOUT_KW "inout" + NAME + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_DIR_SPEC + INOUT_KW "inout" L_PAREN "(" - NAME_REF - IDENT "reg" + ASM_REG_SPEC + NAME_REF + IDENT "reg" R_PAREN ")" WHITESPACE " " PATH_EXPR @@ -54,16 +56,18 @@ SOURCE_FILE IDENT "x" COMMA "," WHITESPACE "\n " - NAME - IDENT "tmp" - WHITESPACE " " - EQ "=" - WHITESPACE " " ASM_REG_OPERAND - OUT_KW "out" + NAME + IDENT "tmp" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_DIR_SPEC + OUT_KW "out" L_PAREN "(" - NAME_REF - IDENT "reg" + ASM_REG_SPEC + NAME_REF + IDENT "reg" R_PAREN ")" WHITESPACE " " UNDERSCORE_EXPR From f3b6965f90e764fc29ff627a6965ccfc68d38ead Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 12:40:41 +0200 Subject: [PATCH 0282/1301] Give InlineAsmOperand a HIR representation --- .../rust-analyzer/crates/hir-def/src/body.rs | 27 +++- .../crates/hir-def/src/body/lower.rs | 2 +- .../crates/hir-def/src/body/lower/asm.rs | 148 ++++++++++-------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 9 +- .../rust-analyzer/crates/hir/src/semantics.rs | 69 +++++--- .../crates/hir/src/semantics/source_to_def.rs | 21 ++- .../crates/hir/src/source_analyzer.rs | 22 +++ .../rust-analyzer/crates/ide-db/src/defs.rs | 32 ++-- .../rust-analyzer/crates/ide-db/src/rename.rs | 2 +- .../rust-analyzer/crates/ide-db/src/search.rs | 29 ++-- .../rust-analyzer/crates/ide/src/doc_links.rs | 4 +- .../rust-analyzer/crates/ide/src/moniker.rs | 4 +- .../crates/ide/src/navigation_target.rs | 2 +- .../ide/src/syntax_highlighting/format.rs | 24 +-- .../ide/src/syntax_highlighting/highlight.rs | 2 +- .../ide/src/syntax_highlighting/inject.rs | 2 +- .../test_data/highlight_asm.html | 32 ++-- .../test_data/highlight_strings.html | 8 +- .../parser/src/grammar/expressions/atom.rs | 16 +- .../parser/src/syntax_kind/generated.rs | 2 + .../test_data/parser/inline/ok/asm_expr.rast | 52 +++--- .../rust-analyzer/crates/syntax/rust.ungram | 8 +- .../crates/syntax/src/ast/generated/nodes.rs | 109 ++++++++++--- 23 files changed, 409 insertions(+), 217 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index f5fe8f8770130..03507189fb99a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -100,7 +100,14 @@ pub struct BodySourceMap { field_map_back: FxHashMap, pat_field_map_back: FxHashMap, - format_args_template_map: FxHashMap>, + template_map: Option< + Box<( + // format_args! + FxHashMap>, + // asm! + FxHashMap>, + )>, + >, expansions: FxHashMap>, MacroFileId>, @@ -426,7 +433,16 @@ impl BodySourceMap { node: InFile<&ast::FormatArgsExpr>, ) -> Option<&[(syntax::TextRange, Name)]> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); - self.format_args_template_map.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref) + self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref) + } + + pub fn asm_template_args( + &self, + node: InFile<&ast::AsmExpr>, + ) -> Option<(ExprId, &[(syntax::TextRange, usize)])> { + let src = node.map(AstPtr::new).map(AstPtr::upcast::); + let expr = self.expr_map.get(&src)?; + Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref)) } /// Get a reference to the body source map's diagnostics. @@ -446,11 +462,14 @@ impl BodySourceMap { field_map_back, pat_field_map_back, expansions, - format_args_template_map, + template_map, diagnostics, binding_definitions, } = self; - format_args_template_map.shrink_to_fit(); + if let Some(template_map) = template_map { + template_map.0.shrink_to_fit(); + template_map.1.shrink_to_fit(); + } expr_map.shrink_to_fit(); expr_map_back.shrink_to_fit(); pat_map.shrink_to_fit(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index dfcb8b81204d2..9c547574ecb1f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1847,7 +1847,7 @@ impl ExprCollector<'_> { }, syntax_ptr, ); - self.source_map.format_args_template_map.insert(idx, mappings); + self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings); idx } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index dafa3a859debd..59b348b77db00 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -1,4 +1,3 @@ -use hir_expand::name::Name; use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ @@ -25,7 +24,7 @@ impl ExprCollector<'_> { let mut named_pos: FxHashMap = Default::default(); let mut named_args: FxHashMap = Default::default(); let mut reg_args: FxHashSet = Default::default(); - for operand in asm.asm_operands() { + for piece in asm.asm_pieces() { let slot = operands.len(); let mut lower_reg = |reg: Option| { let reg = reg?; @@ -39,14 +38,14 @@ impl ExprCollector<'_> { } }; - let op = match operand { - ast::AsmOperand::AsmClobberAbi(clobber_abi) => { + let op = match piece { + ast::AsmPiece::AsmClobberAbi(clobber_abi) => { if let Some(abi_name) = clobber_abi.string_token() { clobber_abis.insert(Symbol::intern(abi_name.text())); } continue; } - ast::AsmOperand::AsmOptions(opt) => { + ast::AsmPiece::AsmOptions(opt) => { opt.asm_options().for_each(|opt| { options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) { T![att_syntax] => AsmOptions::ATT_SYNTAX, @@ -63,71 +62,82 @@ impl ExprCollector<'_> { }); continue; } - ast::AsmOperand::AsmRegOperand(op) => { - let Some(dir_spec) = op.asm_dir_spec() else { - continue; - }; - let Some(reg) = lower_reg(op.asm_reg_spec()) else { - continue; - }; + ast::AsmPiece::AsmOperandNamed(op) => { if let Some(name) = op.name() { let sym = Symbol::intern(&name.text()); named_args.insert(sym.clone(), slot); named_pos.insert(slot, sym); } - if dir_spec.in_token().is_some() { - let expr = self - .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr())); - AsmOperand::In { reg, expr } - } else if dir_spec.out_token().is_some() { - let expr = self - .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr())); - AsmOperand::Out { reg, expr: Some(expr), late: false } - } else if dir_spec.lateout_token().is_some() { - let expr = self - .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr())); - AsmOperand::Out { reg, expr: Some(expr), late: true } - } else if dir_spec.inout_token().is_some() { - let Some(op_expr) = op.asm_operand_expr() else { continue }; - let in_expr = self.collect_expr_opt(op_expr.in_expr()); - let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); - match out_expr { - Some(out_expr) => AsmOperand::SplitInOut { - reg, - in_expr, - out_expr: Some(out_expr), - late: false, - }, - None => AsmOperand::InOut { reg, expr: in_expr, late: false }, + let Some(op) = op.asm_operand() else { continue }; + match op { + ast::AsmOperand::AsmRegOperand(op) => { + let Some(dir_spec) = op.asm_dir_spec() else { + continue; + }; + let Some(reg) = lower_reg(op.asm_reg_spec()) else { + continue; + }; + if dir_spec.in_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::In { reg, expr } + } else if dir_spec.out_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::Out { reg, expr: Some(expr), late: false } + } else if dir_spec.lateout_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::Out { reg, expr: Some(expr), late: true } + } else if dir_spec.inout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); + match out_expr { + Some(out_expr) => AsmOperand::SplitInOut { + reg, + in_expr, + out_expr: Some(out_expr), + late: false, + }, + None => AsmOperand::InOut { reg, expr: in_expr, late: false }, + } + } else if dir_spec.inlateout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); + match out_expr { + Some(out_expr) => AsmOperand::SplitInOut { + reg, + in_expr, + out_expr: Some(out_expr), + late: false, + }, + None => AsmOperand::InOut { reg, expr: in_expr, late: false }, + } + } else { + continue; + } } - } else if dir_spec.inlateout_token().is_some() { - let Some(op_expr) = op.asm_operand_expr() else { continue }; - let in_expr = self.collect_expr_opt(op_expr.in_expr()); - let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); - match out_expr { - Some(out_expr) => AsmOperand::SplitInOut { - reg, - in_expr, - out_expr: Some(out_expr), - late: false, - }, - None => AsmOperand::InOut { reg, expr: in_expr, late: false }, + ast::AsmOperand::AsmLabel(l) => { + AsmOperand::Label(self.collect_block_opt(l.block_expr())) + } + ast::AsmOperand::AsmConst(c) => { + AsmOperand::Const(self.collect_expr_opt(c.expr())) + } + ast::AsmOperand::AsmSym(s) => { + let Some(path) = + s.path().and_then(|p| self.expander.parse_path(self.db, p)) + else { + continue; + }; + AsmOperand::Sym(path) } - } else { - continue; } } - ast::AsmOperand::AsmLabel(l) => { - AsmOperand::Label(self.collect_block_opt(l.block_expr())) - } - ast::AsmOperand::AsmConst(c) => AsmOperand::Const(self.collect_expr_opt(c.expr())), - ast::AsmOperand::AsmSym(s) => { - let Some(path) = s.path().and_then(|p| self.expander.parse_path(self.db, p)) - else { - continue; - }; - AsmOperand::Sym(path) - } }; operands.push(op); } @@ -192,7 +202,7 @@ impl ExprCollector<'_> { rustc_parse_format::Piece::NextArgument(arg) => { // let span = arg_spans.next(); - let _operand_idx = match arg.position { + let operand_idx = match arg.position { rustc_parse_format::ArgumentIs(idx) | rustc_parse_format::ArgumentImplicitlyIs(idx) => { if idx >= operands.len() @@ -206,15 +216,15 @@ impl ExprCollector<'_> { } rustc_parse_format::ArgumentNamed(name) => { let name = Symbol::intern(name); - if let Some(position_span) = to_span(arg.position_span) { - mappings.push(( - position_span, - Name::new_symbol_root(name.clone()), - )); - } named_args.get(&name).copied() } }; + + if let Some(operand_idx) = operand_idx { + if let Some(position_span) = to_span(arg.position_span) { + mappings.push((position_span, operand_idx)); + } + } } } } @@ -224,7 +234,7 @@ impl ExprCollector<'_> { Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }), syntax_ptr, ); - self.source_map.format_args_template_map.insert(idx, mappings); + self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings); idx } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 81d6466c2f31d..c8b227123f1c1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -43,7 +43,7 @@ use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -5246,6 +5246,13 @@ impl Type { } } +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub struct InlineAsmOperand { + owner: DefWithBodyId, + expr: ExprId, + index: usize, +} + // FIXME: Document this #[derive(Debug)] pub struct Callable { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 4e6887295c8a3..0ba0e446578e7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -47,9 +47,9 @@ use crate::{ source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, - ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, - ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, - TypeParam, Union, Variant, VariantDef, + InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, + OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, + Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; const CONTINUE_NO_BREAKS: ControlFlow = ControlFlow::Continue(()); @@ -546,11 +546,11 @@ impl<'db> SemanticsImpl<'db> { ) } - /// Retrieves all the formatting parts of the format_args! template string. + /// Retrieves all the formatting parts of the format_args! (or `asm!`) template string. pub fn as_format_args_parts( &self, string: &ast::String, - ) -> Option)>> { + ) -> Option>)>> { let quote = string.open_quote_text_range()?; let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?; @@ -560,14 +560,31 @@ impl<'db> SemanticsImpl<'db> { let string = ast::String::cast(token)?; let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - let res = source_analyzer - .as_format_args_parts(self.db, format_args.as_ref())? - .map(|(range, res)| (range + quote.end(), res)) - .collect(); - Some(res) + let parent = literal.parent()?; + if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) { + let source_analyzer = self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + let res = source_analyzer + .as_format_args_parts(self.db, format_args.as_ref())? + .map(|(range, res)| (range + quote.end(), res.map(Either::Left))) + .collect(); + Some(res) + } else { + let asm = ast::AsmExpr::cast(parent)?; + let source_analyzer = self.analyze_no_infer(asm.syntax())?; + let asm = self.wrap_node_infile(asm); + let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?; + let res = asm_parts + .iter() + .map(|&(range, index)| { + ( + range + quote.end(), + Some(Either::Right(InlineAsmOperand { owner, expr, index })), + ) + }) + .collect(); + Some(res) + } })() .map_or(ControlFlow::Continue(()), ControlFlow::Break) }) @@ -578,7 +595,7 @@ impl<'db> SemanticsImpl<'db> { &self, original_token: SyntaxToken, offset: TextSize, - ) -> Option<(TextRange, Option)> { + ) -> Option<(TextRange, Option>)> { let original_string = ast::String::cast(original_token.clone())?; let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?; let quote = original_string.open_quote_text_range()?; @@ -599,13 +616,26 @@ impl<'db> SemanticsImpl<'db> { &self, string: ast::String, offset: TextSize, - ) -> Option<(TextRange, Option)> { + ) -> Option<(TextRange, Option>)> { debug_assert!(offset <= string.syntax().text_range().len()); let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + let parent = literal.parent()?; + if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) { + let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + source_analyzer + .resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + .map(|(range, res)| (range, res.map(Either::Left))) + } else { + let asm = ast::AsmExpr::cast(parent)?; + let source_analyzer = &self.analyze_no_infer(asm.syntax())?; + let asm = self.wrap_node_infile(asm); + source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), offset).map( + |(owner, (expr, range, index))| { + (range, Some(Either::Right(InlineAsmOperand { owner, expr, index }))) + }, + ) + } } /// Maps a node down by mapping its first and last token down. @@ -1781,6 +1811,7 @@ to_def_impls![ (crate::Label, ast::Label, label_to_def), (crate::Adt, ast::Adt, adt_to_def), (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def), + (crate::InlineAsmOperand, ast::AsmOperandNamed, asm_operand_to_def), (MacroCallId, ast::MacroCall, macro_call_to_macro_call), ]; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index edeb19030ac00..fc629b9c6d077 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -110,7 +110,7 @@ use syntax::{ AstNode, AstPtr, SyntaxNode, }; -use crate::{db::HirDatabase, InFile}; +use crate::{db::HirDatabase, InFile, InlineAsmOperand}; #[derive(Default)] pub(super) struct SourceToDefCache { @@ -273,6 +273,25 @@ impl SourceToDefCtx<'_, '_> { ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId), } } + + pub(super) fn asm_operand_to_def( + &mut self, + src: InFile<&ast::AsmOperandNamed>, + ) -> Option { + let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?; + let index = asm + .asm_pieces() + .filter_map(|it| match it { + ast::AsmPiece::AsmOperandNamed(it) => Some(it), + _ => None, + }) + .position(|it| it == *src.value)?; + let container = self.find_pat_or_label_container(src.syntax_ref())?; + let (_, source_map) = self.db.body_with_source_map(container); + let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?; + Some(InlineAsmOperand { owner: container, expr, index }) + } + pub(super) fn bind_pat_to_def( &mut self, src: InFile<&ast::IdentPat>, diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index be0116862b9cf..f6f1da1b7d6bd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -904,6 +904,20 @@ impl SourceAnalyzer { }) } + pub(crate) fn resolve_offset_in_asm_template( + &self, + asm: InFile<&ast::AsmExpr>, + offset: TextSize, + ) -> Option<(DefWithBodyId, (ExprId, TextRange, usize))> { + let (def, _, body_source_map) = self.def.as_ref()?; + let (expr, args) = body_source_map.asm_template_args(asm)?; + Some(*def).zip( + args.iter() + .find(|(range, _)| range.contains_inclusive(offset)) + .map(|(range, idx)| (expr, *range, *idx)), + ) + } + pub(crate) fn as_format_args_parts<'a>( &'a self, db: &'a dyn HirDatabase, @@ -927,6 +941,14 @@ impl SourceAnalyzer { )) } + pub(crate) fn as_asm_parts( + &self, + asm: InFile<&ast::AsmExpr>, + ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> { + let (def, _, body_source_map) = self.def.as_ref()?; + Some(*def).zip(body_source_map.asm_template_args(asm)) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 469f83644fc5f..c134c9f672543 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -10,8 +10,8 @@ use either::Either; use hir::{ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field, - Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, - ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, + Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro, + Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; @@ -51,7 +51,7 @@ pub enum Definition { ToolModule(ToolModule), ExternCrateDecl(ExternCrateDecl), InlineAsmRegOrRegClass(()), - InlineAsmRegOperand(()), + InlineAsmOperand(InlineAsmOperand), } impl Definition { @@ -91,7 +91,7 @@ impl Definition { | Definition::TupleField(_) | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmRegOperand(_) => return None, + | Definition::InlineAsmOperand(_) => return None, }; Some(module) } @@ -127,7 +127,7 @@ impl Definition { | Definition::Label(_) | Definition::DeriveHelper(_) | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmRegOperand(_) => return None, + | Definition::InlineAsmOperand(_) => return None, }; Some(vis) } @@ -156,9 +156,7 @@ impl Definition { Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), Definition::ExternCrateDecl(it) => return it.alias_or_name(db), - Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmRegOperand(_) => { - return None - } // FIXME + Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => return None, // FIXME }; Some(name) } @@ -221,7 +219,7 @@ impl Definition { Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, Definition::TupleField(_) => None, - Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmRegOperand(_) => None, + Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => None, }; docs.or_else(|| { @@ -280,7 +278,7 @@ impl Definition { } // FIXME Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(), - Definition::InlineAsmRegOperand(_) => "inline_asm_reg_operand".to_owned(), + Definition::InlineAsmOperand(_) => "inline_asm_reg_operand".to_owned(), } } } @@ -442,7 +440,6 @@ impl NameClass { let _p = tracing::info_span!("NameClass::classify").entered(); let parent = name.syntax().parent()?; - let definition = match_ast! { match parent { ast::Item(it) => classify_item(sema, it)?, @@ -453,6 +450,7 @@ impl NameClass { ast::Variant(it) => Definition::Variant(sema.to_def(&it)?), ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), + ast::AsmOperandNamed(it) => Definition::InlineAsmOperand(sema.to_def(&it)?), _ => return None, } }; @@ -769,6 +767,18 @@ impl From for Definition { } } +impl From for Definition { + fn from(value: InlineAsmOperand) -> Self { + Definition::InlineAsmOperand(value) + } +} + +impl From> for Definition { + fn from(value: Either) -> Self { + value.either(Definition::from, Definition::from) + } +} + impl AsAssocItem for Definition { fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option { match self { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index e8299a4a46347..0435b2f0c6fce 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -208,7 +208,7 @@ impl Definition { | Definition::TupleField(_) | Definition::InlineAsmRegOrRegClass(_) => return None, // FIXME: - Definition::InlineAsmRegOperand(_) => return None, + Definition::InlineAsmOperand(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 12ce5a403fe87..4d0668ffea8df 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -8,10 +8,11 @@ use std::mem; use std::{cell::LazyCell, cmp::Reverse}; use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase}; +use either::Either; use hir::{ sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer, - HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, ItemContainer, ModuleSource, - PathResolution, Semantics, Visibility, + HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer, + ModuleSource, PathResolution, Semantics, Visibility, }; use memchr::memmem::Finder; use parser::SyntaxKind; @@ -917,7 +918,7 @@ impl<'a> FindUsages<'a> { for offset in Self::match_indices(&text, finder, search_range) { tree.token_at_offset(offset).for_each(|token| { let Some(str_token) = ast::String::cast(token.clone()) else { return }; - if let Some((range, nameres)) = + if let Some((range, Some(nameres))) = sema.check_for_format_args_template(token, offset) { if self.found_format_args_ref(file_id, range, str_token, nameres, sink) {} @@ -1087,19 +1088,19 @@ impl<'a> FindUsages<'a> { file_id: EditionedFileId, range: TextRange, token: ast::String, - res: Option, + res: Either, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { - match res.map(Definition::from) { - Some(def) if def == self.def => { - let reference = FileReference { - range, - name: FileReferenceNode::FormatStringEntry(token, range), - category: ReferenceCategory::READ, - }; - sink(file_id, reference) - } - _ => false, + let def = res.either(Definition::from, Definition::from); + if def == self.def { + let reference = FileReference { + range, + name: FileReferenceNode::FormatStringEntry(token, range), + category: ReferenceCategory::READ, + }; + sink(file_id, reference) + } else { + false } } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 184486ec0a1e7..ea16a11d56d02 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -221,7 +221,7 @@ pub(crate) fn resolve_doc_path_for_def( | Definition::Label(_) | Definition::DeriveHelper(_) | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmRegOperand(_) => None, + | Definition::InlineAsmOperand(_) => None, } .map(Definition::from) } @@ -676,7 +676,7 @@ fn filename_and_frag_for_def( | Definition::ToolModule(_) | Definition::DeriveHelper(_) | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmRegOperand(_) => return None, + | Definition::InlineAsmOperand(_) => return None, }; Some((def, res, None)) diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 1f996f716d0eb..14781b2129693 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -223,7 +223,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Variable } } - Definition::Label(..) | Definition::InlineAsmRegOperand(_) => Variable, // For lack of a better variant + Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, // For lack of a better variant Definition::DeriveHelper(..) => Attribute, Definition::BuiltinAttr(..) => Attribute, Definition::ToolModule(..) => Module, @@ -323,7 +323,7 @@ pub(crate) fn def_to_moniker( | Definition::BuiltinAttr(_) | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmRegOperand(_) => return None, + | Definition::InlineAsmOperand(_) => return None, Definition::Local(local) => { if !local.is_param(db) { diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index eb28b78e60ca5..68039ef309d3b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -245,7 +245,7 @@ impl TryToNav for Definition { | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, // FIXME - Definition::InlineAsmRegOperand(_) => None, + Definition::InlineAsmOperand(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 518e71454798f..7234108701a34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -19,21 +19,21 @@ pub(super) fn highlight_format_string( expanded_string: &ast::String, range: TextRange, ) { - if !is_format_string(expanded_string) { + if is_format_string(expanded_string) { + // FIXME: Replace this with the HIR info we have now. + lex_format_specifiers(string, &mut |piece_range, kind| { + if let Some(highlight) = highlight_format_specifier(kind) { + stack.add(HlRange { + range: piece_range + range.start(), + highlight: highlight.into(), + binding_hash: None, + }); + } + }); + return; } - // FIXME: Replace this with the HIR info we have now. - lex_format_specifiers(string, &mut |piece_range, kind| { - if let Some(highlight) = highlight_format_specifier(kind) { - stack.add(HlRange { - range: piece_range + range.start(), - highlight: highlight.into(), - binding_hash: None, - }); - } - }); - if let Some(parts) = sema.as_format_args_parts(string) { parts.into_iter().for_each(|(range, res)| { if let Some(res) = res { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index fe1d0b452aede..96375937a12e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -537,7 +537,7 @@ pub(super) fn highlight_def( Definition::InlineAsmRegOrRegClass(_) => { Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass)) } - Definition::InlineAsmRegOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)), + Definition::InlineAsmOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)), }; let def_crate = def.krate(db); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index bff52943afdf5..5583f1bc8df92 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -316,7 +316,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::ToolModule(_) => SymbolKind::ToolModule, Definition::DeriveHelper(_) => SymbolKind::DeriveHelper, Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass, - Definition::InlineAsmRegOperand(_) => SymbolKind::Local, + Definition::InlineAsmOperand(_) => SymbolKind::Local, }; HlTag::Symbol(symbol) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index 9cedf012f7fde..d322a374b3420 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -50,17 +50,17 @@ let foo = 1; let mut o = 0; core::arch::asm!( - "%input = OpLoad _ {0}", + "%input = OpLoad _ {0}", concat!("%result = ", "bar", " _ %input"), - "OpStore {1} %result", + "OpStore {1} %result", in(reg) &foo, in(reg) &mut o, ); let thread_id: usize; core::arch::asm!(" - mov {0}, gs:[0x30] - mov {0}, [{0}+0x48] + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] ", out(reg) thread_id, options(pure, readonly, nostack)); static UNMAP_BASE: usize; @@ -69,28 +69,28 @@ const OffPtr: usize; const OffFn: usize; core::arch::asm!(" - push {free_type} - push {free_size} - push {base} + push {free_type} + push {free_size} + push {base} mov eax, fs:[30h] mov eax, [eax+8h] - add eax, {off_fn} - mov [eax-{off_fn}+{off_ptr}], eax + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax push eax - jmp {virtual_free} + jmp {virtual_free} ", - off_ptr = const OffPtr, - off_fn = const OffFn, + off_ptr = const OffPtr, + off_fn = const OffFn, - free_size = const 0, - free_type = const MEM_RELEASE, + free_size = const 0, + free_type = const MEM_RELEASE, - virtual_free = sym VirtualFree, + virtual_free = sym VirtualFree, - base = sym UNMAP_BASE, + base = sym UNMAP_BASE, options(noreturn), ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index b387808b297cd..37bbc1e1ee211 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -166,8 +166,8 @@ let i: u64 = 3; let o: u64; core::arch::asm!( - "mov {0}, {1}", - "add {0}, 5", + "mov {0}, {1}", + "add {0}, 5", out(reg) o, in(reg) i, ); @@ -175,6 +175,6 @@ const CONSTANT: () = (): let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); - reuse_twice!("{backslash}"); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + reuse_twice!("{backslash}"); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index d6ccf38e7436e..39ca26fc508c4 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -329,11 +329,11 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { break; } - let op = p.start(); + let op_n = p.start(); // Parse clobber_abi if p.eat_contextual_kw(T![clobber_abi]) { parse_clobber_abi(p); - op.complete(p, ASM_CLOBBER_ABI); + op_n.complete(p, ASM_CLOBBER_ABI); allow_templates = false; continue; } @@ -341,7 +341,7 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { // Parse options if p.eat_contextual_kw(T![options]) { parse_options(p); - op.complete(p, ASM_OPTIONS); + op_n.complete(p, ASM_OPTIONS); allow_templates = false; continue; } @@ -356,12 +356,14 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { false }; + let op = p.start(); let dir_spec = p.start(); if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) { dir_spec.complete(p, ASM_DIR_SPEC); parse_reg(p); expr(p); op.complete(p, ASM_REG_OPERAND); + op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) { dir_spec.complete(p, ASM_DIR_SPEC); parse_reg(p); @@ -370,21 +372,26 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { expr(p); } op.complete(p, ASM_REG_OPERAND); + op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat_contextual_kw(T![label]) { dir_spec.abandon(p); block_expr(p); - op.complete(p, ASM_LABEL); + op.complete(p, ASM_OPERAND_NAMED); + op_n.complete(p, ASM_LABEL); } else if p.eat(T![const]) { dir_spec.abandon(p); expr(p); op.complete(p, ASM_CONST); + op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat_contextual_kw(T![sym]) { dir_spec.abandon(p); paths::type_path(p); op.complete(p, ASM_SYM); + op_n.complete(p, ASM_OPERAND_NAMED); } else if allow_templates { dir_spec.abandon(p); op.abandon(p); + op_n.abandon(p); if expr(p).is_none() { p.err_and_bump("expected asm template"); } @@ -392,6 +399,7 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { } else { dir_spec.abandon(p); op.abandon(p); + op_n.abandon(p); p.err_and_bump("expected asm operand"); if p.at(T!['}']) { break; diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index ee3adac158f41..288a07ef44dc6 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -169,8 +169,10 @@ pub enum SyntaxKind { ASM_LABEL, ASM_OPERAND, ASM_OPERAND_EXPR, + ASM_OPERAND_NAMED, ASM_OPTION, ASM_OPTIONS, + ASM_PIECE, ASM_REG_OPERAND, ASM_REG_SPEC, ASM_SYM, diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast index 2360135db7cfe..4afa9daf5904b 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast @@ -35,43 +35,45 @@ SOURCE_FILE STRING "\"add {x}, {tmp}\"" COMMA "," WHITESPACE "\n " - ASM_REG_OPERAND + ASM_OPERAND_NAMED NAME IDENT "x" WHITESPACE " " EQ "=" WHITESPACE " " - ASM_DIR_SPEC - INOUT_KW "inout" - L_PAREN "(" - ASM_REG_SPEC - NAME_REF - IDENT "reg" - R_PAREN ")" - WHITESPACE " " - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "x" + ASM_REG_OPERAND + ASM_DIR_SPEC + INOUT_KW "inout" + L_PAREN "(" + ASM_REG_SPEC + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" COMMA "," WHITESPACE "\n " - ASM_REG_OPERAND + ASM_OPERAND_NAMED NAME IDENT "tmp" WHITESPACE " " EQ "=" WHITESPACE " " - ASM_DIR_SPEC - OUT_KW "out" - L_PAREN "(" - ASM_REG_SPEC - NAME_REF - IDENT "reg" - R_PAREN ")" - WHITESPACE " " - UNDERSCORE_EXPR - UNDERSCORE "_" + ASM_REG_OPERAND + ASM_DIR_SPEC + OUT_KW "out" + L_PAREN "(" + ASM_REG_SPEC + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + UNDERSCORE_EXPR + UNDERSCORE "_" COMMA "," WHITESPACE "\n " R_PAREN ")" diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 5dace66c32d51..52ad439e4decc 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -395,7 +395,7 @@ OffsetOfExpr = // global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" // format_string := STRING_LITERAL / RAW_STRING_LITERAL AsmExpr = - Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmOperand (',' AsmOperand)*)? ','? ')' + Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')' // operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_" AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)? @@ -404,7 +404,7 @@ AsmDirSpec = 'in' | 'out' | 'lateout' | 'inout' | 'inlateout' // reg_spec := / "\"" "\"" AsmRegSpec = '@string' | NameRef // reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr -AsmRegOperand = (Name '=')? AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr +AsmRegOperand = AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr // clobber_abi := "clobber_abi(" *("," ) [","] ")" AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')' // option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" @@ -415,7 +415,9 @@ AsmLabel = 'label' BlockExpr AsmSym = 'sym' Path AsmConst = 'const' Expr // operand := reg_operand / clobber_abi / options -AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel | AsmSym | AsmConst +AsmOperand = AsmRegOperand | AsmLabel | AsmSym | AsmConst +AsmOperandNamed = (Name '=')? AsmOperand +AsmPiece = AsmOperandNamed | AsmClobberAbi | AsmOptions FormatArgsExpr = Attr* 'builtin' '#' 'format_args' '(' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 01d47c34bbdf6..c81a19f3bda41 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -118,7 +118,7 @@ pub struct AsmExpr { impl ast::HasAttrs for AsmExpr {} impl AsmExpr { #[inline] - pub fn asm_operands(&self) -> AstChildren { support::children(&self.syntax) } + pub fn asm_pieces(&self) -> AstChildren { support::children(&self.syntax) } #[inline] pub fn template(&self) -> AstChildren { support::children(&self.syntax) } #[inline] @@ -159,6 +159,18 @@ impl AsmOperandExpr { pub fn fat_arrow_token(&self) -> Option { support::token(&self.syntax, T![=>]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOperandNamed { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasName for AsmOperandNamed {} +impl AsmOperandNamed { + #[inline] + pub fn asm_operand(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AsmOption { pub(crate) syntax: SyntaxNode, @@ -217,7 +229,6 @@ impl AsmOptions { pub struct AsmRegOperand { pub(crate) syntax: SyntaxNode, } -impl ast::HasName for AsmRegOperand {} impl AsmRegOperand { #[inline] pub fn asm_dir_spec(&self) -> Option { support::child(&self.syntax) } @@ -229,8 +240,6 @@ impl AsmRegOperand { pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } #[inline] pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } - #[inline] - pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -2224,14 +2233,19 @@ impl ast::HasVisibility for Adt {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AsmOperand { - AsmClobberAbi(AsmClobberAbi), AsmConst(AsmConst), AsmLabel(AsmLabel), - AsmOptions(AsmOptions), AsmRegOperand(AsmRegOperand), AsmSym(AsmSym), } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AsmPiece { + AsmClobberAbi(AsmClobberAbi), + AsmOperandNamed(AsmOperandNamed), + AsmOptions(AsmOptions), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AssocItem { Const(Const), @@ -2581,6 +2595,20 @@ impl AstNode for AsmOperandExpr { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for AsmOperandNamed { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for AsmOption { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION } @@ -4589,10 +4617,6 @@ impl AstNode for Adt { } } } -impl From for AsmOperand { - #[inline] - fn from(node: AsmClobberAbi) -> AsmOperand { AsmOperand::AsmClobberAbi(node) } -} impl From for AsmOperand { #[inline] fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) } @@ -4601,10 +4625,6 @@ impl From for AsmOperand { #[inline] fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) } } -impl From for AsmOperand { - #[inline] - fn from(node: AsmOptions) -> AsmOperand { AsmOperand::AsmOptions(node) } -} impl From for AsmOperand { #[inline] fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) } @@ -4616,18 +4636,13 @@ impl From for AsmOperand { impl AstNode for AsmOperand { #[inline] fn can_cast(kind: SyntaxKind) -> bool { - matches!( - kind, - ASM_CLOBBER_ABI | ASM_CONST | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND | ASM_SYM - ) + matches!(kind, ASM_CONST | ASM_LABEL | ASM_REG_OPERAND | ASM_SYM) } #[inline] fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { - ASM_CLOBBER_ABI => AsmOperand::AsmClobberAbi(AsmClobberAbi { syntax }), ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }), ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }), - ASM_OPTIONS => AsmOperand::AsmOptions(AsmOptions { syntax }), ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }), ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }), _ => return None, @@ -4637,15 +4652,49 @@ impl AstNode for AsmOperand { #[inline] fn syntax(&self) -> &SyntaxNode { match self { - AsmOperand::AsmClobberAbi(it) => &it.syntax, AsmOperand::AsmConst(it) => &it.syntax, AsmOperand::AsmLabel(it) => &it.syntax, - AsmOperand::AsmOptions(it) => &it.syntax, AsmOperand::AsmRegOperand(it) => &it.syntax, AsmOperand::AsmSym(it) => &it.syntax, } } } +impl From for AsmPiece { + #[inline] + fn from(node: AsmClobberAbi) -> AsmPiece { AsmPiece::AsmClobberAbi(node) } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmOperandNamed) -> AsmPiece { AsmPiece::AsmOperandNamed(node) } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmOptions) -> AsmPiece { AsmPiece::AsmOptions(node) } +} +impl AstNode for AsmPiece { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, ASM_CLOBBER_ABI | ASM_OPERAND_NAMED | ASM_OPTIONS) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + ASM_CLOBBER_ABI => AsmPiece::AsmClobberAbi(AsmClobberAbi { syntax }), + ASM_OPERAND_NAMED => AsmPiece::AsmOperandNamed(AsmOperandNamed { syntax }), + ASM_OPTIONS => AsmPiece::AsmOptions(AsmOptions { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AsmPiece::AsmClobberAbi(it) => &it.syntax, + AsmPiece::AsmOperandNamed(it) => &it.syntax, + AsmPiece::AsmOptions(it) => &it.syntax, + } + } +} impl From for AssocItem { #[inline] fn from(node: Const) -> AssocItem { AssocItem::Const(node) } @@ -6181,7 +6230,7 @@ impl AstNode for AnyHasName { fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - ASM_REG_OPERAND + ASM_OPERAND_NAMED | CONST | CONST_PARAM | ENUM @@ -6211,9 +6260,9 @@ impl AstNode for AnyHasName { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl From for AnyHasName { +impl From for AnyHasName { #[inline] - fn from(node: AsmRegOperand) -> AnyHasName { AnyHasName { syntax: node.syntax } } + fn from(node: AsmOperandNamed) -> AnyHasName { AnyHasName { syntax: node.syntax } } } impl From for AnyHasName { #[inline] @@ -6460,6 +6509,11 @@ impl std::fmt::Display for AsmOperand { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmPiece { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AssocItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -6560,6 +6614,11 @@ impl std::fmt::Display for AsmOperandExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmOperandNamed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AsmOption { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) From bdb734b63ad83b0e96520daa7ce70137b442ecd0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 13:19:02 +0200 Subject: [PATCH 0283/1301] Support more IDE features for asm operands --- .../rust-analyzer/crates/hir-def/src/body.rs | 13 ++++- .../crates/hir-def/src/body/lower/asm.rs | 14 ++++-- .../crates/hir/src/has_source.rs | 27 ++++++++++- src/tools/rust-analyzer/crates/hir/src/lib.rs | 16 +++++++ .../rust-analyzer/crates/hir/src/semantics.rs | 2 +- .../crates/hir/src/source_analyzer.rs | 6 +-- .../rust-analyzer/crates/ide-db/src/defs.rs | 7 +-- .../rust-analyzer/crates/ide-db/src/rename.rs | 3 +- .../rust-analyzer/crates/ide-db/src/search.rs | 18 ++++++- .../crates/ide/src/navigation_target.rs | 30 ++++++++++-- .../rust-analyzer/crates/ide/src/rename.rs | 48 +++++++++++++++++++ .../test_data/highlight_asm.html | 32 ++++++------- .../test_data/highlight_strings.html | 4 +- 13 files changed, 180 insertions(+), 40 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 03507189fb99a..af972c92469ca 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -105,7 +105,7 @@ pub struct BodySourceMap { // format_args! FxHashMap>, // asm! - FxHashMap>, + FxHashMap)>>, )>, >, @@ -439,7 +439,7 @@ impl BodySourceMap { pub fn asm_template_args( &self, node: InFile<&ast::AsmExpr>, - ) -> Option<(ExprId, &[(syntax::TextRange, usize)])> { + ) -> Option<(ExprId, &[(syntax::TextRange, usize, Option)])> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); let expr = self.expr_map.get(&src)?; Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref)) @@ -482,4 +482,13 @@ impl BodySourceMap { diagnostics.shrink_to_fit(); binding_definitions.shrink_to_fit(); } + + pub fn template_map( + &self, + ) -> Option<&( + FxHashMap, Vec<(tt::TextRange, Name)>>, + FxHashMap, Vec<(tt::TextRange, usize, Option)>>, + )> { + self.template_map.as_deref() + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index 59b348b77db00..8d2c0b3926f99 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -1,3 +1,4 @@ +use hir_expand::name::Name; use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ @@ -202,27 +203,30 @@ impl ExprCollector<'_> { rustc_parse_format::Piece::NextArgument(arg) => { // let span = arg_spans.next(); - let operand_idx = match arg.position { + let (operand_idx, name) = match arg.position { rustc_parse_format::ArgumentIs(idx) | rustc_parse_format::ArgumentImplicitlyIs(idx) => { if idx >= operands.len() || named_pos.contains_key(&idx) || reg_args.contains(&idx) { - None + (None, None) } else { - Some(idx) + (Some(idx), None) } } rustc_parse_format::ArgumentNamed(name) => { let name = Symbol::intern(name); - named_args.get(&name).copied() + ( + named_args.get(&name).copied(), + Some(Name::new_symbol_root(name)), + ) } }; if let Some(operand_idx) = operand_idx { if let Some(position_span) = to_span(arg.position_span) { - mappings.push((position_span, operand_idx)); + mappings.push((position_span, operand_idx, name)); } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 7d52a28b91e91..82c90ac30101b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -14,8 +14,8 @@ use tt::TextRange; use crate::{ db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, - Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, Struct, Trait, - TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, + InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, + Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, }; pub trait HasSource { @@ -292,3 +292,26 @@ impl HasSource for ExternCrateDecl { Some(self.id.lookup(db.upcast()).source(db.upcast())) } } + +impl HasSource for InlineAsmOperand { + type Ast = ast::AsmOperandNamed; + fn source(self, db: &dyn HirDatabase) -> Option> { + let (_body, source_map) = db.body_with_source_map(self.owner); + if let Ok(src) = source_map.expr_syntax(self.expr) { + let root = src.file_syntax(db.upcast()); + return src + .map(|ast| match ast.to_node(&root) { + ast::Expr::AsmExpr(asm) => asm + .asm_pieces() + .filter_map(|it| match it { + ast::AsmPiece::AsmOperandNamed(it) => Some(it), + _ => None, + }) + .nth(self.index), + _ => None, + }) + .transpose(); + } + None + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index c8b227123f1c1..75969bd89940e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5253,6 +5253,22 @@ pub struct InlineAsmOperand { index: usize, } +impl InlineAsmOperand { + pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { + self.owner.into() + } + + pub fn name(&self, db: &dyn HirDatabase) -> Option { + db.body_with_source_map(self.owner) + .1 + .template_map()? + .1 + .get(&self.expr)? + .get(self.index) + .and_then(|(_, _, name)| name.clone()) + } +} + // FIXME: Document this #[derive(Debug)] pub struct Callable { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 0ba0e446578e7..543f2fc089fb5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -576,7 +576,7 @@ impl<'db> SemanticsImpl<'db> { let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?; let res = asm_parts .iter() - .map(|&(range, index)| { + .map(|&(range, index, _)| { ( range + quote.end(), Some(Either::Right(InlineAsmOperand { owner, expr, index })), diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index f6f1da1b7d6bd..4baf9079a055e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -913,8 +913,8 @@ impl SourceAnalyzer { let (expr, args) = body_source_map.asm_template_args(asm)?; Some(*def).zip( args.iter() - .find(|(range, _)| range.contains_inclusive(offset)) - .map(|(range, idx)| (expr, *range, *idx)), + .find(|(range, _, _)| range.contains_inclusive(offset)) + .map(|(range, idx, _)| (expr, *range, *idx)), ) } @@ -944,7 +944,7 @@ impl SourceAnalyzer { pub(crate) fn as_asm_parts( &self, asm: InFile<&ast::AsmExpr>, - ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> { + ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize, Option)]))> { let (def, _, body_source_map) = self.def.as_ref()?; Some(*def).zip(body_source_map.asm_template_args(asm)) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index c134c9f672543..099f26eba78a9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -85,13 +85,13 @@ impl Definition { Definition::Label(it) => it.module(db), Definition::ExternCrateDecl(it) => it.module(db), Definition::DeriveHelper(it) => it.derive().module(db), + Definition::InlineAsmOperand(it) => it.parent(db).module(db), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::TupleField(_) | Definition::ToolModule(_) - | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmOperand(_) => return None, + | Definition::InlineAsmRegOrRegClass(_) => return None, }; Some(module) } @@ -156,7 +156,8 @@ impl Definition { Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), Definition::ExternCrateDecl(it) => return it.alias_or_name(db), - Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => return None, // FIXME + Definition::InlineAsmRegOrRegClass(_) => return None, + Definition::InlineAsmOperand(op) => return op.name(db), }; Some(name) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 0435b2f0c6fce..f1404ed9f22cd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -200,6 +200,7 @@ impl Definition { .and_then(syn_ctx_is_root) } } + Definition::InlineAsmOperand(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) @@ -207,8 +208,6 @@ impl Definition { | Definition::ToolModule(_) | Definition::TupleField(_) | Definition::InlineAsmRegOrRegClass(_) => return None, - // FIXME: - Definition::InlineAsmOperand(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 4d0668ffea8df..ad30c624c417e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -319,6 +319,23 @@ impl Definition { }; } + if let Definition::InlineAsmOperand(op) = self { + let def = match op.parent(db) { + DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), + // FIXME: implement + DefWithBody::InTypeConst(_) => return SearchScope::empty(), + }; + return match def { + Some(def) => SearchScope::file_range( + def.as_ref().original_file_range_with_macro_call_body(db), + ), + None => SearchScope::single_file(file_id), + }; + } + if let Definition::SelfType(impl_) = self { return match impl_.source(db).map(|src| src.syntax().cloned()) { Some(def) => SearchScope::file_range( @@ -909,7 +926,6 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new(name); let include_self_kw_refs = self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self"))); - for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) { self.sema.db.unwind_if_cancelled(); let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 68039ef309d3b..9bc7bf411f05e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -237,15 +237,14 @@ impl TryToNav for Definition { Definition::Trait(it) => it.try_to_nav(db), Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), - Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?), + Definition::ExternCrateDecl(it) => it.try_to_nav(db), + Definition::InlineAsmOperand(it) => it.try_to_nav(db), Definition::BuiltinLifetime(_) | Definition::BuiltinType(_) | Definition::TupleField(_) | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, - // FIXME - Definition::InlineAsmOperand(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), } @@ -696,6 +695,31 @@ impl TryToNav for hir::ConstParam { } } +impl TryToNav for hir::InlineAsmOperand { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { + let InFile { file_id, value } = &self.source(db)?; + let file_id = *file_id; + Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let edition = self.parent(db).module(db).krate().edition(db); + NavigationTarget { + file_id, + name: self + .name(db) + .map_or_else(|| "_".into(), |it| it.display(db, edition).to_smolstr()), + alias: None, + kind: Some(SymbolKind::Local), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + } + }, + )) + } +} + #[derive(Debug)] pub struct UpmappingResult { /// The macro call site. diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 42b7472c645f3..9b9344e98cd73 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -2975,6 +2975,54 @@ fn test() { ); } + #[test] + fn asm_operand() { + check( + "bose", + r#" +//- minicore: asm +fn test() { + core::arch::asm!( + "push {base}", + base$0 = const 0 + ); +} +"#, + r#" +fn test() { + core::arch::asm!( + "push {bose}", + bose = const 0 + ); +} +"#, + ); + } + + #[test] + fn asm_operand2() { + check( + "bose", + r#" +//- minicore: asm +fn test() { + core::arch::asm!( + "push {base$0}", + base = const 0 + ); +} +"#, + r#" +fn test() { + core::arch::asm!( + "push {bose}", + bose = const 0 + ); +} +"#, + ); + } + #[test] fn rename_path_inside_use_tree() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index d322a374b3420..d830a3887217b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -50,17 +50,17 @@ let foo = 1; let mut o = 0; core::arch::asm!( - "%input = OpLoad _ {0}", + "%input = OpLoad _ {0}", concat!("%result = ", "bar", " _ %input"), - "OpStore {1} %result", + "OpStore {1} %result", in(reg) &foo, in(reg) &mut o, ); let thread_id: usize; core::arch::asm!(" - mov {0}, gs:[0x30] - mov {0}, [{0}+0x48] + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] ", out(reg) thread_id, options(pure, readonly, nostack)); static UNMAP_BASE: usize; @@ -69,28 +69,28 @@ const OffPtr: usize; const OffFn: usize; core::arch::asm!(" - push {free_type} - push {free_size} - push {base} + push {free_type} + push {free_size} + push {base} mov eax, fs:[30h] mov eax, [eax+8h] - add eax, {off_fn} - mov [eax-{off_fn}+{off_ptr}], eax + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax push eax - jmp {virtual_free} + jmp {virtual_free} ", - off_ptr = const OffPtr, - off_fn = const OffFn, + off_ptr = const OffPtr, + off_fn = const OffFn, - free_size = const 0, - free_type = const MEM_RELEASE, + free_size = const 0, + free_type = const MEM_RELEASE, - virtual_free = sym VirtualFree, + virtual_free = sym VirtualFree, - base = sym UNMAP_BASE, + base = sym UNMAP_BASE, options(noreturn), ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 37bbc1e1ee211..d5b9fc0e2c421 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -166,8 +166,8 @@ let i: u64 = 3; let o: u64; core::arch::asm!( - "mov {0}, {1}", - "add {0}, 5", + "mov {0}, {1}", + "add {0}, 5", out(reg) o, in(reg) i, ); From 2120b38adf4f2ee294460321bda4709eaa2509da Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 13:19:32 +0200 Subject: [PATCH 0284/1301] Add missing doc comments --- src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index 8d2c0b3926f99..300935db49676 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -1,3 +1,4 @@ +//! Lowering of inline assembly. use hir_expand::name::Name; use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; From 72980c417cb59c33d5814fd69eb958fb90363f26 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 13:41:03 +0200 Subject: [PATCH 0285/1301] Fix name fetching being incorrect for asm operands --- .../rust-analyzer/crates/hir-def/src/body.rs | 6 +- .../crates/hir-def/src/body/lower/asm.rs | 153 +++++++++--------- .../rust-analyzer/crates/hir-def/src/hir.rs | 4 +- .../crates/hir-ty/src/infer/closure.rs | 2 +- .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir-ty/src/infer/mutability.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +- .../rust-analyzer/crates/hir/src/semantics.rs | 2 +- .../crates/hir/src/source_analyzer.rs | 6 +- .../rust-analyzer/crates/ide/src/rename.rs | 12 +- 10 files changed, 107 insertions(+), 93 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index af972c92469ca..34db43939a7ca 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -105,7 +105,7 @@ pub struct BodySourceMap { // format_args! FxHashMap>, // asm! - FxHashMap)>>, + FxHashMap>, )>, >, @@ -439,7 +439,7 @@ impl BodySourceMap { pub fn asm_template_args( &self, node: InFile<&ast::AsmExpr>, - ) -> Option<(ExprId, &[(syntax::TextRange, usize, Option)])> { + ) -> Option<(ExprId, &[(syntax::TextRange, usize)])> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); let expr = self.expr_map.get(&src)?; Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref)) @@ -487,7 +487,7 @@ impl BodySourceMap { &self, ) -> Option<&( FxHashMap, Vec<(tt::TextRange, Name)>>, - FxHashMap, Vec<(tt::TextRange, usize, Option)>>, + FxHashMap, Vec<(tt::TextRange, usize)>>, )> { self.template_map.as_deref() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index 300935db49676..bf6ba41482c47 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -65,80 +65,89 @@ impl ExprCollector<'_> { continue; } ast::AsmPiece::AsmOperandNamed(op) => { - if let Some(name) = op.name() { - let sym = Symbol::intern(&name.text()); - named_args.insert(sym.clone(), slot); - named_pos.insert(slot, sym); + let name = op.name().map(|name| Symbol::intern(&name.text())); + if let Some(name) = &name { + named_args.insert(name.clone(), slot); + named_pos.insert(slot, name.clone()); } let Some(op) = op.asm_operand() else { continue }; - match op { - ast::AsmOperand::AsmRegOperand(op) => { - let Some(dir_spec) = op.asm_dir_spec() else { - continue; - }; - let Some(reg) = lower_reg(op.asm_reg_spec()) else { - continue; - }; - if dir_spec.in_token().is_some() { - let expr = self.collect_expr_opt( - op.asm_operand_expr().and_then(|it| it.in_expr()), - ); - AsmOperand::In { reg, expr } - } else if dir_spec.out_token().is_some() { - let expr = self.collect_expr_opt( - op.asm_operand_expr().and_then(|it| it.in_expr()), - ); - AsmOperand::Out { reg, expr: Some(expr), late: false } - } else if dir_spec.lateout_token().is_some() { - let expr = self.collect_expr_opt( - op.asm_operand_expr().and_then(|it| it.in_expr()), - ); - AsmOperand::Out { reg, expr: Some(expr), late: true } - } else if dir_spec.inout_token().is_some() { - let Some(op_expr) = op.asm_operand_expr() else { continue }; - let in_expr = self.collect_expr_opt(op_expr.in_expr()); - let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); - match out_expr { - Some(out_expr) => AsmOperand::SplitInOut { - reg, - in_expr, - out_expr: Some(out_expr), - late: false, - }, - None => AsmOperand::InOut { reg, expr: in_expr, late: false }, - } - } else if dir_spec.inlateout_token().is_some() { - let Some(op_expr) = op.asm_operand_expr() else { continue }; - let in_expr = self.collect_expr_opt(op_expr.in_expr()); - let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it)); - match out_expr { - Some(out_expr) => AsmOperand::SplitInOut { - reg, - in_expr, - out_expr: Some(out_expr), - late: false, - }, - None => AsmOperand::InOut { reg, expr: in_expr, late: false }, + ( + name.map(Name::new_symbol_root), + match op { + ast::AsmOperand::AsmRegOperand(op) => { + let Some(dir_spec) = op.asm_dir_spec() else { + continue; + }; + let Some(reg) = lower_reg(op.asm_reg_spec()) else { + continue; + }; + if dir_spec.in_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::In { reg, expr } + } else if dir_spec.out_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::Out { reg, expr: Some(expr), late: false } + } else if dir_spec.lateout_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::Out { reg, expr: Some(expr), late: true } + } else if dir_spec.inout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + let out_expr = + op_expr.out_expr().map(|it| self.collect_expr(it)); + match out_expr { + Some(out_expr) => AsmOperand::SplitInOut { + reg, + in_expr, + out_expr: Some(out_expr), + late: false, + }, + None => { + AsmOperand::InOut { reg, expr: in_expr, late: false } + } + } + } else if dir_spec.inlateout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + let out_expr = + op_expr.out_expr().map(|it| self.collect_expr(it)); + match out_expr { + Some(out_expr) => AsmOperand::SplitInOut { + reg, + in_expr, + out_expr: Some(out_expr), + late: false, + }, + None => { + AsmOperand::InOut { reg, expr: in_expr, late: false } + } + } + } else { + continue; } - } else { - continue; } - } - ast::AsmOperand::AsmLabel(l) => { - AsmOperand::Label(self.collect_block_opt(l.block_expr())) - } - ast::AsmOperand::AsmConst(c) => { - AsmOperand::Const(self.collect_expr_opt(c.expr())) - } - ast::AsmOperand::AsmSym(s) => { - let Some(path) = - s.path().and_then(|p| self.expander.parse_path(self.db, p)) - else { - continue; - }; - AsmOperand::Sym(path) - } - } + ast::AsmOperand::AsmLabel(l) => { + AsmOperand::Label(self.collect_block_opt(l.block_expr())) + } + ast::AsmOperand::AsmConst(c) => { + AsmOperand::Const(self.collect_expr_opt(c.expr())) + } + ast::AsmOperand::AsmSym(s) => { + let Some(path) = + s.path().and_then(|p| self.expander.parse_path(self.db, p)) + else { + continue; + }; + AsmOperand::Sym(path) + } + }, + ) } }; operands.push(op); @@ -204,7 +213,7 @@ impl ExprCollector<'_> { rustc_parse_format::Piece::NextArgument(arg) => { // let span = arg_spans.next(); - let (operand_idx, name) = match arg.position { + let (operand_idx, _name) = match arg.position { rustc_parse_format::ArgumentIs(idx) | rustc_parse_format::ArgumentImplicitlyIs(idx) => { if idx >= operands.len() @@ -227,7 +236,7 @@ impl ExprCollector<'_> { if let Some(operand_idx) = operand_idx { if let Some(position_span) = to_span(arg.position_span) { - mappings.push((position_span, operand_idx, name)); + mappings.push((position_span, operand_idx)); } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 7d2c573ebfed4..d9358a28822e7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -307,7 +307,7 @@ pub struct OffsetOf { #[derive(Debug, Clone, PartialEq, Eq)] pub struct InlineAsm { - pub operands: Box<[AsmOperand]>, + pub operands: Box<[(Option, AsmOperand)]>, pub options: AsmOptions, } @@ -485,7 +485,7 @@ impl Expr { match self { Expr::Missing => {} Expr::Path(_) | Expr::OffsetOf(_) => {} - Expr::InlineAsm(it) => it.operands.iter().for_each(|op| match op { + Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { AsmOperand::In { expr, .. } | AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => f(*expr), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 3ad330c1a0b5d..5cad08b93956e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -669,7 +669,7 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { Expr::OffsetOf(_) => (), - Expr::InlineAsm(e) => e.operands.iter().for_each(|op| match op { + Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { AsmOperand::In { expr, .. } | AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index e6eaf2f446d43..a82d091cbb028 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -956,7 +956,7 @@ impl InferenceContext<'_> { }; let diverge = asm.options.contains(AsmOptions::NORETURN); - asm.operands.iter().for_each(|operand| match *operand { + asm.operands.iter().for_each(|(_, operand)| match *operand { AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true), AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => { check_expr_asm_operand(expr, false) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index e841c6630822a..8e52725e53600 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -42,7 +42,7 @@ impl InferenceContext<'_> { match &self.body[tgt_expr] { Expr::Missing => (), Expr::InlineAsm(e) => { - e.operands.iter().for_each(|op| match op { + e.operands.iter().for_each(|(_, op)| match op { AsmOperand::In { expr, .. } | AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 75969bd89940e..3b8b901fa14e3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5259,13 +5259,10 @@ impl InlineAsmOperand { } pub fn name(&self, db: &dyn HirDatabase) -> Option { - db.body_with_source_map(self.owner) - .1 - .template_map()? - .1 - .get(&self.expr)? - .get(self.index) - .and_then(|(_, _, name)| name.clone()) + match &db.body(self.owner)[self.expr] { + hir_def::hir::Expr::InlineAsm(e) => e.operands.get(self.index)?.0.clone(), + _ => None, + } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 543f2fc089fb5..0ba0e446578e7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -576,7 +576,7 @@ impl<'db> SemanticsImpl<'db> { let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?; let res = asm_parts .iter() - .map(|&(range, index, _)| { + .map(|&(range, index)| { ( range + quote.end(), Some(Either::Right(InlineAsmOperand { owner, expr, index })), diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 4baf9079a055e..f6f1da1b7d6bd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -913,8 +913,8 @@ impl SourceAnalyzer { let (expr, args) = body_source_map.asm_template_args(asm)?; Some(*def).zip( args.iter() - .find(|(range, _, _)| range.contains_inclusive(offset)) - .map(|(range, idx, _)| (expr, *range, *idx)), + .find(|(range, _)| range.contains_inclusive(offset)) + .map(|(range, idx)| (expr, *range, *idx)), ) } @@ -944,7 +944,7 @@ impl SourceAnalyzer { pub(crate) fn as_asm_parts( &self, asm: InFile<&ast::AsmExpr>, - ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize, Option)]))> { + ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> { let (def, _, body_source_map) = self.def.as_ref()?; Some(*def).zip(body_source_map.asm_template_args(asm)) } diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 9b9344e98cd73..e46cb5a781f4a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -3008,7 +3008,11 @@ fn test() { fn test() { core::arch::asm!( "push {base$0}", - base = const 0 + "push {base}", + boo = const 0, + virtual_free = sym VIRTUAL_FREE, + base = const 0, + boo = const 0, ); } "#, @@ -3016,7 +3020,11 @@ fn test() { fn test() { core::arch::asm!( "push {bose}", - bose = const 0 + "push {bose}", + boo = const 0, + virtual_free = sym VIRTUAL_FREE, + bose = const 0, + boo = const 0, ); } "#, From 20e9c8d110d6259b8086c2ffd6268d0dd072a2ab Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 15:06:37 +0200 Subject: [PATCH 0286/1301] asm! parsing and lowering fixes --- .../rust-analyzer/crates/hir-def/src/body.rs | 6 ++-- .../crates/hir-def/src/body/lower/asm.rs | 7 +++-- .../rust-analyzer/crates/hir/src/semantics.rs | 5 +++- .../crates/hir/src/source_analyzer.rs | 6 ++-- .../crates/ide/src/highlight_related.rs | 30 +++++++++++++++++++ .../test_data/highlight_asm.html | 26 ++++++++++++++-- .../test_data/highlight_strings.html | 2 +- .../ide/src/syntax_highlighting/tests.rs | 22 ++++++++++++++ .../parser/src/grammar/expressions/atom.rs | 4 +++ .../test_data/parser/inline/ok/asm_expr.rast | 16 +++++----- 10 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 34db43939a7ca..f18083d387356 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -105,7 +105,7 @@ pub struct BodySourceMap { // format_args! FxHashMap>, // asm! - FxHashMap>, + FxHashMap>>, )>, >, @@ -439,7 +439,7 @@ impl BodySourceMap { pub fn asm_template_args( &self, node: InFile<&ast::AsmExpr>, - ) -> Option<(ExprId, &[(syntax::TextRange, usize)])> { + ) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); let expr = self.expr_map.get(&src)?; Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref)) @@ -487,7 +487,7 @@ impl BodySourceMap { &self, ) -> Option<&( FxHashMap, Vec<(tt::TextRange, Name)>>, - FxHashMap, Vec<(tt::TextRange, usize)>>, + FxHashMap, Vec>>, )> { self.template_map.as_deref() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index bf6ba41482c47..448bc2f033f66 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -158,11 +158,14 @@ impl ExprCollector<'_> { if !options.contains(AsmOptions::RAW) { // Don't treat raw asm as a format string. asm.template() - .filter_map(|it| Some((it.clone(), self.expand_macros_to_string(it)?))) - .for_each(|(expr, (s, is_direct_literal))| { + .enumerate() + .filter_map(|(idx, it)| Some((idx, it.clone(), self.expand_macros_to_string(it)?))) + .for_each(|(idx, expr, (s, is_direct_literal))| { + mappings.resize_with(idx + 1, Vec::default); let Ok(text) = s.value() else { return; }; + let mappings = &mut mappings[idx]; let template_snippet = match expr { ast::Expr::Literal(literal) => match literal.kind() { ast::LiteralKind::String(s) => Some(s.text().to_owned()), diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 0ba0e446578e7..2e67a5e454bfa 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -572,9 +572,11 @@ impl<'db> SemanticsImpl<'db> { } else { let asm = ast::AsmExpr::cast(parent)?; let source_analyzer = self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; let asm = self.wrap_node_infile(asm); let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?; let res = asm_parts + .get(line)? .iter() .map(|&(range, index)| { ( @@ -629,8 +631,9 @@ impl<'db> SemanticsImpl<'db> { } else { let asm = ast::AsmExpr::cast(parent)?; let source_analyzer = &self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; let asm = self.wrap_node_infile(asm); - source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), offset).map( + source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), line, offset).map( |(owner, (expr, range, index))| { (range, Some(Either::Right(InlineAsmOperand { owner, expr, index }))) }, diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index f6f1da1b7d6bd..3da67ae23f83b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -907,12 +907,14 @@ impl SourceAnalyzer { pub(crate) fn resolve_offset_in_asm_template( &self, asm: InFile<&ast::AsmExpr>, + line: usize, offset: TextSize, ) -> Option<(DefWithBodyId, (ExprId, TextRange, usize))> { let (def, _, body_source_map) = self.def.as_ref()?; let (expr, args) = body_source_map.asm_template_args(asm)?; Some(*def).zip( - args.iter() + args.get(line)? + .iter() .find(|(range, _)| range.contains_inclusive(offset)) .map(|(range, idx)| (expr, *range, *idx)), ) @@ -944,7 +946,7 @@ impl SourceAnalyzer { pub(crate) fn as_asm_parts( &self, asm: InFile<&ast::AsmExpr>, - ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> { + ) -> Option<(DefWithBodyId, (ExprId, &[Vec<(TextRange, usize)>]))> { let (def, _, body_source_map) = self.def.as_ref()?; Some(*def).zip(body_source_map.asm_template_args(asm)) } diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 5348e855be4b0..4c8e3fc3040c2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -2004,6 +2004,36 @@ fn main() { { return; } +"#, + ) + } + + #[test] + fn asm() { + check( + r#" +//- minicore: asm +#[inline] +pub unsafe fn bootstrap() -> ! { + builtin#asm( + "blabla", + "mrs {tmp}, CONTROL", + // ^^^ read + "blabla", + "bics {tmp}, {spsel}", + // ^^^ read + "blabla", + "msr CONTROL, {tmp}", + // ^^^ read + "blabla", + tmp$0 = inout(reg) 0, + // ^^^ + aaa = in(reg) 2, + aaa = in(reg) msp, + aaa = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index d830a3887217b..15a6386aa3c88 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -50,9 +50,9 @@ let foo = 1; let mut o = 0; core::arch::asm!( - "%input = OpLoad _ {0}", + "%input = OpLoad _ {0}", concat!("%result = ", "bar", " _ %input"), - "OpStore {1} %result", + "OpStore {1} %result", in(reg) &foo, in(reg) &mut o, ); @@ -94,4 +94,26 @@ options(noreturn), ); } +} +// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274 +#[inline] +pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { + // Ensure thumb mode is set. + let rv = (rv as u32) | 1; + let msp = msp as u32; + core::arch::asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index d5b9fc0e2c421..5594a36e7318f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -167,7 +167,7 @@ let o: u64; core::arch::asm!( "mov {0}, {1}", - "add {0}, 5", + "add {0}, 5", out(reg) o, in(reg) i, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index f47b2115bfc68..82833d716b533 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -1331,6 +1331,28 @@ fn main() { ); } } +// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274 +#[inline] +pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { + // Ensure thumb mode is set. + let rv = (rv as u32) | 1; + let msp = msp as u32; + core::arch::asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} "#, expect_file!["./test_data/highlight_asm.html"], false, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 39ca26fc508c4..a1a3e9470923b 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -361,16 +361,20 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) { dir_spec.complete(p, ASM_DIR_SPEC); parse_reg(p); + let op_expr = p.start(); expr(p); + op_expr.complete(p, ASM_OPERAND_EXPR); op.complete(p, ASM_REG_OPERAND); op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) { dir_spec.complete(p, ASM_DIR_SPEC); parse_reg(p); + let op_expr = p.start(); expr(p); if p.eat(T![=>]) { expr(p); } + op_expr.complete(p, ASM_OPERAND_EXPR); op.complete(p, ASM_REG_OPERAND); op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat_contextual_kw(T![label]) { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast index 4afa9daf5904b..f0213d0b5e36a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast @@ -50,11 +50,12 @@ SOURCE_FILE IDENT "reg" R_PAREN ")" WHITESPACE " " - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "x" + ASM_OPERAND_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" COMMA "," WHITESPACE "\n " ASM_OPERAND_NAMED @@ -72,8 +73,9 @@ SOURCE_FILE IDENT "reg" R_PAREN ")" WHITESPACE " " - UNDERSCORE_EXPR - UNDERSCORE "_" + ASM_OPERAND_EXPR + UNDERSCORE_EXPR + UNDERSCORE "_" COMMA "," WHITESPACE "\n " R_PAREN ")" From 13d56dc160a6bf9fca92b86307c50672e3906293 Mon Sep 17 00:00:00 2001 From: arnaudgolfouse Date: Thu, 5 Sep 2024 16:00:48 +0200 Subject: [PATCH 0287/1301] Use a `Vec` in `rustc_interface::Config::locale_resources` This allows a third-party tool to injects its own resources, when receiving the config via `rustc_driver::Callbacks::config`. --- compiler/rustc_driver_impl/src/lib.rs | 2 +- compiler/rustc_interface/src/interface.rs | 4 +++- src/librustdoc/core.rs | 2 +- src/librustdoc/doctest.rs | 2 +- tests/ui-fulldeps/run-compiler-twice.rs | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index cb2fa6e9d746b..b32273fb0c93f 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -321,7 +321,7 @@ fn run_compiler( output_dir: odir, ice_file, file_loader, - locale_resources: DEFAULT_LOCALE_RESOURCES, + locale_resources: DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 04e2b7d45dc93..347dc185043e4 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -311,7 +311,9 @@ pub struct Config { pub output_file: Option, pub ice_file: Option, pub file_loader: Option>, - pub locale_resources: &'static [&'static str], + /// The list of fluent resources, used for lints declared with + /// [`Diagnostic`](rustc_errors::Diagnostic) and [`LintDiagnostic`](rustc_errors::LintDiagnostic). + pub locale_resources: Vec<&'static str>, pub lint_caps: FxHashMap, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 2cde0ac5c5354..b93cd02fca639 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -262,7 +262,7 @@ pub(crate) fn create_config( output_file: None, output_dir: None, file_loader: None, - locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES, + locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps, psess_created: None, hash_untracked_state: None, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 8b6588ea75c83..04b0b6f08db77 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -139,7 +139,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<() output_file: None, output_dir: None, file_loader: None, - locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES, + locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps, psess_created: None, hash_untracked_state: None, diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index 720fc42cc5700..cce4eac0d7c15 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -65,7 +65,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path output_dir: None, ice_file: None, file_loader: None, - locale_resources: &[], + locale_resources: Vec::new(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, From 40fca8f7a8131129b75694109138891e607baab0 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 5 Sep 2024 17:00:55 +0200 Subject: [PATCH 0288/1301] Bump Clippy version -> 0.1.83 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b48b881097f47..5b62e387ac631 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.82" +version = "0.1.83" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 5c4e0761dbca7..9da7112345de9 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.82" +version = "0.1.83" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index fbd4566da58c7..d1188940b46a8 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.82" +version = "0.1.83" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 629ce9d04d432..fe30b10c435ed 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.82" +version = "0.1.83" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 31270241b0b2e..67a1f7cc72c54 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.82" +version = "0.1.83" edition = "2021" publish = false From 3d027d75aff29983cb95da07352bcb8a55487400 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 5 Sep 2024 17:01:08 +0200 Subject: [PATCH 0289/1301] Bump nightly version -> 2024-09-05 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 0be2e81810ebe..854fcda2dabda 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-08-23" +channel = "nightly-2024-09-05" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 4e2e1bf6edc75a75bfb86c912c6f1c23672e531c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Sep 2024 15:23:28 +0200 Subject: [PATCH 0290/1301] fix: Fix parser panicking on invalid asm options --- src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs | 5 +++++ .../crates/parser/src/grammar/expressions/atom.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 10ab6d3ff8939..53b69c12f05d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -1498,6 +1498,11 @@ fn main() { 43..44 '1': i32 58..63 'mut o': i32 66..67 '0': i32 + !95..104 'thread_id': usize + !103..107 '&foo': &'? i32 + !104..107 'foo': i32 + !115..120 '&muto': &'? mut i32 + !119..120 'o': i32 293..294 'o': i32 308..317 'thread_id': usize "#]], diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index a1a3e9470923b..2333e6c862be5 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -434,6 +434,7 @@ fn parse_options(p: &mut Parser<'_>) { let m = p.start(); if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) { p.err_and_bump("expected asm option"); + m.abandon(p); continue; } m.complete(p, ASM_OPTION); From f44bdb5c78a726da1960856c973907de1eb68aec Mon Sep 17 00:00:00 2001 From: David Richey Date: Thu, 5 Sep 2024 10:57:16 -0500 Subject: [PATCH 0291/1301] Add command to report unresolved references --- .../crates/rust-analyzer/src/bin/main.rs | 1 + .../crates/rust-analyzer/src/cli.rs | 1 + .../crates/rust-analyzer/src/cli/flags.rs | 23 +++ .../src/cli/unresolved_references.rs | 175 ++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 21b481c1fa29e..41b42573f0821 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -82,6 +82,7 @@ fn actual_main() -> anyhow::Result { flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?, flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?, flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::UnresolvedReferences(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs index 5eb6ff664f665..a7ec5af89fcbf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs @@ -13,6 +13,7 @@ mod rustc_tests; mod scip; mod ssr; mod symbols; +mod unresolved_references; mod progress_report; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 16d90de661ad1..73e71658d17c5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -124,6 +124,19 @@ xflags::xflags! { optional --proc-macro-srv path: PathBuf } + /// Report unresolved references + cmd unresolved-references { + /// Directory with Cargo.toml. + required path: PathBuf + + /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis. + optional --disable-build-scripts + /// Don't use expand proc macros. + optional --disable-proc-macros + /// Run a custom proc-macro-srv binary. + optional --proc-macro-srv path: PathBuf + } + cmd ssr { /// A structured search replace rule (`$a.foo($b) ==>> bar($a, $b)`) repeated rule: SsrRule @@ -181,6 +194,7 @@ pub enum RustAnalyzerCmd { RunTests(RunTests), RustcTests(RustcTests), Diagnostics(Diagnostics), + UnresolvedReferences(UnresolvedReferences), Ssr(Ssr), Search(Search), Lsif(Lsif), @@ -250,6 +264,15 @@ pub struct Diagnostics { pub proc_macro_srv: Option, } +#[derive(Debug)] +pub struct UnresolvedReferences { + pub path: PathBuf, + + pub disable_build_scripts: bool, + pub disable_proc_macros: bool, + pub proc_macro_srv: Option, +} + #[derive(Debug)] pub struct Ssr { pub rule: Vec, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs new file mode 100644 index 0000000000000..986bd018b424d --- /dev/null +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -0,0 +1,175 @@ +//! Reports references in code that the IDE layer cannot resolve. +use hir::{db::HirDatabase, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics}; +use ide::{AnalysisHost, RootDatabase, TextRange}; +use ide_db::{ + base_db::{SourceDatabase, SourceRootDatabase}, + defs::NameRefClass, + EditionedFileId, FxHashSet, LineIndexDatabase as _, +}; +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; +use parser::SyntaxKind; +use syntax::{ast, AstNode, WalkEvent}; +use vfs::FileId; + +use crate::cli::flags; + +impl flags::UnresolvedReferences { + pub fn run(self) -> anyhow::Result<()> { + const STACK_SIZE: usize = 1024 * 1024 * 8; + + let handle = stdx::thread::Builder::new(stdx::thread::ThreadIntent::LatencySensitive) + .name("BIG_STACK_THREAD".into()) + .stack_size(STACK_SIZE) + .spawn(|| self.run_()) + .unwrap(); + + handle.join() + } + + fn run_(self) -> anyhow::Result<()> { + let root = + vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize(); + let config = crate::config::Config::new( + root.clone(), + lsp_types::ClientCapabilities::default(), + vec![], + None, + ); + let cargo_config = config.cargo(None); + let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { + let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); + ProcMacroServerChoice::Explicit(path) + } else { + ProcMacroServerChoice::Sysroot + }; + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: !self.disable_build_scripts, + with_proc_macro_server, + prefill_caches: false, + }; + let (db, vfs, _proc_macro) = + load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; + let host = AnalysisHost::with_database(db); + let db = host.raw_database(); + let sema = Semantics::new(db); + + let mut visited_files = FxHashSet::default(); + + let work = all_modules(db).into_iter().filter(|module| { + let file_id = module.definition_source_file_id(db).original_file(db); + let source_root = db.file_source_root(file_id.into()); + let source_root = db.source_root(source_root); + !source_root.is_library + }); + + for module in work { + let file_id = module.definition_source_file_id(db).original_file(db); + if !visited_files.contains(&file_id) { + let crate_name = + module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned(); + let file_path = vfs.file_path(file_id.into()); + eprintln!("processing crate: {crate_name}, module: {file_path}",); + + let line_index = db.line_index(file_id.into()); + let file_text = db.file_text(file_id.into()); + + for range in find_unresolved_references(db, &sema, file_id.into(), &module) { + let line_col = line_index.line_col(range.start()); + let line = line_col.line + 1; + let col = line_col.col + 1; + let text = &file_text[range]; + println!("{file_path}:{line}:{col}: {text}"); + } + + visited_files.insert(file_id); + } + } + + eprintln!(); + eprintln!("scan complete"); + + Ok(()) + } +} + +fn all_modules(db: &dyn HirDatabase) -> Vec { + let mut worklist: Vec<_> = + Crate::all(db).into_iter().map(|krate| krate.root_module()).collect(); + let mut modules = Vec::new(); + + while let Some(module) = worklist.pop() { + modules.push(module); + worklist.extend(module.children(db)); + } + + modules +} + +fn find_unresolved_references( + db: &RootDatabase, + sema: &Semantics<'_, RootDatabase>, + file_id: FileId, + module: &Module, +) -> Vec { + let mut unresolved_references = all_unresolved_references(sema, file_id); + + // remove unresolved references which are within inactive code + let mut diagnostics = Vec::new(); + module.diagnostics(db, &mut diagnostics, false); + for diagnostic in diagnostics { + let AnyDiagnostic::InactiveCode(inactive_code) = diagnostic else { + continue; + }; + + let node = inactive_code.node; + let range = node.map(|it| it.text_range()).original_node_file_range_rooted(db); + + if range.file_id != file_id { + continue; + } + + unresolved_references.retain(|r| !range.range.contains_range(*r)); + } + + unresolved_references +} + +fn all_unresolved_references( + sema: &Semantics<'_, RootDatabase>, + file_id: FileId, +) -> Vec { + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let root = file.syntax(); + + let mut unresolved_references = Vec::new(); + for event in root.preorder() { + let WalkEvent::Enter(syntax) = event else { + continue; + }; + let Some(name_ref) = ast::NameRef::cast(syntax) else { + continue; + }; + let Some(descended_name_ref) = name_ref.syntax().first_token().and_then(|tok| { + sema.descend_into_macros_single_exact(tok).parent().and_then(ast::NameRef::cast) + }) else { + continue; + }; + + // if we can classify the name_ref, it's not unresolved + if NameRefClass::classify(sema, &descended_name_ref).is_some() { + continue; + } + + // if we couldn't classify it, but it's in an attr, ignore it. See #10935 + if descended_name_ref.syntax().ancestors().any(|it| it.kind() == SyntaxKind::ATTR) { + continue; + } + + // otherwise, it's unresolved + unresolved_references.push(name_ref.syntax().text_range()); + } + unresolved_references +} From 79081f1271222d4ea12c923d949fc4f1aece2362 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Thu, 5 Sep 2024 14:30:27 -0700 Subject: [PATCH 0292/1301] Correct version of `too_long_first_doc_paragraph` `too_long_first_doc_paragraph` is, empirically, not in the Rust 1.81.0 release. --- clippy_lints/src/doc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 6db63b59e020e..f2c87f0fd70b3 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -450,7 +450,7 @@ declare_clippy_lint! { /// /// and probably spanning a many rows. /// struct Foo {} /// ``` - #[clippy::version = "1.81.0"] + #[clippy::version = "1.82.0"] pub TOO_LONG_FIRST_DOC_PARAGRAPH, style, "ensure that the first line of a documentation paragraph isn't too long" From 0b8cb4a1ebdf3bb9b03f7befe48b3d5e36ba080b Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 5 Sep 2024 00:34:04 +0300 Subject: [PATCH 0293/1301] Make `Ty::boxed_ty` return an `Option` --- clippy_lints/src/escape.rs | 6 +++--- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/methods/utils.rs | 4 ++-- clippy_lints/src/unnecessary_box_returns.rs | 6 ++---- clippy_utils/src/ty.rs | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index a5da52b0be50a..803606979410b 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { } fn is_non_trait_box(ty: Ty<'_>) -> bool { - ty.is_box() && !ty.boxed_ty().is_trait() + ty.boxed_ty().is_some_and(|boxed| !boxed.is_trait()) } struct EscapeDelegate<'a, 'tcx> { @@ -191,8 +191,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { fn is_large_box(&self, ty: Ty<'tcx>) -> bool { // Large types need to be boxed to avoid stack overflows. - if ty.is_box() { - self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack + if let Some(boxed_ty) = ty.boxed_ty() { + self.cx.layout_of(boxed_ty).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack } else { false } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d7126990edb1d..f61bb3a6bf481 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5187,8 +5187,8 @@ impl SelfKind { fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { if ty == parent_ty { true - } else if ty.is_box() { - ty.boxed_ty() == parent_ty + } else if let Some(boxed_ty) = ty.boxed_ty() { + boxed_ty == parent_ty } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { if let ty::Adt(_, args) = ty.kind() { args.types().next().map_or(false, |t| t == parent_ty) diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 0d2b0a3131763..fe860e5ae2601 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -16,7 +16,7 @@ pub(super) fn derefs_to_slice<'tcx>( fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { match ty.kind() { ty::Slice(_) => true, - ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), + ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec), ty::Array(_, size) => size.try_eval_target_usize(cx.tcx, cx.param_env).is_some(), ty::Ref(_, inner, _) => may_slice(cx, *inner), @@ -33,7 +33,7 @@ pub(super) fn derefs_to_slice<'tcx>( } else { match ty.kind() { ty::Slice(_) => Some(expr), - ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), + _ if ty.boxed_ty().is_some_and(|boxed| may_slice(cx, boxed)) => Some(expr), ty::Ref(_, inner, _) => { if may_slice(cx, *inner) { Some(expr) diff --git a/clippy_lints/src/unnecessary_box_returns.rs b/clippy_lints/src/unnecessary_box_returns.rs index 3f130bf5a6731..14f4aa6676b65 100644 --- a/clippy_lints/src/unnecessary_box_returns.rs +++ b/clippy_lints/src/unnecessary_box_returns.rs @@ -75,11 +75,9 @@ impl UnnecessaryBoxReturns { .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder()) .output(); - if !return_ty.is_box() { + let Some(boxed_ty) = return_ty.boxed_ty() else { return; - } - - let boxed_ty = return_ty.boxed_ty(); + }; // It's sometimes useful to return Box if T is unsized, so don't lint those. // Also, don't lint if we know that T is very large, in which case returning diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f80981c11af65..585134209ca3a 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -704,8 +704,8 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if ty.is_box() { - return ty_sig(cx, ty.boxed_ty()); + if let Some(boxed_ty) = ty.boxed_ty() { + return ty_sig(cx, boxed_ty); } match *ty.kind() { ty::Closure(id, subs) => { From 06c86a106915b5875391a7abdd749f1708dc3b50 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Thu, 5 Sep 2024 12:36:38 -0700 Subject: [PATCH 0294/1301] fix: Updating settings should not clobber discovered projects `linkedProjects` is owned by the user's configuration, so when users update this setting, `linkedProjects` is reset. This is problematic when `linkedProjects` also contains projects discovered with `discoverCommand`. The buggy behaviour occurred when: (1) The user configures `discoverCommand` and loads a Rust project. (2) The user changes any setting in VS Code, so rust-analyzer receives `workspace/didChangeConfiguration`. (3) `handle_did_change_configuration` ultimately calls `Client::apply_change_with_sink()`, which updates `config.user_config` and discards any items we added in `linkedProjects`. Instead, separate out `discovered_projects_from_filesystem` and `discovered_projects_from_command` from user configuration, so user settings cannot affect any type of discovered project. This fixes the subtle issue mentioned here: https://github.com/rust-lang/rust-analyzer/pull/17246#issuecomment-2185259122 --- .../crates/project-model/src/lib.rs | 9 ++ .../crates/project-model/src/project_json.rs | 2 + .../crates/rust-analyzer/src/config.rs | 133 ++++++++++-------- .../crates/rust-analyzer/src/main_loop.rs | 2 +- 4 files changed, 85 insertions(+), 61 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index b8ac55ed0d5d1..91bdef4889ca1 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -52,6 +52,15 @@ pub use crate::{ }; pub use cargo_metadata::Metadata; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectJsonFromCommand { + /// The data describing this project, such as its dependencies. + pub data: ProjectJsonData, + /// The build system specific file that describes this project, + /// such as a `my-project/BUCK` file. + pub buildfile: AbsPathBuf, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectManifest { ProjectJson(ManifestPath), diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index a09c7a77abceb..6a88cf022dfb0 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -66,6 +66,8 @@ pub struct ProjectJson { /// e.g. `path/to/sysroot/lib/rustlib/src/rust` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, + /// The path to the rust-project.json file. May be None if this + /// data was generated by the discoverConfig command. manifest: Option, crates: Vec, /// Configuration for CLI commands. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 714c835812f33..e4e8c9c6d9f97 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -24,7 +24,8 @@ use ide_db::{ use itertools::Itertools; use paths::{Utf8Path, Utf8PathBuf}; use project_model::{ - CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, + CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand, + ProjectManifest, RustLibSource, }; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; @@ -761,7 +762,13 @@ enum RatomlFile { #[derive(Debug, Clone)] pub struct Config { - discovered_projects: Vec, + /// Projects that have a Cargo.toml or a rust-project.json in a + /// parent directory, so we can discover them by walking the + /// file system. + discovered_projects_from_filesystem: Vec, + /// Projects whose configuration was generated by a command + /// configured in discoverConfig. + discovered_projects_from_command: Vec, /// The workspace roots as registered by the LSP client workspace_roots: Vec, caps: ClientCapabilities, @@ -1054,19 +1061,19 @@ impl Config { (config, e, should_update) } - pub fn add_linked_projects(&mut self, data: ProjectJsonData, buildfile: AbsPathBuf) { - let linked_projects = &mut self.client_config.0.global.linkedProjects; - - let new_project = ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile }; - match linked_projects { - Some(projects) => { - match projects.iter_mut().find(|p| p.manifest() == new_project.manifest()) { - Some(p) => *p = new_project, - None => projects.push(new_project), - } + pub fn add_discovered_project_from_command( + &mut self, + data: ProjectJsonData, + buildfile: AbsPathBuf, + ) { + for proj in self.discovered_projects_from_command.iter_mut() { + if proj.buildfile == buildfile { + proj.data = data; + return; } - None => *linked_projects = Some(vec![new_project]), } + + self.discovered_projects_from_command.push(ProjectJsonFromCommand { data, buildfile }); } } @@ -1344,7 +1351,8 @@ impl Config { Config { caps: ClientCapabilities::new(caps), - discovered_projects: Vec::new(), + discovered_projects_from_filesystem: Vec::new(), + discovered_projects_from_command: Vec::new(), root_path, snippets: Default::default(), workspace_roots, @@ -1365,7 +1373,7 @@ impl Config { if discovered.is_empty() { tracing::error!("failed to find any projects in {:?}", &self.workspace_roots); } - self.discovered_projects = discovered; + self.discovered_projects_from_filesystem = discovered; } pub fn remove_workspace(&mut self, path: &AbsPath) { @@ -1687,42 +1695,59 @@ impl Config { self.workspace_discoverConfig().as_ref() } - pub fn linked_or_discovered_projects(&self) -> Vec { - match self.linkedProjects().as_slice() { - [] => { - let exclude_dirs: Vec<_> = - self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); - self.discovered_projects - .iter() - .filter(|project| { - !exclude_dirs.iter().any(|p| project.manifest_path().starts_with(p)) - }) - .cloned() - .map(LinkedProject::from) - .collect() + fn discovered_projects(&self) -> Vec { + let exclude_dirs: Vec<_> = + self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); + + let mut projects = vec![]; + for fs_proj in &self.discovered_projects_from_filesystem { + let manifest_path = fs_proj.manifest_path(); + if exclude_dirs.iter().any(|p| manifest_path.starts_with(p)) { + continue; } - linked_projects => linked_projects - .iter() - .filter_map(|linked_project| match linked_project { - ManifestOrProjectJson::Manifest(it) => { - let path = self.root_path.join(it); - ProjectManifest::from_manifest_file(path) - .map_err(|e| tracing::error!("failed to load linked project: {}", e)) - .ok() - .map(Into::into) - } - ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile } => { - let root_path = - buildfile.parent().expect("Unable to get parent of buildfile"); - Some(ProjectJson::new(None, root_path, data.clone()).into()) - } - ManifestOrProjectJson::ProjectJson(it) => { - Some(ProjectJson::new(None, &self.root_path, it.clone()).into()) - } - }) - .collect(), + let buf: Utf8PathBuf = manifest_path.to_path_buf().into(); + projects.push(ManifestOrProjectJson::Manifest(buf)); + } + + for dis_proj in &self.discovered_projects_from_command { + projects.push(ManifestOrProjectJson::DiscoveredProjectJson { + data: dis_proj.data.clone(), + buildfile: dis_proj.buildfile.clone(), + }); } + + projects + } + + pub fn linked_or_discovered_projects(&self) -> Vec { + let linked_projects = self.linkedProjects(); + let projects = if linked_projects.is_empty() { + self.discovered_projects() + } else { + linked_projects.clone() + }; + + projects + .iter() + .filter_map(|linked_project| match linked_project { + ManifestOrProjectJson::Manifest(it) => { + let path = self.root_path.join(it); + ProjectManifest::from_manifest_file(path) + .map_err(|e| tracing::error!("failed to load linked project: {}", e)) + .ok() + .map(Into::into) + } + ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile } => { + let root_path = buildfile.parent().expect("Unable to get parent of buildfile"); + + Some(ProjectJson::new(None, root_path, data.clone()).into()) + } + ManifestOrProjectJson::ProjectJson(it) => { + Some(ProjectJson::new(None, &self.root_path, it.clone()).into()) + } + }) + .collect() } pub fn prefill_caches(&self) -> bool { @@ -2282,18 +2307,6 @@ where se.serialize_str(path.as_str()) } -impl ManifestOrProjectJson { - fn manifest(&self) -> Option<&Utf8Path> { - match self { - ManifestOrProjectJson::Manifest(manifest) => Some(manifest), - ManifestOrProjectJson::DiscoveredProjectJson { buildfile, .. } => { - Some(buildfile.as_ref()) - } - ManifestOrProjectJson::ProjectJson(_) => None, - } - } -} - #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ExprFillDefaultDef { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index ec71b4a7a122f..aa6ff20dc9b40 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -875,7 +875,7 @@ impl GlobalState { self.discover_workspace_queue.op_completed(()); let mut config = Config::clone(&*self.config); - config.add_linked_projects(project, buildfile); + config.add_discovered_project_from_command(project, buildfile); self.update_configuration(config); } DiscoverProjectMessage::Progress { message } => { From 9415e6e6eb9a570acecebd3dda0ed2b5db9fb910 Mon Sep 17 00:00:00 2001 From: Artem Belyakov Date: Sat, 27 Jul 2024 13:33:08 +0300 Subject: [PATCH 0295/1301] Add `manual_div_ceil` --- CHANGELOG.md | 1 + clippy_config/src/conf.rs | 2 +- clippy_config/src/msrvs.rs | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_div_ceil.rs | 158 +++++++++++++++++++ tests/ui/manual_div_ceil.fixed | 30 ++++ tests/ui/manual_div_ceil.rs | 30 ++++ tests/ui/manual_div_ceil.stderr | 35 ++++ tests/ui/manual_div_ceil_with_feature.fixed | 25 +++ tests/ui/manual_div_ceil_with_feature.rs | 25 +++ tests/ui/manual_div_ceil_with_feature.stderr | 47 ++++++ 12 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/manual_div_ceil.rs create mode 100644 tests/ui/manual_div_ceil.fixed create mode 100644 tests/ui/manual_div_ceil.rs create mode 100644 tests/ui/manual_div_ceil.stderr create mode 100644 tests/ui/manual_div_ceil_with_feature.fixed create mode 100644 tests/ui/manual_div_ceil_with_feature.rs create mode 100644 tests/ui/manual_div_ceil_with_feature.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 31fc74192ab11..44a569d1ab54a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5614,6 +5614,7 @@ Released 2018-09-13 [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index a6f1b958bfb17..6c1dd232593a1 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -864,7 +864,7 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width)) }); - let rows = (fields.len() + (columns - 1)) / columns; + let rows = fields.len().div_ceil(columns); let column_widths = (0..columns) .map(|column| { diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 0c673ba8046e6..a70effd637699 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -21,6 +21,7 @@ msrv_aliases! { 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } + 1,73,0 { MANUAL_DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e478ab330e8bc..4804399ef7da1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -301,6 +301,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, + crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO, crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a9d5f82dfd2cf..9514ba8c315db 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -203,6 +203,7 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; +mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; mod manual_is_ascii_check; @@ -936,5 +937,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); + store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs new file mode 100644 index 0000000000000..024c2547dc6eb --- /dev/null +++ b/clippy_lints/src/manual_div_ceil.rs @@ -0,0 +1,158 @@ +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::SpanlessEq; +use rustc_ast::{BinOpKind, LitKind}; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self}; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::Symbol; + +use clippy_config::Conf; + +declare_clippy_lint! { + /// ### What it does + /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation + /// of `x.div_ceil(y)`. + /// + /// ### Why is this bad? + /// It's simpler, clearer and more readable. + /// + /// ### Example + /// ```no_run + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = (x + (y - 1)) / y; + /// ``` + /// Use instead: + /// ```no_run + /// #![feature(int_roundings)] + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = x.div_ceil(y); + /// ``` + #[clippy::version = "1.81.0"] + pub MANUAL_DIV_CEIL, + complexity, + "manually reimplementing `div_ceil`" +} + +pub struct ManualDivCeil { + msrv: Msrv, +} + +impl ManualDivCeil { + #[must_use] + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]); + +impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if !self.msrv.meets(msrvs::MANUAL_DIV_CEIL) { + return; + } + + let mut applicability = Applicability::MachineApplicable; + + if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind + && div_op.node == BinOpKind::Div + && check_int_ty_and_feature(cx, div_lhs) + && check_int_ty_and_feature(cx, div_rhs) + && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind + { + // (x + (y - 1)) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, div_rhs) + { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + return; + } + + // ((y - 1) + x) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, div_rhs) + { + build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); + return; + } + + // (x + y - 1) / y + if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Sub + && add_op.node == BinOpKind::Add + && check_literal(inner_rhs) + && check_eq_expr(cx, add_rhs, div_rhs) + { + build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + match expr_ty.peel_refs().kind() { + ty::Uint(_) => true, + ty::Int(_) => cx + .tcx + .features() + .declared_features + .contains(&Symbol::intern("int_roundings")), + + _ => false, + } +} + +fn check_literal(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Int(Pu128(1), _) = lit.node + { + return true; + } + false +} + +fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { + SpanlessEq::new(cx).eq_expr(lhs, rhs) +} + +fn build_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + lhs: &Expr<'_>, + rhs: &Expr<'_>, + applicability: &mut Applicability, +) { + let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); + + let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})"); + + span_lint_and_sugg( + cx, + MANUAL_DIV_CEIL, + expr.span, + "manually reimplementing `div_ceil`", + "consider using `.div_ceil()`", + sugg, + *applicability, + ); +} diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed new file mode 100644 index 0000000000000..e7801f7376aaf --- /dev/null +++ b/tests/ui/manual_div_ceil.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::manual_div_ceil)] + +fn main() { + let x = 7_u32; + let y = 4_u32; + let z = 11_u32; + + // Lint + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + + let _ = 7_u32.div_ceil(4); //~ ERROR: manually reimplementing `div_ceil` + let _ = (7_i32 as u32).div_ceil(4); //~ ERROR: manually reimplementing `div_ceil` + + // No lint + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; + + let x_i = 7_i32; + let y_i = 4_i32; + let z_i = 11_i32; + + // No lint because `int_roundings` feature is not enabled. + let _ = (z as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (4 - 1)) / 4; +} diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs new file mode 100644 index 0000000000000..2de74c7eaa881 --- /dev/null +++ b/tests/ui/manual_div_ceil.rs @@ -0,0 +1,30 @@ +#![warn(clippy::manual_div_ceil)] + +fn main() { + let x = 7_u32; + let y = 4_u32; + let z = 11_u32; + + // Lint + let _ = (x + (y - 1)) / y; //~ ERROR: manually reimplementing `div_ceil` + let _ = ((y - 1) + x) / y; //~ ERROR: manually reimplementing `div_ceil` + let _ = (x + y - 1) / y; //~ ERROR: manually reimplementing `div_ceil` + + let _ = (7_u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil` + let _ = (7_i32 as u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil` + + // No lint + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; + + let x_i = 7_i32; + let y_i = 4_i32; + let z_i = 11_i32; + + // No lint because `int_roundings` feature is not enabled. + let _ = (z as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (4 - 1)) / 4; +} diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr new file mode 100644 index 0000000000000..dc652dff405f2 --- /dev/null +++ b/tests/ui/manual_div_ceil.stderr @@ -0,0 +1,35 @@ +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:9:13 + | +LL | let _ = (x + (y - 1)) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + | + = note: `-D clippy::manual-div-ceil` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:10:13 + | +LL | let _ = ((y - 1) + x) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:11:13 + | +LL | let _ = (x + y - 1) / y; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:13:13 + | +LL | let _ = (7_u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:14:13 + | +LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/manual_div_ceil_with_feature.fixed b/tests/ui/manual_div_ceil_with_feature.fixed new file mode 100644 index 0000000000000..a1d678c66898c --- /dev/null +++ b/tests/ui/manual_div_ceil_with_feature.fixed @@ -0,0 +1,25 @@ +#![warn(clippy::manual_div_ceil)] +#![feature(int_roundings)] + +fn main() { + let x = 7_i32; + let y = 4_i32; + let z = 3_i32; + let z_u: u32 = 11; + + // Lint. + let _ = x.div_ceil(y); + let _ = x.div_ceil(y); + let _ = x.div_ceil(y); + + let _ = 7_i32.div_ceil(4); + let _ = (7_i32 as u32).div_ceil(4); + let _ = (7_u32 as i32).div_ceil(4); + let _ = z_u.div_ceil(4); + + // No lint. + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; +} diff --git a/tests/ui/manual_div_ceil_with_feature.rs b/tests/ui/manual_div_ceil_with_feature.rs new file mode 100644 index 0000000000000..58cb1dbe34d1c --- /dev/null +++ b/tests/ui/manual_div_ceil_with_feature.rs @@ -0,0 +1,25 @@ +#![warn(clippy::manual_div_ceil)] +#![feature(int_roundings)] + +fn main() { + let x = 7_i32; + let y = 4_i32; + let z = 3_i32; + let z_u: u32 = 11; + + // Lint. + let _ = (x + (y - 1)) / y; + let _ = ((y - 1) + x) / y; + let _ = (x + y - 1) / y; + + let _ = (7_i32 + (4 - 1)) / 4; + let _ = (7_i32 as u32 + (4 - 1)) / 4; + let _ = (7_u32 as i32 + (4 - 1)) / 4; + let _ = (z_u + (4 - 1)) / 4; + + // No lint. + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; +} diff --git a/tests/ui/manual_div_ceil_with_feature.stderr b/tests/ui/manual_div_ceil_with_feature.stderr new file mode 100644 index 0000000000000..361ef9bd9f42a --- /dev/null +++ b/tests/ui/manual_div_ceil_with_feature.stderr @@ -0,0 +1,47 @@ +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:11:13 + | +LL | let _ = (x + (y - 1)) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + | + = note: `-D clippy::manual-div-ceil` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:12:13 + | +LL | let _ = ((y - 1) + x) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:13:13 + | +LL | let _ = (x + y - 1) / y; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:15:13 + | +LL | let _ = (7_i32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_i32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:16:13 + | +LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:17:13 + | +LL | let _ = (7_u32 as i32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_u32 as i32).div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:18:13 + | +LL | let _ = (z_u + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)` + +error: aborting due to 7 previous errors + From cb0b5199ffbeec62ff622349c3bc15d9b9772a50 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Thu, 5 Sep 2024 21:28:04 -0400 Subject: [PATCH 0296/1301] Add more SIMD intrinsics --- src/intrinsic/llvm.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 89d5d582e4c71..e57dc1cc75f60 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -13,6 +13,11 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( func_name: &str, original_function_name: Option<&String>, ) -> Cow<'b, [RValue<'gcc>]> { + // TODO: this might not be a good way to workaround the missing tile builtins. + if func_name == "__builtin_trap" { + return vec![].into(); + } + // Some LLVM intrinsics do not map 1-to-1 to GCC intrinsics, so we add the missing // arguments here. if gcc_func.get_param_count() != args.len() { @@ -287,7 +292,9 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( new_args.push(last_arg); args = new_args.into(); } - "__builtin_ia32_vfmaddsubps512_mask" | "__builtin_ia32_vfmaddsubpd512_mask" => { + "__builtin_ia32_vfmaddsubps512_mask" + | "__builtin_ia32_vfmaddsubpd512_mask" + | "__builtin_ia32_cmpsh_mask_round" => { let mut new_args = args.to_vec(); let last_arg = new_args.pop().expect("last arg"); let arg4_type = gcc_func.get_param_type(3); @@ -1085,6 +1092,17 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.mask.load.pd.512" => "__builtin_ia32_loadapd512_mask", "llvm.x86.avx512.mask.load.d.256" => "__builtin_ia32_movdqa32load256_mask", "llvm.x86.avx512.mask.load.q.256" => "__builtin_ia32_movdqa64load256_mask", + "llvm.x86.avx512fp16.mask.cmp.sh" => "__builtin_ia32_cmpsh_mask_round", + "llvm.x86.avx512fp16.vcomi.sh" => "__builtin_ia32_cmpsh_mask_round", + // TODO: support the tile builtins: + "llvm.x86.ldtilecfg" => "__builtin_trap", + "llvm.x86.sttilecfg" => "__builtin_trap", + "llvm.x86.tileloadd64" => "__builtin_trap", + "llvm.x86.tilerelease" => "__builtin_trap", + "llvm.x86.tilestored64" => "__builtin_trap", + "llvm.x86.tileloaddt164" => "__builtin_trap", + "llvm.x86.tilezero" => "__builtin_trap", + "llvm.x86.tdpbf16ps" => "__builtin_trap", // NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py _ => include!("archs.rs"), From d7996da9dae165c7d6e700f8b734392db30f9697 Mon Sep 17 00:00:00 2001 From: Sour1emon Date: Sat, 31 Aug 2024 16:30:03 -0700 Subject: [PATCH 0297/1301] Add `manual_is_power_of_two` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_is_power_of_two.rs | 88 ++++++++++++++++++++++ tests/ui/manual_is_power_of_two.fixed | 14 ++++ tests/ui/manual_is_power_of_two.rs | 14 ++++ tests/ui/manual_is_power_of_two.stderr | 17 +++++ 7 files changed, 137 insertions(+) create mode 100644 clippy_lints/src/manual_is_power_of_two.rs create mode 100644 tests/ui/manual_is_power_of_two.fixed create mode 100644 tests/ui/manual_is_power_of_two.rs create mode 100644 tests/ui/manual_is_power_of_two.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 44a569d1ab54a..f1de51c936ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5626,6 +5626,7 @@ Released 2018-09-13 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite [`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite +[`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4804399ef7da1..6f468f01b2fde 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -306,6 +306,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, + crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9514ba8c315db..bc16a3b0c014e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -207,6 +207,7 @@ mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; mod manual_is_ascii_check; +mod manual_is_power_of_two; mod manual_let_else; mod manual_main_separator_str; mod manual_non_exhaustive; @@ -938,5 +939,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); + store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs new file mode 100644 index 0000000000000..a65401fc571aa --- /dev/null +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -0,0 +1,88 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_ast::LitKind; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Uint; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0` which are manual + /// reimplementations of `x.is_power_of_two()`` + /// ### Why is this bad? + /// It's simpler and clearer + /// ### Example + /// ```no_run + /// let x: u32 = 1; + /// let result = x.count_ones() == 1; + /// ``` + /// Use instead: + /// ```no_run + /// let x: u32 = 1; + /// let result = x.is_power_of_two(); + /// ``` + #[clippy::version = "1.82.0"] + pub MANUAL_IS_POWER_OF_TWO, + complexity, + "manually reimplementing `is_power_of_two`" +} + +declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); + +impl LateLintPass<'_> for ManualIsPowerOfTwo { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + + // x.count_ones() == 1 + if let ExprKind::Binary(op, left, right) = expr.kind + && BinOpKind::Eq == op.node + && let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind + && method_name.ident.as_str() == "count_ones" + && let ExprKind::Lit(lit) = right.kind + && let LitKind::Int(Pu128(1), _) = lit.node + && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + { + let snippet = snippet_with_applicability(cx, reciever.span, "..", &mut applicability); + let sugg = format!("{snippet}.is_power_of_two()"); + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + sugg, + applicability, + ); + } + + // x & (x - 1) == 0 + if let ExprKind::Binary(op, left, right) = expr.kind + && BinOpKind::Eq == op.node + && let ExprKind::Binary(op1, left1, right1) = left.kind + && BinOpKind::BitAnd == op1.node + && let ExprKind::Binary(op2, left2, right2) = right1.kind + && BinOpKind::Sub == op2.node + && left1.span.eq_ctxt(left2.span) + && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() + && let ExprKind::Lit(lit) = right2.kind + && let LitKind::Int(Pu128(1), _) = lit.node + && let ExprKind::Lit(lit1) = right.kind + && let LitKind::Int(Pu128(0), _) = lit1.node + { + let snippet = snippet_with_applicability(cx, left1.span, "..", &mut applicability); + let sugg = format!("{snippet}.is_power_of_two()"); + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + sugg, + applicability, + ); + } + } +} diff --git a/tests/ui/manual_is_power_of_two.fixed b/tests/ui/manual_is_power_of_two.fixed new file mode 100644 index 0000000000000..beee2eaf665a9 --- /dev/null +++ b/tests/ui/manual_is_power_of_two.fixed @@ -0,0 +1,14 @@ +#![warn(clippy::manual_is_power_of_two)] + +fn main() { + let a = 16_u64; + + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + + let b = 4_i64; + + // is_power_of_two only works for unsigned integers + let _ = b.count_ones() == 1; + let _ = b & (b - 1) == 0; +} diff --git a/tests/ui/manual_is_power_of_two.rs b/tests/ui/manual_is_power_of_two.rs new file mode 100644 index 0000000000000..0810b4c28da75 --- /dev/null +++ b/tests/ui/manual_is_power_of_two.rs @@ -0,0 +1,14 @@ +#![warn(clippy::manual_is_power_of_two)] + +fn main() { + let a = 16_u64; + + let _ = a.count_ones() == 1; + let _ = a & (a - 1) == 0; + + let b = 4_i64; + + // is_power_of_two only works for unsigned integers + let _ = b.count_ones() == 1; + let _ = b & (b - 1) == 0; +} diff --git a/tests/ui/manual_is_power_of_two.stderr b/tests/ui/manual_is_power_of_two.stderr new file mode 100644 index 0000000000000..c7dfe6b11b99e --- /dev/null +++ b/tests/ui/manual_is_power_of_two.stderr @@ -0,0 +1,17 @@ +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:6:13 + | +LL | let _ = a.count_ones() == 1; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + | + = note: `-D clippy::manual-is-power-of-two` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:7:13 + | +LL | let _ = a & (a - 1) == 0; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: aborting due to 2 previous errors + From 5515566829f3b7ba3c302c5058e986657c01cc7e Mon Sep 17 00:00:00 2001 From: Isaac Bess Date: Mon, 2 Sep 2024 17:00:32 -0700 Subject: [PATCH 0298/1301] Improve "Why this is bad" section Co-authored-by: Ruby Lazuli --- clippy_lints/src/manual_is_power_of_two.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs index a65401fc571aa..daa1abf61f1a6 100644 --- a/clippy_lints/src/manual_is_power_of_two.rs +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0` which are manual /// reimplementations of `x.is_power_of_two()`` /// ### Why is this bad? - /// It's simpler and clearer + /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit. /// ### Example /// ```no_run /// let x: u32 = 1; From 6ecb48f6d0becb8ff365b787106a8be429b4637a Mon Sep 17 00:00:00 2001 From: Isaac Bess Date: Mon, 2 Sep 2024 17:01:20 -0700 Subject: [PATCH 0299/1301] Fixed extra backtick Co-authored-by: Ruby Lazuli --- clippy_lints/src/manual_is_power_of_two.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs index daa1abf61f1a6..c7ac668267afb 100644 --- a/clippy_lints/src/manual_is_power_of_two.rs +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -10,8 +10,8 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0` which are manual - /// reimplementations of `x.is_power_of_two()`` + /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, which are manual + /// reimplementations of `x.is_power_of_two()`. /// ### Why is this bad? /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit. /// ### Example From f994797e4612f97a0f489e794f30c2113ce8d61e Mon Sep 17 00:00:00 2001 From: Sour1emon Date: Thu, 5 Sep 2024 18:23:00 -0700 Subject: [PATCH 0300/1301] Add support for different orders of expression --- clippy_lints/src/manual_is_power_of_two.rs | 154 ++++++++++++++------- tests/ui/manual_is_power_of_two.fixed | 6 + tests/ui/manual_is_power_of_two.rs | 6 + tests/ui/manual_is_power_of_two.stderr | 26 +++- 4 files changed, 141 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs index c7ac668267afb..d7879403ebb5a 100644 --- a/clippy_lints/src/manual_is_power_of_two.rs +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::SpanlessEq; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -10,19 +11,19 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, which are manual + /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual /// reimplementations of `x.is_power_of_two()`. /// ### Why is this bad? /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit. /// ### Example /// ```no_run - /// let x: u32 = 1; - /// let result = x.count_ones() == 1; + /// let a: u32 = 4; + /// let result = a.count_ones() == 1; /// ``` /// Use instead: /// ```no_run - /// let x: u32 = 1; - /// let result = x.is_power_of_two(); + /// let a: u32 = 4; + /// let result = a.is_power_of_two(); /// ``` #[clippy::version = "1.82.0"] pub MANUAL_IS_POWER_OF_TWO, @@ -36,53 +37,106 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let mut applicability = Applicability::MachineApplicable; - // x.count_ones() == 1 - if let ExprKind::Binary(op, left, right) = expr.kind - && BinOpKind::Eq == op.node - && let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind - && method_name.ident.as_str() == "count_ones" - && let ExprKind::Lit(lit) = right.kind - && let LitKind::Int(Pu128(1), _) = lit.node - && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + if let ExprKind::Binary(bin_op, left, right) = expr.kind + && bin_op.node == BinOpKind::Eq { - let snippet = snippet_with_applicability(cx, reciever.span, "..", &mut applicability); - let sugg = format!("{snippet}.is_power_of_two()"); - span_lint_and_sugg( - cx, - MANUAL_IS_POWER_OF_TWO, - expr.span, - "manually reimplementing `is_power_of_two`", - "consider using `.is_power_of_two()`", - sugg, - applicability, - ); - } + // a.count_ones() == 1 + if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind + && method_name.ident.as_str() == "count_ones" + && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && check_lit(right, 1) + { + build_sugg(cx, expr, reciever, &mut applicability); + } - // x & (x - 1) == 0 - if let ExprKind::Binary(op, left, right) = expr.kind - && BinOpKind::Eq == op.node - && let ExprKind::Binary(op1, left1, right1) = left.kind - && BinOpKind::BitAnd == op1.node - && let ExprKind::Binary(op2, left2, right2) = right1.kind - && BinOpKind::Sub == op2.node - && left1.span.eq_ctxt(left2.span) - && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() - && let ExprKind::Lit(lit) = right2.kind - && let LitKind::Int(Pu128(1), _) = lit.node - && let ExprKind::Lit(lit1) = right.kind - && let LitKind::Int(Pu128(0), _) = lit1.node - { - let snippet = snippet_with_applicability(cx, left1.span, "..", &mut applicability); - let sugg = format!("{snippet}.is_power_of_two()"); - span_lint_and_sugg( - cx, - MANUAL_IS_POWER_OF_TWO, - expr.span, - "manually reimplementing `is_power_of_two`", - "consider using `.is_power_of_two()`", - sugg, - applicability, - ); + // 1 == a.count_ones() + if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind + && method_name.ident.as_str() == "count_ones" + && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && check_lit(left, 1) + { + build_sugg(cx, expr, reciever, &mut applicability); + } + + // a & (a - 1) == 0 + if let ExprKind::Binary(op1, left1, right1) = left.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = right1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, left1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() + && check_lit(right2, 1) + && check_lit(right, 0) + { + build_sugg(cx, expr, left1, &mut applicability); + } + + // (a - 1) & a == 0; + if let ExprKind::Binary(op1, left1, right1) = left.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = left1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, right1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() + && check_lit(right2, 1) + && check_lit(right, 0) + { + build_sugg(cx, expr, right1, &mut applicability); + } + + // 0 == a & (a - 1); + if let ExprKind::Binary(op1, left1, right1) = right.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = right1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, left1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() + && check_lit(right2, 1) + && check_lit(left, 0) + { + build_sugg(cx, expr, left1, &mut applicability); + } + + // 0 == (a - 1) & a + if let ExprKind::Binary(op1, left1, right1) = right.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = left1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, right1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() + && check_lit(right2, 1) + && check_lit(left, 0) + { + build_sugg(cx, expr, right1, &mut applicability); + } } } } + +fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, reciever: &Expr<'_>, applicability: &mut Applicability) { + let snippet = snippet_with_applicability(cx, reciever.span, "..", applicability); + + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + format!("{snippet}.is_power_of_two()"), + *applicability, + ); +} + +fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool { + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Int(Pu128(num), _) = lit.node + && num == expected_num + { + return true; + } + false +} + +fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { + SpanlessEq::new(cx).eq_expr(lhs, rhs) +} diff --git a/tests/ui/manual_is_power_of_two.fixed b/tests/ui/manual_is_power_of_two.fixed index beee2eaf665a9..dda5fe0ec3e77 100644 --- a/tests/ui/manual_is_power_of_two.fixed +++ b/tests/ui/manual_is_power_of_two.fixed @@ -6,6 +6,12 @@ fn main() { let _ = a.is_power_of_two(); let _ = a.is_power_of_two(); + // Test different orders of expression + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + let b = 4_i64; // is_power_of_two only works for unsigned integers diff --git a/tests/ui/manual_is_power_of_two.rs b/tests/ui/manual_is_power_of_two.rs index 0810b4c28da75..a1d3a95c4102b 100644 --- a/tests/ui/manual_is_power_of_two.rs +++ b/tests/ui/manual_is_power_of_two.rs @@ -6,6 +6,12 @@ fn main() { let _ = a.count_ones() == 1; let _ = a & (a - 1) == 0; + // Test different orders of expression + let _ = 1 == a.count_ones(); + let _ = (a - 1) & a == 0; + let _ = 0 == a & (a - 1); + let _ = 0 == (a - 1) & a; + let b = 4_i64; // is_power_of_two only works for unsigned integers diff --git a/tests/ui/manual_is_power_of_two.stderr b/tests/ui/manual_is_power_of_two.stderr index c7dfe6b11b99e..3cfc6297abf27 100644 --- a/tests/ui/manual_is_power_of_two.stderr +++ b/tests/ui/manual_is_power_of_two.stderr @@ -13,5 +13,29 @@ error: manually reimplementing `is_power_of_two` LL | let _ = a & (a - 1) == 0; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` -error: aborting due to 2 previous errors +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:10:13 + | +LL | let _ = 1 == a.count_ones(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:11:13 + | +LL | let _ = (a - 1) & a == 0; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:12:13 + | +LL | let _ = 0 == a & (a - 1); + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:13:13 + | +LL | let _ = 0 == (a - 1) & a; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: aborting due to 6 previous errors From 7e5a0e5777715dfc5a4ffafeb643c04cc438d4be Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 6 Sep 2024 08:21:08 +0200 Subject: [PATCH 0301/1301] fix: Catch panics from diagnostics computation --- .../crates/rust-analyzer/src/main_loop.rs | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index ec71b4a7a122f..4daf295a69d60 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -4,6 +4,7 @@ use std::{ fmt, ops::Div as _, + panic::AssertUnwindSafe, time::{Duration, Instant}, }; @@ -552,23 +553,33 @@ impl GlobalState { let fetch_semantic = self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some(); move |sender| { - let diags = fetch_native_diagnostics( - &snapshot, - subscriptions.clone(), - slice.clone(), - NativeDiagnosticsFetchKind::Syntax, - ); + // We aren't observing the semantics token cache here + let snapshot = AssertUnwindSafe(&snapshot); + let Ok(diags) = std::panic::catch_unwind(|| { + fetch_native_diagnostics( + &snapshot, + subscriptions.clone(), + slice.clone(), + NativeDiagnosticsFetchKind::Syntax, + ) + }) else { + return; + }; sender .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) .unwrap(); if fetch_semantic { - let diags = fetch_native_diagnostics( - &snapshot, - subscriptions, - slice, - NativeDiagnosticsFetchKind::Semantic, - ); + let Ok(diags) = std::panic::catch_unwind(|| { + fetch_native_diagnostics( + &snapshot, + subscriptions.clone(), + slice.clone(), + NativeDiagnosticsFetchKind::Semantic, + ) + }) else { + return; + }; sender .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic( generation, diags, From 775c5c84a5ce996520f9cad9dab1981fb8532d64 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 6 Sep 2024 09:13:00 +0200 Subject: [PATCH 0302/1301] fix: Don't panic lsp writer thread on dropped receiver --- src/tools/rust-analyzer/Cargo.lock | 12 ++++++------ src/tools/rust-analyzer/lib/lsp-server/Cargo.toml | 2 +- src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs | 10 ++++------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 85ef3a6ba92ca..9223a9cf4fd1e 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -998,23 +998,23 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lsp-server" version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" dependencies = [ "crossbeam-channel", - "ctrlc", "log", - "lsp-types", "serde", "serde_json", ] [[package]] name = "lsp-server" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +version = "0.7.7" dependencies = [ "crossbeam-channel", + "ctrlc", "log", + "lsp-types", "serde", "serde_json", ] @@ -1652,7 +1652,7 @@ dependencies = [ "intern", "itertools", "load-cargo", - "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.6", "lsp-types", "memchr", "mimalloc", diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index fb3411c8ab431..cce007ae54c51 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.6" +version = "0.7.7" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs index c28545fb57412..279a6bce08072 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs @@ -30,7 +30,9 @@ pub(crate) fn stdio_transport() -> (Sender, Receiver, IoThread let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit()); debug!("sending message {:#?}", msg); - reader_sender.send(msg).expect("receiver was dropped, failed to send a message"); + if let Err(e) = reader_sender.send(msg) { + return Err(io::Error::new(io::ErrorKind::Other, e)); + } if is_exit { break; @@ -60,15 +62,11 @@ impl IoThreads { pub fn join(self) -> io::Result<()> { match self.reader.join() { Ok(r) => r?, - Err(err) => { - println!("reader panicked!"); - std::panic::panic_any(err) - } + Err(err) => std::panic::panic_any(err), } match self.writer.join() { Ok(r) => r, Err(err) => { - println!("writer panicked!"); std::panic::panic_any(err); } } From 5f8823bf8f96c842e7915fe7d0dbf8ac2b10d316 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 6 Sep 2024 09:40:22 +0200 Subject: [PATCH 0303/1301] Bump lsp-server --- src/tools/rust-analyzer/Cargo.lock | 12 ++++++------ .../crates/rust-analyzer/src/global_state.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 9223a9cf4fd1e..6df73c2fd1bcf 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -997,12 +997,12 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lsp-server" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +version = "0.7.7" dependencies = [ "crossbeam-channel", + "ctrlc", "log", + "lsp-types", "serde", "serde_json", ] @@ -1010,11 +1010,11 @@ dependencies = [ [[package]] name = "lsp-server" version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9" dependencies = [ "crossbeam-channel", - "ctrlc", "log", - "lsp-types", "serde", "serde_json", ] @@ -1652,7 +1652,7 @@ dependencies = [ "intern", "itertools", "load-cargo", - "lsp-server 0.7.6", + "lsp-server 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "memchr", "mimalloc", diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 35e1da80bf673..89487aa673bf6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -540,7 +540,7 @@ impl GlobalState { } pub(crate) fn respond(&mut self, response: lsp_server::Response) { - if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { + if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { if let Some(err) = &response.error { if err.message.starts_with("server panicked") { self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)) From 2c8e24e9e81b80f96ad7198102a0279681d408d6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 6 Sep 2024 14:04:57 +0200 Subject: [PATCH 0304/1301] fix: Always explicitly set trait ref self types when lowering --- .../crates/hir-ty/src/consteval/tests.rs | 30 ++++++------ .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir-ty/src/infer/path.rs | 8 ++- .../rust-analyzer/crates/hir-ty/src/lower.rs | 49 +++++++++---------- .../crates/hir-ty/src/mir/eval.rs | 18 ++++++- .../crates/hir-ty/src/mir/lower.rs | 21 ++++---- .../src/handlers/invalid_cast.rs | 2 +- .../crates/test-fixture/src/lib.rs | 6 ++- .../crates/test-utils/src/minicore.rs | 8 +-- 9 files changed, 80 insertions(+), 64 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 203e21302cdda..7093fcadcb030 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -1556,7 +1556,7 @@ fn builtin_derive_macro() { Bar, } #[derive(Clone)] - struct X(i32, Z, i64) + struct X(i32, Z, i64); #[derive(Clone)] struct Y { field1: i32, @@ -1574,20 +1574,20 @@ fn builtin_derive_macro() { ); check_number( r#" - //- minicore: default, derive, builtin_impls - #[derive(Default)] - struct X(i32, Y, i64) - #[derive(Default)] - struct Y { - field1: i32, - field2: u8, - } +//- minicore: default, derive, builtin_impls +#[derive(Default)] +struct X(i32, Y, i64); +#[derive(Default)] +struct Y { + field1: i32, + field2: u8, +} - const GOAL: u8 = { - let x = X::default(); - x.1.field2 - }; - "#, +const GOAL: u8 = { + let x = X::default(); + x.1.field2 +}; +"#, 0, ); } @@ -2828,7 +2828,7 @@ fn type_error() { y.0 }; "#, - |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))), + |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::HasErrors)), ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a82d091cbb028..b79aa89db4c54 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -851,7 +851,7 @@ impl InferenceContext<'_> { }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { - self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); + *ty = self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); } TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 0b44bbec70f75..e4841c7b15b60 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -247,8 +247,12 @@ impl InferenceContext<'_> { &self.resolver, self.owner.into(), ); - let trait_ref = - ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None); + let trait_ref = ctx.lower_trait_ref_from_resolved_path( + trait_, + resolved_segment, + self.table.new_type_var(), + ); + self.resolve_trait_assoc_item(trait_ref, segment, id) } (def, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 213400d04a01e..c6c2108e34af4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -516,8 +516,11 @@ impl<'a> TyLoweringContext<'a> { TypeNs::TraitId(trait_) => { let ty = match remaining_segments.len() { 1 => { - let trait_ref = - self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None); + let trait_ref = self.lower_trait_ref_from_resolved_path( + trait_, + resolved_segment, + TyKind::Error.intern(Interner), + ); let segment = remaining_segments.first().unwrap(); let found = self .db @@ -952,11 +955,17 @@ impl<'a> TyLoweringContext<'a> { Substitution::from_iter(Interner, substs) } - fn lower_trait_ref_from_path( + pub(crate) fn lower_trait_ref_from_resolved_path( &self, - path: &Path, - explicit_self_ty: Option, - ) -> Option { + resolved: TraitId, + segment: PathSegment<'_>, + explicit_self_ty: Ty, + ) -> TraitRef { + let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty); + TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } + } + + fn lower_trait_ref_from_path(&self, path: &Path, explicit_self_ty: Ty) -> Option { let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { // FIXME(trait_alias): We need to handle trait alias here. TypeNs::TraitId(tr) => tr, @@ -966,21 +975,7 @@ impl<'a> TyLoweringContext<'a> { Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty)) } - pub(crate) fn lower_trait_ref_from_resolved_path( - &self, - resolved: TraitId, - segment: PathSegment<'_>, - explicit_self_ty: Option, - ) -> TraitRef { - let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty); - TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } - } - - fn lower_trait_ref( - &self, - trait_ref: &HirTraitRef, - explicit_self_ty: Option, - ) -> Option { + fn lower_trait_ref(&self, trait_ref: &HirTraitRef, explicit_self_ty: Ty) -> Option { self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty) } @@ -988,9 +983,9 @@ impl<'a> TyLoweringContext<'a> { &self, segment: PathSegment<'_>, resolved: TraitId, - explicit_self_ty: Option, + explicit_self_ty: Ty, ) -> Substitution { - self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty) + self.substs_from_path_segment(segment, Some(resolved.into()), false, Some(explicit_self_ty)) } pub(crate) fn lower_where_predicate<'b>( @@ -1041,7 +1036,7 @@ impl<'a> TyLoweringContext<'a> { let mut trait_ref = None; let clause = match bound.as_ref() { TypeBound::Path(path, TraitBoundModifier::None) => { - trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref = self.lower_trait_ref_from_path(path, self_ty); trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Path(path, TraitBoundModifier::Maybe) => { @@ -1053,7 +1048,7 @@ impl<'a> TyLoweringContext<'a> { // `?Sized` has no of them. // If we got another trait here ignore the bound completely. let trait_id = self - .lower_trait_ref_from_path(path, Some(self_ty.clone())) + .lower_trait_ref_from_path(path, self_ty.clone()) .map(|trait_ref| trait_ref.hir_trait_id()); if trait_id == sized_trait { self.unsized_types.borrow_mut().insert(self_ty); @@ -1062,7 +1057,7 @@ impl<'a> TyLoweringContext<'a> { } TypeBound::ForLifetime(_, path) => { // FIXME Don't silently drop the hrtb lifetimes here - trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref = self.lower_trait_ref_from_path(path, self_ty); trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Lifetime(l) => { @@ -2126,7 +2121,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< .with_type_param_mode(ParamLoweringMode::Variable); let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); let target_trait = impl_data.target_trait.as_ref()?; - Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?)) + Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?)) } pub(crate) fn return_type_impl_traits( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 89b2ecb1a63e5..0d42617d185c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -421,9 +421,25 @@ impl MirEvalError { } MirEvalError::MirLowerError(func, err) => { let function_name = db.function_data(*func); + let self_ = match func.lookup(db.upcast()).container { + ItemContainerId::ImplId(impl_id) => Some({ + let generics = crate::generics::generics(db.upcast(), impl_id.into()); + let substs = generics.placeholder_subst(db); + db.impl_self_ty(impl_id) + .substitute(Interner, &substs) + .display(db, edition) + .to_string() + }), + ItemContainerId::TraitId(it) => { + Some(db.trait_data(it).name.display(db.upcast(), edition).to_string()) + } + _ => None, + }; writeln!( f, - "MIR lowering for function `{}` ({:?}) failed due:", + "MIR lowering for function `{}{}{}` ({:?}) failed due:", + self_.as_deref().unwrap_or_default(), + if self_.is_some() { "::" } else { "" }, function_name.name.display(db.upcast(), edition), func )?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 9cee494bbe743..a2cb122c54391 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -94,7 +94,8 @@ pub enum MirLowerError { UnresolvedField, UnsizedTemporary(Ty), MissingFunctionDefinition(DefWithBodyId, ExprId), - TypeMismatch(Option), + TypeMismatch(TypeMismatch), + HasErrors, /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), NotSupported(String), @@ -179,15 +180,13 @@ impl MirLowerError { body.pretty_print_expr(db.upcast(), *owner, *it, edition) )?; } - MirLowerError::TypeMismatch(e) => match e { - Some(e) => writeln!( - f, - "Type mismatch: Expected {}, found {}", - e.expected.display(db, edition), - e.actual.display(db, edition), - )?, - None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?, - }, + MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?, + MirLowerError::TypeMismatch(e) => writeln!( + f, + "Type mismatch: Expected {}, found {}", + e.expected.display(db, edition), + e.actual.display(db, edition), + )?, MirLowerError::GenericArgNotProvided(id, subst) => { let parent = id.parent; let param = &db.generic_params(parent)[id.local_id]; @@ -2184,7 +2183,7 @@ pub fn lower_to_mir( root_expr: ExprId, ) -> Result { if infer.has_errors { - return Err(MirLowerError::TypeMismatch(None)); + return Err(MirLowerError::HasErrors); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 2f6f033f25c7e..9eb4e618c01e9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -831,7 +831,7 @@ fn main() { //- minicore: sized fn main() { _ = ((), ()) as (); - //^^^^^^^^^^^^^^ error: non-primitive cast: `(_, _)` as `()` + //^^^^^^^^^^^^^^ error: non-primitive cast: `((), ())` as `()` } "#, ); diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 03e85a898ab0c..faf9d22047d93 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -95,8 +95,10 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static { fn test_crate(&self) -> CrateId { let crate_graph = self.crate_graph(); let mut it = crate_graph.iter(); - let res = it.next().unwrap(); - assert!(it.next().is_none()); + let mut res = it.next().unwrap(); + while crate_graph[res].origin.is_lang() { + res = it.next().unwrap(); + } res } } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index fec270a556045..67629fcf7cc5e 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -171,7 +171,7 @@ pub mod default { macro_rules! impl_default { ($v:literal; $($t:ty)*) => { $( - impl const Default for $t { + impl Default for $t { fn default() -> Self { $v } @@ -686,7 +686,7 @@ pub mod ops { // endregion:fn // region:try mod try_ { - use super::super::convert::Infallible; + use crate::convert::Infallible; pub enum ControlFlow { #[lang = "Continue"] @@ -756,7 +756,7 @@ pub mod ops { // endregion:option // region:result // region:from - use super::super::convert::From; + use crate::convert::From; impl Try for Result { type Output = T; @@ -777,7 +777,7 @@ pub mod ops { impl> FromResidual> for Result { fn from_residual(residual: Result) -> Self { match residual { - Err(e) => Err(From::from(e)), + Err(e) => Err(F::from(e)), Ok(_) => loop {}, } } From bdc9da9361922a76317c35e33a0226f6152bea63 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 6 Sep 2024 14:44:05 +0200 Subject: [PATCH 0305/1301] fix: Properly prevent mir building with unknown types present --- src/tools/rust-analyzer/crates/hir-ty/src/infer.rs | 13 +++++++++++-- src/tools/rust-analyzer/crates/hir-ty/src/mir.rs | 4 ++-- .../src/handlers/mutability_errors.rs | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 0e68ab4bfec5c..8bc3c50725d99 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -708,7 +708,6 @@ impl<'a> InferenceContext<'a> { tuple_field_access_types: _, coercion_casts, } = &mut result; - table.fallback_if_possible(); // Comment from rustc: @@ -754,7 +753,7 @@ impl<'a> InferenceContext<'a> { *has_errors = *has_errors || ty.contains_unknown(); } - *has_errors = !type_mismatches.is_empty(); + *has_errors |= !type_mismatches.is_empty(); type_mismatches.retain(|_, mismatch| { mismatch.expected = table.resolve_completely(mismatch.expected.clone()); @@ -797,20 +796,30 @@ impl<'a> InferenceContext<'a> { }); for (_, subst) in method_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } for (_, subst) in assoc_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } for adjustment in expr_adjustments.values_mut().flatten() { adjustment.target = table.resolve_completely(adjustment.target.clone()); + *has_errors = *has_errors || adjustment.target.contains_unknown(); } for adjustment in pat_adjustments.values_mut().flatten() { *adjustment = table.resolve_completely(adjustment.clone()); + *has_errors = *has_errors || adjustment.contains_unknown(); } result.tuple_field_access_types = tuple_field_accesses_rev .into_iter() .enumerate() .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst))) + .inspect(|(_, subst)| { + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); + }) .collect(); result } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 8cd6ae1232691..8e815aabf2070 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -185,8 +185,8 @@ impl ProjectionElem { never!("Out of bound tuple field"); TyKind::Error.intern(Interner) }), - _ => { - never!("Only tuple has tuple field"); + ty => { + never!("Only tuple has tuple field: {:?}", ty); TyKind::Error.intern(Interner) } }, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e4b1f3ca95998..955427939150b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -824,13 +824,13 @@ fn f() { #[test] fn or_pattern() { + // FIXME: `None` is inferred as unknown here for some reason check_diagnostics( r#" //- minicore: option fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return }; - //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, From e1712ea92f008e3e58cecda8716cfb669f4e62ca Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 13 Jun 2024 21:37:19 -0400 Subject: [PATCH 0306/1301] Fix tools --- src/expr.rs | 12 ++++++------ src/macros.rs | 6 ++++-- src/types.rs | 2 +- tests/target/raw-lifetimes.rs | 15 +++++++++++++++ 4 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 tests/target/raw-lifetimes.rs diff --git a/src/expr.rs b/src/expr.rs index 6ba5217bc920d..75c75c523b08a 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -462,7 +462,7 @@ fn rewrite_empty_block( return None; } - let label_str = rewrite_label(label); + let label_str = rewrite_label(context, label); if attrs.map_or(false, |a| !inner_attributes(a).is_empty()) { return None; } @@ -527,7 +527,7 @@ fn rewrite_single_line_block( if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { let expr_shape = shape.offset_left(last_line_width(prefix))?; let expr_str = block_expr.rewrite(context, expr_shape)?; - let label_str = rewrite_label(label); + let label_str = rewrite_label(context, label); let result = format!("{prefix}{label_str}{{ {expr_str} }}"); if result.len() <= shape.width && !result.contains('\n') { return Some(result); @@ -562,7 +562,7 @@ pub(crate) fn rewrite_block_with_visitor( } let inner_attrs = attrs.map(inner_attributes); - let label_str = rewrite_label(label); + let label_str = rewrite_label(context, label); visitor.visit_block(block, inner_attrs.as_deref(), has_braces); let visitor_context = visitor.get_context(); context @@ -939,7 +939,7 @@ impl<'a> ControlFlow<'a> { fresh_shape }; - let label_string = rewrite_label(self.label); + let label_string = rewrite_label(context, self.label); // 1 = space after keyword. let offset = self.keyword.len() + label_string.len() + 1; @@ -1168,9 +1168,9 @@ impl<'a> Rewrite for ControlFlow<'a> { } } -fn rewrite_label(opt_label: Option) -> Cow<'static, str> { +fn rewrite_label(context: &RewriteContext<'_>, opt_label: Option) -> Cow<'static, str> { match opt_label { - Some(label) => Cow::from(format!("{}: ", label.ident)), + Some(label) => Cow::from(format!("{}: ", context.snippet(label.ident.span))), None => Cow::from(""), } } diff --git a/src/macros.rs b/src/macros.rs index b14b74a493814..524fc666faeef 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1074,7 +1074,7 @@ fn force_space_before(tok: &TokenKind) -> bool { fn ident_like(tok: &Token) -> bool { matches!( tok.kind, - TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_) + TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(..) ) } @@ -1099,7 +1099,9 @@ fn next_space(tok: &TokenKind) -> SpaceState { | TokenKind::OpenDelim(_) | TokenKind::CloseDelim(_) => SpaceState::Never, - TokenKind::Literal(..) | TokenKind::Ident(..) | TokenKind::Lifetime(_) => SpaceState::Ident, + TokenKind::Literal(..) | TokenKind::Ident(..) | TokenKind::Lifetime(..) => { + SpaceState::Ident + } _ => SpaceState::Always, } diff --git a/src/types.rs b/src/types.rs index 875c61113c528..7730aa467ce3a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -548,7 +548,7 @@ impl Rewrite for ast::AnonConst { impl Rewrite for ast::Lifetime { fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option { - Some(rewrite_ident(context, self.ident).to_owned()) + Some(context.snippet(self.ident.span).to_owned()) } } diff --git a/tests/target/raw-lifetimes.rs b/tests/target/raw-lifetimes.rs new file mode 100644 index 0000000000000..62fb9be6882c5 --- /dev/null +++ b/tests/target/raw-lifetimes.rs @@ -0,0 +1,15 @@ +// rustfmt-edition: 2021 + +// Simple idempotence test for raw lifetimes. + +fn test<'r#gen>() -> &'r#gen () { + // Test raw lifetimes... +} + +fn label() { + 'r#label: { + // Test raw labels. + } +} + +fn main() {} From e3ca249e9647396b9a3849e147aa53e168cf4c73 Mon Sep 17 00:00:00 2001 From: WeiTheShinobi Date: Fri, 6 Sep 2024 23:10:42 +0800 Subject: [PATCH 0307/1301] fix `single_match` suggestion --- clippy_lints/src/matches/single_match.rs | 24 ++++++++++++++--- tests/ui/single_match.fixed | 10 +++++-- tests/ui/single_match.rs | 15 +++++++++++ tests/ui/single_match.stderr | 33 +++++++++++++++++++++--- tests/ui/single_match_else.fixed | 2 +- tests/ui/single_match_else.stderr | 2 +- 6 files changed, 75 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 4bd46bced38e3..e7cef5bdbd767 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{expr_block, snippet, SpanRangeExt}; +use clippy_utils::source::{expr_block, snippet, snippet_block_with_context, SpanRangeExt}; use clippy_utils::ty::implements_trait; use clippy_utils::{ is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, @@ -9,7 +9,7 @@ use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_pat, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{sym, Span}; @@ -93,8 +93,24 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { let msg = "this pattern is irrefutable, `match` is useless"; - let sugg = expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app); - span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); + let (sugg, help) = if is_unit_expr(arm.body) { + (String::new(), "`match` expression can be removed") + } else { + let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app) + .0 + .to_string(); + if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) + && let StmtKind::Expr(_) = stmt.kind + && match arm.body.kind { + ExprKind::Block(block, _) => block.span.from_expansion(), + _ => true, + } + { + sugg.push(';'); + } + (sugg, "try") + }; + span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app); return; } diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index 04129ec0f13b9..dcf5a1d33c2ac 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -304,13 +304,19 @@ const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; fn irrefutable_match() { - { println!() } + println!(); - { println!() } + println!(); let i = 0; { let a = 1; let b = 2; } + + + + + + println!() } diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 2d51cee3fd995..3ba5eebd01b71 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -386,4 +386,19 @@ fn irrefutable_match() { }, _ => {}, } + + match i { + i => {}, + _ => {}, + } + + match i { + i => (), + _ => (), + } + + match CONST_I32 { + CONST_I32 => println!(), + _ => {}, + } } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 9578421d2cec5..9240b09c50a9f 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -223,7 +223,7 @@ LL | / match DATA { LL | | DATA => println!(), LL | | _ => {}, LL | | } - | |_____^ help: try: `{ println!() }` + | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless --> tests/ui/single_match.rs:376:5 @@ -232,7 +232,7 @@ LL | / match CONST_I32 { LL | | CONST_I32 => println!(), LL | | _ => {}, LL | | } - | |_____^ help: try: `{ println!() }` + | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless --> tests/ui/single_match.rs:382:5 @@ -254,5 +254,32 @@ LL + let b = 2; LL + } | -error: aborting due to 23 previous errors +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:390:5 + | +LL | / match i { +LL | | i => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: `match` expression can be removed + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:395:5 + | +LL | / match i { +LL | | i => (), +LL | | _ => (), +LL | | } + | |_____^ help: `match` expression can be removed + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:400:5 + | +LL | / match CONST_I32 { +LL | | CONST_I32 => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `println!()` + +error: aborting due to 26 previous errors diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index fb57411aaa426..c2ca746976bd1 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -173,5 +173,5 @@ fn issue_10808(bar: Option) { } fn irrefutable_match() -> Option<&'static ExprNode> { - { Some(&NODE) } + Some(&NODE) } diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 61209053fd0fc..a2801751a430a 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -207,7 +207,7 @@ LL | | let x = 5; LL | | None LL | | }, LL | | } - | |_____^ help: try: `{ Some(&NODE) }` + | |_____^ help: try: `Some(&NODE)` error: aborting due to 10 previous errors From 62f44d7f20cf939ae0bd487d2e70ac1411e9db88 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 6 Sep 2024 12:27:20 -0400 Subject: [PATCH 0308/1301] Add more SIMD intrinsics --- src/intrinsic/llvm.rs | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index e57dc1cc75f60..1a3385a97be89 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -418,6 +418,27 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( new_args.push(old_args.swap_remove(0)); args = new_args.into(); } + "__builtin_ia32_addph512_mask_round" + | "__builtin_ia32_subph512_mask_round" + | "__builtin_ia32_mulph512_mask_round" + | "__builtin_ia32_divph512_mask_round" => { + let mut new_args = args.to_vec(); + let last_arg = new_args.pop().expect("last arg"); + + let arg3_type = gcc_func.get_param_type(2); + let vector_type = arg3_type.dyncast_vector().expect("vector type"); + let zero = builder.context.new_rvalue_zero(vector_type.get_element_type()); + let num_units = vector_type.get_num_units(); + let first_arg = + builder.context.new_rvalue_from_vector(None, arg3_type, &vec![zero; num_units]); + new_args.push(first_arg); + + let arg4_type = gcc_func.get_param_type(3); + let minus_one = builder.context.new_rvalue_from_int(arg4_type, -1); + new_args.push(minus_one); + new_args.push(last_arg); + args = new_args.into(); + } _ => (), } } else { @@ -1094,6 +1115,23 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.mask.load.q.256" => "__builtin_ia32_movdqa64load256_mask", "llvm.x86.avx512fp16.mask.cmp.sh" => "__builtin_ia32_cmpsh_mask_round", "llvm.x86.avx512fp16.vcomi.sh" => "__builtin_ia32_cmpsh_mask_round", + "llvm.x86.avx512fp16.add.ph.512" => "__builtin_ia32_addph512_mask_round", + "llvm.x86.avx512fp16.sub.ph.512" => "__builtin_ia32_subph512_mask_round", + "llvm.x86.avx512fp16.mul.ph.512" => "__builtin_ia32_mulph512_mask_round", + "llvm.x86.avx512fp16.div.ph.512" => "__builtin_ia32_divph512_mask_round", + "llvm.x86.avx512fp16.mask.vfmul.cph.512" => "__builtin_ia32_vfmulcph512_mask_round", + "llvm.x86.avx512fp16.mask.vfmul.csh" => "__builtin_ia32_vfmulcsh_mask_round", + "llvm.x86.avx512fp16.mask.vfcmul.cph.512" => "__builtin_ia32_vfcmulcph512_mask_round", + "llvm.x86.avx512fp16.mask.vfcmul.csh" => "__builtin_ia32_vfcmulcsh_mask_round", + "llvm.x86.avx512fp16.mask.vfmadd.cph.512" => "__builtin_ia32_vfmaddcph512_mask3_round", + "llvm.x86.avx512fp16.maskz.vfmadd.cph.512" => "__builtin_ia32_vfmaddcph512_maskz_round", + "llvm.x86.avx512fp16.mask.vfmadd.csh" => "__builtin_ia32_vfmaddcsh_mask3_round", + "llvm.x86.avx512fp16.maskz.vfmadd.csh" => "__builtin_ia32_vfmaddcsh_maskz_round", + "llvm.x86.avx512fp16.mask.vfcmadd.cph.512" => "__builtin_ia32_vfcmaddcph512_mask3_round", + "llvm.x86.avx512fp16.maskz.vfcmadd.cph.512" => "__builtin_ia32_vfcmaddcph512_maskz_round", + "llvm.x86.avx512fp16.mask.vfcmadd.csh" => "__builtin_ia32_vfcmaddcsh_mask_round", + "llvm.x86.avx512fp16.maskz.vfcmadd.csh" => "__builtin_ia32_vfcmaddcsh_maskz_round", + // TODO: support the tile builtins: "llvm.x86.ldtilecfg" => "__builtin_trap", "llvm.x86.sttilecfg" => "__builtin_trap", @@ -1103,6 +1141,13 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.tileloaddt164" => "__builtin_trap", "llvm.x86.tilezero" => "__builtin_trap", "llvm.x86.tdpbf16ps" => "__builtin_trap", + "llvm.x86.tdpbssd" => "__builtin_trap", + "llvm.x86.tdpbsud" => "__builtin_trap", + "llvm.x86.tdpbusd" => "__builtin_trap", + "llvm.x86.tdpbuud" => "__builtin_trap", + "llvm.x86.tdpfp16ps" => "__builtin_trap", + "llvm.x86.tcmmimfp16ps" => "__builtin_trap", + "llvm.x86.tcmmrlfp16ps" => "__builtin_trap", // NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py _ => include!("archs.rs"), From bcc5a1c77e7453445040184b8879e9fb49f9adb0 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 6 Sep 2024 13:16:57 -0400 Subject: [PATCH 0309/1301] Update libgccjit version --- libgccjit.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgccjit.version b/libgccjit.version index fa2bacc2c8e72..e5f51a197a445 100644 --- a/libgccjit.version +++ b/libgccjit.version @@ -1 +1 @@ -bcafd46296f7898dac02d127e441b1d838ef2afc +a0cb76246d8d00ed9847d9874e5d5658049c332d From 97df334d5fd97725ffc2836c80a9bfae501085d1 Mon Sep 17 00:00:00 2001 From: Matthew Giordano Date: Fri, 6 Sep 2024 14:24:25 -0700 Subject: [PATCH 0310/1301] remove the Clone requirement --- library/alloc/src/rc.rs | 158 ++++++++++++++++---------------- library/alloc/src/sync.rs | 185 +++++++++++++++++++------------------- 2 files changed, 170 insertions(+), 173 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index f6d4174f5ebb5..664bc5ffc3441 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -766,6 +766,84 @@ impl Rc { } } + /// Constructs a new `Rc` in the given allocator while giving you a `Weak` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Rc` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic_in` first allocates the managed allocation for the `Rc`, + /// then calls your closure, giving it a `Weak` to this allocation, + /// and only afterwards completes the construction of the `Rc` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Rc` is not fully-constructed until `Rc::new_cyclic_in` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. + /// + /// # Examples + /// + /// See [`new_cyclic`]. + /// + /// [`new_cyclic`]: Rc::new_cyclic + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_cyclic_in(data_fn: F, alloc: A) -> Rc + where + F: FnOnce(&Weak) -> T, + { + // Note: comments and implementation are copied from Rc::new_cyclic. + + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( + RcBox { + strong: Cell::new(0), + weak: Cell::new(1), + value: mem::MaybeUninit::::uninit(), + }, + alloc, + )); + let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc: alloc }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + let strong = unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(ptr::addr_of_mut!((*inner).value), data); + + let prev_value = (*inner).strong.get(); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + (*inner).strong.set(1); + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + let alloc = weak.into_raw_with_allocator().1; + + Rc::from_inner_in(init_ptr, alloc) + }; + + strong + } + /// Constructs a new `Rc` in the provided allocator, returning an error if the allocation /// fails /// @@ -2264,86 +2342,6 @@ impl Clone for Rc { } } -impl Rc { - /// Constructs a new `Rc` in the given allocator while giving you a `Weak` to the allocation, - /// to allow you to construct a `T` which holds a weak pointer to itself. - /// - /// Generally, a structure circularly referencing itself, either directly or - /// indirectly, should not hold a strong reference to itself to prevent a memory leak. - /// Using this function, you get access to the weak pointer during the - /// initialization of `T`, before the `Rc` is created, such that you can - /// clone and store it inside the `T`. - /// - /// `new_cyclic` first allocates the managed allocation for the `Rc`, - /// then calls your closure, giving it a `Weak` to this allocation, - /// and only afterwards completes the construction of the `Rc` by placing - /// the `T` returned from your closure into the allocation. - /// - /// Since the new `Rc` is not fully-constructed until `Rc::new_cyclic_in` - /// returns, calling [`upgrade`] on the weak reference inside your closure will - /// fail and result in a `None` value. - /// - /// # Panics - /// - /// If `data_fn` panics, the panic is propagated to the caller, and the - /// temporary [`Weak`] is dropped normally. - /// - /// # Examples - /// - /// See [`new_cyclic`]. - /// - /// [`new_cyclic`]: Rc::new_cyclic - /// [`upgrade`]: Weak::upgrade - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_cyclic_in(data_fn: F, alloc: A) -> Rc - where - F: FnOnce(&Weak) -> T, - { - // Note: comments and implementation are copied from Rc::new_cyclic. - - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let uninit_ptr: NonNull<_> = Box::leak(Box::new_in( - RcBox { - strong: Cell::new(0), - weak: Cell::new(1), - value: mem::MaybeUninit::::uninit(), - }, - alloc.clone(), - )) - .into(); - - let init_ptr: NonNull> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc: alloc.clone() }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - let strong = unsafe { - let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).value), data); - - let prev_value = (*inner).strong.get(); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - (*inner).strong.set(1); - - Rc::from_inner_in(init_ptr, alloc) - }; - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - mem::forget(weak); - strong - } -} - #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Rc { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index f808f3313297b..4f8039fd1f8b8 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -785,6 +785,98 @@ impl Arc { } } + /// Constructs a new `Arc` in the given allocator while giving you a `Weak` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Arc` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic_in` first allocates the managed allocation for the `Arc`, + /// then calls your closure, giving it a `Weak` to this allocation, + /// and only afterwards completes the construction of the `Arc` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Arc` is not fully-constructed until `Arc::new_cyclic_in` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. + /// + /// # Example + /// + /// See [`new_cyclic`] + /// + /// [`new_cyclic`]: Arc::new_cyclic + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "arc_new_cyclic", since = "1.60.0")] + pub fn new_cyclic_in(data_fn: F, alloc: A) -> Arc + where + F: FnOnce(&Weak) -> T, + { + // Note: these comments and much of the implementation is copied from Arc::new_cyclic. + + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( + ArcInner { + strong: atomic::AtomicUsize::new(0), + weak: atomic::AtomicUsize::new(1), + data: mem::MaybeUninit::::uninit(), + }, + alloc, + )); + let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc: alloc }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + // Now we can properly initialize the inner value and turn our weak + // reference into a strong reference. + let strong = unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(ptr::addr_of_mut!((*inner).data), data); + + // The above write to the data field must be visible to any threads which + // observe a non-zero strong count. Therefore we need at least "Release" ordering + // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. + // + // "Acquire" ordering is not required. When considering the possible behaviours + // of `data_fn` we only need to look at what it could do with a reference to a + // non-upgradeable `Weak`: + // - It can *clone* the `Weak`, increasing the weak reference count. + // - It can drop those clones, decreasing the weak reference count (but never to zero). + // + // These side effects do not impact us in any way, and no other side effects are + // possible with safe code alone. + let prev_value = (*inner).strong.fetch_add(1, Release); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + let alloc = weak.into_raw_with_allocator().1; + + Arc::from_inner_in(init_ptr, alloc) + }; + + strong + } + /// Constructs a new `Pin>` in the provided allocator. If `T` does not implement `Unpin`, /// then `data` will be pinned in memory and unable to be moved. #[cfg(not(no_global_oom_handling))] @@ -1322,99 +1414,6 @@ impl Arc<[mem::MaybeUninit], A> { } } -impl Arc { - /// Constructs a new `Arc` in the given allocator while giving you a `Weak` to the allocation, - /// to allow you to construct a `T` which holds a weak pointer to itself. - /// - /// Generally, a structure circularly referencing itself, either directly or - /// indirectly, should not hold a strong reference to itself to prevent a memory leak. - /// Using this function, you get access to the weak pointer during the - /// initialization of `T`, before the `Arc` is created, such that you can - /// clone and store it inside the `T`. - /// - /// `new_cyclic` first allocates the managed allocation for the `Arc`, - /// then calls your closure, giving it a `Weak` to this allocation, - /// and only afterwards completes the construction of the `Arc` by placing - /// the `T` returned from your closure into the allocation. - /// - /// Since the new `Arc` is not fully-constructed until `Arc::new_cyclic_in` - /// returns, calling [`upgrade`] on the weak reference inside your closure will - /// fail and result in a `None` value. - /// - /// # Panics - /// - /// If `data_fn` panics, the panic is propagated to the caller, and the - /// temporary [`Weak`] is dropped normally. - /// - /// # Example - /// - /// See [`new_cyclic`] - /// - /// [`new_cyclic`]: Arc::new_cyclic - /// [`upgrade`]: Weak::upgrade - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "arc_new_cyclic", since = "1.60.0")] - pub fn new_cyclic_in(data_fn: F, alloc: A) -> Arc - where - F: FnOnce(&Weak) -> T, - { - // Note: these comments and much of the implementation is copied from Arc::new_cyclic. - - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let uninit_ptr: NonNull<_> = Box::leak(Box::new_in( - ArcInner { - strong: atomic::AtomicUsize::new(0), - weak: atomic::AtomicUsize::new(1), - data: mem::MaybeUninit::::uninit(), - }, - alloc.clone(), - )) - .into(); - let init_ptr: NonNull> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc: alloc.clone() }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - // Now we can properly initialize the inner value and turn our weak - // reference into a strong reference. - let strong = unsafe { - let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).data), data); - - // The above write to the data field must be visible to any threads which - // observe a non-zero strong count. Therefore we need at least "Release" ordering - // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. - // - // "Acquire" ordering is not required. When considering the possible behaviours - // of `data_fn` we only need to look at what it could do with a reference to a - // non-upgradeable `Weak`: - // - It can *clone* the `Weak`, increasing the weak reference count. - // - It can drop those clones, decreasing the weak reference count (but never to zero). - // - // These side effects do not impact us in any way, and no other side effects are - // possible with safe code alone. - let prev_value = (*inner).strong.fetch_add(1, Release); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - - Arc::from_inner_in(init_ptr, alloc) - }; - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - mem::forget(weak); - strong - } -} - impl Arc { /// Constructs an `Arc` from a raw pointer. /// From 550e55fec5cf3a11cf5b7372fda45465e4e6b4d0 Mon Sep 17 00:00:00 2001 From: Matthew Giordano Date: Fri, 6 Sep 2024 15:20:41 -0700 Subject: [PATCH 0311/1301] Remove duplicate impl --- library/alloc/src/rc.rs | 41 +++--------------------------- library/alloc/src/sync.rs | 53 +++------------------------------------ 2 files changed, 6 insertions(+), 88 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 664bc5ffc3441..719f154141b56 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -460,42 +460,7 @@ impl Rc { where F: FnOnce(&Weak) -> T, { - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let uninit_ptr: NonNull<_> = Box::leak(Box::new(RcBox { - strong: Cell::new(0), - weak: Cell::new(1), - value: mem::MaybeUninit::::uninit(), - })) - .into(); - - let init_ptr: NonNull> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc: Global }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - let strong = unsafe { - let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).value), data); - - let prev_value = (*inner).strong.get(); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - (*inner).strong.set(1); - - Rc::from_inner(init_ptr) - }; - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - mem::forget(weak); - strong + Self::new_cyclic_in(data_fn, Global) } /// Constructs a new `Rc` with uninitialized contents. @@ -801,8 +766,6 @@ impl Rc { where F: FnOnce(&Weak) -> T, { - // Note: comments and implementation are copied from Rc::new_cyclic. - // Construct the inner in the "uninitialized" state with a single // weak reference. let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( @@ -836,6 +799,8 @@ impl Rc { // Strong references should collectively own a shared weak reference, // so don't run the destructor for our old weak reference. + // Calling into_raw_with_allocator has the double effect of giving us back the allocator, + // and forgetting the weak reference. let alloc = weak.into_raw_with_allocator().1; Rc::from_inner_in(init_ptr, alloc) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 4f8039fd1f8b8..496865e303b40 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -450,54 +450,7 @@ impl Arc { where F: FnOnce(&Weak) -> T, { - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let uninit_ptr: NonNull<_> = Box::leak(Box::new(ArcInner { - strong: atomic::AtomicUsize::new(0), - weak: atomic::AtomicUsize::new(1), - data: mem::MaybeUninit::::uninit(), - })) - .into(); - let init_ptr: NonNull> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc: Global }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - // Now we can properly initialize the inner value and turn our weak - // reference into a strong reference. - let strong = unsafe { - let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).data), data); - - // The above write to the data field must be visible to any threads which - // observe a non-zero strong count. Therefore we need at least "Release" ordering - // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. - // - // "Acquire" ordering is not required. When considering the possible behaviours - // of `data_fn` we only need to look at what it could do with a reference to a - // non-upgradeable `Weak`: - // - It can *clone* the `Weak`, increasing the weak reference count. - // - It can drop those clones, decreasing the weak reference count (but never to zero). - // - // These side effects do not impact us in any way, and no other side effects are - // possible with safe code alone. - let prev_value = (*inner).strong.fetch_add(1, Release); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - - Arc::from_inner(init_ptr) - }; - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - mem::forget(weak); - strong + Self::new_cyclic_in(data_fn, Global) } /// Constructs a new `Arc` with uninitialized contents. @@ -821,8 +774,6 @@ impl Arc { where F: FnOnce(&Weak) -> T, { - // Note: these comments and much of the implementation is copied from Arc::new_cyclic. - // Construct the inner in the "uninitialized" state with a single // weak reference. let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( @@ -869,6 +820,8 @@ impl Arc { // Strong references should collectively own a shared weak reference, // so don't run the destructor for our old weak reference. + // Calling into_raw_with_allocator has the double effect of giving us back the allocator, + // and forgetting the weak reference. let alloc = weak.into_raw_with_allocator().1; Arc::from_inner_in(init_ptr, alloc) From 46f8d360de99afe38dacc1dd6e29133d856e9fd3 Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Fri, 26 Jul 2024 00:51:30 +0530 Subject: [PATCH 0312/1301] Lint ready --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/non_zero_suggestions.rs | 107 +++++++++++++++++++++++ tests/ui/non_zero_suggestions.rs | 52 +++++++++++ 5 files changed, 163 insertions(+) create mode 100644 clippy_lints/src/non_zero_suggestions.rs create mode 100644 tests/ui/non_zero_suggestions.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f1de51c936ea0..0e5d1688e4a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5773,6 +5773,7 @@ Released 2018-09-13 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty +[`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 6f468f01b2fde..16c64830e70dd 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -557,6 +557,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::non_expressive_names::SIMILAR_NAMES_INFO, crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO, crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bc16a3b0c014e..3604090b68ccc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -273,6 +273,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; +mod non_zero_suggestions; mod nonstandard_macro_braces; mod octal_escapes; mod only_used_in_recursion; @@ -940,5 +941,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); + store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs new file mode 100644 index 0000000000000..b9e7053db900f --- /dev/null +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -0,0 +1,107 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```no_run + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```no_run + /// // example code which does not raise clippy warning + /// ``` + #[clippy::version = "1.81.0"] + pub NON_ZERO_SUGGESTIONS, + restriction, + "suggests using `NonZero#` from `u#` or `i#` for more efficient and type-safe conversions" +} + +declare_lint_pass!(NonZeroSuggestions => [NON_ZERO_SUGGESTIONS]); + +impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Call(func, [arg]) = expr.kind { + if let ExprKind::Path(qpath) = &func.kind { + if let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() { + let fn_name = cx.tcx.item_name(def_id); + let target_ty = cx.typeck_results().expr_ty(expr); + + if let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind { + let receiver_ty = cx.typeck_results().expr_ty(receiver); + if let ty::Adt(adt_def, _) = receiver_ty.kind() { + if adt_def.is_struct() && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) { + if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet); + } + } + } + } + } + } + } + } +} + +fn get_arg_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, rcv_path: &rustc_hir::PathSegment<'_>) -> String { + let arg_snippet = snippet(cx, arg.span, ".."); + if let Some(index) = arg_snippet.rfind(&format!(".{}", rcv_path.ident.name)) { + arg_snippet[..index].trim().to_string() + } else { + arg_snippet.to_string() + } +} + +fn suggest_non_zero_conversion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + fn_name: rustc_span::Symbol, + target_non_zero_type: &str, + arg_snippet: &str, +) { + let suggestion = format!("{}::{}({})", target_non_zero_type, fn_name, arg_snippet); + span_lint_and_sugg( + cx, + NON_ZERO_SUGGESTIONS, + expr.span, + format!( + "Consider using `{}::{}()` for more efficient and type-safe conversion", + target_non_zero_type, fn_name + ), + "Replace with", + suggestion, + Applicability::MachineApplicable, + ); +} + +fn get_target_non_zero_type(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Uint(uint_ty) => Some(match uint_ty { + ty::UintTy::U8 => "NonZeroU8", + ty::UintTy::U16 => "NonZeroU16", + ty::UintTy::U32 => "NonZeroU32", + ty::UintTy::U64 => "NonZeroU64", + ty::UintTy::U128 => "NonZeroU128", + ty::UintTy::Usize => "NonZeroUsize", + }), + ty::Int(int_ty) => Some(match int_ty { + ty::IntTy::I8 => "NonZeroI8", + ty::IntTy::I16 => "NonZeroI16", + ty::IntTy::I32 => "NonZeroI32", + ty::IntTy::I64 => "NonZeroI64", + ty::IntTy::I128 => "NonZeroI128", + ty::IntTy::Isize => "NonZeroIsize", + }), + _ => None, + } +} diff --git a/tests/ui/non_zero_suggestions.rs b/tests/ui/non_zero_suggestions.rs new file mode 100644 index 0000000000000..1a5ee40dc3da8 --- /dev/null +++ b/tests/ui/non_zero_suggestions.rs @@ -0,0 +1,52 @@ +#![warn(clippy::non_zero_suggestions)] + +use std::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, + NonZeroU64, NonZeroU8, NonZeroUsize, +}; + +fn main() { + // Basic cases + let _ = u8::try_from(NonZeroU8::new(5).unwrap().get()); + + let _ = u16::from(NonZeroU16::new(10).unwrap().get()); + + // Different integer types + let _ = u32::from(NonZeroU32::new(15).unwrap().get()); + + let _ = u64::from(NonZeroU64::new(20).unwrap().get()); + + let _ = u128::from(NonZeroU128::new(25).unwrap().get()); + + let _ = usize::from(NonZeroUsize::new(30).unwrap().get()); + + // Signed integer types + let _ = i8::try_from(NonZeroI8::new(-5).unwrap().get()); + + let _ = i16::from(NonZeroI16::new(-10).unwrap().get()); + + let _ = i32::from(NonZeroI32::new(-15).unwrap().get()); + + // Edge cases + + // Complex expression + let _ = u8::from(NonZeroU8::new(5).unwrap().get() + 1); + + // Function call + fn get_non_zero() -> NonZeroU8 { + NonZeroU8::new(42).unwrap() + } + let _ = u8::from(get_non_zero().get()); + + // Method chaining + let _ = u16::from(NonZeroU16::new(100).unwrap().get().checked_add(1).unwrap()); + // This should not trigger the lint + + // Different conversion methods + let _ = u32::try_from(NonZeroU32::new(200).unwrap().get()).unwrap(); + + // Cases that should not trigger the lint + let _ = u8::from(5); + let _ = u16::from(10u8); + let _ = i32::try_from(40u32).unwrap(); +} From 73039f654e2efc3a7384462dcd46289851b80e62 Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Fri, 26 Jul 2024 17:11:51 +0530 Subject: [PATCH 0313/1301] test cases added including some edge cases --- clippy_lints/src/non_zero_suggestions.rs | 22 ++++++- tests/ui/non_zero_suggestions.fixed | 69 ++++++++++++++++++++ tests/ui/non_zero_suggestions.rs | 83 ++++++++++++++---------- tests/ui/non_zero_suggestions.stderr | 59 +++++++++++++++++ 4 files changed, 198 insertions(+), 35 deletions(-) create mode 100644 tests/ui/non_zero_suggestions.fixed create mode 100644 tests/ui/non_zero_suggestions.stderr diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index b9e7053db900f..715d9eda6eeb3 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -9,16 +9,34 @@ use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does + /// Checks for conversions from `NonZero` types to regular integer types, + /// and suggests using `NonZero` types for the target as well. /// /// ### Why is this bad? + /// Converting from `NonZero` types to regular integer types and then back to `NonZero` + /// types is less efficient and loses the type-safety guarantees provided by `NonZero` types. + /// Using `NonZero` types consistently can lead to more optimized code and prevent + /// certain classes of errors related to zero values. /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Bad: Converting NonZeroU32 to u64 unnecessarily + /// let r1 = x / u64::from(y.get()); + /// let r2 = x % u64::from(y.get()); + /// } /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Good: Preserving the NonZero property + /// let r1 = x / NonZeroU64::from(y); + /// let r2 = x % NonZeroU64::from(y); + /// } /// ``` #[clippy::version = "1.81.0"] pub NON_ZERO_SUGGESTIONS, diff --git a/tests/ui/non_zero_suggestions.fixed b/tests/ui/non_zero_suggestions.fixed new file mode 100644 index 0000000000000..b33de1ef03f5d --- /dev/null +++ b/tests/ui/non_zero_suggestions.fixed @@ -0,0 +1,69 @@ +#![warn(clippy::non_zero_suggestions)] + +use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; + +fn main() { + // Positive test cases (lint should trigger) + + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / NonZeroU64::from(y); + let r2 = x % NonZeroU64::from(y); + + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / NonZeroU32::from(b); + + // I8 -> I16 + let c: i16 = 25; + let d = NonZeroI8::new(3).unwrap(); + let r4 = NonZeroI16::from(d); + + // Different operations + let m: u64 = 400; + let n = NonZeroU32::new(20).unwrap(); + let r5 = m / NonZeroU64::from(n); + + // Edge cases + + // Using the max value of a type + let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); + let r6 = NonZeroU64::from(max_u32); + + // Chained method calls + let _ = NonZeroU64::from(NonZeroU32::new(10).unwrap()); + + // Negative test cases (lint should not trigger) + + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r10 = e / f.get(); + + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r11 = g / u64::from(h); + + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r12 = k / NonZeroU64::from(l); +} + +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / NonZeroU64::from(y) +} + +struct Calculator { + value: u64, +} + +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / NonZeroU64::from(divisor) + } +} diff --git a/tests/ui/non_zero_suggestions.rs b/tests/ui/non_zero_suggestions.rs index 1a5ee40dc3da8..27089eaf86ef5 100644 --- a/tests/ui/non_zero_suggestions.rs +++ b/tests/ui/non_zero_suggestions.rs @@ -1,52 +1,69 @@ #![warn(clippy::non_zero_suggestions)] -use std::num::{ - NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, - NonZeroU64, NonZeroU8, NonZeroUsize, -}; +use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; fn main() { - // Basic cases - let _ = u8::try_from(NonZeroU8::new(5).unwrap().get()); + // Positive test cases (lint should trigger) - let _ = u16::from(NonZeroU16::new(10).unwrap().get()); + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / u64::from(y.get()); + let r2 = x % u64::from(y.get()); - // Different integer types - let _ = u32::from(NonZeroU32::new(15).unwrap().get()); + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / u32::from(b.get()); - let _ = u64::from(NonZeroU64::new(20).unwrap().get()); + // I8 -> I16 + let c: i16 = 25; + let d = NonZeroI8::new(3).unwrap(); + let r4 = i16::from(d.get()); - let _ = u128::from(NonZeroU128::new(25).unwrap().get()); + // Different operations + let m: u64 = 400; + let n = NonZeroU32::new(20).unwrap(); + let r5 = m / u64::from(n.get()); - let _ = usize::from(NonZeroUsize::new(30).unwrap().get()); + // Edge cases - // Signed integer types - let _ = i8::try_from(NonZeroI8::new(-5).unwrap().get()); + // Using the max value of a type + let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); + let r6 = u64::from(max_u32.get()); - let _ = i16::from(NonZeroI16::new(-10).unwrap().get()); + // Chained method calls + let _ = u64::from(NonZeroU32::new(10).unwrap().get()); - let _ = i32::from(NonZeroI32::new(-15).unwrap().get()); + // Negative test cases (lint should not trigger) - // Edge cases + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r10 = e / f.get(); - // Complex expression - let _ = u8::from(NonZeroU8::new(5).unwrap().get() + 1); + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r11 = g / u64::from(h); - // Function call - fn get_non_zero() -> NonZeroU8 { - NonZeroU8::new(42).unwrap() - } - let _ = u8::from(get_non_zero().get()); + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r12 = k / NonZeroU64::from(l); +} - // Method chaining - let _ = u16::from(NonZeroU16::new(100).unwrap().get().checked_add(1).unwrap()); - // This should not trigger the lint +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / u64::from(y.get()) +} - // Different conversion methods - let _ = u32::try_from(NonZeroU32::new(200).unwrap().get()).unwrap(); +struct Calculator { + value: u64, +} - // Cases that should not trigger the lint - let _ = u8::from(5); - let _ = u16::from(10u8); - let _ = i32::try_from(40u32).unwrap(); +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / u64::from(divisor.get()) + } } diff --git a/tests/ui/non_zero_suggestions.stderr b/tests/ui/non_zero_suggestions.stderr new file mode 100644 index 0000000000000..b30f9dd1e5ce2 --- /dev/null +++ b/tests/ui/non_zero_suggestions.stderr @@ -0,0 +1,59 @@ +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:11:18 + | +LL | let r1 = x / u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + | + = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:12:18 + | +LL | let r2 = x % u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + +error: Consider using `NonZeroU32::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:17:18 + | +LL | let r3 = a / u32::from(b.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU32::from(b)` + +error: Consider using `NonZeroI16::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:22:14 + | +LL | let r4 = i16::from(d.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroI16::from(d)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:27:18 + | +LL | let r5 = m / u64::from(n.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(n)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:33:14 + | +LL | let r6 = u64::from(max_u32.get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(max_u32)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:36:13 + | +LL | let _ = u64::from(NonZeroU32::new(10).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(NonZeroU32::new(10).unwrap())` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:58:9 + | +LL | x / u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:67:22 + | +LL | self.value / u64::from(divisor.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(divisor)` + +error: aborting due to 9 previous errors + From 6f01273d5d2acec2a726f2115fbf557967c2336a Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Fri, 26 Jul 2024 17:46:41 +0530 Subject: [PATCH 0314/1301] dogfood test passed --- clippy_lints/src/non_zero_suggestions.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index 715d9eda6eeb3..e652f6f510809 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -87,15 +87,12 @@ fn suggest_non_zero_conversion( target_non_zero_type: &str, arg_snippet: &str, ) { - let suggestion = format!("{}::{}({})", target_non_zero_type, fn_name, arg_snippet); + let suggestion = format!("{target_non_zero_type}::{fn_name}({arg_snippet})"); span_lint_and_sugg( cx, NON_ZERO_SUGGESTIONS, expr.span, - format!( - "Consider using `{}::{}()` for more efficient and type-safe conversion", - target_non_zero_type, fn_name - ), + format!("Consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"), "Replace with", suggestion, Applicability::MachineApplicable, From 0f99aa992ea19b0148767c137ff1a936d58b65e0 Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Fri, 26 Jul 2024 18:16:08 +0530 Subject: [PATCH 0315/1301] all tests passed --- clippy_lints/src/non_zero_suggestions.rs | 4 +-- tests/ui/non_zero_suggestions.stderr | 36 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index e652f6f510809..fc2920868d492 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -92,8 +92,8 @@ fn suggest_non_zero_conversion( cx, NON_ZERO_SUGGESTIONS, expr.span, - format!("Consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"), - "Replace with", + format!("consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"), + "replace with", suggestion, Applicability::MachineApplicable, ); diff --git a/tests/ui/non_zero_suggestions.stderr b/tests/ui/non_zero_suggestions.stderr index b30f9dd1e5ce2..4c86720c3086b 100644 --- a/tests/ui/non_zero_suggestions.stderr +++ b/tests/ui/non_zero_suggestions.stderr @@ -1,59 +1,59 @@ -error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:11:18 | LL | let r1 = x / u64::from(y.get()); - | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` | = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` -error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:12:18 | LL | let r2 = x % u64::from(y.get()); - | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` -error: Consider using `NonZeroU32::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU32::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:17:18 | LL | let r3 = a / u32::from(b.get()); - | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU32::from(b)` + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU32::from(b)` -error: Consider using `NonZeroI16::from()` for more efficient and type-safe conversion +error: consider using `NonZeroI16::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:22:14 | LL | let r4 = i16::from(d.get()); - | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroI16::from(d)` + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroI16::from(d)` -error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:27:18 | LL | let r5 = m / u64::from(n.get()); - | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(n)` + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)` -error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:33:14 | LL | let r6 = u64::from(max_u32.get()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(max_u32)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(max_u32)` -error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:36:13 | LL | let _ = u64::from(NonZeroU32::new(10).unwrap().get()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(NonZeroU32::new(10).unwrap())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(10).unwrap())` -error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:58:9 | LL | x / u64::from(y.get()) - | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` -error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion --> tests/ui/non_zero_suggestions.rs:67:22 | LL | self.value / u64::from(divisor.get()) - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(divisor)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(divisor)` error: aborting due to 9 previous errors From bed44418eca84e81c7efa7a3ee45f450b8a834f4 Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Sat, 27 Jul 2024 02:35:16 +0530 Subject: [PATCH 0316/1301] refactored the code --- clippy_lints/src/non_zero_suggestions.rs | 31 ++++++++++++------------ 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index fc2920868d492..1624c365ee948 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -48,23 +48,22 @@ declare_lint_pass!(NonZeroSuggestions => [NON_ZERO_SUGGESTIONS]); impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Call(func, [arg]) = expr.kind { - if let ExprKind::Path(qpath) = &func.kind { - if let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() { - let fn_name = cx.tcx.item_name(def_id); - let target_ty = cx.typeck_results().expr_ty(expr); + if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(qpath) = &func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind + { + let fn_name = cx.tcx.item_name(def_id); + let target_ty = cx.typeck_results().expr_ty(expr); + let receiver_ty = cx.typeck_results().expr_ty(receiver); - if let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind { - let receiver_ty = cx.typeck_results().expr_ty(receiver); - if let ty::Adt(adt_def, _) = receiver_ty.kind() { - if adt_def.is_struct() && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) { - if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - let arg_snippet = get_arg_snippet(cx, arg, rcv_path); - suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet); - } - } - } - } + if let ty::Adt(adt_def, _) = receiver_ty.kind() + && adt_def.is_struct() + && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) + { + if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet); } } } From c6c74083a803f32ffe2447fdcd0afaafaa03179c Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Sat, 27 Jul 2024 03:15:40 +0530 Subject: [PATCH 0317/1301] error notations added --- tests/ui/non_zero_suggestions.fixed | 25 +++++++++++++++++++------ tests/ui/non_zero_suggestions.rs | 25 +++++++++++++++++++------ tests/ui/non_zero_suggestions.stderr | 26 ++++++++++++++++---------- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/tests/ui/non_zero_suggestions.fixed b/tests/ui/non_zero_suggestions.fixed index b33de1ef03f5d..d7e6b19edc1f6 100644 --- a/tests/ui/non_zero_suggestions.fixed +++ b/tests/ui/non_zero_suggestions.fixed @@ -3,40 +3,45 @@ use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; fn main() { - // Positive test cases (lint should trigger) - + /// Positive test cases (lint should trigger) // U32 -> U64 let x: u64 = 100; let y = NonZeroU32::new(10).unwrap(); let r1 = x / NonZeroU64::from(y); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + let r2 = x % NonZeroU64::from(y); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion // U16 -> U32 let a: u32 = 50; let b = NonZeroU16::new(5).unwrap(); let r3 = a / NonZeroU32::from(b); + //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion // I8 -> I16 let c: i16 = 25; let d = NonZeroI8::new(3).unwrap(); let r4 = NonZeroI16::from(d); + //~^ ERROR: consider using `NonZeroI16::from()` for more efficient and type-safe conversion // Different operations let m: u64 = 400; let n = NonZeroU32::new(20).unwrap(); let r5 = m / NonZeroU64::from(n); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - // Edge cases - + /// Edge cases // Using the max value of a type let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); let r6 = NonZeroU64::from(max_u32); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion // Chained method calls let _ = NonZeroU64::from(NonZeroU32::new(10).unwrap()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - // Negative test cases (lint should not trigger) - + /// Negative test cases (lint should not trigger) // Same size types let e: u32 = 200; let f = NonZeroU32::new(20).unwrap(); @@ -56,8 +61,16 @@ fn main() { // Additional function to test the lint in a different context fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { x / NonZeroU64::from(y) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion } +fn no_bin_exp(x: u64, y: NonZeroU32) -> u64 { + NonZeroU64::from(y) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +fn some_fn_that_only_takes_u64(_: u64) {} + struct Calculator { value: u64, } diff --git a/tests/ui/non_zero_suggestions.rs b/tests/ui/non_zero_suggestions.rs index 27089eaf86ef5..8f256dabcb849 100644 --- a/tests/ui/non_zero_suggestions.rs +++ b/tests/ui/non_zero_suggestions.rs @@ -3,40 +3,45 @@ use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; fn main() { - // Positive test cases (lint should trigger) - + /// Positive test cases (lint should trigger) // U32 -> U64 let x: u64 = 100; let y = NonZeroU32::new(10).unwrap(); let r1 = x / u64::from(y.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + let r2 = x % u64::from(y.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion // U16 -> U32 let a: u32 = 50; let b = NonZeroU16::new(5).unwrap(); let r3 = a / u32::from(b.get()); + //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion // I8 -> I16 let c: i16 = 25; let d = NonZeroI8::new(3).unwrap(); let r4 = i16::from(d.get()); + //~^ ERROR: consider using `NonZeroI16::from()` for more efficient and type-safe conversion // Different operations let m: u64 = 400; let n = NonZeroU32::new(20).unwrap(); let r5 = m / u64::from(n.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - // Edge cases - + /// Edge cases // Using the max value of a type let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); let r6 = u64::from(max_u32.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion // Chained method calls let _ = u64::from(NonZeroU32::new(10).unwrap().get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - // Negative test cases (lint should not trigger) - + /// Negative test cases (lint should not trigger) // Same size types let e: u32 = 200; let f = NonZeroU32::new(20).unwrap(); @@ -56,8 +61,16 @@ fn main() { // Additional function to test the lint in a different context fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { x / u64::from(y.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion } +fn no_bin_exp(x: u64, y: NonZeroU32) -> u64 { + u64::from(y.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +fn some_fn_that_only_takes_u64(_: u64) {} + struct Calculator { value: u64, } diff --git a/tests/ui/non_zero_suggestions.stderr b/tests/ui/non_zero_suggestions.stderr index 4c86720c3086b..b231c8d216ed5 100644 --- a/tests/ui/non_zero_suggestions.stderr +++ b/tests/ui/non_zero_suggestions.stderr @@ -1,5 +1,5 @@ error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:11:18 + --> tests/ui/non_zero_suggestions.rs:10:18 | LL | let r1 = x / u64::from(y.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` @@ -8,52 +8,58 @@ LL | let r1 = x / u64::from(y.get()); = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:12:18 + --> tests/ui/non_zero_suggestions.rs:13:18 | LL | let r2 = x % u64::from(y.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` error: consider using `NonZeroU32::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:17:18 + --> tests/ui/non_zero_suggestions.rs:19:18 | LL | let r3 = a / u32::from(b.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU32::from(b)` error: consider using `NonZeroI16::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:22:14 + --> tests/ui/non_zero_suggestions.rs:25:14 | LL | let r4 = i16::from(d.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroI16::from(d)` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:27:18 + --> tests/ui/non_zero_suggestions.rs:31:18 | LL | let r5 = m / u64::from(n.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:33:14 + --> tests/ui/non_zero_suggestions.rs:37:14 | LL | let r6 = u64::from(max_u32.get()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(max_u32)` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:36:13 + --> tests/ui/non_zero_suggestions.rs:41:13 | LL | let _ = u64::from(NonZeroU32::new(10).unwrap().get()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(10).unwrap())` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:58:9 + --> tests/ui/non_zero_suggestions.rs:63:9 | LL | x / u64::from(y.get()) | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:67:22 + --> tests/ui/non_zero_suggestions.rs:68:5 + | +LL | u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:80:22 | LL | self.value / u64::from(divisor.get()) | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(divisor)` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors From d43acb803f1bdabfd3761e54bd9509f8b19fb16a Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Thu, 1 Aug 2024 17:29:07 +0530 Subject: [PATCH 0318/1301] Added checks for binary expr and added different test cases for unfixable cases --- clippy_lints/src/non_zero_suggestions.rs | 53 +++++++++++++------ tests/ui/non_zero_suggestions.fixed | 43 +++++---------- tests/ui/non_zero_suggestions.rs | 43 +++++---------- tests/ui/non_zero_suggestions.stderr | 42 ++++----------- tests/ui/non_zero_suggestions_unfixable.rs | 20 +++++++ .../ui/non_zero_suggestions_unfixable.stderr | 23 ++++++++ 6 files changed, 115 insertions(+), 109 deletions(-) create mode 100644 tests/ui/non_zero_suggestions_unfixable.rs create mode 100644 tests/ui/non_zero_suggestions_unfixable.stderr diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index 1624c365ee948..808de147d722e 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use rustc_ast::ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -48,23 +49,42 @@ declare_lint_pass!(NonZeroSuggestions => [NON_ZERO_SUGGESTIONS]); impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Call(func, [arg]) = expr.kind - && let ExprKind::Path(qpath) = &func.kind - && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() - && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind + if let ExprKind::Binary(op, _, rhs) = expr.kind + && matches!(op.node, BinOpKind::Div | BinOpKind::Rem) { - let fn_name = cx.tcx.item_name(def_id); - let target_ty = cx.typeck_results().expr_ty(expr); - let receiver_ty = cx.typeck_results().expr_ty(receiver); + check_non_zero_conversion(cx, rhs, Applicability::MachineApplicable); + } else { + // Check if the parent expression is a binary operation + let parent_is_binary = cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| { + matches!(node, rustc_hir::Node::Expr(parent_expr) if matches!(parent_expr.kind, ExprKind::Binary(..))) + }); - if let ty::Adt(adt_def, _) = receiver_ty.kind() - && adt_def.is_struct() - && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) - { - if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - let arg_snippet = get_arg_snippet(cx, arg, rcv_path); - suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet); - } + if !parent_is_binary { + check_non_zero_conversion(cx, expr, Applicability::MaybeIncorrect); + } + } + } +} + +fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicability: Applicability) { + // Check if the expression is a function call with one argument + if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(qpath) = &func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind + { + let fn_name = cx.tcx.item_name(def_id); + let target_ty = cx.typeck_results().expr_ty(expr); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + + // Check if the receiver type is a NonZero type + if let ty::Adt(adt_def, _) = receiver_ty.kind() + && adt_def.is_struct() + && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) + { + if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); } } } @@ -85,6 +105,7 @@ fn suggest_non_zero_conversion( fn_name: rustc_span::Symbol, target_non_zero_type: &str, arg_snippet: &str, + applicability: Applicability, ) { let suggestion = format!("{target_non_zero_type}::{fn_name}({arg_snippet})"); span_lint_and_sugg( @@ -94,7 +115,7 @@ fn suggest_non_zero_conversion( format!("consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"), "replace with", suggestion, - Applicability::MachineApplicable, + applicability, ); } diff --git a/tests/ui/non_zero_suggestions.fixed b/tests/ui/non_zero_suggestions.fixed index d7e6b19edc1f6..9851063782e70 100644 --- a/tests/ui/non_zero_suggestions.fixed +++ b/tests/ui/non_zero_suggestions.fixed @@ -1,5 +1,4 @@ #![warn(clippy::non_zero_suggestions)] - use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; fn main() { @@ -19,43 +18,33 @@ fn main() { let r3 = a / NonZeroU32::from(b); //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion - // I8 -> I16 - let c: i16 = 25; - let d = NonZeroI8::new(3).unwrap(); - let r4 = NonZeroI16::from(d); - //~^ ERROR: consider using `NonZeroI16::from()` for more efficient and type-safe conversion - - // Different operations - let m: u64 = 400; - let n = NonZeroU32::new(20).unwrap(); - let r5 = m / NonZeroU64::from(n); + let x = NonZeroU64::from(NonZeroU32::new(5).unwrap()); //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - /// Edge cases - // Using the max value of a type - let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); - let r6 = NonZeroU64::from(max_u32); - //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + /// Negative test cases (lint should not trigger) + // Left hand side expressions should not be triggered + let c: u32 = 50; + let d = NonZeroU16::new(5).unwrap(); + let r4 = u32::from(b.get()) / a; - // Chained method calls - let _ = NonZeroU64::from(NonZeroU32::new(10).unwrap()); - //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + // Should not trigger for any other operand other than `/` and `%` + let r5 = a + u32::from(b.get()); + let r6 = a - u32::from(b.get()); - /// Negative test cases (lint should not trigger) // Same size types let e: u32 = 200; let f = NonZeroU32::new(20).unwrap(); - let r10 = e / f.get(); + let r7 = e / f.get(); // Smaller to larger, but not NonZero let g: u64 = 1000; let h: u32 = 50; - let r11 = g / u64::from(h); + let r8 = g / u64::from(h); // Using From correctly let k: u64 = 300; let l = NonZeroU32::new(15).unwrap(); - let r12 = k / NonZeroU64::from(l); + let r9 = k / NonZeroU64::from(l); } // Additional function to test the lint in a different context @@ -64,13 +53,6 @@ fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion } -fn no_bin_exp(x: u64, y: NonZeroU32) -> u64 { - NonZeroU64::from(y) - //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion -} - -fn some_fn_that_only_takes_u64(_: u64) {} - struct Calculator { value: u64, } @@ -78,5 +60,6 @@ struct Calculator { impl Calculator { fn divide(&self, divisor: NonZeroU32) -> u64 { self.value / NonZeroU64::from(divisor) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion } } diff --git a/tests/ui/non_zero_suggestions.rs b/tests/ui/non_zero_suggestions.rs index 8f256dabcb849..1605c459248c2 100644 --- a/tests/ui/non_zero_suggestions.rs +++ b/tests/ui/non_zero_suggestions.rs @@ -1,5 +1,4 @@ #![warn(clippy::non_zero_suggestions)] - use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; fn main() { @@ -19,43 +18,33 @@ fn main() { let r3 = a / u32::from(b.get()); //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion - // I8 -> I16 - let c: i16 = 25; - let d = NonZeroI8::new(3).unwrap(); - let r4 = i16::from(d.get()); - //~^ ERROR: consider using `NonZeroI16::from()` for more efficient and type-safe conversion - - // Different operations - let m: u64 = 400; - let n = NonZeroU32::new(20).unwrap(); - let r5 = m / u64::from(n.get()); + let x = u64::from(NonZeroU32::new(5).unwrap().get()); //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - /// Edge cases - // Using the max value of a type - let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); - let r6 = u64::from(max_u32.get()); - //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + /// Negative test cases (lint should not trigger) + // Left hand side expressions should not be triggered + let c: u32 = 50; + let d = NonZeroU16::new(5).unwrap(); + let r4 = u32::from(b.get()) / a; - // Chained method calls - let _ = u64::from(NonZeroU32::new(10).unwrap().get()); - //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + // Should not trigger for any other operand other than `/` and `%` + let r5 = a + u32::from(b.get()); + let r6 = a - u32::from(b.get()); - /// Negative test cases (lint should not trigger) // Same size types let e: u32 = 200; let f = NonZeroU32::new(20).unwrap(); - let r10 = e / f.get(); + let r7 = e / f.get(); // Smaller to larger, but not NonZero let g: u64 = 1000; let h: u32 = 50; - let r11 = g / u64::from(h); + let r8 = g / u64::from(h); // Using From correctly let k: u64 = 300; let l = NonZeroU32::new(15).unwrap(); - let r12 = k / NonZeroU64::from(l); + let r9 = k / NonZeroU64::from(l); } // Additional function to test the lint in a different context @@ -64,13 +53,6 @@ fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion } -fn no_bin_exp(x: u64, y: NonZeroU32) -> u64 { - u64::from(y.get()) - //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion -} - -fn some_fn_that_only_takes_u64(_: u64) {} - struct Calculator { value: u64, } @@ -78,5 +60,6 @@ struct Calculator { impl Calculator { fn divide(&self, divisor: NonZeroU32) -> u64 { self.value / u64::from(divisor.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion } } diff --git a/tests/ui/non_zero_suggestions.stderr b/tests/ui/non_zero_suggestions.stderr index b231c8d216ed5..7a57f7983be74 100644 --- a/tests/ui/non_zero_suggestions.stderr +++ b/tests/ui/non_zero_suggestions.stderr @@ -1,5 +1,5 @@ error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:10:18 + --> tests/ui/non_zero_suggestions.rs:9:18 | LL | let r1 = x / u64::from(y.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` @@ -8,58 +8,34 @@ LL | let r1 = x / u64::from(y.get()); = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:13:18 + --> tests/ui/non_zero_suggestions.rs:12:18 | LL | let r2 = x % u64::from(y.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` error: consider using `NonZeroU32::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:19:18 + --> tests/ui/non_zero_suggestions.rs:18:18 | LL | let r3 = a / u32::from(b.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU32::from(b)` -error: consider using `NonZeroI16::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:25:14 - | -LL | let r4 = i16::from(d.get()); - | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroI16::from(d)` - -error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:31:18 - | -LL | let r5 = m / u64::from(n.get()); - | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)` - error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:37:14 + --> tests/ui/non_zero_suggestions.rs:21:13 | -LL | let r6 = u64::from(max_u32.get()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(max_u32)` +LL | let x = u64::from(NonZeroU32::new(5).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:41:13 - | -LL | let _ = u64::from(NonZeroU32::new(10).unwrap().get()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(10).unwrap())` - -error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:63:9 + --> tests/ui/non_zero_suggestions.rs:52:9 | LL | x / u64::from(y.get()) | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:68:5 - | -LL | u64::from(y.get()) - | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` - -error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions.rs:80:22 + --> tests/ui/non_zero_suggestions.rs:62:22 | LL | self.value / u64::from(divisor.get()) | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(divisor)` -error: aborting due to 10 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/non_zero_suggestions_unfixable.rs b/tests/ui/non_zero_suggestions_unfixable.rs new file mode 100644 index 0000000000000..71f5f94dcc7bc --- /dev/null +++ b/tests/ui/non_zero_suggestions_unfixable.rs @@ -0,0 +1,20 @@ +#![warn(clippy::non_zero_suggestions)] +//@no-rustfix +use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; + +fn main() { + let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + let n = NonZeroU32::new(20).unwrap(); + let y = u64::from(n.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + some_fn_that_only_takes_u64(y); +} + +fn return_non_zero(x: u64, y: NonZeroU32) -> u64 { + u64::from(y.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +fn some_fn_that_only_takes_u64(_: u64) {} diff --git a/tests/ui/non_zero_suggestions_unfixable.stderr b/tests/ui/non_zero_suggestions_unfixable.stderr new file mode 100644 index 0000000000000..5e22fddbb1a4b --- /dev/null +++ b/tests/ui/non_zero_suggestions_unfixable.stderr @@ -0,0 +1,23 @@ +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:6:18 + | +LL | let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())` + | + = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:10:13 + | +LL | let y = u64::from(n.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:16:5 + | +LL | u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: aborting due to 3 previous errors + From af3346a85ffd0952f11581d83c6d6d3d57ae205b Mon Sep 17 00:00:00 2001 From: Samarth1696 Date: Sat, 31 Aug 2024 11:53:16 +0530 Subject: [PATCH 0319/1301] Check for get method and new test case in unfixable --- clippy_lints/src/non_zero_suggestions.rs | 1 + tests/ui/non_zero_suggestions_unfixable.rs | 3 +++ tests/ui/non_zero_suggestions_unfixable.stderr | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index 808de147d722e..90a9f2e994b81 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -72,6 +72,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit && let ExprKind::Path(qpath) = &func.kind && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind + && rcv_path.ident.name.as_str() == "get" { let fn_name = cx.tcx.item_name(def_id); let target_ty = cx.typeck_results().expr_ty(expr); diff --git a/tests/ui/non_zero_suggestions_unfixable.rs b/tests/ui/non_zero_suggestions_unfixable.rs index 71f5f94dcc7bc..4eb22a8d4c717 100644 --- a/tests/ui/non_zero_suggestions_unfixable.rs +++ b/tests/ui/non_zero_suggestions_unfixable.rs @@ -10,6 +10,9 @@ fn main() { let y = u64::from(n.get()); //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion some_fn_that_only_takes_u64(y); + + let m = NonZeroU32::try_from(1).unwrap(); + let _z: NonZeroU64 = m.into(); } fn return_non_zero(x: u64, y: NonZeroU32) -> u64 { diff --git a/tests/ui/non_zero_suggestions_unfixable.stderr b/tests/ui/non_zero_suggestions_unfixable.stderr index 5e22fddbb1a4b..787179f2a2d64 100644 --- a/tests/ui/non_zero_suggestions_unfixable.stderr +++ b/tests/ui/non_zero_suggestions_unfixable.stderr @@ -14,7 +14,7 @@ LL | let y = u64::from(n.get()); | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)` error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion - --> tests/ui/non_zero_suggestions_unfixable.rs:16:5 + --> tests/ui/non_zero_suggestions_unfixable.rs:19:5 | LL | u64::from(y.get()) | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` From 1ca3e90db9e98fc55d2446048737169619f5e3b0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 7 Sep 2024 15:11:18 +0200 Subject: [PATCH 0320/1301] Generate scraped examples buttons in JS --- src/librustdoc/html/render/mod.rs | 1 - src/librustdoc/html/sources.rs | 1 - src/librustdoc/html/static/js/main.js | 14 ++--- .../html/static/js/scrape-examples.js | 51 ++++++++++++------- .../html/templates/scraped_source.html | 15 +----- 5 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f67d006116cb6..d7ac3e94ab202 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2537,7 +2537,6 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c &cx.root_path(), highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded(sources::ScrapedInfo { - needs_prev_next_buttons: line_ranges.len() > 1, needs_expansion, offset: line_min, name: &call_data.display_name, diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 551bb56685c99..0d6a4603cd292 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -292,7 +292,6 @@ where pub(crate) struct ScrapedInfo<'a> { pub(crate) offset: usize, - pub(crate) needs_prev_next_buttons: bool, pub(crate) name: &'a str, pub(crate) url: &'a str, pub(crate) title: &'a str, diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 858753a1917d8..a0ec45b5ef38c 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1855,12 +1855,8 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Since the button will be added, no need to keep this listener around. elem.removeEventListener("mouseover", addCopyButton); - // If this is a scrapped example, there will already be a "button-holder" element. - let parent = elem.querySelector(".button-holder"); - if (!parent) { - parent = document.createElement("div"); - parent.className = "button-holder"; - } + const parent = document.createElement("div"); + parent.className = "button-holder"; const runButton = elem.querySelector(".test-arrow"); if (runButton !== null) { @@ -1876,6 +1872,12 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyButtonAnimation(copyButton); }); parent.appendChild(copyButton); + + if (!elem.parentElement.classList.contains("scraped-example")) { + return; + } + const scrapedWrapped = elem.parentElement; + window.updateScrapedExample(scrapedWrapped, parent); } function showHideCodeExampleButtons(event) { diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 06e42814d33b0..98c53b8656f52 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -36,13 +36,30 @@ elt.querySelector(".rust").scrollTo(0, scrollOffset); } - function updateScrapedExample(example, isHidden) { - const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); + function createScrapeButton(parent, className, content) { + const button = document.createElement("button"); + button.className = className; + button.innerText = content; + parent.insertBefore(button, parent.firstChild); + return button; + } + + window.updateScrapedExample = (example, buttonHolder) => { let locIndex = 0; const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); const link = example.querySelector(".scraped-example-title a"); + let expandButton = null; + + if (!example.classList.contains("expanded")) { + expandButton = createScrapeButton(buttonHolder, "expand", "↕"); + } + const isHidden = example.parentElement.classList.contains("more-scraped-examples"); + const locs = example.locs; if (locs.length > 1) { + const next = createScrapeButton(buttonHolder, "next", "≻"); + const prev = createScrapeButton(buttonHolder, "prev", "≺"); + // Toggle through list of examples in a given file const onChangeLoc = changeIndex => { removeClass(highlights[locIndex], "focus"); @@ -57,22 +74,19 @@ link.innerHTML = title; }; - example.querySelector(".prev") - .addEventListener("click", () => { - onChangeLoc(() => { - locIndex = (locIndex - 1 + locs.length) % locs.length; - }); + prev.addEventListener("click", () => { + onChangeLoc(() => { + locIndex = (locIndex - 1 + locs.length) % locs.length; }); + }); - example.querySelector(".next") - .addEventListener("click", () => { - onChangeLoc(() => { - locIndex = (locIndex + 1) % locs.length; - }); + next.addEventListener("click", () => { + onChangeLoc(() => { + locIndex = (locIndex + 1) % locs.length; }); + }); } - const expandButton = example.querySelector(".expand"); if (expandButton) { expandButton.addEventListener("click", () => { if (hasClass(example, "expanded")) { @@ -83,13 +97,16 @@ } }); } + }; + function setupLoc(example, isHidden) { + example.locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); // Start with the first example in view - scrollToLoc(example, locs[0][0], isHidden); + scrollToLoc(example, example.locs[0][0], isHidden); } const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example"); - onEachLazy(firstExamples, el => updateScrapedExample(el, false)); + onEachLazy(firstExamples, el => setupLoc(el, false)); onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => { // Allow users to click the left border of the
section to close it, // since the section can be large and finding the [+] button is annoying. @@ -102,11 +119,11 @@ const moreExamples = toggle.querySelectorAll(".scraped-example"); toggle.querySelector("summary").addEventListener("click", () => { // Wrapping in setTimeout ensures the update happens after the elements are actually - // visible. This is necessary since updateScrapedExample calls scrollToLoc which + // visible. This is necessary since setupLoc calls scrollToLoc which // depends on offsetHeight, a property that requires an element to be visible to // compute correctly. setTimeout(() => { - onEachLazy(moreExamples, el => updateScrapedExample(el, true)); + onEachLazy(moreExamples, el => setupLoc(el, true)); }); }, {once: true}); }); diff --git a/src/librustdoc/html/templates/scraped_source.html b/src/librustdoc/html/templates/scraped_source.html index e1fc2e6937820..bd54bbf58d508 100644 --- a/src/librustdoc/html/templates/scraped_source.html +++ b/src/librustdoc/html/templates/scraped_source.html @@ -1,8 +1,8 @@
{# #}
{{info.name +}} ({{info.title}}) {# #} -
-
{# #} +
{# #} +
{# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr Do not show "1 2 3 4 5 ..." in web search results. #}
{# #} @@ -18,16 +18,5 @@ {{code_html|safe}} {# #} {# #} - {% if info.needs_prev_next_buttons || info.needs_expansion %} -
- {% if info.needs_prev_next_buttons %} - {# #} - - {% endif %} - {% if info.needs_expansion %} - - {% endif %} -
- {% endif %}
{# #}
{# #} From 896ccaa8acddcca4f44b7627210533d0f77459df Mon Sep 17 00:00:00 2001 From: Adam Sandberg Ericsson Date: Sun, 14 Apr 2024 16:47:44 +0100 Subject: [PATCH 0321/1301] restate GlobalAlloc method safety preconditions in terms of what the caller has to do for greater clarity --- library/core/src/alloc/global.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index a6f799c4a7deb..68f00d07529b1 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -124,8 +124,8 @@ pub unsafe trait GlobalAlloc { /// /// # Safety /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure that `layout` has non-zero size. + /// `layout` must have non-zero size. Attempting to allocate for a zero-sized `layout` may + /// result in undefined behavior. /// /// (Extension subtraits might provide more specific bounds on /// behavior, e.g., guarantee a sentinel address or a null pointer @@ -156,14 +156,14 @@ pub unsafe trait GlobalAlloc { /// /// # Safety /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: + /// The caller must ensure: /// - /// * `ptr` must denote a block of memory currently allocated via - /// this allocator, + /// * `ptr` is a block of memory currently allocated via this allocator and, /// - /// * `layout` must be the same layout that was used - /// to allocate that block of memory. + /// * `layout` is the same layout that was used to allocate that block of + /// memory. + /// + /// Otherwise undefined behavior can result. #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); @@ -172,7 +172,8 @@ pub unsafe trait GlobalAlloc { /// /// # Safety /// - /// This function is unsafe for the same reasons that `alloc` is. + /// The caller has to ensure that `layout` has non-zero size. Like `alloc` + /// zero sized `layout` can result in undefined behaviour. /// However the allocated block of memory is guaranteed to be initialized. /// /// # Errors @@ -220,20 +221,21 @@ pub unsafe trait GlobalAlloc { /// /// # Safety /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: + /// The caller must ensure that: /// - /// * `ptr` must be currently allocated via this allocator, + /// * `ptr` is allocated via this allocator, /// - /// * `layout` must be the same layout that was used + /// * `layout` is the same layout that was used /// to allocate that block of memory, /// - /// * `new_size` must be greater than zero. + /// * `new_size` is greater than zero. /// /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, - /// must not overflow `isize` (i.e., the rounded value must be less than or + /// does not overflow `isize` (i.e., the rounded value must be less than or /// equal to `isize::MAX`). /// + /// If these are not followed, undefined behaviour can result. + /// /// (Extension subtraits might provide more specific bounds on /// behavior, e.g., guarantee a sentinel address or a null pointer /// in response to a zero-size allocation request.) From ae5326b967d5a3d34d3c562882feeacd9839950c Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:06:32 +0200 Subject: [PATCH 0322/1301] visit struct fields in uninit fallback check --- clippy_utils/src/ty.rs | 11 +++++++---- tests/ui/uninit_vec.rs | 32 ++++++++++++++++++++++++++++++++ tests/ui/uninit_vec.stderr | 24 +++++++++++++++++++++++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f80981c11af65..5756e56c262e0 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -606,10 +606,13 @@ fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), // Unions are always fine right now. // This includes MaybeUninit, the main way people use uninitialized memory. - // For ADTs, we could look at all fields just like for tuples, but that's potentially - // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to - // just use an `#[allow()]`. - ty::Adt(adt, _) => adt.is_union(), + ty::Adt(adt, _) if adt.is_union() => true, + // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit + // can themselves be uninit too. + // This purposefully ignores enums as they may have a discriminant that can't be uninit. + ty::Adt(adt, args) if adt.is_struct() => adt + .all_fields() + .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args))), // For the rest, conservatively assume that they cannot be uninit. _ => false, } diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index 0cc77a8775d57..464f88140bdba 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -150,4 +150,36 @@ fn main() { vec.set_len(10); } } + + fn nested_union() { + let mut vec: Vec>> = Vec::with_capacity(1); + unsafe { + vec.set_len(1); + } + } + + struct Recursive(*const Recursive, MaybeUninit); + fn recursive_union() { + // Make sure we don't stack overflow on recursive types. + // The pointer acts as the base case because it can't be uninit regardless of its pointee. + + let mut vec: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { + vec.set_len(1); + } + } + + #[repr(u8)] + enum Enum { + Variant(T), + } + fn union_in_enum() { + // Enums can have a discriminant that can't be uninit, so this should still warn + let mut vec: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { + vec.set_len(1); + } + } } diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index e8b77d653f089..e7c81cf792f23 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -125,5 +125,27 @@ LL | vec.set_len(10); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 12 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:166:9 + | +LL | let mut vec: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:179:9 + | +LL | let mut vec: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 14 previous errors From 4db9c01f8b1e7cfc82a5bcd522d72f9e6a84fdf1 Mon Sep 17 00:00:00 2001 From: DeSevilla Date: Sat, 7 Sep 2024 13:36:09 -0400 Subject: [PATCH 0323/1301] Renamed variable and fixed comments referring to renamed FileDescriptor --- src/tools/miri/src/shims/unix/fd.rs | 14 +++++++------- src/tools/miri/src/shims/unix/fs.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 3ca5f6bb2dff5..d756c06ebdb12 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -21,7 +21,7 @@ pub(crate) enum FlockOp { Unlock, } -/// Represents an open file descriptor. +/// Represents an open file description. pub trait FileDescription: std::fmt::Debug + Any { fn name(&self) -> &'static str; @@ -303,7 +303,7 @@ pub struct FdTable { impl VisitProvenance for FdTable { fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { - // All our FileDescriptor do not have any tags. + // All our FileDescriptionRef do not have any tags. } } @@ -411,7 +411,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn flock(&mut self, fd: i32, op: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(file_descriptor) = this.machine.fds.get(fd) else { + let Some(fd_ref) = this.machine.fds.get(fd) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; @@ -436,8 +436,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("unsupported flags {:#x}", op); }; - let result = file_descriptor.flock(this.machine.communicate(), parsed_op)?; - drop(file_descriptor); + let result = fd_ref.flock(this.machine.communicate(), parsed_op)?; + drop(fd_ref); // return `0` if flock is successful let result = result.map(|()| 0i32); Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) @@ -539,7 +539,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // Isolation check is done via `FileDescriptor` trait. + // Isolation check is done via `FileDescription` trait. trace!("Reading from FD {}, size {}", fd, count); @@ -604,7 +604,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // Isolation check is done via `FileDescriptor` trait. + // Isolation check is done via `FileDescription` trait. // Check that the *entire* buffer is actually valid memory. this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index e00758bb98de6..58d3a73b481d2 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -557,7 +557,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // Isolation check is done via `FileDescriptor` trait. + // Isolation check is done via `FileDescription` trait. let seek_from = if whence == this.eval_libc_i32("SEEK_SET") { if offset < 0 { From 25efc04ea554301374ac8e6270dbc7f65fead347 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Sat, 7 Sep 2024 12:00:34 -0800 Subject: [PATCH 0324/1301] Fix typo --- clippy_lints/src/ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 125f694996c1c..7d24d25dc2fa4 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// with the appropriate `.to_owned()`/`to_string()` calls. /// /// ### Why is this bad? - /// Requiring the argument to be of the specific size + /// Requiring the argument to be of the specific type /// makes the function less useful for no benefit; slices in the form of `&[T]` /// or `&str` usually suffice and can be obtained from other types, too. /// From 42d03f6633e3ebc939d3d04d7605301f99d22a54 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 7 Sep 2024 20:02:24 -0400 Subject: [PATCH 0325/1301] Add support for more SIMD intrinsics --- build_system/src/build.rs | 1 + src/intrinsic/simd.rs | 32 ++++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/build_system/src/build.rs b/build_system/src/build.rs index d96683afa359c..d5d099fb14cfa 100644 --- a/build_system/src/build.rs +++ b/build_system/src/build.rs @@ -136,6 +136,7 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu &"build", &"--target", &config.target, + // TODO: remove this feature? &"--features", &"std/compiler-builtins-no-f16-f128", ]; diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index a0d8ff6346f3e..79b345982c6e0 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -739,11 +739,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return Err(()); }}; } - let (elem_ty_str, elem_ty) = if let ty::Float(ref f) = *in_elem.kind() { + let (elem_ty_str, elem_ty, cast_type) = if let ty::Float(ref f) = *in_elem.kind() { let elem_ty = bx.cx.type_float_from_ty(*f); match f.bit_width() { - 32 => ("f", elem_ty), - 64 => ("", elem_ty), + 16 => ("", elem_ty, Some(bx.cx.double_type)), + 32 => ("f", elem_ty, None), + 64 => ("", elem_ty, None), _ => { return_error!(InvalidMonomorphization::FloatingPointVector { span, @@ -787,17 +788,28 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( for i in 0..in_len { let index = bx.context.new_rvalue_from_long(bx.ulong_type, i as i64); // we have to treat fpowi specially, since fpowi's second argument is always an i32 - let arguments = if name == sym::simd_fpowi { - vec![ + let mut arguments = vec![]; + if name == sym::simd_fpowi { + arguments = vec![ bx.extract_element(args[0].immediate(), index).to_rvalue(), args[1].immediate(), - ] + ]; } else { - args.iter() - .map(|arg| bx.extract_element(arg.immediate(), index).to_rvalue()) - .collect() + for arg in args { + let mut element = bx.extract_element(arg.immediate(), index).to_rvalue(); + // FIXME: it would probably be better to not have casts here and use the proper + // instructions. + if let Some(typ) = cast_type { + element = bx.context.new_cast(None, element, typ); + } + arguments.push(element); + } }; - vector_elements.push(bx.context.new_call(None, function, &arguments)); + let mut result = bx.context.new_call(None, function, &arguments); + if cast_type.is_some() { + result = bx.context.new_cast(None, result, elem_ty); + } + vector_elements.push(result); } let c = bx.context.new_rvalue_from_vector(None, vec_ty, &vector_elements); Ok(c) From 3bcc2b788886ba12585b9be8d6cc088d60ca0416 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Wed, 4 Sep 2024 08:52:57 -0400 Subject: [PATCH 0326/1301] assist: ensure replace_qualified_name_with_use applies to the first path segment --- .../replace_qualified_name_with_use.rs | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 65330b34c4ad2..1101c20f1b785 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -29,7 +29,7 @@ pub(crate) fn replace_qualified_name_with_use( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let original_path: ast::Path = ctx.find_node_at_offset()?; + let mut original_path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements if original_path.syntax().ancestors().find_map(ast::UseTree::cast).is_some() { cov_mark::hit!(not_applicable_in_use); @@ -37,8 +37,7 @@ pub(crate) fn replace_qualified_name_with_use( } if original_path.qualifier().is_none() { - cov_mark::hit!(dont_import_trivial_paths); - return None; + original_path = original_path.parent_path()?; } // only offer replacement for non assoc items @@ -236,12 +235,6 @@ fs::Path ); } - #[test] - fn dont_import_trivial_paths() { - cov_mark::check!(dont_import_trivial_paths); - check_assist_not_applicable(replace_qualified_name_with_use, r"impl foo$0 for () {}"); - } - #[test] fn test_replace_not_applicable_in_use() { cov_mark::check!(not_applicable_in_use); @@ -271,6 +264,29 @@ fn main() { ); } + #[test] + fn assist_runs_on_first_segment() { + check_assist( + replace_qualified_name_with_use, + r" +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + $0std::fmt::Debug; + let x: std::fmt::Debug = std::fmt::Debug; +} + ", + r" +use std::fmt; + +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + fmt::Debug; + let x: fmt::Debug = fmt::Debug; +} + ", + ); + } + #[test] fn does_not_replace_in_submodules() { check_assist( From ca262a34acdde2f72d15535e9f535021a9cb8d62 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 1 Sep 2024 07:02:40 +0300 Subject: [PATCH 0327/1301] Automatically add semicolon when completing unit-returning functions But provide a config to suppress that. I didn't check whether we are in statement expression position, because this is hard in completion (due to the natural incompleteness of source code when completion is invoked), and anyway using function returning unit as an argument to something seems... dubious. --- .../ide-completion/src/completions/dot.rs | 2 +- .../crates/ide-completion/src/config.rs | 1 + .../crates/ide-completion/src/render.rs | 18 +- .../ide-completion/src/render/function.rs | 184 ++++++++++++++++-- .../crates/ide-completion/src/tests.rs | 1 + .../ide-completion/src/tests/flyimport.rs | 16 +- .../src/tests/raw_identifiers.rs | 8 +- .../ide-completion/src/tests/special.rs | 2 +- .../crates/rust-analyzer/src/config.rs | 5 + .../src/integrated_benchmarks.rs | 3 + .../docs/user/generated_config.adoc | 7 + .../rust-analyzer/editors/code/package.json | 10 + 12 files changed, 217 insertions(+), 40 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index d55bc3ea5d03a..f2c360a9d5bfd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -600,7 +600,7 @@ fn foo(a: A) { a.$0 } struct A {} trait Trait { fn the_method(&self); } impl Trait for A {} -fn foo(a: A) { a.the_method()$0 } +fn foo(a: A) { a.the_method();$0 } "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index d885b82ec90df..0d403f49b7a87 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -19,6 +19,7 @@ pub struct CompletionConfig { pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option, + pub add_semicolon_to_unit: bool, pub snippet_cap: Option, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 2bec2eb87bc4b..f2e9de9382c6b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1185,7 +1185,7 @@ fn main() { fo$0 } label: "main()", source_range: 68..70, delete: 68..70, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1244,7 +1244,7 @@ fn main() { let _: m::Spam = S$0 } label: "main()", source_range: 75..76, delete: 75..76, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1331,7 +1331,7 @@ fn main() { som$0 } label: "main()", source_range: 56..59, delete: 56..59, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1342,7 +1342,7 @@ fn main() { som$0 } label: "something_deprecated()", source_range: 56..59, delete: 56..59, - insert: "something_deprecated()$0", + insert: "something_deprecated();$0", kind: SymbolKind( Function, ), @@ -1413,7 +1413,7 @@ impl S { label: "bar()", source_range: 94..94, delete: 94..94, - insert: "bar()$0", + insert: "bar();$0", kind: SymbolKind( Method, ), @@ -1540,7 +1540,7 @@ fn foo(s: S) { s.$0 } label: "the_method()", source_range: 81..81, delete: 81..81, - insert: "the_method()$0", + insert: "the_method();$0", kind: SymbolKind( Method, ), @@ -2789,7 +2789,7 @@ fn main() { r#" mod m { pub fn r#type {} } fn main() { - m::r#type()$0 + m::r#type();$0 } "#, ) @@ -2963,7 +2963,7 @@ fn main() { label: "flush()", source_range: 193..193, delete: 193..193, - insert: "flush()$0", + insert: "flush();$0", kind: SymbolKind( Method, ), @@ -2990,7 +2990,7 @@ fn main() { label: "write()", source_range: 193..193, delete: 193..193, - insert: "write()$0", + insert: "write();$0", kind: SymbolKind( Method, ), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 29820cb2036de..7e5e69665f1fa 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -1,10 +1,12 @@ //! Renderer for function calls. +use std::ops::ControlFlow; + use hir::{db::HirDatabase, AsAssocItem, HirDisplay}; use ide_db::{SnippetCap, SymbolKind}; use itertools::Itertools; use stdx::{format_to, to_lower_snake_case}; -use syntax::{format_smolstr, AstNode, Edition, SmolStr, ToSmolStr}; +use syntax::{ast, format_smolstr, AstNode, Edition, SmolStr, SyntaxKind, ToSmolStr, T}; use crate::{ context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind}, @@ -160,7 +162,16 @@ fn render( .lookup_by(name.unescaped().display(db).to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { - add_call_parens(&mut item, completion, cap, call, escaped_call, self_param, params); + add_call_parens( + &mut item, + completion, + cap, + call, + escaped_call, + self_param, + params, + &ret_type, + ); } match ctx.import_to_add { @@ -217,10 +228,11 @@ pub(super) fn add_call_parens<'b>( escaped_name: SmolStr, self_param: Option, params: Vec, + ret_type: &hir::Type, ) -> &'b mut Builder { cov_mark::hit!(inserts_parens_for_function_calls); - let (snippet, label_suffix) = if self_param.is_none() && params.is_empty() { + let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() { (format!("{escaped_name}()$0"), "()") } else { builder.trigger_call_info(); @@ -265,6 +277,35 @@ pub(super) fn add_call_parens<'b>( (snippet, "(…)") }; + if ret_type.is_unit() && ctx.config.add_semicolon_to_unit { + let next_non_trivia_token = + std::iter::successors(ctx.token.next_token(), |it| it.next_token()) + .find(|it| !it.kind().is_trivia()); + let in_match_arm = ctx.token.parent_ancestors().try_for_each(|ancestor| { + if ast::MatchArm::can_cast(ancestor.kind()) { + ControlFlow::Break(true) + } else if matches!(ancestor.kind(), SyntaxKind::EXPR_STMT | SyntaxKind::BLOCK_EXPR) { + ControlFlow::Break(false) + } else { + ControlFlow::Continue(()) + } + }); + // FIXME: This will assume expr macros are not inside match, we need to somehow go to the "parent" of the root node. + let in_match_arm = match in_match_arm { + ControlFlow::Continue(()) => false, + ControlFlow::Break(it) => it, + }; + let complete_token = if in_match_arm { T![,] } else { T![;] }; + if next_non_trivia_token.map(|it| it.kind()) != Some(complete_token) { + cov_mark::hit!(complete_semicolon); + let ch = if in_match_arm { ',' } else { ';' }; + if snippet.ends_with("$0") { + snippet.insert(snippet.len() - "$0".len(), ch); + } else { + snippet.push(ch); + } + } + } builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet) } @@ -393,7 +434,7 @@ fn main() { no_$0 } "#, r#" fn no_args() {} -fn main() { no_args()$0 } +fn main() { no_args();$0 } "#, ); @@ -405,7 +446,7 @@ fn main() { with_$0 } "#, r#" fn with_args(x: i32, y: String) {} -fn main() { with_args(${1:x}, ${2:y})$0 } +fn main() { with_args(${1:x}, ${2:y});$0 } "#, ); @@ -414,14 +455,14 @@ fn main() { with_args(${1:x}, ${2:y})$0 } r#" struct S; impl S { - fn foo(&self) {} + fn foo(&self) -> i32 { 0 } } fn bar(s: &S) { s.f$0 } "#, r#" struct S; impl S { - fn foo(&self) {} + fn foo(&self) -> i32 { 0 } } fn bar(s: &S) { s.foo()$0 } "#, @@ -444,7 +485,7 @@ impl S { fn foo(&self, x: i32) {} } fn bar(s: &S) { - s.foo(${1:x})$0 + s.foo(${1:x});$0 } "#, ); @@ -463,7 +504,7 @@ impl S { struct S {} impl S { fn foo(&self, x: i32) { - self.foo(${1:x})$0 + self.foo(${1:x});$0 } } "#, @@ -486,7 +527,7 @@ struct S; impl S { fn foo(&self) {} } -fn main() { S::foo(${1:&self})$0 } +fn main() { S::foo(${1:&self});$0 } "#, ); } @@ -503,7 +544,7 @@ fn main() { with_$0 } "#, r#" fn with_args(x: i32, y: String) {} -fn main() { with_args($0) } +fn main() { with_args($0); } "#, ); } @@ -518,7 +559,7 @@ fn main() { f$0 } "#, r#" fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {} -fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 } +fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_});$0 } "#, ); } @@ -540,7 +581,7 @@ struct Foo {} fn ref_arg(x: &Foo) {} fn main() { let x = Foo {}; - ref_arg(${1:&x})$0 + ref_arg(${1:&x});$0 } "#, ); @@ -563,7 +604,7 @@ struct Foo {} fn ref_arg(x: &mut Foo) {} fn main() { let x = Foo {}; - ref_arg(${1:&mut x})$0 + ref_arg(${1:&mut x});$0 } "#, ); @@ -596,7 +637,7 @@ impl Bar { fn main() { let x = Foo {}; let y = Bar {}; - y.apply_foo(${1:&x})$0 + y.apply_foo(${1:&x});$0 } "#, ); @@ -617,7 +658,7 @@ fn main() { fn take_mutably(mut x: &i32) {} fn main() { - take_mutably(${1:x})$0 + take_mutably(${1:x});$0 } "#, ); @@ -650,7 +691,7 @@ fn qux(Foo { bar }: Foo) { } fn main() { - qux(${1:foo})$0 + qux(${1:foo});$0 } "#, ); @@ -736,6 +777,115 @@ fn g(foo: ()#[baz = "qux"] mut ba$0) r#" fn f(foo: (), #[baz = "qux"] mut bar: u32) {} fn g(foo: (), #[baz = "qux"] mut bar: u32) +"#, + ); + } + + #[test] + fn complete_semicolon_for_unit() { + cov_mark::check!(complete_semicolon); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo() {} +fn bar() { + foo();$0 +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a});$0 +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0; +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a})$0; +} +"#, + ); + check_edit_with_config( + CompletionConfig { add_semicolon_to_unit: false, ..TEST_CONFIG }, + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a})$0 +} +"#, + ); + } + + #[test] + fn complete_comma_for_unit_match_arm() { + cov_mark::check!(complete_semicolon); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => fo$0 + } +} +"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => foo(),$0 + } +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => fo$0, + } +} +"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => foo()$0, + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 04ba7e1f41b62..9d77d9700718c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -70,6 +70,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), + add_semicolon_to_unit: true, snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { granularity: ImportGranularity::Crate, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 8350fdcc4c07c..0b532064fb2be 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -53,7 +53,7 @@ fn main() { use dep::io::stdin; fn main() { - stdin()$0 + stdin();$0 } "#, ); @@ -274,7 +274,7 @@ fn trait_function_fuzzy_completion() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestStruct::weird_function()$0 + dep::test_mod::TestStruct::weird_function();$0 } "#, ); @@ -368,7 +368,7 @@ use dep::test_mod::TestTrait; fn main() { let test_struct = dep::test_mod::TestStruct {}; - test_struct.random_method()$0 + test_struct.random_method();$0 } "#, ); @@ -419,7 +419,7 @@ impl foo::TestTrait for fundamental::Box { fn main() { let t = fundamental::Box(TestStruct); - t.some_method()$0 + t.some_method();$0 } "#, ); @@ -466,7 +466,7 @@ impl foo::TestTrait for &TestStruct { fn main() { let t = &TestStruct; - t.some_method()$0 + t.some_method();$0 } "#, ); @@ -507,7 +507,7 @@ fn completion(whatever: T) { use foo::{NotInScope, Wrapper}; fn completion(whatever: T) { - whatever.inner().not_in_scope()$0 + whatever.inner().not_in_scope();$0 } "#, ); @@ -579,7 +579,7 @@ fn main() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestAlias::random_method()$0 + dep::test_mod::TestAlias::random_method();$0 } "#, ); @@ -702,7 +702,7 @@ fn main() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestStruct::another_function()$0 + dep::test_mod::TestStruct::another_function();$0 } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs index bc630189edc91..d81b3d697aa8a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs @@ -30,7 +30,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::dyn()$0 + a::dyn();$0 "#]], ); @@ -45,7 +45,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::dyn()$0 + a::dyn();$0 "#]], ); } @@ -63,7 +63,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::r#dyn()$0 + a::r#dyn();$0 "#]], ); @@ -78,7 +78,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::r#dyn()$0 + a::r#dyn();$0 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 2ae7d37889e53..508f6248dd414 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -63,7 +63,7 @@ fn _alpha() {} "#, r#" fn main() { - _alpha()$0 + _alpha();$0 } fn _alpha() {} "#, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e4e8c9c6d9f97..35be6d0cf7a13 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -420,6 +420,10 @@ config_data! { assist_termSearch_fuel: usize = 1800, + /// Whether to automatically add a semicolon when completing unit-returning functions. + /// + /// In `match` arms it completes a comma instead. + completion_addSemicolonToUnit: bool = true, /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. completion_autoimport_enable: bool = true, @@ -1441,6 +1445,7 @@ impl Config { CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), CallableCompletionDef::None => None, }, + add_semicolon_to_unit: *self.completion_addSemicolonToUnit(source_root), snippet_cap: SnippetCap::new(self.completion_snippet()), insert_use: self.insert_use_config(source_root), prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 28f4b809d6c82..118469df730ce 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -167,6 +167,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -213,6 +214,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -257,6 +259,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index 4fcf580e75f5a..f37fd7f4ab3c4 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -261,6 +261,13 @@ Aliased as `"checkOnSave.targets"`. Whether `--workspace` should be passed to `cargo check`. If false, `-p ` will be passed instead. -- +[[rust-analyzer.completion.addSemicolonToUnit]]rust-analyzer.completion.addSemicolonToUnit (default: `true`):: ++ +-- +Whether to automatically add a semicolon when completing unit-returning functions. + +In `match` arms it completes a comma instead. +-- [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: + -- diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 0b029460b3220..b66f0a64d575a 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1027,6 +1027,16 @@ } } }, + { + "title": "completion", + "properties": { + "rust-analyzer.completion.addSemicolonToUnit": { + "markdownDescription": "Whether to automatically add a semicolon when completing unit-returning functions.\n\nIn `match` arms it completes a comma instead.", + "default": true, + "type": "boolean" + } + } + }, { "title": "completion", "properties": { From 81227a309c63a6f721bf5552b0579e53790064f3 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sun, 1 Sep 2024 22:07:47 +0200 Subject: [PATCH 0328/1301] Better testing infra for ratoml --- .../crates/rust-analyzer/src/config.rs | 95 +++++---- .../rust-analyzer/src/handlers/request.rs | 28 ++- .../crates/rust-analyzer/src/lsp/ext.rs | 17 +- .../rust-analyzer/tests/slow-tests/ratoml.rs | 189 +++++++++++++----- .../rust-analyzer/docs/dev/lsp-extensions.md | 2 +- 5 files changed, 228 insertions(+), 103 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e4e8c9c6d9f97..b6a1bd158663c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -75,6 +75,8 @@ config_data! { /// How many worker threads to handle priming caches. The default `0` means to pick automatically. cachePriming_numThreads: NumThreads = NumThreads::Physical, + /// Custom completion snippets. + completion_snippets_custom: FxHashMap = Config::completion_snippets_default(), /// These directories will be ignored by rust-analyzer. They are @@ -438,48 +440,6 @@ config_data! { completion_postfix_enable: bool = true, /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. completion_privateEditable_enable: bool = false, - /// Custom completion snippets. - completion_snippets_custom: FxHashMap = serde_json::from_str(r#"{ - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - }"#).unwrap(), /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. completion_termSearch_enable: bool = false, /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). @@ -889,7 +849,7 @@ impl Config { // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`. config.snippets.clear(); - let snips = self.completion_snippets_custom(None).to_owned(); + let snips = self.completion_snippets_custom().to_owned(); for (name, def) in snips.iter() { if def.prefix.is_empty() && def.postfix.is_empty() { @@ -1266,7 +1226,7 @@ pub struct NotificationsConfig { pub cargo_toml_not_found: bool, } -#[derive(Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub enum RustfmtConfig { Rustfmt { extra_args: Vec, enable_range_formatting: bool }, CustomCommand { command: String, args: Vec }, @@ -1897,6 +1857,53 @@ impl Config { } } + pub(crate) fn completion_snippets_default() -> FxHashMap { + serde_json::from_str( + r#"{ + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + } + }"#, + ) + .unwrap() + } + pub fn rustfmt(&self, source_root_id: Option) -> RustfmtConfig { match &self.rustfmt_overrideCommand(source_root_id) { Some(args) if !args.is_empty() => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 09d5086201779..bcbd970a0d214 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -40,7 +40,10 @@ use crate::{ hack_recover_crate_name, line_index::LineEndings, lsp::{ - ext::InternalTestingFetchConfigParams, + ext::{ + InternalTestingFetchConfigOption, InternalTestingFetchConfigParams, + InternalTestingFetchConfigResponse, + }, from_proto, to_proto, utils::{all_edits_are_disjoint, invalid_params_error}, LspError, @@ -2292,7 +2295,7 @@ pub(crate) fn fetch_dependency_list( pub(crate) fn internal_testing_fetch_config( state: GlobalStateSnapshot, params: InternalTestingFetchConfigParams, -) -> anyhow::Result { +) -> anyhow::Result> { let source_root = params .text_document .map(|it| { @@ -2302,15 +2305,18 @@ pub(crate) fn internal_testing_fetch_config( .map_err(anyhow::Error::from) }) .transpose()?; - serde_json::to_value(match &*params.config { - "local" => state.config.assist(source_root).assist_emit_must_use, - "workspace" => matches!( - state.config.rustfmt(source_root), - RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } - ), - _ => return Err(anyhow::anyhow!("Unknown test config key: {}", params.config)), - }) - .map_err(Into::into) + Ok(Some(match params.config { + InternalTestingFetchConfigOption::AssistEmitMustUse => { + InternalTestingFetchConfigResponse::AssistEmitMustUse( + state.config.assist(source_root).assist_emit_must_use, + ) + } + InternalTestingFetchConfigOption::CheckWorkspace => { + InternalTestingFetchConfigResponse::CheckWorkspace( + state.config.flycheck_workspace(source_root), + ) + } + })) } /// Searches for the directory of a Rust crate given this crate's root file path. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index ed6f16a17302b..618481bbc66b6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -16,9 +16,22 @@ use serde::{Deserialize, Serialize}; pub enum InternalTestingFetchConfig {} +#[derive(Deserialize, Serialize, Debug)] +pub enum InternalTestingFetchConfigOption { + AssistEmitMustUse, + CheckWorkspace, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub enum InternalTestingFetchConfigResponse { + AssistEmitMustUse(bool), + CheckWorkspace(bool), +} + impl Request for InternalTestingFetchConfig { type Params = InternalTestingFetchConfigParams; - type Result = serde_json::Value; + // Option is solely to circumvent Default bound. + type Result = Option; const METHOD: &'static str = "rust-analyzer-internal/internalTestingFetchConfig"; } @@ -26,7 +39,7 @@ impl Request for InternalTestingFetchConfig { #[serde(rename_all = "camelCase")] pub struct InternalTestingFetchConfigParams { pub text_document: Option, - pub config: String, + pub config: InternalTestingFetchConfigOption, } pub enum AnalyzerStatus {} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs index 295d1d4e8e909..77048ad83d484 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs @@ -9,18 +9,13 @@ use lsp_types::{ use paths::Utf8PathBuf; use rust_analyzer::config::Config; -use rust_analyzer::lsp::ext::{InternalTestingFetchConfig, InternalTestingFetchConfigParams}; +use rust_analyzer::lsp::ext::{ + InternalTestingFetchConfig, InternalTestingFetchConfigOption, InternalTestingFetchConfigParams, + InternalTestingFetchConfigResponse, +}; use serde_json::json; use test_utils::skip_slow_tests; -enum QueryType { - Local, - /// A query whose config key is a part of the global configs, so that - /// testing for changes to this config means testing if global changes - /// take affect. - Workspace, -} - struct RatomlTest { urls: Vec, server: Server, @@ -158,20 +153,24 @@ impl RatomlTest { }); } - fn query(&self, query: QueryType, source_file_idx: usize) -> bool { - let config = match query { - QueryType::Local => "local".to_owned(), - QueryType::Workspace => "workspace".to_owned(), - }; + fn query( + &self, + query: InternalTestingFetchConfigOption, + source_file_idx: usize, + expected: InternalTestingFetchConfigResponse, + ) { let res = self.server.send_request::( InternalTestingFetchConfigParams { text_document: Some(TextDocumentIdentifier { uri: self.urls[source_file_idx].clone(), }), - config, + config: query, }, ); - res.as_bool().unwrap() + assert_eq!( + serde_json::from_value::(res).unwrap(), + expected + ) } } @@ -206,7 +205,11 @@ enum Value { })), ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } /// Checks if client config can be modified. @@ -311,7 +314,11 @@ enum Value { None, ); - assert!(server.query(QueryType::Local, 2)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 2, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -341,12 +348,20 @@ enum Value { None, ); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); server.create( "//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml", RatomlTest::EMIT_MUST_USE.to_owned(), ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -378,9 +393,17 @@ assist.emitMustUse = true"#, None, ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.edit(2, String::new()); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -412,9 +435,17 @@ assist.emitMustUse = true"#, None, ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(2); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -461,7 +492,11 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -508,9 +543,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); server.edit(1, "assist.emitMustUse = true".to_owned()); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -557,9 +600,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(1); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -606,9 +657,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.create("//- /p1/p2/rust-analyzer.toml", RatomlTest::EMIT_MUST_NOT_USE.to_owned()); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -656,9 +715,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(1); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -705,8 +772,16 @@ enum Value { None, ); - assert!(server.query(QueryType::Local, 3)); - assert!(server.query(QueryType::Local, 4)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 4, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -744,7 +819,11 @@ fn ratoml_multiple_ratoml_in_single_source_root() { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } /// If a root is non-local, so we cannot find what its parent is @@ -836,7 +915,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -848,7 +927,11 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ) } #[test] @@ -868,7 +951,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -880,9 +963,17 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); - server.edit(1, "rustfmt.rangeFormatting.enable = false".to_owned()); - assert!(!server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ); + server.edit(1, "check.workspace = true".to_owned()); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(true), + ); } #[test] @@ -902,7 +993,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -914,7 +1005,15 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ); server.delete(1); - assert!(!server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(true), + ); } diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 68afacf2db5cb..b7bac4d29fad9 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/libx_pthread_rwlock_moved.rs:LL:CC + | +LL | libc::pthread_rwlock_unlock(&mut rw2 as *mut _); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_rwlock_t can't be moved after first use + | + = 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 + = note: BACKTRACE: + = note: inside `main` at $DIR/libx_pthread_rwlock_moved.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 9e9526c6ab12fce8113d6c18d348572f4f98492b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 7 Sep 2024 16:18:45 +0200 Subject: [PATCH 0331/1301] Special-case suggestions for null pointers constness cast --- clippy_lints/src/casts/mod.rs | 6 +- clippy_lints/src/casts/ptr_cast_constness.rs | 62 ++++++++++++++------ tests/ui/ptr_cast_constness.fixed | 13 ++++ tests/ui/ptr_cast_constness.rs | 13 ++++ tests/ui/ptr_cast_constness.stderr | 22 ++++++- 5 files changed, 97 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index c31716fbcee1d..fccddf558ed82 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -410,19 +410,23 @@ declare_clippy_lint! { /// ### Why is this bad? /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another - /// type. + /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly. /// /// ### Example /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr as *mut u32; /// let ptr = mut_ptr as *const u32; + /// let ptr1 = std::ptr::null::() as *mut u32; + /// let ptr2 = std::ptr::null_mut::() as *const u32; /// ``` /// Use instead: /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr.cast_mut(); /// let ptr = mut_ptr.cast_const(); + /// let ptr1 = std::ptr::null_mut::(); + /// let ptr2 = std::ptr::null::(); /// ``` #[clippy::version = "1.72.0"] pub PTR_CAST_CONSTNESS, diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 7513e18d408b1..98bc38ed134df 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,10 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, Mutability}; +use rustc_hir::{Expr, ExprKind, Mutability, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_span::sym; use super::PTR_CAST_CONSTNESS; @@ -16,8 +18,7 @@ pub(super) fn check<'tcx>( cast_to: Ty<'tcx>, msrv: &Msrv, ) { - if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) - && let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() + if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() && matches!( (from_mutbl, to_mutbl), @@ -26,20 +27,47 @@ pub(super) fn check<'tcx>( && from_ty == to_ty && !from_ty.has_erased_regions() { - let sugg = Sugg::hir(cx, cast_expr, "_"); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; + if let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { + Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), + Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")), + _ => None, + } + { + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + format!("`as` casting to make a {source} null pointer into a {target} null pointer"), + format!("use `{target_func}()` directly instead"), + format!("{prefix}::ptr::{target_func}::<{after_lt}"), + app, + ); + return; + } - span_lint_and_sugg( - cx, - PTR_CAST_CONSTNESS, - expr.span, - "`as` casting between raw pointers while changing only its constness", - format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); + if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing only its constness", + format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 21ac42196e1bb..5bf9a30b01617 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -68,3 +68,16 @@ fn _msrv_1_65() { let _ = ptr.cast_mut(); let _ = mut_ptr.cast_const(); } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); + + // Make sure the lint is triggered inside a macro + let _ = inline!(std::ptr::null_mut::()); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); +} diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index 5ce590b2b7e47..2575a5923e124 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -68,3 +68,16 @@ fn _msrv_1_65() { let _ = ptr as *mut u32; let _ = mut_ptr as *const u32; } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = ptr::null::() as *mut String; + let _ = ptr::null_mut::() as *const u32; + + // Make sure the lint is triggered inside a macro + let _ = inline!(ptr::null::() as *mut u32); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); +} diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 2c52ebd3464d6..0806ca62806d1 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -43,5 +43,25 @@ error: `as` casting between raw pointers while changing only its constness LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` -error: aborting due to 7 previous errors +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:75:13 + | +LL | let _ = ptr::null::() as *mut String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: `as` casting to make a mutable null pointer into a const null pointer + --> tests/ui/ptr_cast_constness.rs:76:13 + | +LL | let _ = ptr::null_mut::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:79:21 + | +LL | let _ = inline!(ptr::null::() as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors From 30608732c2f68c423e4a62e6c20cdf28f6d38559 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 7 Sep 2024 21:57:29 +0200 Subject: [PATCH 0332/1301] Handle null pointer constness cast through methods This covers two cases: - `core::ptr::null::().cast_mut()` -> `core::ptr::null_mut::()` - `core::ptr::null_mut::().cast_const()` -> `core::ptr::null::()` --- clippy_lints/src/casts/mod.rs | 5 ++++ clippy_lints/src/casts/ptr_cast_constness.rs | 27 ++++++++++++++++++++ tests/ui/ptr_cast_constness.fixed | 4 +++ tests/ui/ptr_cast_constness.rs | 4 +++ tests/ui/ptr_cast_constness.stderr | 24 +++++++++++++++-- 5 files changed, 62 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index fccddf558ed82..39f50a347c064 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -419,6 +419,8 @@ declare_clippy_lint! { /// let ptr = mut_ptr as *const u32; /// let ptr1 = std::ptr::null::() as *mut u32; /// let ptr2 = std::ptr::null_mut::() as *const u32; + /// let ptr3 = std::ptr::null::().cast_mut(); + /// let ptr4 = std::ptr::null_mut::().cast_const(); /// ``` /// Use instead: /// ```no_run @@ -427,6 +429,8 @@ declare_clippy_lint! { /// let ptr = mut_ptr.cast_const(); /// let ptr1 = std::ptr::null_mut::(); /// let ptr2 = std::ptr::null::(); + /// let ptr3 = std::ptr::null_mut::(); + /// let ptr4 = std::ptr::null::(); /// ``` #[clippy::version = "1.72.0"] pub PTR_CAST_CONSTNESS, @@ -813,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, &self.msrv); cast_slice_different_sizes::check(cx, expr, &self.msrv); + ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 98bc38ed134df..7518dd2435aee 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -71,3 +71,30 @@ pub(super) fn check<'tcx>( } } } + +pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind + && let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) { + (Some(sym::ptr_null), "cast_mut") => "null_mut", + (Some(sym::ptr_null_mut), "cast_const") => "null", + _ => return, + } + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + { + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "changing constness of a null pointer", + format!("use `{method}()` directly instead"), + format!("{prefix}::ptr::{method}::<{after_lt}"), + app, + ); + } +} diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 5bf9a30b01617..9a5272c7adc36 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -74,10 +74,14 @@ fn null_pointers() { use std::ptr; let _ = std::ptr::null_mut::(); let _ = std::ptr::null::(); + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); // Make sure the lint is triggered inside a macro let _ = inline!(std::ptr::null_mut::()); + let _ = inline!(std::ptr::null_mut::()); // Do not lint inside macros from external crates let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); } diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index 2575a5923e124..43ab5f5ba7ccc 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -74,10 +74,14 @@ fn null_pointers() { use std::ptr; let _ = ptr::null::() as *mut String; let _ = ptr::null_mut::() as *const u32; + let _ = ptr::null::().cast_mut(); + let _ = ptr::null_mut::().cast_const(); // Make sure the lint is triggered inside a macro let _ = inline!(ptr::null::() as *mut u32); + let _ = inline!(ptr::null::().cast_mut()); // Do not lint inside macros from external crates let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); } diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 0806ca62806d1..a693793a4ae96 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -55,13 +55,33 @@ error: `as` casting to make a mutable null pointer into a const null pointer LL | let _ = ptr::null_mut::() as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:77:13 + | +LL | let _ = ptr::null::().cast_mut(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:78:13 + | +LL | let _ = ptr::null_mut::().cast_const(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:79:21 + --> tests/ui/ptr_cast_constness.rs:81:21 | LL | let _ = inline!(ptr::null::() as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` | = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 10 previous errors +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:82:21 + | +LL | let _ = inline!(ptr::null::().cast_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors From 06d1aa257e4809cee5364e4f0a0ee6faf0706dc6 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Mon, 9 Sep 2024 16:36:37 +0800 Subject: [PATCH 0333/1301] Remove unnecessary symbols and add missing symbols Signed-off-by: cuishuang --- src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs | 2 +- .../crates/ide-assists/src/handlers/inline_type_alias.rs | 1 + .../crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs | 1 + src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs | 2 +- .../crates/rust-analyzer/tests/slow-tests/ratoml.rs | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index b1a6eed2fbc71..86dd4aef090ff 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -1,4 +1,4 @@ -//! Compiled declarative macro expanders (`macro_rules!`` and `macro`) +//! Compiled declarative macro expanders (`macro_rules!` and `macro`) use base_db::CrateId; use intern::sym; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index f6624d6c872d0..66dffde505c13 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -43,6 +43,7 @@ use super::inline_call::split_refs_and_uses; // fn foo() { // let _: i32 = 3; // } +// ``` pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let name = ctx.find_node_at_offset::()?; let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 0fa46ef43a1ef..149cb4c43849d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -25,6 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // struct S { // field: i32 // } +// ``` enum WrapUnwrapOption { WrapDerive { derive: TextRange, attr: ast::Attr }, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e4e8c9c6d9f97..3295ec258e2f0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -60,7 +60,7 @@ mod patch_old_style; // However, editor specific config, which the server doesn't know about, should // be specified directly in `package.json`. // -// To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep +// To deprecate an option by replacing it with another name use `new_name` | `old_name` so that we keep // parsing the old name. config_data! { /// Configs that apply on a workspace-wide scope. There are 2 levels on which a global configuration can be configured diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs index 295d1d4e8e909..a3e7528b39b1b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs @@ -765,7 +765,7 @@ fn ratoml_multiple_ratoml_in_single_source_root() { // [dependencies] // p2 = { path = "../p2" } -// #, +// "#, // r#" // //- /p1/src/lib.rs // enum Value { From 544ccedd552ddbeda9a63adffdd8fcc1b39cb3fe Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:50:54 +0800 Subject: [PATCH 0334/1301] feat: Allow hir-def prettifier formatting into one-line --- .../crates/hir-def/src/body/pretty.rs | 70 +++++++++++++++---- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 55740a68acd6e..6555d8061e7c4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -16,6 +16,13 @@ use crate::{ use super::*; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum LineFormat { + Oneline, + Newline, + Indentation, +} + pub(super) fn print_body_hir( db: &dyn DefDatabase, body: &Body, @@ -52,7 +59,14 @@ pub(super) fn print_body_hir( } }; - let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: header, + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); let function_data = &db.function_data(it); @@ -95,8 +109,14 @@ pub(super) fn print_expr_hir( expr: ExprId, edition: Edition, ) -> String { - let mut p = - Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; p.print_expr(expr); p.buf } @@ -109,10 +129,10 @@ macro_rules! w { macro_rules! wln { ($dst:expr) => { - { let _ = writeln!($dst); } + { $dst.newline(); } }; ($dst:expr, $($arg:tt)*) => { - { let _ = writeln!($dst, $($arg)*); } + { let _ = w!($dst, $($arg)*); $dst.newline(); } }; } @@ -121,24 +141,30 @@ struct Printer<'a> { body: &'a Body, buf: String, indent_level: usize, - needs_indent: bool, + line_format: LineFormat, edition: Edition, } impl Write for Printer<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { for line in s.split_inclusive('\n') { - if self.needs_indent { + if matches!(self.line_format, LineFormat::Indentation) { match self.buf.chars().rev().find(|ch| *ch != ' ') { Some('\n') | None => {} _ => self.buf.push('\n'), } self.buf.push_str(&" ".repeat(self.indent_level)); - self.needs_indent = false; } self.buf.push_str(line); - self.needs_indent = line.ends_with('\n'); + + if matches!(self.line_format, LineFormat::Newline | LineFormat::Indentation) { + self.line_format = if line.ends_with('\n') { + LineFormat::Indentation + } else { + LineFormat::Newline + }; + } } Ok(()) @@ -161,14 +187,28 @@ impl Printer<'_> { } } + // Add a newline if the current line is not empty. + // If the current line is empty, add a space instead. + // + // Do not use [`writeln!()`] or [`wln!()`] here, which will result in + // infinite recursive calls to this function. fn newline(&mut self) { - match self.buf.chars().rev().find_position(|ch| *ch != ' ') { - Some((_, '\n')) | None => {} - Some((idx, _)) => { - if idx != 0 { - self.buf.drain(self.buf.len() - idx..); + if matches!(self.line_format, LineFormat::Oneline) { + match self.buf.chars().last() { + Some(' ') | None => {} + Some(_) => { + w!(self, " "); + } + } + } else { + match self.buf.chars().rev().find_position(|ch| *ch != ' ') { + Some((_, '\n')) | None => {} + Some((idx, _)) => { + if idx != 0 { + self.buf.drain(self.buf.len() - idx..); + } + w!(self, "\n"); } - writeln!(self).unwrap() } } } From 352bd9f8e1c68dfdc1f4758566832e17b1543804 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:52:04 +0800 Subject: [PATCH 0335/1301] feat: add prettifier for Pat --- .../rust-analyzer/crates/hir-def/src/body.rs | 11 ++++++++++ .../crates/hir-def/src/body/pretty.rs | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index f18083d387356..6493826f073a7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -227,6 +227,17 @@ impl Body { pretty::print_expr_hir(db, self, owner, expr, edition) } + pub fn pretty_print_pat( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, + ) -> String { + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) + } + fn new( db: &dyn DefDatabase, owner: DefWithBodyId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 6555d8061e7c4..3333956adb22c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -121,6 +121,26 @@ pub(super) fn print_expr_hir( p.buf } +pub(super) fn print_pat_hir( + db: &dyn DefDatabase, + body: &Body, + _owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, +) -> String { + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline }, + edition, + }; + p.print_pat(pat); + p.buf +} + macro_rules! w { ($dst:expr, $($arg:tt)*) => { { let _ = write!($dst, $($arg)*); } From 81fae18c9e3eff9b69a466beba9e7081278b3a90 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:53:09 +0800 Subject: [PATCH 0336/1301] fix: use `pretty_print_pat` for params in fn --- .../rust-analyzer/crates/hir/src/display.rs | 13 +- .../crates/ide/src/hover/tests.rs | 115 ++++++++++++++++++ 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 923dca646673c..c2b2fbef75177 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -99,17 +99,20 @@ impl HirDisplay for Function { } // FIXME: Use resolved `param.ty` once we no longer discard lifetimes + let body = db.body(self.id.into()); for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) { - let local = param.as_local(db).map(|it| it.name(db)); if !first { f.write_str(", ")?; } else { first = false; } - match local { - Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?, - None => f.write_str("_: ")?, - } + + let pat_id = body.params[param.idx - body.self_param.is_some() as usize]; + let pat_str = + body.pretty_print_pat(db.upcast(), self.id.into(), pat_id, true, f.edition()); + f.write_str(&pat_str)?; + + f.write_str(": ")?; type_ref.hir_fmt(f)?; } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index f4528dd754c21..d1e51a2d28a53 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8742,3 +8742,118 @@ fn foo() { "#]], ); } + + +#[test] +fn test_hover_function_with_pat_param() { + check( + r#"fn test_1$0((start_range, end_range): (u32, u32), a: i32) {}"#, + expect![[r#" + *test_1* + + ```rust + test + ``` + + ```rust + fn test_1((start_range, end_range): (u32, u32), a: i32) + ``` + "#]], + ); + + // Test case with tuple pattern and mutable parameters + check( + r#"fn test_2$0((mut x, y): (i32, i32)) {}"#, + expect![[r#" + *test_2* + + ```rust + test + ``` + + ```rust + fn test_2((mut x, y): (i32, i32)) + ``` + "#]], + ); + + // Test case with a pattern in a reference type + check( + r#"fn test_3$0(&(a, b): &(i32, i32)) {}"#, + expect![[r#" + *test_3* + + ```rust + test + ``` + + ```rust + fn test_3(&(a, b): &(i32, i32)) + ``` + "#]], + ); + + // Test case with complex pattern (struct destructuring) + check( + r#"struct Point { x: i32, y: i32 } fn test_4$0(Point { x, y }: Point) {}"#, + expect![[r#" + *test_4* + + ```rust + test + ``` + + ```rust + fn test_4(Point { x: x, y: y, }: Point) + ``` + "#]], + ); + + // Test case with a nested pattern + check( + r#"fn test_5$0(((a, b), c): ((i32, i32), i32)) {}"#, + expect![[r#" + *test_5* + + ```rust + test + ``` + + ```rust + fn test_5(((a, b), c): ((i32, i32), i32)) + ``` + "#]], + ); + + // Test case with an unused variable in the pattern + check( + r#"fn test_6$0((_, y): (i32, i64)) {}"#, + expect![[r#" + *test_6* + + ```rust + test + ``` + + ```rust + fn test_6((_, y): (i32, i64)) + ``` + "#]], + ); + + // Test case with a complex pattern involving both tuple and struct + check( + r#"struct Foo { a: i32, b: i32 } fn test_7$0((x, Foo { a, b }): (i32, Foo)) {}"#, + expect![[r#" + *test_7* + + ```rust + test + ``` + + ```rust + fn test_7((x, Foo { a: a, b: b, }): (i32, Foo)) + ``` + "#]], + ); +} From cbfa3578134e09a5a296cba3dc8b7951569ca603 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:55:59 +0800 Subject: [PATCH 0337/1301] fix: add parenthesis for or-pattern --- .../crates/hir-def/src/body/pretty.rs | 2 ++ .../rust-analyzer/crates/ide/src/hover/tests.rs | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 3333956adb22c..64d0c16f525f7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -599,12 +599,14 @@ impl Printer<'_> { w!(self, ")"); } Pat::Or(pats) => { + w!(self, "("); for (i, pat) in pats.iter().enumerate() { if i != 0 { w!(self, " | "); } self.print_pat(*pat); } + w!(self, ")"); } Pat::Record { path, args, ellipsis } => { match path { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index d1e51a2d28a53..1a97d99f05129 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8743,7 +8743,6 @@ fn foo() { ); } - #[test] fn test_hover_function_with_pat_param() { check( @@ -8856,4 +8855,20 @@ fn test_hover_function_with_pat_param() { ``` "#]], ); + + // Test case with Enum and Or pattern + check( + r#"enum MyEnum { A(i32), B(i32) } fn test_8$0((MyEnum::A(x) | MyEnum::B(x)): MyEnum) {}"#, + expect![[r#" + *test_8* + + ```rust + test + ``` + + ```rust + fn test_8((MyEnum::A(x) | MyEnum::B(x)): MyEnum) + ``` + "#]], + ); } From b0db9c285dae65fc30a72d6862264df23feae430 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Mon, 9 Sep 2024 16:43:46 +0800 Subject: [PATCH 0338/1301] Remove unnecessary symbols and add missing symbols Signed-off-by: cuishuang --- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- tests/ui/crashes/ice-3969.rs | 4 ++-- tests/ui/crashes/ice-6251.rs | 2 +- tests/ui/floating_point_arithmetic_nostd.rs | 2 +- tests/ui/unsafe_removed_from_name.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 69c5bc57e2996..9e198c7e55cd7 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -717,7 +717,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx // check that: // 1. This is a method with only one argument that doesn't come from a trait. // 2. That it has `Borrow` in its generic predicates. -// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, BTreeSet`, `BTreeMap`). +// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap`). fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) diff --git a/tests/ui/crashes/ice-3969.rs b/tests/ui/crashes/ice-3969.rs index d5676cbd91d19..ac09ce08753af 100644 --- a/tests/ui/crashes/ice-3969.rs +++ b/tests/ui/crashes/ice-3969.rs @@ -1,7 +1,7 @@ // https://github.com/rust-lang/rust-clippy/issues/3969 // used to crash: error: internal compiler error: // src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs +// std::iter::Iterator>::Item` test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs // Check that tautalogically false bounds are accepted, and are used // in type inference. @@ -18,7 +18,7 @@ struct Dst { struct TwoStrs(str, str) where str: Sized; -//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetim +//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetime //~| NOTE: `-D trivial-bounds` implied by `-D warnings` fn unsized_local() diff --git a/tests/ui/crashes/ice-6251.rs b/tests/ui/crashes/ice-6251.rs index a81137a646558..73e919b6dd2eb 100644 --- a/tests/ui/crashes/ice-6251.rs +++ b/tests/ui/crashes/ice-6251.rs @@ -1,5 +1,5 @@ // originally from glacier/fixed/77329.rs -// assertion failed: `(left == right) ; different DefIds +// assertion failed: `(left == right)` ; different DefIds //@no-rustfix fn bug() -> impl Iterator { std::iter::empty() diff --git a/tests/ui/floating_point_arithmetic_nostd.rs b/tests/ui/floating_point_arithmetic_nostd.rs index 47c113d61c041..8ea75fae89b65 100644 --- a/tests/ui/floating_point_arithmetic_nostd.rs +++ b/tests/ui/floating_point_arithmetic_nostd.rs @@ -4,7 +4,7 @@ #![no_std] // The following should not lint, as the suggested methods `{f16,f32,f64,f128}.mul_add()` -// and ``{f16,f32,f64,f128}::abs()` are not available in no_std +// and `{f16,f32,f64,f128}::abs()` are not available in no_std pub fn mul_add() { let a: f64 = 1234.567; diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index e0e0ded140fc0..e9e6c8312f5eb 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -7,7 +7,7 @@ use std::cell::UnsafeCell as TotallySafeCell; //~| NOTE: `-D clippy::unsafe-removed-from-name` implied by `-D warnings` use std::cell::UnsafeCell as TotallySafeCellAgain; -//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain +//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain` // Shouldn't error use std::cell::RefCell as ProbablyNotUnsafe; From 1185eb081aae651fc6324630ed01203f92100d02 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Mon, 9 Sep 2024 06:49:03 -0700 Subject: [PATCH 0339/1301] ci: bump actions/checkout to v4 --- src/tools/miri/.github/workflows/sysroots.yml | 2 +- src/tools/miri/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/.github/workflows/sysroots.yml b/src/tools/miri/.github/workflows/sysroots.yml index 73a10768db908..6a4f44ddd507a 100644 --- a/src/tools/miri/.github/workflows/sysroots.yml +++ b/src/tools/miri/.github/workflows/sysroots.yml @@ -13,7 +13,7 @@ jobs: name: Build the sysroots runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build the sysroots run: | cargo install -f rustup-toolchain-install-master diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index dbb0e8a2925b3..72555e8c40d4f 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -187,7 +187,7 @@ Here is an example job for GitHub Actions: name: "Miri" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Miri run: | rustup toolchain install nightly --component miri From e1653b6d5e39612a7731d209f1eb6b5d5cbe656e Mon Sep 17 00:00:00 2001 From: roife Date: Wed, 4 Sep 2024 03:29:56 +0800 Subject: [PATCH 0340/1301] feat: better name suggestions for fn --- .../ide-db/src/syntax_helpers/suggest_name.rs | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 6ee526a67ea56..e60deb3bf59b1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -18,7 +18,9 @@ const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "Partia /// /// **NOTE**: they all must be snake lower case const USELESS_NAMES: &[&str] = - &["new", "default", "option", "some", "none", "ok", "err", "str", "string"]; + &["new", "default", "option", "some", "none", "ok", "err", "str", "string", "from", "into"]; + +const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// Generic types replaced by their first argument /// @@ -189,6 +191,10 @@ fn normalize(name: &str) -> Option { return None; } + if USELESS_NAME_PREFIXES.iter().any(|prefix| name.starts_with(prefix)) { + return None; + } + if !is_valid_name(&name) { return None; } @@ -831,4 +837,92 @@ fn foo(some_struct: S) { $0some_struct.some_field$0 } "some_field", ); } + + #[test] + fn from_and_to_func() { + check( + r#" +//- minicore: from +struct Foo; +struct Bar; + +impl From for Bar { + fn from(_: Foo) -> Self { + Bar; + } +} + +fn f(_: Bar) {} + +fn main() { + let foo = Foo {}; + f($0Bar::from(foo)$0); +} +"#, + "bar", + ); + + check( + r#" +//- minicore: from +struct Foo; +struct Bar; + +impl From for Bar { + fn from(_: Foo) -> Self { + Bar; + } +} + +fn f(_: Bar) {} + +fn main() { + let foo = Foo {}; + f($0Into::::into(foo)$0); +} +"#, + "bar", + ); + } + + #[test] + fn useless_name_prefix() { + check( + r#" +struct Foo; +struct Bar; + +impl Bar { + fn from_foo(_: Foo) -> Self { + Foo {} + } +} + +fn main() { + let foo = Foo {}; + let _ = $0Bar::from_foo(foo)$0; +} +"#, + "bar", + ); + + check( + r#" +struct Foo; +struct Bar; + +impl Bar { + fn with_foo(_: Foo) -> Self { + Bar {} + } +} + +fn main() { + let foo = Foo {}; + let _ = $0Bar::with_foo(foo)$0; +} +"#, + "bar", + ); + } } From d4eeb31c2eb4bee9f2e5791d01c8d55048b46a31 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 9 Sep 2024 16:01:47 +0200 Subject: [PATCH 0341/1301] use fd_num for file descriptors, so we can use fd for file description --- src/tools/miri/src/shims/unix/fd.rs | 85 ++++++++++---------- src/tools/miri/src/shims/unix/fs.rs | 78 ++++++++---------- src/tools/miri/src/shims/unix/linux/epoll.rs | 12 +-- 3 files changed, 83 insertions(+), 92 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index d756c06ebdb12..48bf959538b4f 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -303,7 +303,7 @@ pub struct FdTable { impl VisitProvenance for FdTable { fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { - // All our FileDescriptionRef do not have any tags. + // All our FileDescription instances do not have any tags. } } @@ -337,18 +337,18 @@ impl FdTable { } pub fn insert(&mut self, fd_ref: FileDescriptionRef) -> i32 { - self.insert_ref_with_min_fd(fd_ref, 0) + self.insert_with_min_num(fd_ref, 0) } - /// Insert a file description, giving it a file descriptor that is at least `min_fd`. - fn insert_ref_with_min_fd(&mut self, file_handle: FileDescriptionRef, min_fd: i32) -> i32 { + /// Insert a file description, giving it a file descriptor that is at least `min_fd_num`. + fn insert_with_min_num(&mut self, file_handle: FileDescriptionRef, min_fd_num: i32) -> i32 { // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in // between used FDs, the find_map combinator will return it. If the first such unused FD // is after all other used FDs, the find_map combinator will return None, and we will use // the FD following the greatest FD thus far. let candidate_new_fd = - self.fds.range(min_fd..).zip(min_fd..).find_map(|((fd, _fh), counter)| { - if *fd != counter { + self.fds.range(min_fd_num..).zip(min_fd_num..).find_map(|((fd_num, _fd), counter)| { + if *fd_num != counter { // There was a gap in the fds stored, return the first unused one // (note that this relies on BTreeMap iterating in key order) Some(counter) @@ -357,61 +357,61 @@ impl FdTable { None } }); - let new_fd = candidate_new_fd.unwrap_or_else(|| { + let new_fd_num = candidate_new_fd.unwrap_or_else(|| { // find_map ran out of BTreeMap entries before finding a free fd, use one plus the // maximum fd in the map - self.fds.last_key_value().map(|(fd, _)| fd.strict_add(1)).unwrap_or(min_fd) + self.fds.last_key_value().map(|(fd_num, _)| fd_num.strict_add(1)).unwrap_or(min_fd_num) }); - self.fds.try_insert(new_fd, file_handle).unwrap(); - new_fd + self.fds.try_insert(new_fd_num, file_handle).unwrap(); + new_fd_num } - pub fn get(&self, fd: i32) -> Option { - let fd = self.fds.get(&fd)?; + pub fn get(&self, fd_num: i32) -> Option { + let fd = self.fds.get(&fd_num)?; Some(fd.clone()) } - pub fn remove(&mut self, fd: i32) -> Option { - self.fds.remove(&fd) + pub fn remove(&mut self, fd_num: i32) -> Option { + self.fds.remove(&fd_num) } - pub fn is_fd(&self, fd: i32) -> bool { - self.fds.contains_key(&fd) + pub fn is_fd_num(&self, fd_num: i32) -> bool { + self.fds.contains_key(&fd_num) } } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, Scalar> { + fn dup(&mut self, old_fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(dup_fd) = this.machine.fds.get(old_fd) else { + let Some(fd) = this.machine.fds.get(old_fd_num) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; - Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, 0))) + Ok(Scalar::from_i32(this.machine.fds.insert(fd))) } - fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, Scalar> { + fn dup2(&mut self, old_fd_num: i32, new_fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(dup_fd) = this.machine.fds.get(old_fd) else { + let Some(fd) = this.machine.fds.get(old_fd_num) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; - if new_fd != old_fd { + if new_fd_num != old_fd_num { // Close new_fd if it is previously opened. // If old_fd and new_fd point to the same description, then `dup_fd` ensures we keep the underlying file description alive. - if let Some(file_description) = this.machine.fds.fds.insert(new_fd, dup_fd) { + if let Some(old_new_fd) = this.machine.fds.fds.insert(new_fd_num, fd) { // Ignore close error (not interpreter's) according to dup2() doc. - file_description.close(this.machine.communicate(), this)?.ok(); + old_new_fd.close(this.machine.communicate(), this)?.ok(); } } - Ok(Scalar::from_i32(new_fd)) + Ok(Scalar::from_i32(new_fd_num)) } - fn flock(&mut self, fd: i32, op: i32) -> InterpResult<'tcx, Scalar> { + fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(fd_ref) = this.machine.fds.get(fd) else { + let Some(fd) = this.machine.fds.get(fd_num) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; @@ -436,8 +436,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("unsupported flags {:#x}", op); }; - let result = fd_ref.flock(this.machine.communicate(), parsed_op)?; - drop(fd_ref); + let result = fd.flock(this.machine.communicate(), parsed_op)?; + drop(fd); // return `0` if flock is successful let result = result.map(|()| 0i32); Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) @@ -452,7 +452,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { args.len() ); } - let fd = this.read_scalar(&args[0])?.to_i32()?; + let fd_num = this.read_scalar(&args[0])?.to_i32()?; let cmd = this.read_scalar(&args[1])?.to_i32()?; // We only support getting the flags for a descriptor. @@ -461,7 +461,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` // always sets this flag when opening a file. However we still need to check that the // file itself is open. - Ok(Scalar::from_i32(if this.machine.fds.is_fd(fd) { + Ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { this.eval_libc_i32("FD_CLOEXEC") } else { this.fd_not_found()? @@ -481,9 +481,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let start = this.read_scalar(&args[2])?.to_i32()?; - match this.machine.fds.get(fd) { - Some(dup_fd) => - Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, start))), + match this.machine.fds.get(fd_num) { + Some(fd) => Ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), None => Ok(Scalar::from_i32(this.fd_not_found()?)), } } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { @@ -494,7 +493,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(Scalar::from_i32(-1)); } - this.ffullsync_fd(fd) + this.ffullsync_fd(fd_num) } else { throw_unsup_format!("the {:#x} command is not supported for `fcntl`)", cmd); } @@ -503,12 +502,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn close(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let fd = this.read_scalar(fd_op)?.to_i32()?; + let fd_num = this.read_scalar(fd_op)?.to_i32()?; - let Some(file_description) = this.machine.fds.remove(fd) else { + let Some(fd) = this.machine.fds.remove(fd_num) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; - let result = file_description.close(this.machine.communicate(), this)?; + let result = fd.close(this.machine.communicate(), this)?; // return `0` if close is successful let result = result.map(|()| 0i32); Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) @@ -532,7 +531,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// and keeps the cursor unchanged. fn read( &mut self, - fd: i32, + fd_num: i32, buf: Pointer, count: u64, offset: Option, @@ -541,7 +540,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Isolation check is done via `FileDescription` trait. - trace!("Reading from FD {}, size {}", fd, count); + trace!("Reading from FD {}, size {}", fd_num, count); // Check that the *entire* buffer is actually valid memory. this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?; @@ -554,7 +553,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { 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) else { + let Some(fd) = this.machine.fds.get(fd_num) else { trace!("read: FD not found"); return Ok(Scalar::from_target_isize(this.fd_not_found()?, this)); }; @@ -597,7 +596,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn write( &mut self, - fd: i32, + fd_num: i32, buf: Pointer, count: u64, offset: Option, @@ -618,7 +617,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned(); // We temporarily dup the FD to be able to retain mutable access to `this`. - let Some(fd) = this.machine.fds.get(fd) else { + let Some(fd) = this.machine.fds.get(fd_num) else { return Ok(Scalar::from_target_isize(this.fd_not_found()?, this)); }; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 58d3a73b481d2..e1697a4741559 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -554,7 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) } - fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { + fn lseek64(&mut self, fd_num: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescription` trait. @@ -580,13 +580,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let communicate = this.machine.communicate(); - let Some(file_description) = this.machine.fds.get(fd) else { + let Some(fd) = this.machine.fds.get(fd_num) else { return Ok(Scalar::from_i64(this.fd_not_found()?)); }; - let result = file_description - .seek(communicate, seek_from)? - .map(|offset| i64::try_from(offset).unwrap()); - drop(file_description); + let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap()); + drop(fd); let result = this.try_unwrap_io_result(result)?; Ok(Scalar::from_i64(result)) @@ -721,7 +719,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(Scalar::from_i32(this.fd_not_found()?)); } - let metadata = match FileMetadata::from_fd(this, fd)? { + let metadata = match FileMetadata::from_fd_num(this, fd)? { Some(metadata) => metadata, None => return Ok(Scalar::from_i32(-1)), }; @@ -808,7 +806,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If the path is empty, and the AT_EMPTY_PATH flag is set, we query the open file // represented by dirfd, whether it's a directory or otherwise. let metadata = if path.as_os_str().is_empty() && empty_path_flag { - FileMetadata::from_fd(this, dirfd)? + FileMetadata::from_fd_num(this, dirfd)? } else { FileMetadata::from_path(this, &path, follow_symlink)? }; @@ -1260,7 +1258,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })) } - fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> { + fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // Reject if isolation is enabled. @@ -1270,30 +1268,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(Scalar::from_i32(this.fd_not_found()?)); } - let Some(file_description) = this.machine.fds.get(fd) else { + let Some(fd) = this.machine.fds.get(fd_num) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // FIXME: Support ftruncate64 for all FDs - let FileHandle { file, writable } = - file_description.downcast::().ok_or_else(|| { - err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors") - })?; + let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { + err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors") + })?; if *writable { if let Ok(length) = length.try_into() { let result = file.set_len(length); - drop(file_description); + drop(fd); let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; Ok(Scalar::from_i32(result)) } else { - drop(file_description); + drop(fd); let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; Ok(Scalar::from_i32(-1)) } } else { - drop(file_description); + drop(fd); // The file is not writable let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; @@ -1321,18 +1318,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.ffullsync_fd(fd) } - fn ffullsync_fd(&mut self, fd: i32) -> InterpResult<'tcx, Scalar> { + fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(file_description) = this.machine.fds.get(fd) else { + let Some(fd) = this.machine.fds.get(fd_num) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. - let FileHandle { file, writable } = - file_description.downcast::().ok_or_else(|| { - err_unsup_format!("`fsync` is only supported on file-backed file descriptors") - })?; + let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { + err_unsup_format!("`fsync` is only supported on file-backed file descriptors") + })?; let io_result = maybe_sync_file(file, *writable, File::sync_all); - drop(file_description); + drop(fd); Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } @@ -1348,16 +1344,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(Scalar::from_i32(this.fd_not_found()?)); } - let Some(file_description) = this.machine.fds.get(fd) else { + let Some(fd) = this.machine.fds.get(fd) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. - let FileHandle { file, writable } = - file_description.downcast::().ok_or_else(|| { - err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors") - })?; + let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { + err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors") + })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); - drop(file_description); + drop(fd); Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } @@ -1396,18 +1391,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(Scalar::from_i32(this.fd_not_found()?)); } - let Some(file_description) = this.machine.fds.get(fd) else { + let Some(fd) = this.machine.fds.get(fd) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. - let FileHandle { file, writable } = - file_description.downcast::().ok_or_else(|| { - err_unsup_format!( - "`sync_data_range` is only supported on file-backed file descriptors" - ) - })?; + let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { + err_unsup_format!("`sync_data_range` is only supported on file-backed file descriptors") + })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); - drop(file_description); + drop(fd); Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } @@ -1699,15 +1691,15 @@ impl FileMetadata { FileMetadata::from_meta(ecx, metadata) } - fn from_fd<'tcx>( + fn from_fd_num<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - fd: i32, + fd_num: i32, ) -> InterpResult<'tcx, Option> { - let Some(file_description) = ecx.machine.fds.get(fd) else { + let Some(fd) = ecx.machine.fds.get(fd_num) else { return ecx.fd_not_found().map(|_: i32| None); }; - let file = &file_description + let file = &fd .downcast::() .ok_or_else(|| { err_unsup_format!( @@ -1717,7 +1709,7 @@ impl FileMetadata { .file; let metadata = file.metadata(); - drop(file_description); + drop(fd); FileMetadata::from_meta(ecx, metadata) } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index ee86cf5f26d7f..d91ce45e101d9 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -51,7 +51,7 @@ impl EpollEventInstance { #[derive(Clone, Debug)] pub struct EpollEventInterest { /// The file descriptor value of the file description registered. - file_descriptor: i32, + fd_num: i32, /// The events bitmask retrieved from `epoll_event`. events: u32, /// The data retrieved from `epoll_event`. @@ -62,7 +62,7 @@ pub struct EpollEventInterest { /// Ready list of the epoll instance under which this EpollEventInterest is registered. ready_list: Rc>>, /// The file descriptor value that this EpollEventInterest is registered under. - epfd: i32, + epfd_num: i32, } /// EpollReadyEvents reflects the readiness of a file description. @@ -339,11 +339,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Create an epoll_interest. let interest = Rc::new(RefCell::new(EpollEventInterest { - file_descriptor: fd, + fd_num: fd, events, data, ready_list: Rc::clone(ready_list), - epfd: epfd_value, + epfd_num: epfd_value, })); if op == epoll_ctl_add { @@ -553,7 +553,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if is_updated { // Edge-triggered notification only notify one thread even if there are // multiple threads block on the same epfd. - let epfd = this.machine.fds.get(epoll_interest.borrow().epfd).unwrap(); + let epfd = this.machine.fds.get(epoll_interest.borrow().epfd_num).unwrap(); // This unwrap can never fail because if the current epoll instance were // closed and its epfd value reused, the upgrade of weak_epoll_interest @@ -615,7 +615,7 @@ fn check_and_update_one_event_interest<'tcx>( // If there is any event that we are interested in being specified as ready, // insert an epoll_return to the ready list. if flags != 0 { - let epoll_key = (id, epoll_event_interest.file_descriptor); + let epoll_key = (id, epoll_event_interest.fd_num); let ready_list = &mut epoll_event_interest.ready_list.borrow_mut(); let event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); // Triggers the notification by inserting it to the ready list. From 1f1363335922b76e6940cf632a391f3315f564cf Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 17 Aug 2024 21:38:10 +0200 Subject: [PATCH 0342/1301] Bump ui_test --- Cargo.toml | 2 +- tests/compile-test.rs | 10 ++- tests/ui/asm_syntax_not_x86.rs | 3 +- tests/ui/asm_syntax_x86.rs | 4 +- tests/ui/asm_syntax_x86.stderr | 70 +++++++++++++++++++ tests/ui/crashes/ice-7410.rs | 3 +- .../entrypoint_recursion.rs | 2 +- .../no_std_main_recursion.rs | 2 +- tests/ui/def_id_nocore.rs | 2 +- tests/ui/empty_loop_no_std.rs | 2 +- tests/ui/enum_clike_unportable_variant.rs | 2 +- tests/ui/macro_use_imports.fixed | 2 +- tests/ui/macro_use_imports.rs | 2 +- tests/ui/macro_use_imports_expect.rs | 2 +- tests/ui/mixed_attributes_style.rs | 3 +- tests/ui/mixed_attributes_style.stderr | 3 +- tests/ui/non_octal_unix_permissions.fixed | 2 +- tests/ui/non_octal_unix_permissions.rs | 2 +- tests/ui/result_large_err.rs | 2 +- tests/ui/single_call_fn.rs | 2 +- tests/ui/transmute_32bit.rs | 2 +- tests/ui/transmute_64bit.rs | 2 +- 22 files changed, 96 insertions(+), 30 deletions(-) create mode 100644 tests/ui/asm_syntax_x86.stderr diff --git a/Cargo.toml b/Cargo.toml index 5b62e387ac631..cf810798d8cc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ anstream = "0.6.0" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.25" +ui_test = "0.26.4" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 9754254cdd0df..c7243348ce46f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -13,7 +13,6 @@ use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::custom_flags::Flag; use ui_test::spanned::Spanned; -use ui_test::test_result::TestRun; use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling}; use std::collections::{BTreeMap, HashMap}; @@ -469,15 +468,14 @@ fn applicability_ord(applicability: &Applicability) -> u8 { impl Flag for DiagnosticCollector { fn post_test_action( &self, - _config: &ui_test::per_test_config::TestConfig<'_>, - _cmd: &mut std::process::Command, + _config: &ui_test::per_test_config::TestConfig, output: &std::process::Output, - _build_manager: &ui_test::build_manager::BuildManager<'_>, - ) -> Result, ui_test::Errored> { + _build_manager: &ui_test::build_manager::BuildManager, + ) -> Result<(), ui_test::Errored> { if !output.stderr.is_empty() { self.sender.send(output.stderr.clone()).unwrap(); } - Ok(Vec::new()) + Ok(()) } fn clone_inner(&self) -> Box { diff --git a/tests/ui/asm_syntax_not_x86.rs b/tests/ui/asm_syntax_not_x86.rs index 33cea4806814b..a7d29cc239e53 100644 --- a/tests/ui/asm_syntax_not_x86.rs +++ b/tests/ui/asm_syntax_not_x86.rs @@ -1,5 +1,4 @@ -//@ignore-target-i686 -//@ignore-target-x86 +//@ignore-target: i686 x86 //@needs-asm-support #[warn(clippy::inline_asm_x86_intel_syntax)] diff --git a/tests/ui/asm_syntax_x86.rs b/tests/ui/asm_syntax_x86.rs index 835943b43894c..5ceaceb7527b3 100644 --- a/tests/ui/asm_syntax_x86.rs +++ b/tests/ui/asm_syntax_x86.rs @@ -1,6 +1,4 @@ -//@revisions: i686 x86_64 -//@[i686] only-target-i686 -//@[x86_64] only-target-x86_64 +//@only-target: i686 x86_64 #[warn(clippy::inline_asm_x86_intel_syntax)] mod warn_intel { diff --git a/tests/ui/asm_syntax_x86.stderr b/tests/ui/asm_syntax_x86.stderr new file mode 100644 index 0000000000000..1911ef66e2391 --- /dev/null +++ b/tests/ui/asm_syntax_x86.stderr @@ -0,0 +1,70 @@ +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:8:9 + | +LL | asm!(""); + | ^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_intel_syntax)]` + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:10:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:12:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:18:5 + | +LL | global_asm!(""); + | ^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:20:5 + | +LL | global_asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:33:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_att_syntax)]` + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:35:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:41:5 + | +LL | global_asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 8 previous errors + diff --git a/tests/ui/crashes/ice-7410.rs b/tests/ui/crashes/ice-7410.rs index a2683b3ce3402..ccf6d7ff94f23 100644 --- a/tests/ui/crashes/ice-7410.rs +++ b/tests/ui/crashes/ice-7410.rs @@ -1,6 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple -//@ignore-target-windows +//@ignore-target: apple windows #![feature(lang_items, start, libc)] #![no_std] diff --git a/tests/ui/crate_level_checks/entrypoint_recursion.rs b/tests/ui/crate_level_checks/entrypoint_recursion.rs index aa76688d80101..5d853d97bc35b 100644 --- a/tests/ui/crate_level_checks/entrypoint_recursion.rs +++ b/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -1,4 +1,4 @@ -//@ignore-target-apple +//@ignore-target: apple #![feature(rustc_attrs)] diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs index 32eba9695920c..9e5b2a4890348 100644 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,5 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple +//@ignore-target: apple #![feature(lang_items, start, libc)] #![no_std] diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index 190d636ebf34a..c9650312db878 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -1,4 +1,4 @@ -//@ignore-target-apple +//@ignore-target: apple #![feature(no_core, lang_items, start)] #![no_core] diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs index 5fe32351ed450..1bb895bda75d7 100644 --- a/tests/ui/empty_loop_no_std.rs +++ b/tests/ui/empty_loop_no_std.rs @@ -1,5 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple +//@ignore-target: apple #![warn(clippy::empty_loop)] #![feature(lang_items, start, libc)] diff --git a/tests/ui/enum_clike_unportable_variant.rs b/tests/ui/enum_clike_unportable_variant.rs index c50404c5047bb..37849179d6a0b 100644 --- a/tests/ui/enum_clike_unportable_variant.rs +++ b/tests/ui/enum_clike_unportable_variant.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #![warn(clippy::enum_clike_unportable_variant)] #![allow(unused, non_upper_case_globals)] diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 38ed5a957e73e..28844fab74793 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -2,7 +2,7 @@ //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index ae6cc16ed2769..5381f29598987 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -2,7 +2,7 @@ //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/tests/ui/macro_use_imports_expect.rs b/tests/ui/macro_use_imports_expect.rs index df6d5b9fbabf2..60cce1d24a289 100644 --- a/tests/ui/macro_use_imports_expect.rs +++ b/tests/ui/macro_use_imports_expect.rs @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/tests/ui/mixed_attributes_style.rs b/tests/ui/mixed_attributes_style.rs index 1a646c2652236..93ab9392cf8bd 100644 --- a/tests/ui/mixed_attributes_style.rs +++ b/tests/ui/mixed_attributes_style.rs @@ -82,7 +82,8 @@ mod issue_12530 { #![allow(dead_code)] } } - /// Nested mod //~ ERROR: item has both inner and outer attributes + /// Nested mod + //~^ ERROR: item has both inner and outer attributes #[allow(unused)] mod nest_mod_2 { #![allow(unused)] diff --git a/tests/ui/mixed_attributes_style.stderr b/tests/ui/mixed_attributes_style.stderr index a1d3fc430f6cf..abdc2df23cf12 100644 --- a/tests/ui/mixed_attributes_style.stderr +++ b/tests/ui/mixed_attributes_style.stderr @@ -46,13 +46,14 @@ error: item has both inner and outer attributes --> tests/ui/mixed_attributes_style.rs:85:5 | LL | / /// Nested mod +LL | | LL | | #[allow(unused)] LL | | mod nest_mod_2 { LL | | #![allow(unused)] | |_________________________^ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:90:9 + --> tests/ui/mixed_attributes_style.rs:91:9 | LL | / #[allow(dead_code)] LL | | mod inner_mod { diff --git a/tests/ui/non_octal_unix_permissions.fixed b/tests/ui/non_octal_unix_permissions.fixed index 237f5f5b97a4f..f68d5e30d27e7 100644 --- a/tests/ui/non_octal_unix_permissions.fixed +++ b/tests/ui/non_octal_unix_permissions.fixed @@ -1,4 +1,4 @@ -//@ignore-target-windows +//@ignore-target: windows #![warn(clippy::non_octal_unix_permissions)] use std::fs::{DirBuilder, File, OpenOptions, Permissions}; diff --git a/tests/ui/non_octal_unix_permissions.rs b/tests/ui/non_octal_unix_permissions.rs index c8da5dbcec282..647c3769d2c3f 100644 --- a/tests/ui/non_octal_unix_permissions.rs +++ b/tests/ui/non_octal_unix_permissions.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows +//@ignore-target: windows #![warn(clippy::non_octal_unix_permissions)] use std::fs::{DirBuilder, File, OpenOptions, Permissions}; diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index b25348bf99617..9c39f023da2da 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #![warn(clippy::result_large_err)] #![allow(clippy::large_enum_variant)] diff --git a/tests/ui/single_call_fn.rs b/tests/ui/single_call_fn.rs index 55e7508a9573a..a0597664da55c 100644 --- a/tests/ui/single_call_fn.rs +++ b/tests/ui/single_call_fn.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 //@aux-build:proc_macros.rs #![allow(clippy::redundant_closure_call, unused)] #![warn(clippy::single_call_fn)] diff --git a/tests/ui/transmute_32bit.rs b/tests/ui/transmute_32bit.rs index 8e1316ca39d4f..e162bbb2d9296 100644 --- a/tests/ui/transmute_32bit.rs +++ b/tests/ui/transmute_32bit.rs @@ -1,4 +1,4 @@ -//@ignore-64bit +//@ignore-bitwidth: 64 #[warn(clippy::wrong_transmute)] fn main() { diff --git a/tests/ui/transmute_64bit.rs b/tests/ui/transmute_64bit.rs index 767cc503a39df..fd0ad74bcfa0a 100644 --- a/tests/ui/transmute_64bit.rs +++ b/tests/ui/transmute_64bit.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #[warn(clippy::wrong_transmute)] fn main() { From 129acd2e08cbe614be11acb7a4f1f5169dba3a23 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 23:14:31 +0800 Subject: [PATCH 0343/1301] feat: use shorthand when pretty-print record pat --- .../crates/hir-def/src/body/pretty.rs | 33 ++++++++++-- .../crates/ide/src/hover/tests.rs | 52 ++++++++++++++++++- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 64d0c16f525f7..37167fcb8155b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -616,12 +616,37 @@ impl Printer<'_> { w!(self, " {{"); let edition = self.edition; + let oneline = matches!(self.line_format, LineFormat::Oneline); self.indented(|p| { - for arg in args.iter() { - w!(p, "{}: ", arg.name.display(self.db.upcast(), edition)); - p.print_pat(arg.pat); - wln!(p, ","); + for (idx, arg) in args.iter().enumerate() { + let field_name = arg.name.display(self.db.upcast(), edition).to_string(); + + let mut same_name = false; + if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] { + if let Binding { name, mode: BindingAnnotation::Unannotated, .. } = + &self.body.bindings[*id] + { + if name.as_str() == field_name { + same_name = true; + } + } + } + + w!(p, "{}", field_name); + + if !same_name { + w!(p, ": "); + p.print_pat(arg.pat); + } + + // Do not print the extra comma if the line format is oneline + if oneline && idx == args.len() - 1 { + w!(p, " "); + } else { + wln!(p, ","); + } } + if *ellipsis { wln!(p, ".."); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 1a97d99f05129..f2e5d24fcc682 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8803,7 +8803,7 @@ fn test_hover_function_with_pat_param() { ``` ```rust - fn test_4(Point { x: x, y: y, }: Point) + fn test_4(Point { x, y }: Point) ``` "#]], ); @@ -8851,7 +8851,7 @@ fn test_hover_function_with_pat_param() { ``` ```rust - fn test_7((x, Foo { a: a, b: b, }): (i32, Foo)) + fn test_7((x, Foo { a, b }): (i32, Foo)) ``` "#]], ); @@ -8871,4 +8871,52 @@ fn test_hover_function_with_pat_param() { ``` "#]], ); + + // Test case with a pattern as a function parameter + check( + r#"struct Foo { a: i32, b: i32 } fn test_9$0(Foo { a, b }: Foo) {}"#, + expect![[r#" + *test_9* + + ```rust + test + ``` + + ```rust + fn test_9(Foo { a, b }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with a different name + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: b1 }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: b1 }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with annotations + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: mut b }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: mut b }: Foo) + ``` + "#]], + ); } From 91b2c17ffb0f0e34e586fabff47d915c5caeed95 Mon Sep 17 00:00:00 2001 From: cui fliter Date: Mon, 9 Sep 2024 23:23:15 +0800 Subject: [PATCH 0344/1301] Add missing backtick in comment (#6317) Signed-off-by: cuishuang --- src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index d35a249a80a31..80515077abd59 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -448,7 +448,7 @@ pub fn load_config( }) } -// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir` +// Check for the presence of known config file names (`rustfmt.toml`, `.rustfmt.toml`) in `dir` // // Return the path if a config file exists, empty if no file exists, and Error for IO errors fn get_toml_path(dir: &Path) -> Result, Error> { From 182a20378b3a844e3677ff93d96f8ac70b9f009a Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Mon, 9 Sep 2024 06:51:19 -0700 Subject: [PATCH 0345/1301] ci: bump actions/checkout to v4 --- .github/workflows/check_diff.yml | 2 +- .github/workflows/integration.yml | 2 +- .github/workflows/linux.yml | 2 +- .github/workflows/mac.yml | 2 +- .github/workflows/rustdoc_check.yml | 2 +- .github/workflows/upload-assets.yml | 2 +- .github/workflows/windows.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/check_diff.yml b/.github/workflows/check_diff.yml index 2f2beb7691595..99daa0addf571 100644 --- a/.github/workflows/check_diff.yml +++ b/.github/workflows/check_diff.yml @@ -21,7 +21,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install rustup run: | diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index f0dd0cf73bbd4..bda374562bc1b 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -64,7 +64,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index bce9b0c8d5a95..3a5e6ab54047b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index f98c9830a4d22..2c766d0573b96 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -22,7 +22,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/.github/workflows/rustdoc_check.yml b/.github/workflows/rustdoc_check.yml index cd0c3218971e2..6e8a7ecd7ad81 100644 --- a/.github/workflows/rustdoc_check.yml +++ b/.github/workflows/rustdoc_check.yml @@ -11,7 +11,7 @@ jobs: name: rustdoc check steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install rustup run: | diff --git a/.github/workflows/upload-assets.yml b/.github/workflows/upload-assets.yml index 7dfaa4b9204c2..7a639b469e849 100644 --- a/.github/workflows/upload-assets.yml +++ b/.github/workflows/upload-assets.yml @@ -31,7 +31,7 @@ jobs: target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index ec37c714b0851..728f1b90b13f5 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,7 +33,7 @@ jobs: - name: disable git eol translation run: git config --global core.autocrlf false - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: Install Rustup using win.rustup.rs From 32d9597200ab93cd64dee1751991a994372bad45 Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 3 Sep 2024 23:07:41 +0800 Subject: [PATCH 0346/1301] refactor: introduce NameGenerator in suggest_name --- .../src/handlers/generate_delegate_trait.rs | 35 ++- .../src/handlers/introduce_named_generic.rs | 21 +- .../ide-completion/src/completions/pattern.rs | 3 +- .../ide-db/src/syntax_helpers/suggest_name.rs | 212 +++++++++++++----- .../rust-analyzer/crates/syntax/src/lib.rs | 2 +- 5 files changed, 192 insertions(+), 81 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index c22d19574fb49..a55323eb59d8d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -584,7 +584,7 @@ fn resolve_name_conflicts( for old_strukt_param in old_strukt_params.generic_params() { // Get old name from `strukt` - let mut name = SmolStr::from(match &old_strukt_param { + let name = SmolStr::from(match &old_strukt_param { ast::GenericParam::ConstParam(c) => c.name()?.to_string(), ast::GenericParam::LifetimeParam(l) => { l.lifetime()?.lifetime_ident_token()?.to_string() @@ -593,8 +593,19 @@ fn resolve_name_conflicts( }); // The new name cannot be conflicted with generics in trait, and the renamed names. - name = suggest_name::for_unique_generic_name(&name, old_impl_params); - name = suggest_name::for_unique_generic_name(&name, ¶ms); + let param_list_to_names = |param_list: &GenericParamList| { + param_list.generic_params().flat_map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()), + p => Some(p.to_string()), + }) + }; + let existing_names = param_list_to_names(old_impl_params) + .chain(param_list_to_names(¶ms)) + .collect_vec(); + let mut name_generator = suggest_name::NameGenerator::new_with_names( + existing_names.iter().map(|s| s.as_str()), + ); + let name = name_generator.suggest_name(&name); match old_strukt_param { ast::GenericParam::ConstParam(c) => { if let Some(const_ty) = c.ty() { @@ -1213,9 +1224,9 @@ struct S { b : B, } -impl Trait for S { - fn f(&self, a: T0) -> T0 { - as Trait>::f(&self.b, a) +impl Trait for S { + fn f(&self, a: T1) -> T1 { + as Trait>::f(&self.b, a) } } "#, @@ -1527,12 +1538,12 @@ where b : B, } -impl Trait for S +impl Trait for S where - T10: AnotherTrait + T3: AnotherTrait { fn f(&self, a: T) -> T { - as Trait>::f(&self.b, a) + as Trait>::f(&self.b, a) } }"#, ); @@ -1589,12 +1600,12 @@ where b : B, } -impl Trait for S +impl Trait for S where - T0: AnotherTrait + T2: AnotherTrait { fn f(&self, a: T) -> T { - as Trait>::f(&self.b, a) + as Trait>::f(&self.b, a) } }"#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs index a734a6cc2bc88..bf6ac1719f318 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -1,6 +1,7 @@ use ide_db::syntax_helpers::suggest_name; +use itertools::Itertools; use syntax::{ - ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams}, + ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams, HasName}, ted, }; @@ -33,8 +34,18 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_> let impl_trait_type = edit.make_mut(impl_trait_type); let fn_ = edit.make_mut(fn_); let fn_generic_param_list = fn_.get_or_create_generic_param_list(); - let type_param_name = - suggest_name::for_impl_trait_as_generic(&impl_trait_type, &fn_generic_param_list); + + let existing_names = fn_generic_param_list + .generic_params() + .flat_map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()), + p => Some(p.to_string()), + }) + .collect_vec(); + let type_param_name = suggest_name::NameGenerator::new_with_names( + existing_names.iter().map(|s| s.as_str()), + ) + .for_impl_trait_as_generic(&impl_trait_type); let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list)) .clone_for_update(); @@ -116,7 +127,7 @@ fn foo<$0B: Bar check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B0) {}"#, + r#"fn foo(bar: B1) {}"#, ); } @@ -125,7 +136,7 @@ fn foo<$0B: Bar check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B2) {}"#, + r#"fn foo(bar: B4) {}"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index 2a06fc4017557..8f38e02ed7685 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs @@ -48,11 +48,12 @@ pub(crate) fn complete_pattern( // Suggest name only in let-stmt and fn param if pattern_ctx.should_suggest_name { + let mut name_generator = suggest_name::NameGenerator::new(); if let Some(suggested) = ctx .expected_type .as_ref() .map(|ty| ty.strip_references()) - .and_then(|ty| suggest_name::for_type(&ty, ctx.db, ctx.edition)) + .and_then(|ty| name_generator.for_type(&ty, ctx.db, ctx.edition)) { acc.suggest_name(ctx, &suggested); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index e60deb3bf59b1..2679cbef61b3b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -1,12 +1,14 @@ //! This module contains functions to suggest names for expressions, functions and other items +use std::{collections::hash_map::Entry, str::FromStr}; + use hir::Semantics; use itertools::Itertools; -use rustc_hash::FxHashSet; +use rustc_hash::FxHashMap; use stdx::to_lower_snake_case; use syntax::{ ast::{self, HasName}, - match_ast, AstNode, Edition, SmolStr, + match_ast, AstNode, Edition, SmolStr, SmolStrBuilder, }; use crate::RootDatabase; @@ -62,71 +64,131 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; -/// Suggest a name for given type. +/// Generator for new names /// -/// The function will strip references first, and suggest name from the inner type. +/// The generator keeps track of existing names and suggests new names that do +/// not conflict with existing names. /// -/// - If `ty` is an ADT, it will suggest the name of the ADT. -/// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type. -/// - If `ty` is a trait, it will suggest the name of the trait. -/// - If `ty` is an `impl Trait`, it will suggest the name of the first trait. +/// The generator will try to resolve conflicts by adding a numeric suffix to +/// the name, e.g. `a`, `a1`, `a2`, ... /// -/// If the suggested name conflicts with reserved keywords, it will return `None`. -pub fn for_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option { - let ty = ty.strip_references(); - name_of_type(&ty, db, edition) -} - -/// Suggest a unique name for generic parameter. -/// -/// `existing_params` is used to check if the name conflicts with existing -/// generic parameters. +/// # Examples +/// ```rust +/// let mut generator = NameGenerator::new(); +/// assert_eq!(generator.suggest_name("a"), "a"); +/// assert_eq!(generator.suggest_name("a"), "a1"); /// -/// The function checks if the name conflicts with existing generic parameters. -/// If so, it will try to resolve the conflict by adding a number suffix, e.g. -/// `T`, `T0`, `T1`, ... -pub fn for_unique_generic_name(name: &str, existing_params: &ast::GenericParamList) -> SmolStr { - let param_names = existing_params - .generic_params() - .map(|param| match param { - ast::GenericParam::TypeParam(t) => t.name().unwrap().to_string(), - p => p.to_string(), - }) - .collect::>(); - let mut name = name.to_owned(); - let base_len = name.len(); - let mut count = 0; - while param_names.contains(&name) { - name.truncate(base_len); - name.push_str(&count.to_string()); - count += 1; - } - - name.into() +/// assert_eq!(generator.suggest_name("b2"), "b2"); +/// assert_eq!(generator.suggest_name("b"), "b3"); +/// ``` +#[derive(Debug, Default)] +pub struct NameGenerator { + pool: FxHashMap, } -/// Suggest name of impl trait type -/// -/// `existing_params` is used to check if the name conflicts with existing -/// generic parameters. -/// -/// # Current implementation -/// -/// In current implementation, the function tries to get the name from the first -/// character of the name for the first type bound. -/// -/// If the name conflicts with existing generic parameters, it will try to -/// resolve the conflict with `for_unique_generic_name`. -pub fn for_impl_trait_as_generic( - ty: &ast::ImplTraitType, - existing_params: &ast::GenericParamList, -) -> SmolStr { - let c = ty - .type_bound_list() - .and_then(|bounds| bounds.syntax().text().char_at(0.into())) - .unwrap_or('T'); - - for_unique_generic_name(c.encode_utf8(&mut [0; 4]), existing_params) +impl NameGenerator { + /// Create a new empty generator + pub fn new() -> Self { + Self { pool: FxHashMap::default() } + } + + /// Create a new generator with existing names. When suggesting a name, it will + /// avoid conflicts with existing names. + pub fn new_with_names<'a>(existing_names: impl Iterator) -> Self { + let mut generator = Self::new(); + existing_names.for_each(|name| generator.insert(name)); + generator + } + + /// Suggest a name without conflicts. If the name conflicts with existing names, + /// it will try to resolve the conflict by adding a numeric suffix. + pub fn suggest_name(&mut self, name: &str) -> SmolStr { + let (prefix, suffix) = Self::split_numeric_suffix(name); + let prefix = SmolStr::new(prefix); + let suffix = suffix.unwrap_or(0); + + match self.pool.entry(prefix.clone()) { + Entry::Vacant(entry) => { + entry.insert(suffix); + SmolStr::from_str(name).unwrap() + } + Entry::Occupied(mut entry) => { + let count = entry.get_mut(); + *count = (*count + 1).max(suffix); + + let mut new_name = SmolStrBuilder::new(); + new_name.push_str(&prefix); + new_name.push_str(count.to_string().as_str()); + new_name.finish() + } + } + } + + /// Suggest a name for given type. + /// + /// The function will strip references first, and suggest name from the inner type. + /// + /// - If `ty` is an ADT, it will suggest the name of the ADT. + /// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type. + /// - If `ty` is a trait, it will suggest the name of the trait. + /// - If `ty` is an `impl Trait`, it will suggest the name of the first trait. + /// + /// If the suggested name conflicts with reserved keywords, it will return `None`. + pub fn for_type( + &mut self, + ty: &hir::Type, + db: &RootDatabase, + edition: Edition, + ) -> Option { + let name = name_of_type(ty, db, edition)?; + Some(self.suggest_name(&name)) + } + + /// Suggest name of impl trait type + /// + /// # Current implementation + /// + /// In current implementation, the function tries to get the name from the first + /// character of the name for the first type bound. + /// + /// If the name conflicts with existing generic parameters, it will try to + /// resolve the conflict with `for_unique_generic_name`. + pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr { + let c = ty + .type_bound_list() + .and_then(|bounds| bounds.syntax().text().char_at(0.into())) + .unwrap_or('T'); + + self.suggest_name(&c.to_string()) + } + + /// Insert a name into the pool + fn insert(&mut self, name: &str) { + let (prefix, suffix) = Self::split_numeric_suffix(name); + let prefix = SmolStr::new(prefix); + let suffix = suffix.unwrap_or(0); + + match self.pool.entry(prefix) { + Entry::Vacant(entry) => { + entry.insert(suffix); + } + Entry::Occupied(mut entry) => { + let count = entry.get_mut(); + *count = (*count).max(suffix); + } + } + } + + /// Remove the numeric suffix from the name + /// + /// # Examples + /// `a1b2c3` -> `a1b2c` + fn split_numeric_suffix(name: &str) -> (&str, Option) { + let pos = + name.rfind(|c: char| !c.is_numeric()).expect("Name cannot be empty or all-numeric"); + let (prefix, suffix) = name.split_at(pos + 1); + (prefix, suffix.parse().ok()) + } } /// Suggest name of variable for given expression @@ -290,9 +352,10 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option { fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); + let ty = ty.remove_ref().unwrap_or(ty); let edition = sema.scope(expr.syntax())?.krate().edition(sema.db); - for_type(&ty, sema.db, edition) + name_of_type(&ty, sema.db, edition) } fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option { @@ -925,4 +988,29 @@ fn main() { "bar", ); } + + #[test] + fn conflicts_with_existing_names() { + let mut generator = NameGenerator::new(); + assert_eq!(generator.suggest_name("a"), "a"); + assert_eq!(generator.suggest_name("a"), "a1"); + assert_eq!(generator.suggest_name("a"), "a2"); + assert_eq!(generator.suggest_name("a"), "a3"); + + assert_eq!(generator.suggest_name("b"), "b"); + assert_eq!(generator.suggest_name("b2"), "b2"); + assert_eq!(generator.suggest_name("b"), "b3"); + assert_eq!(generator.suggest_name("b"), "b4"); + assert_eq!(generator.suggest_name("b3"), "b5"); + + // --------- + let mut generator = NameGenerator::new_with_names(["a", "b", "b2", "c4"].into_iter()); + assert_eq!(generator.suggest_name("a"), "a1"); + assert_eq!(generator.suggest_name("a"), "a2"); + + assert_eq!(generator.suggest_name("b"), "b3"); + assert_eq!(generator.suggest_name("b2"), "b4"); + + assert_eq!(generator.suggest_name("c"), "c5"); + } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index b68374848b90c..7fb80b2e7d81d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -65,7 +65,7 @@ pub use rowan::{ TokenAtOffset, WalkEvent, }; pub use rustc_lexer::unescape; -pub use smol_str::{format_smolstr, SmolStr, ToSmolStr}; +pub use smol_str::{format_smolstr, SmolStr, SmolStrBuilder, ToSmolStr}; /// `Parse` is the result of the parsing: a syntax tree and a collection of /// errors. From b304701b670e45d4d1a43691f0b3083423d41e31 Mon Sep 17 00:00:00 2001 From: roife Date: Wed, 4 Sep 2024 01:38:34 +0800 Subject: [PATCH 0347/1301] feat: generate names for tuple-struct in add-missing-match-arms --- .../src/handlers/add_missing_match_arms.rs | 130 ++++++++++++++---- 1 file changed, 106 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index b6abb06a2afc1..a211ca8f2d675 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,12 +1,13 @@ use std::iter::{self, Peekable}; use either::Either; -use hir::{sym, Adt, Crate, HasAttrs, HasSource, ImportPathConfig, ModuleDef, Semantics}; +use hir::{sym, Adt, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics}; +use ide_db::syntax_helpers::suggest_name; use ide_db::RootDatabase; use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; use itertools::Itertools; use syntax::ast::edit_in_place::Removable; -use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat}; +use syntax::ast::{self, make, AstNode, MatchArmList, MatchExpr, Pat}; use crate::{utils, AssistContext, AssistId, AssistKind, Assists}; @@ -90,7 +91,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|variant| { Some(( - build_pat(ctx.db(), module, variant, cfg)?, + build_pat(ctx, module, variant, cfg)?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -141,9 +142,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants - .into_iter() - .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); + let patterns = + variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) }) @@ -174,9 +174,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants - .into_iter() - .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); + let patterns = + variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); @@ -438,33 +437,39 @@ fn resolve_array_of_enum_def( } fn build_pat( - db: &RootDatabase, + ctx: &AssistContext<'_>, module: hir::Module, var: ExtendedVariant, cfg: ImportPathConfig, ) -> Option { + let db = ctx.db(); match var { ExtendedVariant::Variant(var) => { let edition = module.krate().edition(db); let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition); - // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though - Some(match var.source(db)?.value.kind() { - ast::StructKind::Tuple(field_list) => { - let pats = - iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); + let fields = var.fields(db); + let pat = match var.kind(db) { + hir::StructKind::Tuple => { + let mut name_generator = suggest_name::NameGenerator::new(); + let pats = fields.into_iter().map(|f| { + let name = name_generator.for_type(&f.ty(db), db, edition); + match name { + Some(name) => make::ext::simple_ident_pat(make::name(&name)).into(), + None => make::wildcard_pat().into(), + } + }); make::tuple_struct_pat(path, pats).into() } - ast::StructKind::Record(field_list) => { - let pats = field_list.fields().map(|f| { - make::ext::simple_ident_pat( - f.name().expect("Record field must have a name"), - ) - .into() - }); + hir::StructKind::Record => { + let pats = fields + .into_iter() + .map(|f| make::name(f.name(db).as_str())) + .map(|name| make::ext::simple_ident_pat(name).into()); make::record_pat(path, pats).into() } - ast::StructKind::Unit => make::path_pat(path), - }) + hir::StructKind::Unit => make::path_pat(path), + }; + Some(pat) } ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), @@ -1976,4 +1981,81 @@ fn a() { }"#, ) } + + #[test] + fn suggest_name_for_tuple_struct_patterns() { + // single tuple struct + check_assist( + add_missing_match_arms, + r#" +struct S; + +pub enum E { + A + B(S), +} + +fn f() { + let value = E::A; + match value { + $0 + } +} +"#, + r#" +struct S; + +pub enum E { + A + B(S), +} + +fn f() { + let value = E::A; + match value { + $0E::A => todo!(), + E::B(s) => todo!(), + } +} +"#, + ); + + // multiple tuple struct patterns + check_assist( + add_missing_match_arms, + r#" +struct S1; +struct S2; + +pub enum E { + A + B(S1, S2), +} + +fn f() { + let value = E::A; + match value { + $0 + } +} +"#, + r#" +struct S1; +struct S2; + +pub enum E { + A + B(S1, S2), +} + +fn f() { + let value = E::A; + match value { + $0E::A => todo!(), + E::B(s1, s2) => todo!(), + } +} +"#, + ); + } } From 51dbed2482577a33c20e4c4f08a33fbb372a2703 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 9 Sep 2024 18:59:35 +0200 Subject: [PATCH 0348/1301] sync methods: pass around places, not pointer-typed operands --- src/tools/miri/src/concurrency/init_once.rs | 7 +- src/tools/miri/src/concurrency/sync.rs | 46 ++++----- src/tools/miri/src/shims/unix/macos/sync.rs | 5 +- src/tools/miri/src/shims/unix/sync.rs | 101 ++++++++------------ src/tools/miri/src/shims/windows/sync.rs | 5 +- 5 files changed, 69 insertions(+), 95 deletions(-) diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index d709e4e6eaedb..9c2c6ae133023 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -1,7 +1,6 @@ use std::collections::VecDeque; use rustc_index::Idx; -use rustc_middle::ty::layout::TyAndLayout; use super::sync::EvalContextExtPriv as _; use super::vector_clock::VClock; @@ -30,14 +29,12 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn init_once_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx>, - lock_layout: TyAndLayout<'tcx>, + lock: &MPlaceTy<'tcx>, offset: u64, ) -> InterpResult<'tcx, InitOnceId> { let this = self.eval_context_mut(); this.get_or_create_id( - lock_op, - lock_layout, + lock, offset, |ecx| &mut ecx.machine.sync.init_onces, |_| Ok(Default::default()), diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index a6a2a03b1b080..ff55af60ee82b 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -4,7 +4,7 @@ use std::time::Duration; use rustc_data_structures::fx::FxHashMap; use rustc_index::{Idx, IndexVec}; -use rustc_middle::ty::layout::TyAndLayout; +use rustc_target::abi::Size; use super::init_once::InitOnce; use super::vector_clock::VClock; @@ -206,21 +206,21 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { #[inline] fn get_or_create_id( &mut self, - lock_op: &OpTy<'tcx>, - lock_layout: TyAndLayout<'tcx>, + lock: &MPlaceTy<'tcx>, offset: u64, get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec, create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>, ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - let value_place = - this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?; + let offset = Size::from_bytes(offset); + assert!(lock.layout.size >= offset + this.machine.layouts.u32.size); + let id_place = lock.offset(offset, this.machine.layouts.u32, this)?; let next_index = get_objs(this).next_index(); // Since we are lazy, this update has to be atomic. let (old, success) = this .atomic_compare_exchange_scalar( - &value_place, + &id_place, &ImmTy::from_uint(0u32, this.machine.layouts.u32), Scalar::from_u32(next_index.to_u32()), AtomicRwOrd::Relaxed, // deliberately *no* synchronization @@ -258,18 +258,18 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { /// - `obj` must be the new sync object. fn create_id( &mut self, - lock_op: &OpTy<'tcx>, - lock_layout: TyAndLayout<'tcx>, + lock: &MPlaceTy<'tcx>, offset: u64, get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec, obj: T, ) -> InterpResult<'tcx, Id> { let this = self.eval_context_mut(); - let value_place = - this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?; + let offset = Size::from_bytes(offset); + assert!(lock.layout.size >= offset + this.machine.layouts.u32.size); + let id_place = lock.offset(offset, this.machine.layouts.u32, this)?; let new_index = get_objs(this).push(obj); - this.write_scalar(Scalar::from_u32(new_index.to_u32()), &value_place)?; + this.write_scalar(Scalar::from_u32(new_index.to_u32()), &id_place)?; Ok(new_index) } @@ -302,15 +302,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Eagerly create and initialize a new mutex. fn mutex_create( &mut self, - lock_op: &OpTy<'tcx>, - lock_layout: TyAndLayout<'tcx>, + lock: &MPlaceTy<'tcx>, offset: u64, data: Option, ) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); this.create_id( - lock_op, - lock_layout, + lock, offset, |ecx| &mut ecx.machine.sync.mutexes, Mutex { data, ..Default::default() }, @@ -321,8 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// `initialize_data` must return any additional data that a user wants to associate with the mutex. fn mutex_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx>, - lock_layout: TyAndLayout<'tcx>, + lock: &MPlaceTy<'tcx>, offset: u64, initialize_data: impl for<'a> FnOnce( &'a mut MiriInterpCx<'tcx>, @@ -330,8 +327,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); this.get_or_create_id( - lock_op, - lock_layout, + lock, offset, |ecx| &mut ecx.machine.sync.mutexes, |ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }), @@ -350,8 +346,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn rwlock_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx>, - lock_layout: TyAndLayout<'tcx>, + lock: &MPlaceTy<'tcx>, offset: u64, initialize_data: impl for<'a> FnOnce( &'a mut MiriInterpCx<'tcx>, @@ -360,8 +355,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, RwLockId> { let this = self.eval_context_mut(); this.get_or_create_id( - lock_op, - lock_layout, + lock, offset, |ecx| &mut ecx.machine.sync.rwlocks, |ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }), @@ -380,14 +374,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn condvar_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx>, - lock_layout: TyAndLayout<'tcx>, + lock: &MPlaceTy<'tcx>, offset: u64, ) -> InterpResult<'tcx, CondvarId> { let this = self.eval_context_mut(); this.get_or_create_id( - lock_op, - lock_layout, + lock, offset, |ecx| &mut ecx.machine.sync.condvars, |_| Ok(Default::default()), diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 882c08cca154c..8f9237a1d46ea 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -14,12 +14,13 @@ use crate::*; impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn os_unfair_lock_getid(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> { + fn os_unfair_lock_getid(&mut self, lock_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); + let lock = this.deref_pointer(lock_ptr)?; // os_unfair_lock holds a 32-bit value, is initialized with zero and // must be assumed to be opaque. Therefore, we can just store our // internal mutex ID in the structure without anyone noticing. - this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0, |_| Ok(None)) + this.mutex_get_or_create_id(&lock, 0, |_| Ok(None)) } } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 9d664985fd41e..31de1362638cc 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -18,10 +18,10 @@ fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u fn mutexattr_get_kind<'tcx>( ecx: &MiriInterpCx<'tcx>, - attr_op: &OpTy<'tcx>, + attr_ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( - attr_op, + attr_ptr, mutexattr_kind_offset(ecx)?, ecx.libc_ty_layout("pthread_mutexattr_t"), ecx.machine.layouts.i32, @@ -31,11 +31,11 @@ fn mutexattr_get_kind<'tcx>( fn mutexattr_set_kind<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - attr_op: &OpTy<'tcx>, + attr_ptr: &OpTy<'tcx>, kind: i32, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( - attr_op, + attr_ptr, mutexattr_kind_offset(ecx)?, Scalar::from_i32(kind), ecx.libc_ty_layout("pthread_mutexattr_t"), @@ -94,15 +94,14 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { /// Eagerly create and initialize a new mutex. fn mutex_create<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - mutex_op: &OpTy<'tcx>, + mutex_ptr: &OpTy<'tcx>, kind: i32, ) -> InterpResult<'tcx> { - // FIXME: might be worth changing mutex_create to take the mplace - // rather than the `OpTy`. - let address = ecx.read_pointer(mutex_op)?.addr().bytes(); + let mutex = ecx.deref_pointer(mutex_ptr)?; + let address = mutex.ptr().addr().bytes(); let kind = translate_kind(ecx, kind)?; let data = Some(AdditionalMutexData { address, kind }); - ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?; + ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, data)?; Ok(()) } @@ -112,24 +111,18 @@ fn mutex_create<'tcx>( /// return an error if it has. fn mutex_get_id<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - mutex_op: &OpTy<'tcx>, + mutex_ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, MutexId> { - let address = ecx.read_pointer(mutex_op)?.addr().bytes(); - - // FIXME: might be worth changing mutex_get_or_create_id to take the mplace - // rather than the `OpTy`. - let id = ecx.mutex_get_or_create_id( - mutex_op, - ecx.libc_ty_layout("pthread_mutex_t"), - mutex_id_offset(ecx)?, - |ecx| { - // This is called if a static initializer was used and the lock has not been assigned - // an ID yet. We have to determine the mutex kind from the static initializer. - let kind = kind_from_static_initializer(ecx, mutex_op)?; - - Ok(Some(AdditionalMutexData { kind, address })) - }, - )?; + let mutex = ecx.deref_pointer(mutex_ptr)?; + let address = mutex.ptr().addr().bytes(); + + let id = ecx.mutex_get_or_create_id(&mutex, mutex_id_offset(ecx)?, |ecx| { + // This is called if a static initializer was used and the lock has not been assigned + // an ID yet. We have to determine the mutex kind from the static initializer. + let kind = kind_from_static_initializer(ecx, &mutex)?; + + Ok(Some(AdditionalMutexData { kind, address })) + })?; // Check that the mutex has not been moved since last use. let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads"); @@ -143,20 +136,15 @@ fn mutex_get_id<'tcx>( /// Returns the kind of a static initializer. fn kind_from_static_initializer<'tcx>( ecx: &MiriInterpCx<'tcx>, - mutex_op: &OpTy<'tcx>, + mutex: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, MutexKind> { // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT. let kind = match &*ecx.tcx.sess.target.os { "linux" => { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; - - ecx.deref_pointer_and_read( - mutex_op, - offset, - ecx.libc_ty_layout("pthread_mutex_t"), - ecx.machine.layouts.i32, - )? - .to_i32()? + let kind_place = + mutex.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)?; + ecx.read_scalar(&kind_place)?.to_i32()? } | "illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"), os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), @@ -211,16 +199,14 @@ fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { fn rwlock_get_id<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - rwlock_op: &OpTy<'tcx>, + rwlock_ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, RwLockId> { - let address = ecx.read_pointer(rwlock_op)?.addr().bytes(); + let rwlock = ecx.deref_pointer(rwlock_ptr)?; + let address = rwlock.ptr().addr().bytes(); - let id = ecx.rwlock_get_or_create_id( - rwlock_op, - ecx.libc_ty_layout("pthread_rwlock_t"), - rwlock_id_offset(ecx)?, - |_| Ok(Some(AdditionalRwLockData { address })), - )?; + let id = ecx.rwlock_get_or_create_id(&rwlock, rwlock_id_offset(ecx)?, |_| { + Ok(Some(AdditionalRwLockData { address })) + })?; // Check that the rwlock has not been moved since last use. let data = ecx.rwlock_get_data(id).expect("data should be always exist for pthreads"); @@ -246,10 +232,10 @@ fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u fn condattr_get_clock_id<'tcx>( ecx: &MiriInterpCx<'tcx>, - attr_op: &OpTy<'tcx>, + attr_ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( - attr_op, + attr_ptr, condattr_clock_offset(ecx)?, ecx.libc_ty_layout("pthread_condattr_t"), ecx.machine.layouts.i32, @@ -259,11 +245,11 @@ fn condattr_get_clock_id<'tcx>( fn condattr_set_clock_id<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - attr_op: &OpTy<'tcx>, + attr_ptr: &OpTy<'tcx>, clock_id: i32, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( - attr_op, + attr_ptr, condattr_clock_offset(ecx)?, Scalar::from_i32(clock_id), ecx.libc_ty_layout("pthread_condattr_t"), @@ -337,21 +323,18 @@ fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 { fn cond_get_id<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - cond_op: &OpTy<'tcx>, + cond_ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, CondvarId> { - ecx.condvar_get_or_create_id( - cond_op, - ecx.libc_ty_layout("pthread_cond_t"), - cond_id_offset(ecx)?, - ) + let cond = ecx.deref_pointer(cond_ptr)?; + ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?) } fn cond_reset_id<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - cond_op: &OpTy<'tcx>, + cond_ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( - cond_op, + cond_ptr, cond_id_offset(ecx)?, Scalar::from_i32(0), ecx.libc_ty_layout("pthread_cond_t"), @@ -361,10 +344,10 @@ fn cond_reset_id<'tcx>( fn cond_get_clock_id<'tcx>( ecx: &MiriInterpCx<'tcx>, - cond_op: &OpTy<'tcx>, + cond_ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( - cond_op, + cond_ptr, cond_clock_offset(ecx), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.i32, @@ -374,11 +357,11 @@ fn cond_get_clock_id<'tcx>( fn cond_set_clock_id<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - cond_op: &OpTy<'tcx>, + cond_ptr: &OpTy<'tcx>, clock_id: i32, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( - cond_op, + cond_ptr, cond_clock_offset(ecx), Scalar::from_i32(clock_id), ecx.libc_ty_layout("pthread_cond_t"), diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index e1fbb77037cfb..51b0129356baa 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -10,9 +10,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Windows sync primitives are pointer sized. // We only use the first 4 bytes for the id. - fn init_once_get_id(&mut self, init_once_op: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> { + fn init_once_get_id(&mut self, init_once_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> { let this = self.eval_context_mut(); - this.init_once_get_or_create_id(init_once_op, this.windows_ty_layout("INIT_ONCE"), 0) + let init_once = this.deref_pointer(init_once_ptr)?; + this.init_once_get_or_create_id(&init_once, 0) } /// Returns `true` if we were succssful, `false` if we would block. From 218057c56543957e1519fad88095a2c14e77024d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 9 Sep 2024 19:07:51 +0200 Subject: [PATCH 0349/1301] sync: store extra sync primitive data in a Box so the type can be kept local --- src/tools/miri/src/concurrency/sync.rs | 48 ++++------------- src/tools/miri/src/lib.rs | 5 +- src/tools/miri/src/shims/unix/sync.rs | 72 +++++++++++++++++--------- 3 files changed, 59 insertions(+), 66 deletions(-) diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index ff55af60ee82b..7098cc5b95bef 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::collections::{hash_map::Entry, VecDeque}; use std::ops::Not; use std::time::Duration; @@ -66,27 +67,6 @@ pub(super) use declare_id; declare_id!(MutexId); -/// The mutex kind. -#[derive(Debug, Clone, Copy)] -#[non_exhaustive] -pub enum MutexKind { - Invalid, - Normal, - Default, - Recursive, - ErrorCheck, -} - -#[derive(Debug)] -/// Additional data that may be used by shim implementations. -pub struct AdditionalMutexData { - /// The mutex kind, used by some mutex implementations like pthreads mutexes. - pub kind: MutexKind, - - /// The address of the mutex. - pub address: u64, -} - /// The mutex state. #[derive(Default, Debug)] struct Mutex { @@ -100,18 +80,11 @@ struct Mutex { clock: VClock, /// Additional data that can be set by shim implementations. - data: Option, + data: Option>, } declare_id!(RwLockId); -#[derive(Debug)] -/// Additional data that may be used by shim implementations. -pub struct AdditionalRwLockData { - /// The address of the rwlock. - pub address: u64, -} - /// The read-write lock state. #[derive(Default, Debug)] struct RwLock { @@ -146,7 +119,7 @@ struct RwLock { clock_current_readers: VClock, /// Additional data that can be set by shim implementations. - data: Option, + data: Option>, } declare_id!(CondvarId); @@ -304,7 +277,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, lock: &MPlaceTy<'tcx>, offset: u64, - data: Option, + data: Option>, ) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); this.create_id( @@ -323,7 +296,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { offset: u64, initialize_data: impl for<'a> FnOnce( &'a mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, Option>, + ) -> InterpResult<'tcx, Option>>, ) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); this.get_or_create_id( @@ -336,12 +309,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Retrieve the additional data stored for a mutex. - fn mutex_get_data<'a>(&'a mut self, id: MutexId) -> Option<&'a AdditionalMutexData> + fn mutex_get_data<'a, T: 'static>(&'a mut self, id: MutexId) -> Option<&'a T> where 'tcx: 'a, { let this = self.eval_context_ref(); - this.machine.sync.mutexes[id].data.as_ref() + this.machine.sync.mutexes[id].data.as_deref().and_then(|p| p.downcast_ref::()) } fn rwlock_get_or_create_id( @@ -350,8 +323,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { offset: u64, initialize_data: impl for<'a> FnOnce( &'a mut MiriInterpCx<'tcx>, - ) - -> InterpResult<'tcx, Option>, + ) -> InterpResult<'tcx, Option>>, ) -> InterpResult<'tcx, RwLockId> { let this = self.eval_context_mut(); this.get_or_create_id( @@ -364,12 +336,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Retrieve the additional data stored for a rwlock. - fn rwlock_get_data<'a>(&'a mut self, id: RwLockId) -> Option<&'a AdditionalRwLockData> + fn rwlock_get_data<'a, T: 'static>(&'a mut self, id: RwLockId) -> Option<&'a T> where 'tcx: 'a, { let this = self.eval_context_ref(); - this.machine.sync.rwlocks[id].data.as_ref() + this.machine.sync.rwlocks[id].data.as_deref().and_then(|p| p.downcast_ref::()) } fn condvar_get_or_create_id( diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 273b1107ed0b9..8a59206943d18 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -133,10 +133,7 @@ pub use crate::concurrency::{ cpu_affinity::MAX_CPUS, data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _}, init_once::{EvalContextExt as _, InitOnceId}, - sync::{ - AdditionalMutexData, AdditionalRwLockData, CondvarId, EvalContextExt as _, MutexId, - MutexKind, RwLockId, SynchronizationObjects, - }, + sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects}, thread::{ BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor, TimeoutClock, UnblockCallback, diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 31de1362638cc..dacbe973af260 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -59,6 +59,25 @@ fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResu Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG)) } +/// The mutex kind. +#[derive(Debug, Clone, Copy)] +pub enum MutexKind { + Normal, + Default, + Recursive, + ErrorCheck, +} + +#[derive(Debug)] +/// Additional data that we attach with each mutex instance. +pub struct AdditionalMutexData { + /// The mutex kind, used by some mutex implementations like pthreads mutexes. + pub kind: MutexKind, + + /// The address of the mutex. + pub address: u64, +} + // pthread_mutex_t is between 24 and 48 bytes, depending on the platform. // We ignore the platform layout and store our own fields: // - id: u32 @@ -100,8 +119,8 @@ fn mutex_create<'tcx>( let mutex = ecx.deref_pointer(mutex_ptr)?; let address = mutex.ptr().addr().bytes(); let kind = translate_kind(ecx, kind)?; - let data = Some(AdditionalMutexData { address, kind }); - ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, data)?; + let data = Box::new(AdditionalMutexData { address, kind }); + ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, Some(data))?; Ok(()) } @@ -121,11 +140,13 @@ fn mutex_get_id<'tcx>( // an ID yet. We have to determine the mutex kind from the static initializer. let kind = kind_from_static_initializer(ecx, &mutex)?; - Ok(Some(AdditionalMutexData { kind, address })) + Ok(Some(Box::new(AdditionalMutexData { kind, address }))) })?; // Check that the mutex has not been moved since last use. - let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads"); + let data = ecx + .mutex_get_data::(id) + .expect("data should always exist for pthreads"); if data.address != address { throw_ub_format!("pthread_mutex_t can't be moved after first use") } @@ -171,6 +192,13 @@ fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tc // We ignore the platform layout and store our own fields: // - id: u32 +#[derive(Debug)] +/// Additional data that may be used by shim implementations. +pub struct AdditionalRwLockData { + /// The address of the rwlock. + pub address: u64, +} + fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, @@ -205,11 +233,13 @@ fn rwlock_get_id<'tcx>( let address = rwlock.ptr().addr().bytes(); let id = ecx.rwlock_get_or_create_id(&rwlock, rwlock_id_offset(ecx)?, |_| { - Ok(Some(AdditionalRwLockData { address })) + Ok(Some(Box::new(AdditionalRwLockData { address }))) })?; // Check that the rwlock has not been moved since last use. - let data = ecx.rwlock_get_data(id).expect("data should be always exist for pthreads"); + let data = ecx + .rwlock_get_data::(id) + .expect("data should always exist for pthreads"); if data.address != address { throw_ub_format!("pthread_rwlock_t can't be moved after first use") } @@ -473,8 +503,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let id = mutex_get_id(this, mutex_op)?; - let kind = - this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind; + let kind = this + .mutex_get_data::(id) + .expect("data should always exist for pthread mutexes") + .kind; let ret = if this.mutex_is_locked(id) { let owner_thread = this.mutex_get_owner(id); @@ -492,10 +524,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_lock(id); 0 } - _ => - throw_unsup_format!( - "called pthread_mutex_lock on an unsupported type of mutex" - ), } } } else { @@ -511,8 +539,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let id = mutex_get_id(this, mutex_op)?; - let kind = - this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind; + let kind = this + .mutex_get_data::(id) + .expect("data should always exist for pthread mutexes") + .kind; Ok(Scalar::from_i32(if this.mutex_is_locked(id) { let owner_thread = this.mutex_get_owner(id); @@ -526,10 +556,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_lock(id); 0 } - _ => - throw_unsup_format!( - "called pthread_mutex_trylock on an unsupported type of mutex" - ), } } } else { @@ -543,8 +569,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let id = mutex_get_id(this, mutex_op)?; - let kind = - this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind; + let kind = this + .mutex_get_data::(id) + .expect("data should always exist for pthread mutexes") + .kind; if let Some(_old_locked_count) = this.mutex_unlock(id)? { // The mutex was locked by the current thread. @@ -564,10 +592,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ), MutexKind::ErrorCheck | MutexKind::Recursive => Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))), - _ => - throw_unsup_format!( - "called pthread_mutex_unlock on an unsupported type of mutex" - ), } } } From fe5183e627731f076e1aee9133f71190b5b67c1a Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 23 Aug 2024 01:59:26 -0700 Subject: [PATCH 0350/1301] Fix the examples in cg_clif --- example/float-minmax-pass.rs | 12 ++++++------ example/std_example.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/example/float-minmax-pass.rs b/example/float-minmax-pass.rs index c54574d801d3a..ad46e18c11c0d 100644 --- a/example/float-minmax-pass.rs +++ b/example/float-minmax-pass.rs @@ -9,13 +9,13 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); use std::intrinsics::simd::*; fn main() { - let x = f32x4(1.0, 2.0, 3.0, 4.0); - let y = f32x4(2.0, 1.0, 4.0, 3.0); + let x = f32x4([1.0, 2.0, 3.0, 4.0]); + let y = f32x4([2.0, 1.0, 4.0, 3.0]); #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] let nan = f32::NAN; @@ -24,13 +24,13 @@ fn main() { #[cfg(any(target_arch = "mips", target_arch = "mips64"))] let nan = f32::from_bits(f32::NAN.to_bits() - 1); - let n = f32x4(nan, nan, nan, nan); + let n = f32x4([nan, nan, nan, nan]); unsafe { let min0 = simd_fmin(x, y); let min1 = simd_fmin(y, x); assert_eq!(min0, min1); - let e = f32x4(1.0, 1.0, 3.0, 3.0); + let e = f32x4([1.0, 1.0, 3.0, 3.0]); assert_eq!(min0, e); let minn = simd_fmin(x, n); assert_eq!(minn, x); @@ -40,7 +40,7 @@ fn main() { let max0 = simd_fmax(x, y); let max1 = simd_fmax(y, x); assert_eq!(max0, max1); - let e = f32x4(2.0, 2.0, 4.0, 4.0); + let e = f32x4([2.0, 2.0, 4.0, 4.0]); assert_eq!(max0, e); let maxn = simd_fmax(x, n); assert_eq!(maxn, x); diff --git a/example/std_example.rs b/example/std_example.rs index e99763e272233..f27d4ef57e0c0 100644 --- a/example/std_example.rs +++ b/example/std_example.rs @@ -166,7 +166,7 @@ fn main() { enum Never {} } - foo(I64X2(0, 0)); + foo(I64X2([0, 0])); transmute_fat_pointer(); @@ -204,7 +204,7 @@ fn rust_call_abi() { } #[repr(simd)] -struct I64X2(i64, i64); +struct I64X2([i64; 2]); #[allow(improper_ctypes_definitions)] extern "C" fn foo(_a: I64X2) {} From bd5873ae1206127011d7d4918d51ffeab5ce4ac0 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Tue, 10 Sep 2024 01:16:16 -0400 Subject: [PATCH 0351/1301] bump toolchain to nightly-2024-09-10 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 7b7081bfe815b..80723123274a0 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-08-17" +channel = "nightly-2024-09-10" components = ["llvm-tools", "rustc-dev"] From 0d12c5809f10fa6dea73af038677949a9b0286b1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 10 Sep 2024 09:57:58 +0200 Subject: [PATCH 0352/1301] Preparing for merge from rustc --- 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 3fdad2a91e9fc..48cc89b109d73 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -54fdef7799d9ff9470bb5cabd29fde9471a99eaa +304b7f801bab31233680879ca4fb6eb294706a59 From 6e70bd4d07deb838af1471258742cf1564bce632 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 10 Sep 2024 10:04:09 +0200 Subject: [PATCH 0353/1301] fmt --- .../provenance/int_copy_looses_provenance3.rs | 2 +- .../miri/tests/fail/uninit/padding-enum.rs | 29 ++++++++++--------- .../tests/fail/uninit/padding-enum.stderr | 4 +-- .../miri/tests/fail/uninit/padding-struct.rs | 15 ++++++---- .../tests/fail/uninit/padding-struct.stderr | 4 +-- .../miri/tests/fail/uninit/padding-union.rs | 15 ++++++---- .../tests/fail/uninit/padding-union.stderr | 4 +-- .../tests/fail/uninit/padding-wide-ptr.rs | 27 +++++++++-------- .../tests/fail/uninit/padding-wide-ptr.stderr | 4 +-- src/tools/miri/tests/pass/arrays.rs | 4 ++- .../miri/tests/pass/async-niche-aliasing.rs | 2 +- 11 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs index 48a48ce4587ee..d6bbfd7d94c76 100644 --- a/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs +++ b/src/tools/miri/tests/fail/provenance/int_copy_looses_provenance3.rs @@ -11,7 +11,7 @@ enum E { // Doing a copy at integer type should lose provenance. // This tests the case where provenacne is hiding in the discriminant of an enum. fn main() { - assert_eq!(mem::size_of::(), 2*mem::size_of::()); + assert_eq!(mem::size_of::(), 2 * mem::size_of::()); // We want to store provenance in the enum discriminant, but the value still needs to // be valid atfor the type. So we split provenance and data. diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.rs b/src/tools/miri/tests/fail/uninit/padding-enum.rs index 3852ac5c477d5..a9628799b7d30 100644 --- a/src/tools/miri/tests/fail/uninit/padding-enum.rs +++ b/src/tools/miri/tests/fail/uninit/padding-enum.rs @@ -7,17 +7,20 @@ enum E { Some(&'static (), &'static (), usize), } -fn main() { unsafe { - let mut p: mem::MaybeUninit = mem::MaybeUninit::zeroed(); - // The copy when `E` is returned from `transmute` should destroy padding - // (even when we use `write_unaligned`, which under the hood uses an untyped copy). - p.as_mut_ptr().write_unaligned(mem::transmute((0usize, 0usize, 0usize))); - // This is a `None`, so everything but the discriminant is padding. - assert!(matches!(*p.as_ptr(), E::None)); +fn main() { + unsafe { + let mut p: mem::MaybeUninit = mem::MaybeUninit::zeroed(); + // The copy when `E` is returned from `transmute` should destroy padding + // (even when we use `write_unaligned`, which under the hood uses an untyped copy). + p.as_mut_ptr().write_unaligned(mem::transmute((0usize, 0usize, 0usize))); + // This is a `None`, so everything but the discriminant is padding. + assert!(matches!(*p.as_ptr(), E::None)); - // Turns out the discriminant is (currently) stored - // in the 2nd pointer, so the first half is padding. - let c = &p as *const _ as *const u8; - let _val = *c.add(0); // Get a padding byte. - //~^ERROR: uninitialized -} } + // Turns out the discriminant is (currently) stored + // in the 2nd pointer, so the first half is padding. + let c = &p as *const _ as *const u8; + // Read a padding byte. + let _val = *c.add(0); + //~^ERROR: uninitialized + } +} diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.stderr b/src/tools/miri/tests/fail/uninit/padding-enum.stderr index c571f18874076..312cf6d20fadd 100644 --- a/src/tools/miri/tests/fail/uninit/padding-enum.stderr +++ b/src/tools/miri/tests/fail/uninit/padding-enum.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory --> $DIR/padding-enum.rs:LL:CC | -LL | let _val = *c.add(0); // Get a padding byte. - | ^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +LL | let _val = *c.add(0); + | ^^^^^^^^^ using uninitialized data, but this operation requires initialized memory | = 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/uninit/padding-struct.rs b/src/tools/miri/tests/fail/uninit/padding-struct.rs index dd3be50343902..4cdc6f3a10450 100644 --- a/src/tools/miri/tests/fail/uninit/padding-struct.rs +++ b/src/tools/miri/tests/fail/uninit/padding-struct.rs @@ -3,9 +3,12 @@ use std::mem; #[repr(C)] struct Pair(u8, u16); -fn main() { unsafe { - let p: Pair = mem::transmute(0u32); // The copy when `Pair` is returned from `transmute` should destroy padding. - let c = &p as *const _ as *const u8; - let _val = *c.add(1); // Get the padding byte. - //~^ERROR: uninitialized -} } +fn main() { + unsafe { + let p: Pair = mem::transmute(0u32); // The copy when `Pair` is returned from `transmute` should destroy padding. + let c = &p as *const _ as *const u8; + // Read the padding byte. + let _val = *c.add(1); + //~^ERROR: uninitialized + } +} diff --git a/src/tools/miri/tests/fail/uninit/padding-struct.stderr b/src/tools/miri/tests/fail/uninit/padding-struct.stderr index 8dc40a482ac52..19e3969279bde 100644 --- a/src/tools/miri/tests/fail/uninit/padding-struct.stderr +++ b/src/tools/miri/tests/fail/uninit/padding-struct.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory --> $DIR/padding-struct.rs:LL:CC | -LL | let _val = *c.add(1); // Get the padding byte. - | ^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +LL | let _val = *c.add(1); + | ^^^^^^^^^ using uninitialized data, but this operation requires initialized memory | = 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/uninit/padding-union.rs b/src/tools/miri/tests/fail/uninit/padding-union.rs index 2e9e0a40d6c68..294578607b37d 100644 --- a/src/tools/miri/tests/fail/uninit/padding-union.rs +++ b/src/tools/miri/tests/fail/uninit/padding-union.rs @@ -6,9 +6,12 @@ union U { field: (u8, u16), } -fn main() { unsafe { - let p: U = mem::transmute(0u32); // The copy when `U` is returned from `transmute` should destroy padding. - let c = &p as *const _ as *const [u8; 4]; - let _val = *c; // Read the entire thing, definitely contains the padding byte. - //~^ERROR: uninitialized -} } +fn main() { + unsafe { + let p: U = mem::transmute(0u32); // The copy when `U` is returned from `transmute` should destroy padding. + let c = &p as *const _ as *const [u8; 4]; + // Read the entire thing, definitely contains the padding byte. + let _val = *c; + //~^ERROR: uninitialized + } +} diff --git a/src/tools/miri/tests/fail/uninit/padding-union.stderr b/src/tools/miri/tests/fail/uninit/padding-union.stderr index 04002da4f195c..61a8d1c6ba642 100644 --- a/src/tools/miri/tests/fail/uninit/padding-union.stderr +++ b/src/tools/miri/tests/fail/uninit/padding-union.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: constructing invalid value at [1]: encountered uninitialized memory, but expected an integer --> $DIR/padding-union.rs:LL:CC | -LL | let _val = *c; // Read the entire thing, definitely contains the padding byte. - | ^^ constructing invalid value at [1]: encountered uninitialized memory, but expected an integer +LL | let _val = *c; + | ^^ constructing invalid value at [1]: encountered uninitialized memory, but expected an integer | = 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/uninit/padding-wide-ptr.rs b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs index 0403a9caba66d..4e363dbf81e08 100644 --- a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs +++ b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs @@ -3,16 +3,19 @@ use std::mem; // If this is `None`, the metadata becomes padding. type T = Option<&'static str>; -fn main() { unsafe { - let mut p: mem::MaybeUninit = mem::MaybeUninit::zeroed(); - // The copy when `T` is returned from `transmute` should destroy padding - // (even when we use `write_unaligned`, which under the hood uses an untyped copy). - p.as_mut_ptr().write_unaligned(mem::transmute((0usize, 0usize))); - // Null epresents `None`. - assert!(matches!(*p.as_ptr(), None)); +fn main() { + unsafe { + let mut p: mem::MaybeUninit = mem::MaybeUninit::zeroed(); + // The copy when `T` is returned from `transmute` should destroy padding + // (even when we use `write_unaligned`, which under the hood uses an untyped copy). + p.as_mut_ptr().write_unaligned(mem::transmute((0usize, 0usize))); + // Null epresents `None`. + assert!(matches!(*p.as_ptr(), None)); - // The second part, with the length, becomes padding. - let c = &p as *const _ as *const u8; - let _val = *c.add(mem::size_of::<*const u8>()); // Get a padding byte. - //~^ERROR: uninitialized -} } + // The second part, with the length, becomes padding. + let c = &p as *const _ as *const u8; + // Read a padding byte. + let _val = *c.add(mem::size_of::<*const u8>()); + //~^ERROR: uninitialized + } +} diff --git a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr index 0da72550b2e08..24194c4b02a95 100644 --- a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr +++ b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory --> $DIR/padding-wide-ptr.rs:LL:CC | -LL | let _val = *c.add(mem::size_of::<*const u8>()); // Get a padding byte. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +LL | let _val = *c.add(mem::size_of::<*const u8>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory | = 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/pass/arrays.rs b/src/tools/miri/tests/pass/arrays.rs index b0c6f54cab87c..5cb04d0f8be19 100644 --- a/src/tools/miri/tests/pass/arrays.rs +++ b/src/tools/miri/tests/pass/arrays.rs @@ -62,7 +62,9 @@ fn debug() { } fn huge_zst() { - fn id(x: T) -> T { x } + fn id(x: T) -> T { + x + } // A "huge" zero-sized array. Make sure we don't loop over it in any part of Miri. let val = [(); usize::MAX]; diff --git a/src/tools/miri/tests/pass/async-niche-aliasing.rs b/src/tools/miri/tests/pass/async-niche-aliasing.rs index 7f19afb33e43b..ab82c929a94c2 100644 --- a/src/tools/miri/tests/pass/async-niche-aliasing.rs +++ b/src/tools/miri/tests/pass/async-niche-aliasing.rs @@ -3,10 +3,10 @@ use std::{ future::Future, + mem::MaybeUninit, pin::Pin, sync::Arc, task::{Context, Poll, Wake}, - mem::MaybeUninit, }; struct ThingAdder<'a> { From 16f3eb969f319b5846e96b38c46a4933c24e03d8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 10 Sep 2024 18:48:54 +0300 Subject: [PATCH 0354/1301] Correctly escape strings in our quote macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a small change, but it was the cause of 90% of the errors in `rust-analyzer diagnostics .` 🫢 With this change and #18085 together, all remaining errors are type errors. This may mean we can enable more errors, but this is out of scope for this PR. --- .../macro_expansion_tests/builtin_fn_macro.rs | 18 ++++++++++++++++++ .../crates/hir-expand/src/builtin/fn_macro.rs | 8 ++++---- .../crates/hir-expand/src/builtin/quote.rs | 5 +++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 37ae1ab39b494..a3b48831a0b0f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -528,3 +528,21 @@ fn main() { foobar; } "##]], ); } + +#[test] +fn test_quote_string() { + check( + r##" +#[rustc_builtin_macro] +macro_rules! stringify {} + +fn main() { stringify!("hello"); } +"##, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! stringify {} + +fn main() { "\"hello\""; } +"##]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 15fed45caf02f..d04225b87227b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -483,7 +483,7 @@ fn concat_expand( match it.kind { tt::LitKind::Char => { if let Ok(c) = unescape_char(it.symbol.as_str()) { - text.extend(c.escape_default()); + text.push(c); } record_span(it.span); } @@ -491,11 +491,11 @@ fn concat_expand( format_to!(text, "{}", it.symbol.as_str()) } tt::LitKind::Str => { - text.push_str(it.symbol.as_str()); + text.push_str(unescape_str(&it.symbol).as_str()); record_span(it.span); } tt::LitKind::StrRaw(_) => { - format_to!(text, "{}", it.symbol.as_str().escape_debug()); + format_to!(text, "{}", it.symbol.as_str()); record_span(it.span); } tt::LitKind::Byte @@ -813,7 +813,7 @@ fn include_str_expand( fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option { let krate = db.lookup_intern_macro_call(arg_id).krate; - db.crate_graph()[krate].env.get(key.as_str()).map(|it| it.escape_debug().to_string()) + db.crate_graph()[krate].env.get(key.as_str()) } fn env_expand( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 5c33f817f9e44..fc248c906dac4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -3,6 +3,7 @@ use intern::{sym, Symbol}; use span::Span; +use syntax::ToSmolStr; use tt::IdentIsRaw; use crate::name::Name; @@ -211,8 +212,8 @@ impl_to_to_tokentrees! { _span: crate::tt::Literal => self { self }; _span: crate::tt::Ident => self { self }; _span: crate::tt::Punct => self { self }; - span: &str => self { crate::tt::Literal{symbol: Symbol::intern(self), span, kind: tt::LitKind::Str, suffix: None }}; - span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self), span, kind: tt::LitKind::Str, suffix: None }}; + span: &str => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }}; + span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }}; span: Name => self { let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str()); crate::tt::Ident{sym: Symbol::intern(s), span, is_raw } From a169a5bec870c72ff4c99fb2d56d6cb5bc3ce874 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Wed, 11 Sep 2024 01:40:13 +0900 Subject: [PATCH 0355/1301] Skip checks for cast to dyn traits --- .../crates/hir-ty/src/infer/cast.rs | 39 ++++++++ .../crates/hir-ty/src/infer/unify.rs | 4 + .../src/handlers/invalid_cast.rs | 96 +++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index ae914044368ca..caa3960a227cf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -120,6 +120,13 @@ impl CastCheck { }); } + // Chalk doesn't support trait upcasting and fails to solve some obvious goals + // when the trait environment contains some recursive traits (See issue #18047) + // We skip cast checks for such cases for now, until the next-gen solver. + if contains_dyn_trait(&self.cast_ty) { + return Ok(()); + } + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) { apply_adjustments(self.source_expr, adj); set_coercion_cast(self.source_expr); @@ -410,3 +417,35 @@ fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result bool { + use std::ops::ControlFlow; + + use chalk_ir::{ + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, + }; + + struct DynTraitVisitor; + + impl TypeVisitor for DynTraitVisitor { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + match ty.kind(Interner) { + TyKind::Dyn(_) => ControlFlow::Break(()), + _ => ty.super_visit_with(self.as_dyn(), outer_binder), + } + } + } + + ty.visit_with(DynTraitVisitor.as_dyn(), DebruijnIndex::INNERMOST).is_break() +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 99ad678980987..d38380e4a3af5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -902,6 +902,10 @@ impl<'a> InferenceTable<'a> { /// Check if given type is `Sized` or not pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool { + // Early return for some obvious types + if matches!(ty.kind(Interner), TyKind::Scalar(..) | TyKind::Ref(..) | TyKind::Raw(..)) { + return true; + } if let Some((AdtId::StructId(id), subst)) = ty.as_adt() { let struct_data = self.db.struct_data(id); if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 9eb4e618c01e9..90527c578e413 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -556,6 +556,7 @@ fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) { ); } + #[ignore = "issue #18047"] #[test] fn ptr_to_trait_obj_wrap_upcast() { check_diagnostics( @@ -1004,4 +1005,99 @@ fn _slice(bar: &[i32]) -> bool { &["E0308"], ); } + + #[test] + fn trait_upcasting() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +#![feature(trait_upcasting)] +trait Foo {} +trait Bar: Foo {} + +impl dyn Bar { + fn bar(&self) { + _ = self as &dyn Foo; + } +} +"#, + ); + } + + #[test] + fn issue_18047() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +trait LocalFrom { + fn from(_: T) -> Self; +} +trait LocalInto { + fn into(self) -> T; +} + +impl LocalInto for T +where + U: LocalFrom, +{ + fn into(self) -> U { + U::from(self) + } +} + +impl LocalFrom for T { + fn from(t: T) -> T { + t + } +} + +trait Foo { + type ErrorType; + type Assoc; +} + +trait Bar { + type ErrorType; +} + +struct ErrorLike; + +impl LocalFrom for ErrorLike +where + E: Trait + 'static, +{ + fn from(_: E) -> Self { + loop {} + } +} + +trait Baz { + type Assoc: Bar; + type Error: LocalInto; +} + +impl Baz for T +where + T: Foo, + T::ErrorType: LocalInto, + U: Bar, + ::ErrorType: LocalInto, +{ + type Assoc = U; + type Error = T::ErrorType; +} +struct S; +trait Trait {} +impl Trait for S {} + +fn test() +where + T: Baz, + T::Assoc: 'static, +{ + let _ = &S as &dyn Trait; +} +"#, + ); + } } From d05323c7b4ebeaeaffe3ea8e0d02a8b01db67ddd Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 24 Aug 2024 23:11:30 -0700 Subject: [PATCH 0356/1301] rustdoc: redesign toolbar and disclosure widgets This adds labels to the icons and moves them away from the search box. These changes are made together, because they work together, but are based on several complaints: * The [+/-] thing are a Reddit-ism. They don't look like buttons, but look like syntax , (some of these are laundry lists with more suggestions, but they all mention [+/-] looking wrong) * The settings, help, and summary buttons are also too hard to recognize , , , ("Not all functionality is self-explanatory, for example the [+] button in the top right corner, the theme picker or the settings button.") The toggle-all and toggle-individual buttons both need done at once, since we want them to look like they go together. This changes them from both being [+/-] to both being arrows. Settings and Help are also migrated, so that the whole group can benefit from being described using actual words. Additionally, the Help button is only shown on SERPs, not all the time. This is done for two major reasons: * Most of what's in there is search-related. The things that aren't are keyboard commands, and the search box tells you about that anyway. Pressing ? will temporarily show the button and its popover. * I'm trading it off by showing the help button, even on mobile. It's useful since you can use the search engine suggestions there. * The three buttons were causing line wrapping on too many desktop layouts. --- src/librustdoc/html/sources.rs | 35 ++- src/librustdoc/html/static/css/noscript.css | 5 +- src/librustdoc/html/static/css/rustdoc.css | 221 +++++++++++++----- src/librustdoc/html/static/js/main.js | 122 +++++----- src/librustdoc/html/static/js/search.js | 6 +- src/librustdoc/html/static/js/settings.js | 19 +- src/librustdoc/html/static/js/storage.js | 29 ++- src/librustdoc/html/templates/print_item.html | 13 +- src/librustdoc/html/templates/source.html | 13 +- src/librustdoc/lib.rs | 1 + src/tools/compiletest/src/runtest.rs | 2 +- src/tools/html-checker/main.rs | 2 +- tests/rustdoc-gui/help-page.goml | 8 +- tests/rustdoc-gui/item-info.goml | 2 +- tests/rustdoc-gui/mobile.goml | 15 -- tests/rustdoc-gui/notable-trait.goml | 5 +- tests/rustdoc-gui/pocket-menu.goml | 3 +- tests/rustdoc-gui/scrape-examples-layout.goml | 4 +- tests/rustdoc-gui/search-form-elements.goml | 40 +--- tests/rustdoc-gui/search-result-display.goml | 13 +- tests/rustdoc-gui/settings-button.goml | 8 +- tests/rustdoc-gui/settings.goml | 2 +- tests/rustdoc-gui/shortcuts.goml | 10 +- .../sidebar-source-code-display.goml | 2 +- tests/rustdoc-gui/sidebar.goml | 6 +- tests/rustdoc-gui/source-anchor-scroll.goml | 8 +- tests/rustdoc-gui/source-code-page.goml | 4 +- .../src/theme_css/custom-theme.css | 1 + tests/rustdoc-gui/toggle-docs.goml | 12 +- .../rustdoc-gui/type-declation-overflow.goml | 17 +- tests/rustdoc/html-no-source.rs | 8 +- tests/rustdoc/source-version-separator.rs | 4 +- .../version-separator-without-source.rs | 8 +- 33 files changed, 392 insertions(+), 256 deletions(-) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 551bb56685c99..bbd427bbdd205 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -26,8 +26,11 @@ pub(crate) fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), E let dst = cx.dst.join("src").join(krate.name(cx.tcx()).as_str()); cx.shared.ensure_dir(&dst)?; + let crate_name = krate.name(cx.tcx()); + let crate_name = crate_name.as_str(); - let mut collector = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() }; + let mut collector = + SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default(), crate_name }; collector.visit_crate(krate); Ok(()) } @@ -115,6 +118,8 @@ struct SourceCollector<'a, 'tcx> { /// Root destination to place all HTML output into dst: PathBuf, emitted_local_sources: FxHashSet, + + crate_name: &'a str, } impl DocVisitor for SourceCollector<'_, '_> { @@ -210,6 +215,9 @@ impl SourceCollector<'_, '_> { }, ); + let src_fname = p.file_name().expect("source has no filename").to_os_string(); + let mut fname = src_fname.clone(); + let root_path = PathBuf::from("../../").join(root_path.into_inner()); let mut root_path = root_path.to_string_lossy(); if let Some(c) = root_path.as_bytes().last() @@ -217,12 +225,12 @@ impl SourceCollector<'_, '_> { { root_path += "/"; } + let mut file_path = Path::new(&self.crate_name).join(&*cur.borrow()); + file_path.push(&fname); + fname.push(".html"); let mut cur = self.dst.join(cur.into_inner()); shared.ensure_dir(&cur)?; - let src_fname = p.file_name().expect("source has no filename").to_os_string(); - let mut fname = src_fname.clone(); - fname.push(".html"); cur.push(&fname); let title = format!("{} - source", src_fname.to_string_lossy()); @@ -250,7 +258,7 @@ impl SourceCollector<'_, '_> { cx, &root_path, highlight::DecorationInfo::default(), - SourceContext::Standalone, + SourceContext::Standalone { file_path }, ) }, &shared.style_files, @@ -313,10 +321,11 @@ struct ScrapedSource<'a, Code: std::fmt::Display> { struct Source { lines: RangeInclusive, code_html: Code, + file_path: Option<(String, String)>, } pub(crate) enum SourceContext<'a> { - Standalone, + Standalone { file_path: PathBuf }, Embedded(ScrapedInfo<'a>), } @@ -345,9 +354,19 @@ pub(crate) fn print_src( }); let lines = s.lines().count(); match source_context { - SourceContext::Standalone => { - Source { lines: (1..=lines), code_html: code }.render_into(&mut writer).unwrap() + SourceContext::Standalone { file_path } => Source { + lines: (1..=lines), + code_html: code, + file_path: if let Some(file_name) = file_path.file_name() + && let Some(file_path) = file_path.parent() + { + Some((file_path.display().to_string(), file_name.display().to_string())) + } else { + None + }, } + .render_into(&mut writer) + .unwrap(), SourceContext::Embedded(info) => { let lines = (1 + info.offset)..=(lines + info.offset); ScrapedSource { info, lines, code_html: code }.render_into(&mut writer).unwrap(); diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index e62b16267f102..477a79d63e948 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -61,6 +61,8 @@ nav.sub { --copy-path-img-hover-filter: invert(35%); --code-example-button-color: #7f7f7f; --code-example-button-hover-color: #595959; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(35%); --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); @@ -87,7 +89,6 @@ nav.sub { --search-tab-button-not-selected-background: #e6e6e6; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #fff; - --settings-menu-filter: none; --stab-background-color: #fff5d6; --stab-code-color: #000; --code-highlight-kw-color: #8959a8; @@ -192,6 +193,8 @@ nav.sub { --search-tab-button-not-selected-background: #252525; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #353535; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(65%); --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ab8ac1; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 38154dee3e287..2a9b4c9588380 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -165,7 +165,7 @@ h1, h2, h3, h4 { .main-heading h1 { margin: 0; padding: 0; - flex-grow: 1; + grid-area: main-heading-h1; /* We use overflow-wrap: break-word for Safari, which doesn't recognize `anywhere`: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap */ overflow-wrap: break-word; @@ -174,8 +174,12 @@ h1, h2, h3, h4 { overflow-wrap: anywhere; } .main-heading { - display: flex; - flex-wrap: wrap; + position: relative; + display: grid; + grid-template-areas: + "main-heading-h1 main-heading-toolbar" + "main-heading-sub-heading main-heading-toolbar"; + grid-template-columns: 1fr max-content; padding-bottom: 6px; margin-bottom: 15px; } @@ -218,9 +222,10 @@ h1, h2, h3, h4, h5, h6, .search-results .result-name, .item-name > a, .out-of-band, +.sub-heading, span.since, a.src, -#help-button > a, +rustdoc-toolbar, summary.hideme, .scraped-example-list, /* This selector is for the items listed in the "all items" page. */ @@ -884,9 +889,19 @@ both the code example and the line numbers, so we need to remove the radius in t overflow-x: auto; } -.out-of-band { +.sub-heading { flex-grow: 0; font-size: 1.125rem; + grid-area: main-heading-sub-heading; +} + +.main-heading rustdoc-toolbar, .main-heading .out-of-band { + grid-area: main-heading-toolbar; +} +rustdoc-toolbar { + display: flex; + flex-direction: row; + flex-wrap: nowrap; } .docblock code, .docblock-short code, @@ -1282,10 +1297,16 @@ so that we can apply CSS-filters to change the arrow color in themes */ border-color: var(--settings-input-color) !important; } +#settings.popover { + --popover-arrow-offset: 118px; + top: 26px; +} + /* use larger max-width for help popover, but not for help.html */ #help.popover { max-width: 600px; - --popover-arrow-offset: 48px; + --popover-arrow-offset: 36px; + top: 26px; } #help dt { @@ -1293,10 +1314,15 @@ so that we can apply CSS-filters to change the arrow color in themes */ clear: left; margin-right: 0.5rem; } +#help dd { + margin-bottom: 0.5rem; +} #help span.top, #help span.bottom { text-align: center; display: block; font-size: 1.125rem; + padding: 0 0.5rem; + text-wrap-style: balance; } #help span.top { margin: 10px 0; @@ -1308,10 +1334,13 @@ so that we can apply CSS-filters to change the arrow color in themes */ clear: both; border-top: 1px solid var(--border-color); } +.side-by-side { + display: flex; + margin-bottom: 20px; +} .side-by-side > div { width: 50%; - float: left; - padding: 0 20px 20px 17px; + padding: 0 20px 0 17px; } .item-info .stab { @@ -1374,7 +1403,9 @@ so that we can apply CSS-filters to change the arrow color in themes */ } .rightside:not(a), -.out-of-band { +.out-of-band, +.sub-heading, +rustdoc-toolbar { color: var(--right-side-color); } @@ -1588,8 +1619,8 @@ instead, we check that it's not a "finger" cursor. display: block; } -.out-of-band > span.since { - font-size: 1.25rem; +.main-heading span.since::before { + content: "Since "; } .sub-variant h4 { @@ -1691,6 +1722,8 @@ a.tooltip:hover::after { } #search-tabs { + grid-area: main-heading-sub-heading; + margin-top: 0.25rem; display: flex; flex-direction: row; gap: 1px; @@ -1752,9 +1785,10 @@ a.tooltip:hover::after { border-bottom: 1px solid var(--border-color); } -#settings-menu, #help-button { +#settings-menu, #help-button, button#toggle-all-docs { margin-left: var(--button-left-margin); display: flex; + line-height: initial; } #sidebar-button { display: none; @@ -1778,33 +1812,35 @@ a.tooltip:hover::after { .hide-sidebar .src #sidebar-button { position: static; } -#settings-menu > a, #help-button > a, #sidebar-button > a { +#settings-menu > a, #help-button > a, #sidebar-button > a, button#toggle-all-docs { display: flex; align-items: center; justify-content: center; - background-color: var(--button-background-color); - border: 1px solid var(--border-color); + flex-direction: column; + border: 1px solid transparent; border-radius: var(--button-border-radius); - color: var(--settings-button-color); - /* Rare exception to specifying font sizes in rem. Since this is acting - as an icon, it's okay to specify their sizes in pixels. */ - font-size: 20px; + color: var(--main-color); +} +#settings-menu > a, #help-button > a, button#toggle-all-docs { + width: 80px; +} +#sidebar-button > a { + background-color: var(--button-background-color); + border-color: var(--border-color); width: 33px; } -#settings-menu > a:hover, #settings-menu > a:focus, -#help-button > a:hover, #help-button > a:focus, -#sidebar-button > a:hover, #sidebar-button > a:focus { +#settings-menu > a:hover, #settings-menu > a:focus-visible, +#help-button > a:hover, #help-button > a:focus-visible, +#sidebar-button > a:hover, #sidebar-button > a:focus-visible, +button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { border-color: var(--settings-button-border-focus); + text-decoration: none; } -#settings-menu > a { - line-height: 0; - font-size: 0; -} #settings-menu > a:before { /* Wheel */ - content: url('data:image/svg+xml,\ '); - width: 22px; - height: 22px; + width: 18px; + height: 18px; filter: var(--settings-menu-filter); } +button#toggle-all-docs:before { + /* Custom arrow icon */ + content: url('data:image/svg+xml,\ + '); + width: 18px; + height: 18px; + filter: var(--settings-menu-filter); +} + +#help-button > a:before { + /* Question mark with circle */ + content: url('data:image/svg+xml,\ + \ + ?'); + width: 18px; + height: 18px; + filter: var(--settings-menu-filter); +} + +button#toggle-all-docs:before, +#help-button > a:before, +#settings-menu > a:before { + filter: var(--settings-menu-filter); + margin: 8px; +} + +@media not (pointer: coarse) { + button#toggle-all-docs:hover:before, + #help-button > a:hover:before, + #settings-menu > a:hover:before { + filter: var(--settings-menu-hover-filter); + } +} + +rustdoc-toolbar span.label { + font-size: initial; + font-variant-caps: small-caps; + text-transform: lowercase; + flex-grow: 1; +} + #sidebar-button > a:before { /* sidebar resizer image */ content: url('data:image/svg+xml,\ - \ + \ \ '); width: 22px; @@ -1941,10 +2020,10 @@ details.toggle > summary.hideme > span { } details.toggle > summary::before { - /* toggle plus */ - background: url('data:image/svg+xml,') no-repeat top left; + /* arrow pointing left */ + background: url('data:image/svg+xml,\ + '); content: ""; cursor: pointer; width: 16px; @@ -2027,10 +2106,10 @@ details.toggle[open] > summary.hideme > span { } details.toggle[open] > summary::before { - /* toggle minus */ - background: url('data:image/svg+xml,') no-repeat top left; + /* arrow pointing down */ + background: url('data:image/svg+xml,\ + '); } details.toggle[open] > summary::after { @@ -2076,6 +2155,15 @@ However, it's not needed with smaller screen width because the doc/code block is opacity: 0.75; } +/* help button is mostly for search */ +#help-button:not(.help-open), +#alternative-display #toggle-all-docs { + display: none; +} +#alternative-display #help-button { + display: flex; +} + /* Media Queries */ /* Make sure all the buttons line wrap at the same time */ @@ -2083,6 +2171,12 @@ However, it's not needed with smaller screen width because the doc/code block is #search-tabs .count { display: block; } + .side-by-side { + flex-direction: column-reverse; + } + .side-by-side > div { + width: auto; + } } /* @@ -2099,6 +2193,27 @@ in src-script.js and main.js scroll-margin-top: 45px; } + /* We don't display this button on mobile devices. */ + #copy-path { + display: none; + } + + /* Text label takes up too much space at this size. */ + rustdoc-toolbar span.label { + display: none; + } + #settings-menu > a, #help-button > a, button#toggle-all-docs { + width: 33px; + } + #settings.popover { + --popover-arrow-offset: 48px; + top: calc(100% - 8px); + } + #help.popover { + --popover-arrow-offset: 12px; + top: calc(100% - 8px); + } + .rustdoc { /* Sidebar should overlay main content, rather than pushing main content to the right. Turn off `display: flex` on the body element. */ @@ -2110,20 +2225,6 @@ in src-script.js and main.js padding-top: 0px; } - .main-heading { - flex-direction: column; - } - - .out-of-band { - text-align: left; - margin-left: initial; - padding: initial; - } - - .out-of-band .since::before { - content: "Since "; - } - /* Hide the logo and item name from the sidebar. Those are displayed in the mobile-topbar instead. */ .sidebar .logo-container, @@ -2227,16 +2328,11 @@ in src-script.js and main.js left: -11px; } - /* We don't display these buttons on mobile devices. */ - #copy-path, #help-button { - display: none; - } - /* sidebar button becomes topbar button */ #sidebar-button > a:before { content: url('data:image/svg+xml,\ - \ + \ \ '); width: 22px; @@ -2536,6 +2632,8 @@ by default. --copy-path-img-hover-filter: invert(35%); --code-example-button-color: #7f7f7f; --code-example-button-hover-color: #595959; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(35%); --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); @@ -2562,7 +2660,6 @@ by default. --search-tab-button-not-selected-background: #e6e6e6; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #fff; - --settings-menu-filter: none; --stab-background-color: #fff5d6; --stab-code-color: #000; --code-highlight-kw-color: #8959a8; @@ -2666,7 +2763,8 @@ by default. --search-tab-button-not-selected-background: #252525; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #353535; - --settings-menu-filter: none; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(65%); --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ab8ac1; @@ -2777,7 +2875,8 @@ Original by Dempfi (https://github.com/dempfi/ayu) --search-tab-button-not-selected-background: transparent !important; --search-tab-button-selected-border-top-color: none; --search-tab-button-selected-background: #141920 !important; - --settings-menu-filter: invert(100%); + --settings-menu-filter: invert(70%); + --settings-menu-hover-filter: invert(100%); --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ff7733; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 858753a1917d8..53326f0fcadf1 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -22,14 +22,14 @@ function hideMain() { } function showMain() { - removeClass(document.getElementById(MAIN_ID), "hidden"); -} - -function blurHandler(event, parentElem, hideCallback) { - if (!parentElem.contains(document.activeElement) && - !parentElem.contains(event.relatedTarget) - ) { - hideCallback(); + const main = document.getElementById(MAIN_ID); + removeClass(main, "hidden"); + const mainHeading = main.querySelector(".main-heading"); + if (mainHeading && searchState.rustdocToolbar) { + if (searchState.rustdocToolbar.parentElement) { + searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + } + mainHeading.appendChild(searchState.rustdocToolbar); } } @@ -167,6 +167,14 @@ function switchDisplayedElement(elemToDisplay) { el.appendChild(elemToDisplay); hideMain(); removeClass(el, "hidden"); + + const mainHeading = elemToDisplay.querySelector(".main-heading"); + if (mainHeading && searchState.rustdocToolbar) { + if (searchState.rustdocToolbar.parentElement) { + searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + } + mainHeading.appendChild(searchState.rustdocToolbar); + } } function browserSupportsHistoryApi() { @@ -194,33 +202,36 @@ function preLoadCss(cssUrl) { document.head.append(script); } - getSettingsButton().onclick = event => { - if (event.ctrlKey || event.altKey || event.metaKey) { - return; - } - window.hideAllModals(false); - addClass(getSettingsButton(), "rotate"); - event.preventDefault(); - // Sending request for the CSS and the JS files at the same time so it will - // hopefully be loaded when the JS will generate the settings content. - loadScript(getVar("static-root-path") + getVar("settings-js")); - // Pre-load all theme CSS files, so that switching feels seamless. - // - // When loading settings.html as a standalone page, the equivalent HTML is - // generated in context.rs. - setTimeout(() => { - const themes = getVar("themes").split(","); - for (const theme of themes) { - // if there are no themes, do nothing - // "".split(",") == [""] - if (theme !== "") { - preLoadCss(getVar("root-path") + theme + ".css"); - } + if (getSettingsButton()) { + getSettingsButton().onclick = event => { + if (event.ctrlKey || event.altKey || event.metaKey) { + return; } - }, 0); - }; + window.hideAllModals(false); + addClass(getSettingsButton(), "rotate"); + event.preventDefault(); + // Sending request for the CSS and the JS files at the same time so it will + // hopefully be loaded when the JS will generate the settings content. + loadScript(getVar("static-root-path") + getVar("settings-js")); + // Pre-load all theme CSS files, so that switching feels seamless. + // + // When loading settings.html as a standalone page, the equivalent HTML is + // generated in context.rs. + setTimeout(() => { + const themes = getVar("themes").split(","); + for (const theme of themes) { + // if there are no themes, do nothing + // "".split(",") == [""] + if (theme !== "") { + preLoadCss(getVar("root-path") + theme + ".css"); + } + } + }, 0); + }; + } window.searchState = { + rustdocToolbar: document.querySelector("rustdoc-toolbar"), loadingText: "Loading search results...", input: document.getElementsByClassName("search-input")[0], outputElement: () => { @@ -919,8 +930,7 @@ function preLoadCss(cssUrl) { e.open = true; } }); - innerToggle.title = "collapse all docs"; - innerToggle.children[0].innerText = "\u2212"; // "\u2212" is "−" minus sign + innerToggle.children[0].innerText = "Summary"; } function collapseAllDocs() { @@ -934,8 +944,7 @@ function preLoadCss(cssUrl) { e.open = false; } }); - innerToggle.title = "expand all docs"; - innerToggle.children[0].innerText = "+"; + innerToggle.children[0].innerText = "Show all"; } function toggleAllDocs() { @@ -1328,7 +1337,13 @@ function preLoadCss(cssUrl) { } function helpBlurHandler(event) { - blurHandler(event, getHelpButton(), window.hidePopoverMenus); + if (!getHelpButton().contains(document.activeElement) && + !getHelpButton().contains(event.relatedTarget) && + !getSettingsButton().contains(document.activeElement) && + !getSettingsButton().contains(event.relatedTarget) + ) { + window.hidePopoverMenus(); + } } function buildHelpMenu() { @@ -1431,9 +1446,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * Hide all the popover menus. */ window.hidePopoverMenus = () => { - onEachLazy(document.querySelectorAll(".search-form .popover"), elem => { + onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); + const button = getHelpButton(); + if (button) { + removeClass(button, "help-open"); + } }; /** @@ -1458,7 +1477,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm function showHelp() { // Prevent `blur` events from being dispatched as a result of closing // other modals. - getHelpButton().querySelector("a").focus(); + const button = getHelpButton(); + addClass(button, "help-open"); + button.querySelector("a").focus(); const menu = getHelpMenu(true); if (menu.style.display === "none") { window.hideAllModals(); @@ -1466,28 +1487,15 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm } } + const helpLink = document.querySelector(`#${HELP_BUTTON_ID} > a`); if (isHelpPage) { - showHelp(); - document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => { - // Already on the help page, make help button a no-op. - const target = event.target; - if (target.tagName !== "A" || - target.parentElement.id !== HELP_BUTTON_ID || - event.ctrlKey || - event.altKey || - event.metaKey) { - return; - } - event.preventDefault(); - }); - } else { - document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => { + buildHelpMenu(); + } else if (helpLink) { + helpLink.addEventListener("click", event => { // By default, have help button open docs in a popover. // If user clicks with a moderator, though, use default browser behavior, // probably opening in a new window or tab. - const target = event.target; - if (target.tagName !== "A" || - target.parentElement.id !== HELP_BUTTON_ID || + if (!helpLink.contains(helpLink) || event.ctrlKey || event.altKey || event.metaKey) { diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 4da0bbc787d9e..15b1046a27cdb 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3605,7 +3605,8 @@ async function showResults(results, go_to_first, filterCrates) { crates += "
"; } - let output = `

Results${crates}

`; + let output = `
\ +

Results${crates}

`; if (results.query.error !== null) { const error = results.query.error; error.forEach((value, index) => { @@ -3662,6 +3663,9 @@ async function showResults(results, go_to_first, filterCrates) { resultsElem.appendChild(ret_returned[0]); search.innerHTML = output; + if (searchState.rustdocToolbar) { + search.querySelector(".main-heading").appendChild(searchState.rustdocToolbar); + } const crateSearch = document.getElementById("crate-search"); if (crateSearch) { crateSearch.addEventListener("input", updateCrate); diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index c52a19ef98738..183663b94fc28 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,7 +1,7 @@ // Local js definitions: /* global getSettingValue, updateLocalStorage, updateTheme */ -/* global addClass, removeClass, onEach, onEachLazy, blurHandler */ -/* global MAIN_ID, getVar, getSettingsButton */ +/* global addClass, removeClass, onEach, onEachLazy */ +/* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ "use strict"; @@ -267,15 +267,16 @@ } function settingsBlurHandler(event) { - blurHandler(event, getSettingsButton(), window.hidePopoverMenus); + if (!getHelpButton().contains(document.activeElement) && + !getHelpButton().contains(event.relatedTarget) && + !getSettingsButton().contains(document.activeElement) && + !getSettingsButton().contains(event.relatedTarget) + ) { + window.hidePopoverMenus(); + } } - if (isSettingsPage) { - // We replace the existing "onclick" callback to do nothing if clicked. - getSettingsButton().onclick = event => { - event.preventDefault(); - }; - } else { + if (!isSettingsPage) { // We replace the existing "onclick" callback. const settingsButton = getSettingsButton(); const settingsMenu = document.getElementById("settings"); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index d75fb7a7fb5ab..344743c87edd3 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -274,16 +274,29 @@ class RustdocSearchElement extends HTMLElement { spellcheck="false" placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…" type="search"> -
- ? -
- `; } } window.customElements.define("rustdoc-search", RustdocSearchElement); +class RustdocToolbarElement extends HTMLElement { + constructor() { + super(); + } + connectedCallback() { + // Avoid replacing the children after they're already here. + if (this.firstElementChild) { + return; + } + const rootPath = getVar("root-path"); + this.innerHTML = ` +
+ Settings +
+ +
+ Help +
`; + } +} +window.customElements.define("rustdoc-toolbar", RustdocToolbarElement); diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 76e770453b6ea..1b8c8293088fd 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -10,17 +10,16 @@

Copy item path {# #} {# #}

{# #} - + + {% if !stability_since_raw.is_empty() %} - {{ stability_since_raw|safe +}} · {#+ #} + {{ stability_since_raw|safe +}} {% endif %} {% match src_href %} {% when Some with (href) %} - source · {#+ #} + {% if !stability_since_raw.is_empty() +%} · {%+ endif %} + source {#+ #} {% else %} {% endmatch %} - {# #} - {# #} + {# #} diff --git a/src/librustdoc/html/templates/source.html b/src/librustdoc/html/templates/source.html index 60a47f1b5de7d..9daa0cf8ff688 100644 --- a/src/librustdoc/html/templates/source.html +++ b/src/librustdoc/html/templates/source.html @@ -1,4 +1,15 @@ -
+{% match file_path %} +{% when Some with ((path, name)) %} +
{# #} +

{# #} +
{{path}}/
+ {{name}} +

{# #} + {# #} +
+{% else %} +{% endmatch %} +
{# #} {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr Do not show "1 2 3 4 5 ..." in web search results. #}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 6649e1721a48b..5f303d96c40df 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -10,6 +10,7 @@
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
+#![feature(os_str_display)]
 #![feature(round_char_boundary)]
 #![feature(test)]
 #![feature(type_alias_impl_trait)]
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index c18f569e52867..d24dd334a3438 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2739,7 +2739,7 @@ impl<'test> TestCx<'test> {
 
         #[rustfmt::skip]
         let tidy_args = [
-            "--new-blocklevel-tags", "rustdoc-search",
+            "--new-blocklevel-tags", "rustdoc-search,rustdoc-toolbar",
             "--indent", "yes",
             "--indent-spaces", "2",
             "--wrap", "0",
diff --git a/src/tools/html-checker/main.rs b/src/tools/html-checker/main.rs
index ecfbb1955e779..5cdc4d53ab500 100644
--- a/src/tools/html-checker/main.rs
+++ b/src/tools/html-checker/main.rs
@@ -31,7 +31,7 @@ fn check_html_file(file: &Path) -> usize {
         .arg("--mute-id") // this option is useful in case we want to mute more warnings
         .arg("yes")
         .arg("--new-blocklevel-tags")
-        .arg("rustdoc-search") // custom elements
+        .arg("rustdoc-search,rustdoc-toolbar") // custom elements
         .arg("--mute")
         .arg(&to_mute_s)
         .arg(file);
diff --git a/tests/rustdoc-gui/help-page.goml b/tests/rustdoc-gui/help-page.goml
index f1a2675128ce4..6d6e353ae362b 100644
--- a/tests/rustdoc-gui/help-page.goml
+++ b/tests/rustdoc-gui/help-page.goml
@@ -4,7 +4,7 @@ set-window-size: (1000, 1000) // Try desktop size first.
 wait-for: "#help"
 assert-css: ("#help", {"display": "block"})
 assert-css: ("#help dd", {"font-size": "16px"})
-click: "#help-button > a"
+assert-false: "#help-button > a"
 assert-css: ("#help", {"display": "block"})
 compare-elements-property: (".sub", "#help", ["offsetWidth"])
 compare-elements-position: (".sub", "#help", ["x"])
@@ -50,7 +50,8 @@ call-function: ("check-colors", {
 })
 
 // This test ensures that opening the help popover without switching pages works.
-go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a"
+wait-for: "#search-tabs" // Waiting for the search.js to load.
 set-window-size: (1000, 1000) // Only supported on desktop.
 assert-false: "#help"
 click: "#help-button > a"
@@ -62,7 +63,8 @@ compare-elements-property-false: (".sub", "#help", ["offsetWidth"])
 compare-elements-position-false: (".sub", "#help", ["x"])
 
 // This test ensures that the "the rustdoc book" anchor link within the help popover works.
-go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a"
+wait-for: "#search-tabs" // Waiting for the search.js to load.
 set-window-size: (1000, 1000) // Popover only appears when the screen width is >700px.
 assert-false: "#help"
 click: "#help-button > a"
diff --git a/tests/rustdoc-gui/item-info.goml b/tests/rustdoc-gui/item-info.goml
index 7a0194c6cc1f6..debda8d06c2c7 100644
--- a/tests/rustdoc-gui/item-info.goml
+++ b/tests/rustdoc-gui/item-info.goml
@@ -20,7 +20,7 @@ store-position: (
     {"x": second_line_x, "y": second_line_y},
 )
 assert: |first_line_x| != |second_line_x| && |first_line_x| == 516 && |second_line_x| == 272
-assert: |first_line_y| != |second_line_y| && |first_line_y| == 688 && |second_line_y| == 711
+assert: |first_line_y| != |second_line_y| && |first_line_y| == 715 && |second_line_y| == 738
 
 // Now we ensure that they're not rendered on the same line.
 set-window-size: (1100, 800)
diff --git a/tests/rustdoc-gui/mobile.goml b/tests/rustdoc-gui/mobile.goml
index e576385cd5370..a9eee53dd1d50 100644
--- a/tests/rustdoc-gui/mobile.goml
+++ b/tests/rustdoc-gui/mobile.goml
@@ -5,23 +5,8 @@ set-window-size: (400, 600)
 set-font-size: 18
 wait-for: 100 // wait a bit for the resize and the font-size change to be fully taken into account.
 
-// The out-of-band info (source, stable version, collapse) should be below the
-// h1 when the screen gets narrow enough.
-assert-css: (".main-heading", {
-  "display": "flex",
-  "flex-direction": "column"
-})
-
 assert-property: (".mobile-topbar h2", {"offsetHeight": 33})
 
-// Note: We can't use assert-text here because the 'Since' is set by CSS and
-// is therefore not part of the DOM.
-assert-css: (".content .out-of-band .since::before", { "content": "\"Since \"" })
-
-set-window-size: (1000, 1000)
-wait-for: 100 // wait a bit for the resize to be fully taken into account.
-assert-css-false: (".content .out-of-band .since::before", { "content": "\"Since \"" })
-
 // On the settings page, the theme buttons should not line-wrap. Instead, they should
 // all be placed as a group on a line below the setting name "Theme."
 go-to: "file://" + |DOC_PATH| + "/settings.html"
diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml
index e2a8a43007eb0..b8fa26b17f6b6 100644
--- a/tests/rustdoc-gui/notable-trait.goml
+++ b/tests/rustdoc-gui/notable-trait.goml
@@ -248,12 +248,13 @@ click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
 assert-count: ("//*[@class='tooltip popover']", 1)
 assert-false: "//*[@class='sidebar shown']"
 
-// Also check the focus handling for the help button.
+// Also check the focus handling for the settings button.
 set-window-size: (1100, 600)
 reload:
 assert-count: ("//*[@class='tooltip popover']", 0)
 click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
 assert-count: ("//*[@class='tooltip popover']", 1)
-click: "#help-button a"
+click: "#settings-menu a"
+wait-for: "#settings"
 assert-count: ("//*[@class='tooltip popover']", 0)
 assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"
diff --git a/tests/rustdoc-gui/pocket-menu.goml b/tests/rustdoc-gui/pocket-menu.goml
index ec31f492abe7c..4a062fec7516d 100644
--- a/tests/rustdoc-gui/pocket-menu.goml
+++ b/tests/rustdoc-gui/pocket-menu.goml
@@ -1,6 +1,7 @@
 // This test ensures that the "pocket menus" are working as expected.
 include: "utils.goml"
-go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test"
+wait-for: "#crate-search"
 // First we check that the help menu doesn't exist yet.
 assert-false: "#help-button .popover"
 // Then we display the help menu.
diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml
index 6bea352bce4bb..55187a3e59647 100644
--- a/tests/rustdoc-gui/scrape-examples-layout.goml
+++ b/tests/rustdoc-gui/scrape-examples-layout.goml
@@ -72,8 +72,8 @@ click: ".scraped-example .button-holder .expand"
 store-value: (offset_y, 4)
 
 // First with desktop
-assert-position: (".scraped-example", {"y": 226})
-assert-position: (".scraped-example .prev", {"y": 226 + |offset_y|})
+assert-position: (".scraped-example", {"y": 253})
+assert-position: (".scraped-example .prev", {"y": 253 + |offset_y|})
 
 // Gradient background should be at the top of the code block.
 assert-css: (".scraped-example .example-wrap::before", {"top": "0px"})
diff --git a/tests/rustdoc-gui/search-form-elements.goml b/tests/rustdoc-gui/search-form-elements.goml
index 63d2ceb3e7c1c..efe39f7a9d1bf 100644
--- a/tests/rustdoc-gui/search-form-elements.goml
+++ b/tests/rustdoc-gui/search-form-elements.goml
@@ -1,13 +1,14 @@
 // This test ensures that the elements in ".search-form" have the expected display.
 include: "utils.goml"
-go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test"
+wait-for: "#search-tabs" // Waiting for the search.js to load.
 show-text: true
 
 define-function: (
     "check-search-colors",
     [
         theme, border, background, search_input_color, search_input_border_focus,
-        menu_button_border, menu_button_a_color, menu_button_a_border_hover, menu_a_color,
+        menu_button_a_color, menu_button_a_border_hover, menu_a_color,
     ],
     block {
         call-function: ("switch-theme", {"theme": |theme|})
@@ -29,30 +30,22 @@ define-function: (
                 "color": |search_input_color|,
             },
         )
-        assert-css: (
-            "#help-button",
-            {"border-color": |menu_button_border|},
-        )
         assert-css: (
             "#help-button > a",
             {
                 "color": |menu_button_a_color|,
-                "border-color": |border|,
-                "background-color": |background|,
+                "border-color": "transparent",
+                "background-color": "transparent",
             },
         )
         // Hover help button.
         move-cursor-to: "#help-button"
-        assert-css: (
-            "#help-button:hover",
-            {"border-color": |menu_button_border|},
-        )
         assert-css: (
             "#help-button > a",
             {
                 "color": |menu_button_a_color|,
                 "border-color": |menu_button_a_border_hover|,
-                "background-color": |background|,
+                "background-color": "transparent",
             },
         )
         // Link color inside
@@ -63,30 +56,22 @@ define-function: (
                 "color": |menu_a_color|,
             },
         )
-        assert-css: (
-            "#settings-menu",
-            {"border-color": |menu_button_border|},
-        )
         assert-css: (
             "#settings-menu > a",
             {
                 "color": |menu_button_a_color|,
-                "border-color": |border|,
-                "background-color": |background|,
+                "border-color": "transparent",
+                "background-color": "transparent",
             },
         )
         // Hover settings menu.
         move-cursor-to: "#settings-menu"
-        assert-css: (
-            "#settings-menu:hover",
-            {"border-color": |menu_button_border|},
-        )
         assert-css: (
             "#settings-menu:hover > a",
             {
                 "color": |menu_button_a_color|,
                 "border-color": |menu_button_a_border_hover|,
-                "background-color": |background|,
+                "background-color": "transparent",
             },
         )
     },
@@ -100,8 +85,7 @@ call-function: (
         "background": "#141920",
         "search_input_color": "#fff",
         "search_input_border_focus": "#5c6773",
-        "menu_button_border": "#c5c5c5",
-        "menu_button_a_color": "#fff",
+        "menu_button_a_color": "#c5c5c5",
         "menu_button_a_border_hover": "#e0e0e0",
         "menu_a_color": "#39afd7",
     }
@@ -114,8 +98,7 @@ call-function: (
         "background": "#f0f0f0",
         "search_input_color": "#111",
         "search_input_border_focus": "#008dfd",
-        "menu_button_border": "#ddd",
-        "menu_button_a_color": "#000",
+        "menu_button_a_color": "#ddd",
         "menu_button_a_border_hover": "#ffb900",
         "menu_a_color": "#d2991d",
     }
@@ -128,7 +111,6 @@ call-function: (
         "background": "#fff",
         "search_input_color": "#000",
         "search_input_border_focus": "#66afe9",
-        "menu_button_border": "#000",
         "menu_button_a_color": "#000",
         "menu_button_a_border_hover": "#717171",
         "menu_a_color": "#3873ad",
diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml
index 3ca46f3c56931..c69ca59d44a7c 100644
--- a/tests/rustdoc-gui/search-result-display.goml
+++ b/tests/rustdoc-gui/search-result-display.goml
@@ -51,7 +51,10 @@ set-window-size: (900, 900)
 
 // First we check the current width, height and position.
 assert-css: ("#crate-search", {"width": "223px"})
-assert-css: (".search-results-title", {"height": "50px", "width": "640px"})
+store-size: (".search-results-title", {
+    "height": search_results_title_height,
+    "width": search_results_title_width,
+})
 assert-css: ("#search", {"width": "640px"})
 
 // Then we update the text of one of the `";
+        crates = "
in 
" + + "
"; + crates += "
"; } let output = `
\ -

Results${crates}

`; +

Results

${crates}
`; if (results.query.error !== null) { const error = results.query.error; error.forEach((value, index) => { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 344743c87edd3..d77804d045e36 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -293,10 +293,10 @@ class RustdocToolbarElement extends HTMLElement { -
Help -
`; +
+ `; } } window.customElements.define("rustdoc-toolbar", RustdocToolbarElement); diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 1b8c8293088fd..2c9f8dbddf090 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -1,11 +1,16 @@
{# #} -

- {{typ}} - {# The breadcrumbs of the item path, like std::string #} + {% if !path_components.is_empty() %} + {% for component in path_components %} {{component.name}}:: {% endfor %} - {{name}} {# #} + + {% endif %} +

+ {{typ}} + + {{name}} + {# #} {# #} diff --git a/tests/rustdoc-gui/anchors.goml b/tests/rustdoc-gui/anchors.goml index 61b2e8880c602..3168c8e17c52f 100644 --- a/tests/rustdoc-gui/anchors.goml +++ b/tests/rustdoc-gui/anchors.goml @@ -12,8 +12,7 @@ define-function: ( call-function: ("switch-theme", {"theme": |theme|}) assert-css: ("#toggle-all-docs", {"color": |main_color|}) - assert-css: (".main-heading h1 a:nth-of-type(1)", {"color": |main_heading_color|}) - assert-css: (".main-heading a:nth-of-type(2)", {"color": |main_heading_type_color|}) + assert-css: (".main-heading h1 span", {"color": |main_heading_type_color|}) assert-css: ( ".rightside a.src", {"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|}, @@ -55,7 +54,7 @@ define-function: ( assert-css: ("#top-doc-prose-title", {"color": |title_color|}) assert-css: (".sidebar .block a", {"color": |sidebar_link_color|}) - assert-css: (".main-heading h1 a", {"color": |title_color|}) + assert-css: (".main-heading h1", {"color": |title_color|}) // We move the cursor over the "Implementations" title so the anchor is displayed. move-cursor-to: "h2#implementations" diff --git a/tests/rustdoc-gui/item-info.goml b/tests/rustdoc-gui/item-info.goml index debda8d06c2c7..2824a5711405c 100644 --- a/tests/rustdoc-gui/item-info.goml +++ b/tests/rustdoc-gui/item-info.goml @@ -20,7 +20,7 @@ store-position: ( {"x": second_line_x, "y": second_line_y}, ) assert: |first_line_x| != |second_line_x| && |first_line_x| == 516 && |second_line_x| == 272 -assert: |first_line_y| != |second_line_y| && |first_line_y| == 715 && |second_line_y| == 738 +assert: |first_line_y| != |second_line_y| && |first_line_y| == 710 && |second_line_y| == 733 // Now we ensure that they're not rendered on the same line. set-window-size: (1100, 800) diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml index 55187a3e59647..803e8e499f476 100644 --- a/tests/rustdoc-gui/scrape-examples-layout.goml +++ b/tests/rustdoc-gui/scrape-examples-layout.goml @@ -72,8 +72,8 @@ click: ".scraped-example .button-holder .expand" store-value: (offset_y, 4) // First with desktop -assert-position: (".scraped-example", {"y": 253}) -assert-position: (".scraped-example .prev", {"y": 253 + |offset_y|}) +assert-position: (".scraped-example", {"y": 248}) +assert-position: (".scraped-example .prev", {"y": 248 + |offset_y|}) // Gradient background should be at the top of the code block. assert-css: (".scraped-example .example-wrap::before", {"top": "0px"}) @@ -82,8 +82,8 @@ assert-css: (".scraped-example .example-wrap::after", {"bottom": "0px"}) // Then with mobile set-window-size: (600, 600) store-size: (".scraped-example .scraped-example-title", {"height": title_height}) -assert-position: (".scraped-example", {"y": 284}) -assert-position: (".scraped-example .prev", {"y": 284 + |offset_y| + |title_height|}) +assert-position: (".scraped-example", {"y": 277}) +assert-position: (".scraped-example .prev", {"y": 277 + |offset_y| + |title_height|}) define-function: ( "check_title_and_code_position", diff --git a/tests/rustdoc-gui/search-filter.goml b/tests/rustdoc-gui/search-filter.goml index d6421599a207b..c5038e0892b04 100644 --- a/tests/rustdoc-gui/search-filter.goml +++ b/tests/rustdoc-gui/search-filter.goml @@ -56,7 +56,8 @@ assert-property: ("#crate-search", {"value": "lib2"}) assert-false: "#results .externcrate" // Checking that the text for the "title" is correct (the "all crates" comes from the "-element "#crate-search") to be shrunk */ - min-width: 5em; + min-width: 0; } #crate-search { - min-width: 115px; padding: 0 23px 0 4px; /* prevents the {} + +fn new_node(_c: Vec Propagate<::Output<'a>>>>) -> T { + todo!() +} + +impl Propagate for T {} +struct Noop; + +impl Transform for Noop { + type Output<'a> = (); +} + +fn main() { + let _node: Noop = new_node(vec![Box::new(Noop)]); +} diff --git a/tests/crashes/130627.rs b/tests/crashes/130627.rs new file mode 100644 index 0000000000000..59d3606592bf1 --- /dev/null +++ b/tests/crashes/130627.rs @@ -0,0 +1,20 @@ +//@ known-bug: #130627 + +#![feature(trait_alias)] + +trait Test {} + +#[diagnostic::on_unimplemented( + message="message", + label="label", + note="note" +)] +trait Alias = Test; + +// Use trait alias as bound on type parameter. +fn foo(v: &T) { +} + +pub fn main() { + foo(&1); +} diff --git a/tests/crashes/130687.rs b/tests/crashes/130687.rs new file mode 100644 index 0000000000000..361be0905dfca --- /dev/null +++ b/tests/crashes/130687.rs @@ -0,0 +1,4 @@ +//@ known-bug: #130687 +//@ only-x86_64 +pub struct Data([u8; usize::MAX >> 16]); +const _: &'static Data = &Data([0; usize::MAX >> 16]); diff --git a/tests/crashes/130779.rs b/tests/crashes/130779.rs new file mode 100644 index 0000000000000..f0fd81fff4449 --- /dev/null +++ b/tests/crashes/130779.rs @@ -0,0 +1,11 @@ +//@ known-bug: #130779 +#![feature(never_patterns)] + +enum E { A } + +fn main() { + match E::A { + ! | + if true => {} + } +} diff --git a/tests/crashes/130921.rs b/tests/crashes/130921.rs new file mode 100644 index 0000000000000..b7cb1303937e6 --- /dev/null +++ b/tests/crashes/130921.rs @@ -0,0 +1,10 @@ +//@ known-bug: #130921 +//@ compile-flags: -Zvalidate-mir -Copt-level=0 --crate-type lib + +pub fn hello() -> [impl Sized; 2] { + if false { + let x = hello(); + let _: &[i32] = &x; + } + todo!() +} diff --git a/tests/crashes/130970.rs b/tests/crashes/130970.rs new file mode 100644 index 0000000000000..e1f59c155a56a --- /dev/null +++ b/tests/crashes/130970.rs @@ -0,0 +1,9 @@ +//@ known-bug: #130970 +//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir + +fn main() { + extern "C" { + static symbol: [usize]; + } + println!("{}", symbol[0]); +} From a78fd694d47608eb382478b038ad0e91b6f5150d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 12:16:35 +0200 Subject: [PATCH 1067/1301] extend comment in global_llvm_features regarding target-cpu=native handling --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index a2a5499597c34..201cfbb1918c8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -536,6 +536,11 @@ pub(crate) fn global_llvm_features( // -Ctarget-cpu=native match sess.opts.cg.target_cpu { Some(ref s) if s == "native" => { + // We have already figured out the actual CPU name with `LLVMRustGetHostCPUName` and set + // that for LLVM, so the features implied by that CPU name will be available everywhere. + // However, that is not sufficient: e.g. `skylake` alone is not sufficient to tell if + // some of the instructions are available or not. So we have to also explicitly ask for + // the exact set of features available on the host, and enable all of them. let features_string = unsafe { let ptr = llvm::LLVMGetHostCPUFeatures(); let features_string = if !ptr.is_null() { From e9635685859626cc4ea252eac65beef0f6066e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 29 Sep 2024 12:18:11 +0200 Subject: [PATCH 1068/1301] cleanup: don't `.into()` identical types --- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 7 ++----- compiler/rustc_interface/src/interface.rs | 2 +- compiler/rustc_lint/src/if_let_rescope.rs | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e1fd8bea1f30c..4c34ffc5d4ec1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -396,11 +396,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { for i in 0..dest_len { let place = self.project_index(&dest, i)?; - let value = if i == index { - elem.clone() - } else { - self.project_index(&input, i)?.into() - }; + let value = + if i == index { elem.clone() } else { self.project_index(&input, i)? }; self.copy_op(&value, &place)?; } } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c2241773c8ccb..3b8077bb3fcee 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -427,7 +427,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), }; - let mut locale_resources = Vec::from(config.locale_resources); + let mut locale_resources = config.locale_resources; locale_resources.push(codegen_backend.locale_resource()); let mut sess = rustc_session::build_session( diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 229d0c3642174..c6218fe1e74e7 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -304,7 +304,7 @@ impl Subdiagnostic for IfLetRescopeRewrite { .chain(repeat('}').take(closing_brackets.count)) .collect(), )); - let msg = f(diag, crate::fluent_generated::lint_suggestion.into()); + let msg = f(diag, crate::fluent_generated::lint_suggestion); diag.multipart_suggestion_with_style( msg, suggestions, From 1a28599981a02ee865e51470be1009300a720faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 29 Sep 2024 12:51:28 +0200 Subject: [PATCH 1069/1301] cleanup: don't manually `unwrap_or_default()` --- compiler/rustc_expand/src/proc_macro_server.rs | 6 ++---- compiler/rustc_hir_typeck/src/upvar.rs | 10 +++------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index a7b251ab2527e..0dc35618ff889 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -627,8 +627,7 @@ impl server::TokenStream for Rustc<'_, '_> { base: Option, trees: Vec>, ) -> Self::TokenStream { - let mut stream = - if let Some(base) = base { base } else { tokenstream::TokenStream::default() }; + let mut stream = base.unwrap_or_default(); for tree in trees { for tt in (tree, &mut *self).to_internal() { stream.push_tree(tt); @@ -642,8 +641,7 @@ impl server::TokenStream for Rustc<'_, '_> { base: Option, streams: Vec, ) -> Self::TokenStream { - let mut stream = - if let Some(base) = base { base } else { tokenstream::TokenStream::default() }; + let mut stream = base.unwrap_or_default(); for s in streams { stream.push_stream(s); } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index c5843b883a17b..63cf483aa2274 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1369,13 +1369,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (&var_hir_id, _) in upvars.iter() { let mut diagnostics_info = Vec::new(); - let auto_trait_diagnostic = if let Some(diagnostics_info) = - self.compute_2229_migrations_for_trait(min_captures, var_hir_id, closure_clause) - { - diagnostics_info - } else { - FxIndexMap::default() - }; + let auto_trait_diagnostic = self + .compute_2229_migrations_for_trait(min_captures, var_hir_id, closure_clause) + .unwrap_or_default(); let drop_reorder_diagnostic = if let Some(diagnostics_info) = self .compute_2229_migrations_for_drop( From 6f5f21adfc88a914c507ba8c9a0f1f34ae292d44 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 28 Sep 2024 22:37:59 +0200 Subject: [PATCH 1070/1301] Rename doctest attribute `standalone-crate` into `standalone_crate` for coherency --- library/core/src/panic/location.rs | 2 +- .../src/write-documentation/documentation-tests.md | 4 ++-- src/librustdoc/html/markdown.rs | 6 +++--- tests/run-make/doctests-merge/doctest-standalone.rs | 4 ++-- tests/rustdoc-ui/doctest/standalone-warning-2024.rs | 2 +- .../rustdoc-ui/doctest/standalone-warning-2024.stderr | 10 +++++----- tests/rustdoc-ui/doctest/standalone-warning.rs | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 97408a4ac180e..1ad5c07d15cd0 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -44,7 +44,7 @@ impl<'a> Location<'a> { /// /// # Examples /// - /// ```standalone-crate + /// ```standalone_crate /// use std::panic::Location; /// /// /// Returns the [`Location`] at which it is called. diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md index 94ef34ea5a51a..c93893b5ada84 100644 --- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md +++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md @@ -414,11 +414,11 @@ In some cases, doctests cannot be merged. For example, if you have: The problem with this code is that, if you change any other doctests, it'll likely break when runing `rustdoc --test`, making it tricky to maintain. -This is where the `standalone-crate` attribute comes in: it tells `rustdoc` that a doctest +This is where the `standalone_crate` attribute comes in: it tells `rustdoc` that a doctest should not be merged with the others. So the previous code should use it: ```rust -//! ```standalone-crate +//! ```standalone_crate //! let location = std::panic::Location::caller(); //! assert_eq!(location.line(), 4); //! ``` diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index e1a8dc6e50cc3..8ae5484feda7a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1264,7 +1264,7 @@ impl LangString { seen_rust_tags = !seen_other_tags || seen_rust_tags; data.no_run = true; } - LangStringToken::LangToken("standalone-crate") => { + LangStringToken::LangToken("standalone_crate") => { data.standalone_crate = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; } @@ -1315,12 +1315,12 @@ impl LangString { "use `test_harness` to run functions marked `#[test]` instead of a \ potentially-implicit `main` function", ), - "standalone" | "standalone_crate" => { + "standalone" | "standalone_crate" | "standalone-crate" => { if let Some(extra) = extra && extra.sp.at_least_rust_2024() { Some( - "use `standalone-crate` to compile this code block \ + "use `standalone_crate` to compile this code block \ separately", ) } else { diff --git a/tests/run-make/doctests-merge/doctest-standalone.rs b/tests/run-make/doctests-merge/doctest-standalone.rs index 4b6a1c6ab616c..ac9f8f9272aa2 100644 --- a/tests/run-make/doctests-merge/doctest-standalone.rs +++ b/tests/run-make/doctests-merge/doctest-standalone.rs @@ -1,11 +1,11 @@ #![crate_name = "foo"] #![crate_type = "lib"] -//! ```standalone-crate +//! ```standalone_crate //! foo::init(); //! ``` -/// ```standalone-crate +/// ```standalone_crate /// foo::init(); /// ``` pub fn init() { diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.rs b/tests/rustdoc-ui/doctest/standalone-warning-2024.rs index 33ed16a87fa38..aac4303154697 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.rs +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.rs @@ -11,6 +11,6 @@ //! bla //! ``` //! -//! ```standalone_crate +//! ```standalone-crate //! bla //! ``` diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr index ef2e236431985..d69d03d8657c1 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr @@ -5,12 +5,12 @@ error: unknown attribute `standalone` 11 | | //! bla 12 | | //! ``` 13 | | //! -14 | | //! ```standalone_crate +14 | | //! ```standalone-crate 15 | | //! bla 16 | | //! ``` | |_______^ | - = help: use `standalone-crate` to compile this code block separately + = help: use `standalone_crate` to compile this code block separately = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` note: the lint level is defined here --> $DIR/standalone-warning-2024.rs:8:9 @@ -19,19 +19,19 @@ note: the lint level is defined here | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` -error: unknown attribute `standalone_crate` +error: unknown attribute `standalone-crate` --> $DIR/standalone-warning-2024.rs:10:1 | 10 | / //! ```standalone 11 | | //! bla 12 | | //! ``` 13 | | //! -14 | | //! ```standalone_crate +14 | | //! ```standalone-crate 15 | | //! bla 16 | | //! ``` | |_______^ | - = help: use `standalone-crate` to compile this code block separately + = help: use `standalone_crate` to compile this code block separately = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: aborting due to 2 previous errors diff --git a/tests/rustdoc-ui/doctest/standalone-warning.rs b/tests/rustdoc-ui/doctest/standalone-warning.rs index 323d8f5f58065..ce081c7641c61 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning.rs +++ b/tests/rustdoc-ui/doctest/standalone-warning.rs @@ -5,6 +5,6 @@ //! bla //! ``` //! -//! ```standalone_crate +//! ```standalone-crate //! bla //! ``` From 71cd918dc78c84d41bb89c2fc8effcfd000f4e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 29 Sep 2024 13:31:30 +0200 Subject: [PATCH 1071/1301] cleanup: don't clone types that are Copy --- compiler/rustc_middle/src/middle/stability.rs | 2 +- .../rustc_mir_transform/src/coroutine/by_move_body.rs | 10 +++++----- compiler/rustc_mir_transform/src/single_use_consts.rs | 5 ++--- compiler/rustc_pattern_analysis/src/constructor.rs | 8 ++++---- compiler/rustc_resolve/src/diagnostics.rs | 6 +----- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 54cfd99583215..ee34ccd889f79 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -217,7 +217,7 @@ pub fn early_report_macro_deprecation( suggestion_span: span, note: depr.note, path, - since_kind: deprecated_since_kind(is_in_effect, depr.since.clone()), + since_kind: deprecated_since_kind(is_in_effect, depr.since), }; lint_buffer.buffer_lint(deprecation_lint(is_in_effect), node_id, span, diag); } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 65442877d2dd5..cc4b7689d407c 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -223,14 +223,14 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); - body_def.constness(tcx.constness(coroutine_def_id).clone()); - body_def.coroutine_kind(tcx.coroutine_kind(coroutine_def_id).clone()); + body_def.constness(tcx.constness(coroutine_def_id)); + body_def.coroutine_kind(tcx.coroutine_kind(coroutine_def_id)); body_def.def_ident_span(tcx.def_ident_span(coroutine_def_id)); body_def.def_span(tcx.def_span(coroutine_def_id)); - body_def.explicit_predicates_of(tcx.explicit_predicates_of(coroutine_def_id).clone()); + body_def.explicit_predicates_of(tcx.explicit_predicates_of(coroutine_def_id)); body_def.generics_of(tcx.generics_of(coroutine_def_id).clone()); - body_def.param_env(tcx.param_env(coroutine_def_id).clone()); - body_def.predicates_of(tcx.predicates_of(coroutine_def_id).clone()); + body_def.param_env(tcx.param_env(coroutine_def_id)); + body_def.predicates_of(tcx.predicates_of(coroutine_def_id)); // The type of the coroutine is the `by_move_coroutine_ty`. body_def.type_of(ty::EarlyBinder::bind(by_move_coroutine_ty)); diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs index 9884b6dd1c35e..277a33c031152 100644 --- a/compiler/rustc_mir_transform/src/single_use_consts.rs +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -185,15 +185,14 @@ impl<'tcx> MutVisitor<'tcx> for LocalReplacer<'tcx> { && let Some(local) = place.as_local() && local == self.local { - let const_op = self + let const_op = *self .operand .as_ref() .unwrap_or_else(|| { bug!("the operand was already stolen"); }) .constant() - .unwrap() - .clone(); + .unwrap(); var_debug_info.value = VarDebugInfoContents::Const(const_op); } } diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 3cb7576154fc1..8fce42663453a 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -735,10 +735,10 @@ impl Clone for Constructor { Constructor::UnionField => Constructor::UnionField, Constructor::Bool(b) => Constructor::Bool(*b), Constructor::IntRange(range) => Constructor::IntRange(*range), - Constructor::F16Range(lo, hi, end) => Constructor::F16Range(lo.clone(), *hi, *end), - Constructor::F32Range(lo, hi, end) => Constructor::F32Range(lo.clone(), *hi, *end), - Constructor::F64Range(lo, hi, end) => Constructor::F64Range(lo.clone(), *hi, *end), - Constructor::F128Range(lo, hi, end) => Constructor::F128Range(lo.clone(), *hi, *end), + Constructor::F16Range(lo, hi, end) => Constructor::F16Range(*lo, *hi, *end), + Constructor::F32Range(lo, hi, end) => Constructor::F32Range(*lo, *hi, *end), + Constructor::F64Range(lo, hi, end) => Constructor::F64Range(*lo, *hi, *end), + Constructor::F128Range(lo, hi, end) => Constructor::F128Range(*lo, *hi, *end), Constructor::Str(value) => Constructor::Str(value.clone()), Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()), Constructor::Or => Constructor::Or, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 46e30c614ab4d..582db97e1ce87 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1469,11 +1469,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| { - if unused_ident.name == ident.name { - Some((def_id.clone(), unused_ident.clone())) - } else { - None - } + if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None } }); if let Some((def_id, unused_ident)) = unused_macro { From 0bebedd7993117c56fb5b7301c39fdecf9798787 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 29 Sep 2024 14:45:08 +0200 Subject: [PATCH 1072/1301] Document a bit more how the SDK version actually works --- compiler/rustc_codegen_ssa/src/back/link.rs | 48 ++++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 80e8111516ee1..8a023fac95685 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2959,11 +2959,12 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool } } -/// We need to communicate four things to the linker on Apple/Darwin targets: +/// We need to communicate five things to the linker on Apple/Darwin targets: /// - The architecture. /// - The operating system (and that it's an Apple platform). -/// - The deployment target. /// - The environment / ABI. +/// - The deployment target. +/// - The SDK version. fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { if !sess.target.is_like_osx { return; @@ -3039,7 +3040,38 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo let (major, minor, patch) = current_apple_deployment_target(&sess.target); let min_version = format!("{major}.{minor}.{patch}"); - // Lie about the SDK version, we don't know it here + // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode: + // - By dyld to give extra warnings and errors, see e.g.: + // + // + // - By system frameworks to change certain behaviour. For example, the default value of + // `-[NSView wantsBestResolutionOpenGLSurface]` is `YES` when the SDK version is >= 10.15. + // + // + // We do not currently know the actual SDK version though, so we have a few options: + // 1. Use the minimum version supported by rustc. + // 2. Use the same as the deployment target. + // 3. Use an arbitary recent version. + // 4. Omit the version. + // + // The first option is too low / too conservative, and means that users will not get the + // same behaviour from a binary compiled with rustc as with one compiled by clang. + // + // The second option is similarly conservative, and also wrong since if the user specified a + // higher deployment target than the SDK they're compiling/linking with, the runtime might + // make invalid assumptions about the capabilities of the binary. + // + // The third option requires that `rustc` is periodically kept up to date with Apple's SDK + // version, and is also wrong for similar reasons as above. + // + // The fourth option is bad because while `ld`, `otool`, `vtool` and such understand it to + // mean "absent" or `n/a`, dyld doesn't actually understand it, and will end up interpreting + // it as 0.0, which is again too low/conservative. + // + // Currently, we lie about the SDK version, and choose the second option. + // + // FIXME(madsmtm): Parse the SDK version from the SDK root instead. + // let sdk_version = &*min_version; // From the man page for ld64 (`man ld`): @@ -3053,11 +3085,13 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo cmd.link_args(&["-platform_version", platform_name, &*min_version, sdk_version]); } else { // cc == Cc::Yes + // // We'd _like_ to use `-target` everywhere, since that can uniquely - // communicate all the required details, but that doesn't work on GCC, - // and since we don't know whether the `cc` compiler is Clang, GCC, or - // something else, we fall back to other options that also work on GCC - // when compiling for macOS. + // communicate all the required details except for the SDK version + // (which is read by Clang itself from the SDKROOT), but that doesn't + // work on GCC, and since we don't know whether the `cc` compiler is + // Clang, GCC, or something else, we fall back to other options that + // also work on GCC when compiling for macOS. // // Targets other than macOS are ill-supported by GCC (it doesn't even // support e.g. `-miphoneos-version-min`), so in those cases we can From 8964c487261273eabb6973a4663322e82d82569f Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 29 Sep 2024 14:00:34 +0200 Subject: [PATCH 1073/1301] Add run-make test to check the SDK version(s) that rustc produces --- tests/run-make/apple-sdk-version/foo.rs | 1 + tests/run-make/apple-sdk-version/rmake.rs | 86 +++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/run-make/apple-sdk-version/foo.rs create mode 100644 tests/run-make/apple-sdk-version/rmake.rs diff --git a/tests/run-make/apple-sdk-version/foo.rs b/tests/run-make/apple-sdk-version/foo.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/apple-sdk-version/foo.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/apple-sdk-version/rmake.rs b/tests/run-make/apple-sdk-version/rmake.rs new file mode 100644 index 0000000000000..a16c84ddc597a --- /dev/null +++ b/tests/run-make/apple-sdk-version/rmake.rs @@ -0,0 +1,86 @@ +//! Test codegen when setting SDK version on Apple platforms. +//! +//! This is important since its a compatibility hazard. The linker will +//! generate load commands differently based on what minimum OS it can assume. +//! +//! See https://github.com/rust-lang/rust/issues/129432. + +//@ only-apple + +use run_make_support::{apple_os, cmd, run_in_tmpdir, rustc, target}; + +/// Run vtool to check the `sdk` field in LC_BUILD_VERSION. +/// +/// On lower deployment targets, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS and similar +/// are used instead of LC_BUILD_VERSION, but both name the relevant variable `sdk`. +#[track_caller] +fn has_sdk_version(file: &str, version: &str) { + cmd("vtool") + .arg("-show-build") + .arg(file) + .run() + .assert_stdout_contains(format!("sdk {version}")); +} + +fn main() { + // Fetch rustc's inferred deployment target. + let current_deployment_target = + rustc().target(target()).print("deployment-target").run().stdout_utf8(); + let current_deployment_target = + current_deployment_target.strip_prefix("deployment_target=").unwrap().trim(); + + // Fetch current SDK version via. xcrun. + // + // Assumes a standard Xcode distribution, where e.g. the macOS SDK's Mac Catalyst + // and the iPhone Simulator version is the same as for the iPhone SDK. + let sdk_name = match apple_os() { + "macos" => "macosx", + "ios" => "iphoneos", + "watchos" => "watchos", + "tvos" => "appletvos", + "visionos" => "xros", + _ => unreachable!(), + }; + let current_sdk_version = + cmd("xcrun").arg("--show-sdk-version").arg("--sdk").arg(sdk_name).run().stdout_utf8(); + let current_sdk_version = current_sdk_version.trim(); + + // Check the SDK version in the object file produced by the codegen backend. + rustc().target(target()).crate_type("lib").emit("obj").input("foo.rs").output("foo.o").run(); + // Set to 0, which means not set or "n/a". + has_sdk_version("foo.o", "n/a"); + + // Test that version makes it to the linker. + for (crate_type, file_ext) in [("bin", ""), ("dylib", ".dylib")] { + // Non-simulator watchOS targets don't support dynamic linking, + // for simplicity we disable the test on all watchOS targets. + if crate_type == "dylib" && apple_os() == "watchos" { + continue; + } + + // Test with clang + let file_name = format!("foo_cc{file_ext}"); + rustc() + .target(target()) + .crate_type("bin") + .arg("-Clinker-flavor=gcc") + .input("foo.rs") + .output(&file_name) + .run(); + has_sdk_version(&file_name, current_sdk_version); + + // Test with ld64 + let file_name = format!("foo_ld{file_ext}"); + rustc() + .target(target()) + .crate_type("bin") + .arg("-Clinker-flavor=ld") + .input("foo.rs") + .output(&file_name) + .run(); + // FIXME(madsmtm): This uses the current deployment target + // instead of the current SDK version like Clang does. + // https://github.com/rust-lang/rust/issues/129432 + has_sdk_version(&file_name, current_deployment_target); + } +} From 6b06ceb2fd118a7c6ee62675777a1d503348ef0c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 29 Sep 2024 13:58:49 +0200 Subject: [PATCH 1074/1301] Do not specify an SDK version in object files This is unnecessary, since it ends up being overwritten when linking anyhow, and it feels wrong to embed some arbitrary SDK version in here. --- compiler/rustc_codegen_ssa/src/back/metadata.rs | 10 +++++++--- .../rustc_target/src/spec/base/apple/mod.rs | 17 ----------------- compiler/rustc_target/src/spec/mod.rs | 2 +- tests/run-make/apple-sdk-version/rmake.rs | 9 +++++++++ 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 06433484ea3cd..8857fda1e9728 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -402,13 +402,17 @@ fn macho_object_build_version_for_target(target: &Target) -> object::write::Mach let platform = rustc_target::spec::current_apple_platform(target).expect("unknown Apple target OS"); let min_os = rustc_target::spec::current_apple_deployment_target(target); - let (sdk_major, sdk_minor) = - rustc_target::spec::current_apple_sdk_version(platform).expect("unknown Apple target OS"); let mut build_version = object::write::MachOBuildVersion::default(); build_version.platform = platform; build_version.minos = pack_version(min_os); - build_version.sdk = pack_version((sdk_major, sdk_minor, 0)); + // The version here does not _really_ matter, since it is only used at runtime, and we specify + // it when linking the final binary, so we will omit the version. This is also what LLVM does, + // and the tooling also allows this (and shows the SDK version as `n/a`). Finally, it is the + // semantically correct choice, as the SDK has not influenced the binary generated by rustc at + // this point in time. + build_version.sdk = 0; + build_version } diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 81b5a936d35ff..73763cf034ca0 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -158,23 +158,6 @@ pub(crate) fn base( (opts, llvm_target(os, arch, abi), arch.target_arch()) } -pub fn sdk_version(platform: u32) -> Option<(u16, u8)> { - // NOTE: These values are from an arbitrary point in time but shouldn't make it into the final - // binary since the final link command will have the current SDK version passed to it. - match platform { - object::macho::PLATFORM_MACOS => Some((13, 1)), - object::macho::PLATFORM_IOS - | object::macho::PLATFORM_IOSSIMULATOR - | object::macho::PLATFORM_TVOS - | object::macho::PLATFORM_TVOSSIMULATOR - | object::macho::PLATFORM_MACCATALYST => Some((16, 2)), - object::macho::PLATFORM_WATCHOS | object::macho::PLATFORM_WATCHOSSIMULATOR => Some((9, 1)), - // FIXME: Upgrade to `object-rs` 0.33+ implementation with visionOS platform definition - 11 | 12 => Some((1, 0)), - _ => None, - } -} - pub fn platform(target: &Target) -> Option { Some(match (&*target.os, &*target.abi) { ("macos", _) => object::macho::PLATFORM_MACOS, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f327c1fd17902..70ef21120e5a5 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -61,7 +61,7 @@ pub mod crt_objects; mod base; pub use base::apple::{ deployment_target_for_target as current_apple_deployment_target, - platform as current_apple_platform, sdk_version as current_apple_sdk_version, + platform as current_apple_platform, }; pub use base::avr_gnu::ef_avr_arch; diff --git a/tests/run-make/apple-sdk-version/rmake.rs b/tests/run-make/apple-sdk-version/rmake.rs index a16c84ddc597a..6463ec0040359 100644 --- a/tests/run-make/apple-sdk-version/rmake.rs +++ b/tests/run-make/apple-sdk-version/rmake.rs @@ -50,6 +50,15 @@ fn main() { // Set to 0, which means not set or "n/a". has_sdk_version("foo.o", "n/a"); + // Check the SDK version in the .rmeta file, as set in `create_object_file`. + // + // This is just to ensure that we don't set some odd version in `create_object_file`, + // if the rmeta file is packed in a different way in the future, this can safely be removed. + rustc().target(target()).crate_type("rlib").input("foo.rs").output("libfoo.rlib").run(); + // Extra .rmeta file (which is encoded as an object file). + cmd("ar").arg("-x").arg("libfoo.rlib").arg("lib.rmeta").run(); + has_sdk_version("lib.rmeta", "n/a"); + // Test that version makes it to the linker. for (crate_type, file_ext) in [("bin", ""), ("dylib", ".dylib")] { // Non-simulator watchOS targets don't support dynamic linking, From 40d413f9fea106a39f1129d3949369ea55a4877c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 29 Sep 2024 11:56:06 -0400 Subject: [PATCH 1075/1301] Don't give method suggestions when method probe fails due to bad impl of Deref --- compiler/rustc_hir_typeck/src/method/mod.rs | 6 +++++- compiler/rustc_hir_typeck/src/method/probe.rs | 8 +------- compiler/rustc_hir_typeck/src/method/suggest.rs | 2 ++ .../ui/methods/dont-suggest-import-on-deref-err.rs | 13 +++++++++++++ .../methods/dont-suggest-import-on-deref-err.stderr | 12 ++++++++++++ 5 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 tests/ui/methods/dont-suggest-import-on-deref-err.rs create mode 100644 tests/ui/methods/dont-suggest-import-on-deref-err.stderr diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 586b753f45493..6d6e9f248a465 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -18,8 +18,8 @@ use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; use rustc_span::symbol::Ident; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, NormalizeExt}; use tracing::{debug, instrument}; @@ -66,6 +66,9 @@ pub(crate) enum MethodError<'tcx> { // Found a match, but the return type is wrong BadReturnType, + + // Error has already been emitted, no need to emit another one. + ErrorReported(ErrorGuaranteed), } // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which @@ -120,6 +123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(PrivateMatch(..)) => false, Err(IllegalSizedBound { .. }) => true, Err(BadReturnType) => false, + Err(ErrorReported(_)) => false, } } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 371380e575dbc..3bb7070d61dfa 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -446,13 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => bug!("unexpected bad final type in method autoderef"), }; self.demand_eqtype(span, ty, Ty::new_error(self.tcx, guar)); - return Err(MethodError::NoMatch(NoMatchData { - static_candidates: Vec::new(), - unsatisfied_predicates: Vec::new(), - out_of_scope_traits: Vec::new(), - similar_candidate: None, - mode, - })); + return Err(MethodError::ErrorReported(guar)); } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index e03be4f43f73d..43564413ffb02 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -386,6 +386,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return err.emit(); } + MethodError::ErrorReported(guar) => guar, + MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), } } diff --git a/tests/ui/methods/dont-suggest-import-on-deref-err.rs b/tests/ui/methods/dont-suggest-import-on-deref-err.rs new file mode 100644 index 0000000000000..c24ab7919824b --- /dev/null +++ b/tests/ui/methods/dont-suggest-import-on-deref-err.rs @@ -0,0 +1,13 @@ +use std::clone::Clone; +use std::ops::Deref; + +#[derive(Clone)] +pub struct Foo {} + +impl Deref for Foo {} +//~^ ERROR not all trait items implemented + +pub fn main() { + let f = Foo {}; + let _ = f.clone(); +} diff --git a/tests/ui/methods/dont-suggest-import-on-deref-err.stderr b/tests/ui/methods/dont-suggest-import-on-deref-err.stderr new file mode 100644 index 0000000000000..20a63ff375d19 --- /dev/null +++ b/tests/ui/methods/dont-suggest-import-on-deref-err.stderr @@ -0,0 +1,12 @@ +error[E0046]: not all trait items implemented, missing: `Target`, `deref` + --> $DIR/dont-suggest-import-on-deref-err.rs:7:1 + | +LL | impl Deref for Foo {} + | ^^^^^^^^^^^^^^^^^^ missing `Target`, `deref` in implementation + | + = help: implement the missing item: `type Target = /* Type */;` + = help: implement the missing item: `fn deref(&self) -> &::Target { todo!() }` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0046`. From 486440fc7482adf1c57681ffaa34cf9b5fb157eb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 29 Sep 2024 11:52:25 -0400 Subject: [PATCH 1076/1301] Tweak comments and remove trivial return --- compiler/rustc_hir_typeck/src/method/mod.rs | 14 ++++---- .../rustc_hir_typeck/src/method/suggest.rs | 32 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 6d6e9f248a465..3e9cb0ac2c80c 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -46,17 +46,17 @@ pub(crate) struct MethodCallee<'tcx> { #[derive(Debug)] pub(crate) enum MethodError<'tcx> { - // Did not find an applicable method, but we did find various near-misses that may work. + /// Did not find an applicable method, but we did find various near-misses that may work. NoMatch(NoMatchData<'tcx>), - // Multiple methods might apply. + /// Multiple methods might apply. Ambiguity(Vec), - // Found an applicable method, but it is not visible. The third argument contains a list of - // not-in-scope traits which may work. + /// Found an applicable method, but it is not visible. The third argument contains a list of + /// not-in-scope traits which may work. PrivateMatch(DefKind, DefId, Vec), - // Found a `Self: Sized` bound where `Self` is a trait object. + /// Found a `Self: Sized` bound where `Self` is a trait object. IllegalSizedBound { candidates: Vec, needs_mut: bool, @@ -64,10 +64,10 @@ pub(crate) enum MethodError<'tcx> { self_expr: &'tcx hir::Expr<'tcx>, }, - // Found a match, but the return type is wrong + /// Found a match, but the return type is wrong BadReturnType, - // Error has already been emitted, no need to emit another one. + /// Error has already been emitted, no need to emit another one. ErrorReported(ErrorGuaranteed), } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 43564413ffb02..a37e9744293c3 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -229,20 +229,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } match error { - MethodError::NoMatch(mut no_match_data) => { - return self.report_no_match_method_error( - span, - rcvr_ty, - item_name, - call_id, - source, - args, - sugg_span, - &mut no_match_data, - expected, - trait_missing_method, - ); - } + MethodError::NoMatch(mut no_match_data) => self.report_no_match_method_error( + span, + rcvr_ty, + item_name, + call_id, + source, + args, + sugg_span, + &mut no_match_data, + expected, + trait_missing_method, + ), MethodError::Ambiguity(mut sources) => { let mut err = struct_span_code_err!( @@ -263,7 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut sources, Some(sugg_span), ); - return err.emit(); + err.emit() } MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { @@ -284,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|| self.tcx.def_span(def_id)); err.span_label(sp, format!("private {kind} defined here")); self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true); - return err.emit(); + err.emit() } MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => { @@ -383,7 +381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - return err.emit(); + err.emit() } MethodError::ErrorReported(guar) => guar, From 7caf2cdc2cc50310f556fe07be63b2b85feabc77 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 08:30:46 +0200 Subject: [PATCH 1077/1301] interpret: always enable write_immediate sanity checks --- compiler/rustc_const_eval/src/interpret/operand.rs | 1 + compiler/rustc_const_eval/src/interpret/place.rs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 31ee3e6519afc..b96643ea70f2e 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -118,6 +118,7 @@ impl Immediate { (Immediate::Scalar(scalar), Abi::Scalar(s)) => { assert_eq!(scalar.size(), s.size(cx)); if !matches!(s.primitive(), abi::Pointer(..)) { + // This is not a pointer, it should not carry provenance. assert!(matches!(scalar, Scalar::Int(..))); } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 32f90254a9470..d22d966faeae9 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -652,6 +652,8 @@ where M::after_local_write(self, local, /*storage_live*/ false)?; } // Double-check that the value we are storing and the local fit to each other. + // Things can ge wrong in quite weird ways when this is violated. + // Unfortunately this is too expensive to do in release builds. if cfg!(debug_assertions) { src.assert_matches_abi(local_layout.abi, self); } @@ -672,9 +674,9 @@ where layout: TyAndLayout<'tcx>, dest: MemPlace, ) -> InterpResult<'tcx> { - if cfg!(debug_assertions) { - value.assert_matches_abi(layout.abi, self); - } + // We use the sizes from `value` below. + // Ensure that matches the type of the place it is written to. + value.assert_matches_abi(layout.abi, self); // Note that it is really important that the type here is the right one, and matches the // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here // to handle padding properly, which is only correct if we never look at this data with the From c27b54ac4ad88bc2aa07260cebec31e886a3c6ad Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sun, 29 Sep 2024 10:02:06 -0700 Subject: [PATCH 1078/1301] rustdoc-gui: fix test cases --- tests/rustdoc-gui/scrape-examples-layout.goml | 4 ++-- tests/rustdoc-gui/search-result-display.goml | 2 +- tests/rustdoc-gui/sidebar-source-code-display.goml | 2 +- tests/rustdoc-gui/source-anchor-scroll.goml | 8 ++++---- tests/rustdoc-gui/toggle-docs-mobile.goml | 12 ++++++------ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml index fd0774c91b675..96c78bbfe8b88 100644 --- a/tests/rustdoc-gui/scrape-examples-layout.goml +++ b/tests/rustdoc-gui/scrape-examples-layout.goml @@ -90,8 +90,8 @@ assert-css: (".scraped-example .example-wrap::after", {"bottom": "0px"}) // Then with mobile set-window-size: (600, 600) store-size: (".scraped-example .scraped-example-title", {"height": title_height}) -assert-position: (".scraped-example", {"y": 281}) -assert-position: (".scraped-example .prev", {"y": 281 + |offset_y| + |title_height|}) +assert-position: (".scraped-example", {"y": 287}) +assert-position: (".scraped-example .prev", {"y": 287 + |offset_y| + |title_height|}) define-function: ( "check_title_and_code_position", diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml index 156244f92b4f5..1521267956a88 100644 --- a/tests/rustdoc-gui/search-result-display.goml +++ b/tests/rustdoc-gui/search-result-display.goml @@ -64,7 +64,7 @@ set-text: ( ) // Then we compare again to confirm the height didn't change. -assert-size: ("#crate-search", {"width": 509}) +assert-size: ("#crate-search", {"width": 370}) assert-size: (".search-results-title", { "height": |search_results_title_height|, }) diff --git a/tests/rustdoc-gui/sidebar-source-code-display.goml b/tests/rustdoc-gui/sidebar-source-code-display.goml index c3e02c4e9b409..742453c173b9d 100644 --- a/tests/rustdoc-gui/sidebar-source-code-display.goml +++ b/tests/rustdoc-gui/sidebar-source-code-display.goml @@ -141,7 +141,7 @@ click: "#sidebar-button" wait-for-css: (".src .sidebar > *", {"visibility": "hidden"}) // We scroll to line 117 to change the scroll position. scroll-to: '//*[@id="117"]' -store-value: (y_offset, "2564") +store-value: (y_offset, "2570") assert-window-property: {"pageYOffset": |y_offset|} // Expanding the sidebar... click: "#sidebar-button" diff --git a/tests/rustdoc-gui/source-anchor-scroll.goml b/tests/rustdoc-gui/source-anchor-scroll.goml index 166890abe4b36..f87946457051f 100644 --- a/tests/rustdoc-gui/source-anchor-scroll.goml +++ b/tests/rustdoc-gui/source-anchor-scroll.goml @@ -8,13 +8,13 @@ set-window-size: (600, 800) assert-property: ("html", {"scrollTop": "0"}) click: '//a[text() = "barbar" and @href="#5-7"]' -assert-property: ("html", {"scrollTop": "194"}) +assert-property: ("html", {"scrollTop": "200"}) click: '//a[text() = "bar" and @href="#28-36"]' -assert-property: ("html", {"scrollTop": "225"}) +assert-property: ("html", {"scrollTop": "231"}) click: '//a[normalize-space() = "sub_fn" and @href="#2-4"]' -assert-property: ("html", {"scrollTop": "122"}) +assert-property: ("html", {"scrollTop": "128"}) // We now check that clicking on lines doesn't change the scroll // Extra information: the "sub_fn" function header is on line 1. click: '//*[@id="6"]' -assert-property: ("html", {"scrollTop": "122"}) +assert-property: ("html", {"scrollTop": "128"}) diff --git a/tests/rustdoc-gui/toggle-docs-mobile.goml b/tests/rustdoc-gui/toggle-docs-mobile.goml index 59233d94fcc45..b69aa6e30ca0b 100644 --- a/tests/rustdoc-gui/toggle-docs-mobile.goml +++ b/tests/rustdoc-gui/toggle-docs-mobile.goml @@ -3,12 +3,12 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" set-window-size: (433, 600) assert-attribute: (".top-doc", {"open": ""}) -click: (4, 260) // This is the position of the top doc comment toggle +click: (4, 270) // This is the position of the top doc comment toggle assert-attribute-false: (".top-doc", {"open": ""}) -click: (4, 260) +click: (4, 270) assert-attribute: (".top-doc", {"open": ""}) // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. -click: (3, 260) +click: (3, 270) assert-attribute: (".top-doc", {"open": ""}) // Assert the position of the toggle on the top doc block. @@ -24,10 +24,10 @@ assert-position: ( // Now we do the same but with a little bigger width set-window-size: (600, 600) assert-attribute: (".top-doc", {"open": ""}) -click: (4, 260) // New Y position since all search elements are back on one line. +click: (4, 270) // New Y position since all search elements are back on one line. assert-attribute-false: (".top-doc", {"open": ""}) -click: (4, 260) +click: (4, 270) assert-attribute: (".top-doc", {"open": ""}) // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. -click: (3, 260) +click: (3, 270) assert-attribute: (".top-doc", {"open": ""}) From ad8a5ce4cae159b8c9703be969cfe17c877e38f5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 18:51:03 +0200 Subject: [PATCH 1079/1301] let rustfmt format imports --- .../bench-cargo-miri/backtraces/src/main.rs | 3 +- .../miri/bench-cargo-miri/serde2/src/main.rs | 3 +- src/tools/miri/cargo-miri/src/phases.rs | 3 +- src/tools/miri/miri-script/src/args.rs | 3 +- src/tools/miri/miri-script/src/commands.rs | 7 +--- src/tools/miri/rustfmt.toml | 7 +++- src/tools/miri/src/alloc_addresses/mod.rs | 5 +-- .../miri/src/alloc_addresses/reuse_pool.rs | 4 +- src/tools/miri/src/alloc_bytes.rs | 3 +- src/tools/miri/src/bin/miri.rs | 22 ++++------ src/tools/miri/src/borrow_tracker/mod.rs | 3 +- .../stacked_borrows/diagnostics.rs | 2 +- .../src/borrow_tracker/stacked_borrows/mod.rs | 19 ++++----- .../borrow_tracker/stacked_borrows/stack.rs | 6 +-- .../tree_borrows/diagnostics.rs | 8 ++-- .../src/borrow_tracker/tree_borrows/mod.rs | 13 +++--- .../src/borrow_tracker/tree_borrows/tree.rs | 13 +++--- .../borrow_tracker/tree_borrows/tree/tests.rs | 3 +- .../src/borrow_tracker/tree_borrows/unimap.rs | 3 +- src/tools/miri/src/concurrency/data_race.rs | 21 ++++------ .../miri/src/concurrency/range_object_map.rs | 2 +- src/tools/miri/src/concurrency/sync.rs | 3 +- src/tools/miri/src/concurrency/thread.rs | 1 - .../miri/src/concurrency/vector_clock.rs | 15 +++---- src/tools/miri/src/concurrency/weak_memory.rs | 15 +++---- src/tools/miri/src/eval.rs | 16 +++---- src/tools/miri/src/helpers.rs | 19 +++------ src/tools/miri/src/intrinsics/atomic.rs | 3 +- src/tools/miri/src/intrinsics/mod.rs | 6 +-- src/tools/miri/src/intrinsics/simd.rs | 4 +- src/tools/miri/src/lib.rs | 42 +++++++++---------- src/tools/miri/src/machine.rs | 31 +++++--------- src/tools/miri/src/operator.rs | 3 +- src/tools/miri/src/provenance_gc.rs | 1 - src/tools/miri/src/shims/backtrace.rs | 6 ++- src/tools/miri/src/shims/env.rs | 3 +- src/tools/miri/src/shims/foreign_items.rs | 17 ++++---- src/tools/miri/src/shims/native_lib.rs | 3 +- src/tools/miri/src/shims/os_str.rs | 3 +- src/tools/miri/src/shims/unix/env.rs | 3 +- src/tools/miri/src/shims/unix/fd.rs | 3 +- .../miri/src/shims/unix/foreign_items.rs | 9 ++-- src/tools/miri/src/shims/unix/fs.rs | 15 +++---- .../miri/src/shims/unix/linux/eventfd.rs | 3 +- .../src/shims/unix/linux/foreign_items.rs | 3 +- src/tools/miri/src/shims/unix/linux/mem.rs | 3 +- src/tools/miri/src/shims/unix/mem.rs | 3 +- src/tools/miri/src/shims/unix/mod.rs | 11 ++--- src/tools/miri/src/shims/unix/thread.rs | 3 +- .../miri/src/shims/unix/unnamed_socket.rs | 3 +- .../miri/src/shims/windows/foreign_items.rs | 4 +- src/tools/miri/src/shims/windows/handle.rs | 3 +- src/tools/miri/src/shims/windows/mod.rs | 3 +- src/tools/miri/src/shims/x86/avx.rs | 2 +- src/tools/miri/src/shims/x86/mod.rs | 4 +- src/tools/miri/test-cargo-miri/src/main.rs | 3 +- .../miri/test-cargo-miri/subcrate/test.rs | 1 - .../concurrency/libc_pthread_join_multiple.rs | 3 +- .../miri/tests/fail-dep/libc/affinity.rs | 4 +- .../fail-dep/libc/env-set_var-data-race.rs | 3 +- .../miri/tests/fail/coroutine-pinned-moved.rs | 6 +-- .../data_race/atomic_read_na_write_race2.rs | 3 +- .../data_race/atomic_write_na_read_race1.rs | 3 +- .../data_race/atomic_write_na_write_race2.rs | 3 +- .../fail/tree_borrows/children-can-alias.rs | 3 +- .../tests/pass-dep/concurrency/linux-futex.rs | 3 +- .../concurrency/tls_pthread_drop_order.rs | 3 +- src/tools/miri/tests/pass-dep/libc/gettid.rs | 3 +- .../miri/tests/pass-dep/libc/libc-affinity.rs | 3 +- .../miri/tests/pass-dep/libc/libc-fs-flock.rs | 4 +- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 3 +- .../miri/tests/pass-dep/tokio/file-io.rs | 1 + .../miri/tests/pass/async-niche-aliasing.rs | 12 +++--- ...atomic-compare-exchange-weak-never-fail.rs | 3 +- src/tools/miri/tests/pass/atomic.rs | 5 +-- .../tests/pass/box-custom-alloc-aliasing.rs | 12 +++--- src/tools/miri/tests/pass/box-custom-alloc.rs | 3 +- .../address_reuse_happens_before.rs | 3 +- .../pass/concurrency/sync_singlethread.rs | 3 +- src/tools/miri/tests/pass/coroutine.rs | 6 +-- .../miri/tests/pass/dyn-arbitrary-self.rs | 10 ++--- .../tests/pass/function_calls/abi_compat.rs | 4 +- .../tests/pass/intrinsics/portable-simd.rs | 3 +- ...iri-3541-dyn-vtable-trait-normalization.rs | 3 +- src/tools/miri/tests/pass/leak-in-static.rs | 6 +-- src/tools/miri/tests/pass/ptr_int_casts.rs | 3 +- src/tools/miri/tests/pass/shims/env/var.rs | 3 +- .../tests/pass/shims/windows-threadname.rs | 3 +- src/tools/miri/tests/pass/slices.rs | 3 +- .../coroutine-self-referential.rs | 6 +-- .../pass/stacked-borrows/stack-printing.rs | 6 +-- .../tests/pass/tree_borrows/tree-borrows.rs | 3 +- src/tools/miri/tests/ui.rs | 3 +- 93 files changed, 255 insertions(+), 319 deletions(-) diff --git a/src/tools/miri/bench-cargo-miri/backtraces/src/main.rs b/src/tools/miri/bench-cargo-miri/backtraces/src/main.rs index eba51c60dbc5b..b1a58712becb6 100644 --- a/src/tools/miri/bench-cargo-miri/backtraces/src/main.rs +++ b/src/tools/miri/bench-cargo-miri/backtraces/src/main.rs @@ -1,8 +1,9 @@ //! Extracted from the backtrace crate's test test_frame_conversion -use backtrace::{Backtrace, BacktraceFrame}; use std::fmt::Write; +use backtrace::{Backtrace, BacktraceFrame}; + fn main() { let mut frames = vec![]; backtrace::trace(|frame| { diff --git a/src/tools/miri/bench-cargo-miri/serde2/src/main.rs b/src/tools/miri/bench-cargo-miri/serde2/src/main.rs index 83c04b9dd5f57..8842d4ce31077 100644 --- a/src/tools/miri/bench-cargo-miri/serde2/src/main.rs +++ b/src/tools/miri/bench-cargo-miri/serde2/src/main.rs @@ -1,9 +1,10 @@ // Like serde1, but in two threads concurrently. And we don't print. static JSON: &str = r#"{"buffer":[-29,-42,-40,-37,-28,-5,-21,1,-24,-8,-20,4,-18,26,-24,44,-26,66,-30,86,-37,88,-41,72,-46,50,-31,28,23,14,64,16,51,26,32,34,39,42,48,35,58,0,72,-36,69,-59,58,-98,54,-124,36,-103,12,-110,5,-173,-19,-146,-59,-4,-42,51,1,-23,-6,-30,-6,45,46,47,70,6,55,19,60,38,62,42,47,61,46,40,42,-19,22,-34,6,-35,-50,-61,-141,-37,-171,17,-163,26,-180,46,-154,80,-63,48,-4,18,20,50,47,58,53,44,61,57,85,37,80,0,86,-8,106,-95,49,-213,-8,-131,47,49,63,40,-39,-69,-74,-37,-20,63,-12,58,-14,-12,25,-31,41,11,45,76,47,167,5,261,-37,277,-83,183,-172,35,-122,-79,138,-70,266,69,124,228,0,391,-29,594,-84,702,-78,627,-8,551,-13,509,13,372,120,352,125,622,127,691,223,362,126,386,-33,915,198,958,457,456,298,500,233,1027,469,1096,426,918,160,1067,141,1220,189,1245,164,1375,297,1378,503,1299,702,1550,929,1799,855,1752,547,1830,602,1928,832,1736,796,1735,933,1961,1385,1935,1562,2105,1485,2716,1449,2948,1305,2768,1205,2716,1346,2531,1450,2470,1653,3117,2111,3370,2176,2696,1947,2925,2305,3846,2658,2425,2184,-877,1981,-2261,2623,-1645,2908,-1876,2732,-2704,2953,-2484,3116,-2120,2954,-2442,3216,-2466,3499,-2192,3234,-2392,3361,-2497,3869,-2078,3772,-1858,3915,-2066,4438,-2285,2934,-2294,-280,-2066,-1762,-1992,-1412,-2298,-1535,-2399,-1789,-2223,-1419,-2244,-1334,-2092,-1476,-1777,-1396,-2014,-1571,-2199,-1574,-1843,-1167,-1910,-1446,-2007,-1818,-1506,-1331,-2526,-2048,-5535,-4573,-7148,-5828,-6422,-5327,-5840,-5488,-5992,-6144,-6014,-6164,-6109,-6234,-6271,-6388,-6288,-6156,-6517,-6249,-6794,-6602,-6822,-6418,-6788,-6245,-6490,-6560,-6394,-6794,-7920,-6937,-10397,-7140,-11428,-6972,-11019,-6610,-11141,-6665,-11913,-7046,-11979,-7235,-11599,-7015,-11854,-6912,-12161,-7441,-12136,-7761,-12861,-7292,-13390,-7254,-12345,-7809,-12490,-7463,-13983,-6969,-10489,-8465,-2382,-11054,1272,-12247,-270,-12060,-323,-12113,502,-12486,-697,-12251,-1086,-12141,-181,-13116,-670,-13509,-1173,-12592,-443,-12811,-449,-13698,-934,-12850,-747,-13083,-873,-15036,-1161,-11478,-1047,-2669,-1407,1006,-1658,-1146,-1195,-1297,-1421,-73,-1946,-977,-1590,-1499,-1577,-1010,-1862,-1256,-1389,-962,-1692,-509,-2613,-1317,-2087,-1359,-1997,-1034,-2891,-2024,-119,-84,5651,5723,8074,8306,7156,6870,6985,7106,7312,8403,7114,8096,7173,7848,7082,7827,6761,7189,6985,7368,7076,7835,6992,7297,7453,7260,7016,7755,6025,7429,8533,7352,14150,7628,17142,7077,16399,6947,15939,7475,16564,7069,16463,6882,16400,7602,17031,7233,16543,6517,15395,7018,15985,7104,16689,6869,15655,7622,16155,7198,17884,6022,14056,8856,5665,14484,1815,16782,3034,15786,3107,15664,2312,16517,2965,16443,3036,16120,2287,16584,2479,16720,2693,16073,2535,16159,2958,16609,3067,16086,2716,16579,3035,17752,3092,13704,2499,5265,2620,1452,2808,3024,2444,3275,2839,2267,3340,2857,2968,3232,3066,2867,3152,3072,2248,2961,2413,2807,3238,3237,2368,2699,2262,2392,3537,3339,827,823,-5020,-5359,-7095,-7857,-5973,-6274,-6208,-6279,-6934,-7181,-6893,-6647,-7146,-6687,-7026,-7328,-6451,-6924,-6763,-6535,-7109,-6639,-6926,-6559,-7188,-6799,-6727,-6955,-5786,-6554,-8543,-6796,-14465,-7190,-17356,-6641,-16372,-6529,-15941,-6898,-16526,-6434,-16219,-6520,-16222,-7449,-17077,-7097,-16665,-6476,-15675,-7026,-16498,-6848,-17147,-6271,-15894,-7069,-16266,-7032,-17817,-5991,-13796,-8594,-5421,-14349,-1649,-17288,-2847,-16525,-2974,-15945,-2324,-16482,-3022,-16593,-3097,-16451,-2420,-16780,-2649,-16641,-2836,-15900,-2660,-16214,-3050,-16827,-3111,-15993,-2741,-16151,-2994,-17537,-2933,-13812,-2314,-5216,-2475,-1125,-2648,-2801,-2290,-3285,-2796,-2243,-3415,-2642,-3109,-3000,-3271,-2839,-3408,-3161,-2497,-2876,-2603,-2570,-3351,-3173,-2416,-2832,-2235,-2408,-3405,-3186,-613,-768,5271,5201,7376,7644,6241,6176,6366,6275,6964,7124,6831,6508,6998,6566,6836,7230,6277,6777,6589,6376,6934,6536,6819,6494,7160,6749,6736,6900,5822,6476,8593,6747,14520,7204,17448,6637,16490,6483,16033,6906,16600,6511,16304,6568,16279,7438,17079,7072,16624,6463,15577,7028,16343,6877,16990,6331,15760,7121,16140,7023,17719,5944,13748,8575,5401,14336,1645,17210,2880,16419,3036,15896,2382,16483,3074,16584,3143,16425,2443,16782,2650,16695,2825,15978,2632,16272,3015,16880,3084,16096,2709,16289,2965,17641,2932,13887,2323,5330,2474,1286,2656,2954,2309,3410,2803,2373,3414,2795,3106,3151,3263,2952,3403,3241,2483,2969,2568,2681,3316,3245,2383,2837,2199,2390,3396,3165,641,706,-5230,-5323,-7307,-7790,-6136,-6317,-6268,-6419,-6884,-7278,-6766,-6666,-6976,-6731,-6853,-7406,-6308,-6958,-6636,-6553,-6978,-6703,-6829,-6647,-7156,-6883,-6737,-7017,-5814,-6581,-8575,-6833,-14490,-7270,-17411,-6699,-16466,-6539,-16016,-6931,-16571,-6504,-16257,-6551,-16202,-7408,-16983,-7021,-16545,-6410,-15512,-6976,-16305,-6803,-17017,-6243,-15820,-7037,-16197,-6923,-17802,-5820,-13840,-8455,-5475,-14227,-1724,-17099,-2923,-16314,-3008,-15801,-2362,-16392,-3088,-16506,-3163,-16356,-2503,-16700,-2717,-16605,-2855,-15904,-2710,-16226,-3108,-16870,-3089,-16101,-2747,-16257,-3087,-17584,-2975,-13868,-2324,-5343,-2548,-1275,-2673,-2917,-2213,-3363,-2694,-2311,-3251,-2744,-2867,-3129,-3034,-2939,-3190,-3234,-2346,-2964,-2639,-2658,-3558,-3241,-2670,-2892,-2453,-2437,-3564,-3175,-771,-779,5105,5171,7308,7655,6265,6204,6397,6288,7024,7172,6903,6586,7002,6627,6777,7308,6190,6889,6537,6465,7011,6613,6985,6631,7393,6934,7073,7072,6112,6615,8751,6859,14672,7282,17448,6652,16146,6448,15565,6899,16151,6547,15860,6591,16048,7446,17065,7064,16661,6368,15774,6857,16524,6677,16825,6071,15577,6900,16119,7040,17490,6118,13495,8696,5432,14446,1678,17366,3036,16488,3624,15834,3012,16382,3575,16465,3685,16301,2815,16708,2982,16679,3356,15952,2934,16049,3290,16352,3964,15605,3612,16222,3647,17764,4272,13865,3977,5384,3592,1580,3794,3243,3627,3670,3622,2758,4007,3130,3835,3294,3964,3065,4468,3408,3933,3234,3789,3118,4634,3643,4211,3174,4155,3176,5512,4400,2792,1730,-3702,-4499,-5940,-6691,-4265,-5094,-4381,-5215,-4918,-5746,-4217,-4871,-4402,-4981,-4479,-5525,-3732,-4968,-4118,-4924,-4300,-5349,-3422,-5021,-3876,-4886,-4087,-4860,-2790,-4254,-5025,-4196,-10898,-4415,-13419,-4007,-12198,-4121,-11995,-4413,-12471,-3808,-11937,-3920,-11792,-4583,-12284,-3776,-12085,-3107,-11421,-3583,-11226,-3081,-11157,-2768,-10580,-3914,-10424,-3197,-11040,-1715,-9822,-5144,-6189,-11154,-4236,-13029,-5134,-11598,-5507,-10949,-4921,-11142,-4999,-11180,-4883,-11184,-4366,-11090,-4548,-10887,-4818,-10708,-4866,-10534,-5253,-10272,-5179,-9894,-4633,-10029,-4773,-10382,-4977,-8674,-4668,-5292,-4651,-3928,-4629,-4465,-4312,-3994,-4459,-3528,-4570,-4400,-4272,-4601,-4482,-4035,-4627,-4334,-4080,-4498,-4045,-3835,-4204,-3526,-3695,-3646,-4045,-4101,-4856,-4628,-3338,-3235,-673,-508,28,147,-453,-639,11,0,8,-2,7,0,7,-3,11,-8,15,-9,17,-6,17,-5,13,-3,7,0,3,0,-2,0,-4,0,-4,-2,-6,0,-14,-2,-17,-4,-8,0,-7,5,-17,7,-18,10,-7,18,-2,25,-3,27,0,31,4,34,4,34,8,36,8,37,2,36,4,34,8,28,3,15,0,11,0,12,-5,8,-4,10,0,23,-4,31,-8,30,-2,30,0,26,-6,22,-6,20,-12,15,-19,10,-10,13,-14,6,-43,-13,-43,-16,-9,-12,-10,-29,-42,-40,-37,-28,-5,-21,1,-24,-8,-20,4,-18,26,-24,44,-26,66,-30,86,-37,88,-41,72,-46,50,-31,28,23,14,64,16,51,26,32,34,39,42,48,35,58,0,72,-36,69,-59,58,-98,54,-124,36,-103,12,-110,5,-173,-19,-146,-59,-4,-42,51,1,-23,-6,-30,-6,45,46,47,70,6,55,19,60,38,62,42,47,61,46,40,42,-19,22,-34,6,-35,-50,-61,-141,-37,-171,17,-163,26,-180,46,-154,80,-63,48,-4,18,20,50,47,58,53,44,61,57,85,37,80,0,86,-8,106,-95,49,-213,-8,-131,47,49,63,40,-39,-69,-74,-37,-20,63,-12,58,-14,-12,25,-31,41,11,45,76,47,167,5,261,-37,277,-83,183,-172,35,-122,-79,138,-70,266,69,124,228,0,391,-29,594,-84,702,-78,627,-8,551,-13,509,13,372,120,352,125,622,127,691,223,362,126,386,-33,915,198,958,457,456,298,500,233,1027,469,1096,426,918,160,1067,141,1220,189,1245,164,1375,297,1378,503,1299,702,1550,929,1799,855,1752,547,1830,602,1928,832,1736,796,1735,933,1961,1385,1935,1562,2105,1485,2716,1449,2948,1305,2768,1205,2716,1346,2531,1450,2470,1653,3117,2111,3370,2176,2696,1947,2925,2305,3846,2658,2425,2184,-877,1981,-2261,2623,-1645,2908,-1876,2732,-2704,2953,-2484,3116,-2120,2954,-2442,3216,-2466,3499,-2192,3234,-2392,3361,-2497,3869,-2078,3772,-1858,3915,-2066,4438,-2285,2934,-2294,-280,-2066,-1762,-1992,-1412,-2298,-1535,-2399,-1789,-2223,-1419,-2244,-1334,-2092,-1476,-1777,-1396,-2014,-1571,-2199,-1574,-1843,-1167,-1910,-1446,-2007,-1818,-1506,-1331,-2526,-2048,-5535,-4573,-7148,-5828,-6422,-5327,-5840,-5488,-5992,-6144,-6014,-6164,-6109,-6234,-6271,-6388,-6288,-6156,-6517,-6249,-6794,-6602,-6822,-6418,-6788,-6245,-6490,-6560,-6394,-6794,-7920,-6937,-10397,-7140,-11428,-6972,-11019,-6610,-11141,-6665,-11913,-7046,-11979,-7235,-11599,-7015,-11854,-6912,-12161,-7441,-12136,-7761,-12861,-7292,-13390,-7254,-12345,-7809,-12490,-7463,-13983,-6969,-10489,-8465,-2382,-11054,1272,-12247,-270,-12060,-323,-12113,502,-12486,-697,-12251,-1086,-12141,-181,-13116,-670,-13509,-1173,-12592,-443,-12811,-449,-13698,-934,-12850,-747,-13083,-873,-15036,-1161,-11478,-1047,-2669,-1407,1006,-1658,-1146,-1195,-1297,-1421,-73,-1946,-977,-1590,-1499,-1577,-1010,-1862,-1256,-1389,-962,-1692,-509,-2613,-1317,-2087,-1359,-1997,-1034,-2891,-2024,-119,-84,5651,5723,8074,8306,7156,6870,6985,7106,7312,8403,7114,8096,7173,7848,7082,7827,6761,7189,6985,7368]}"#; -use serde::Deserialize; use std::thread; +use serde::Deserialize; + #[derive(Deserialize)] #[allow(unused)] struct DeriveStruct { diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index e13ecbbe54ba5..52bc8e1a3b6d7 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -8,7 +8,8 @@ use std::{env, thread}; use rustc_version::VersionMeta; -use crate::{setup::*, util::*}; +use crate::setup::*; +use crate::util::*; const CARGO_MIRI_HELP: &str = r"Runs binary crates and tests in Miri diff --git a/src/tools/miri/miri-script/src/args.rs b/src/tools/miri/miri-script/src/args.rs index c1688ca0fb6b2..55d9de4233d9b 100644 --- a/src/tools/miri/miri-script/src/args.rs +++ b/src/tools/miri/miri-script/src/args.rs @@ -1,5 +1,4 @@ -use std::env; -use std::iter; +use std::{env, iter}; use anyhow::{Result, bail}; diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index b0e62d5cda6f0..36175c8dd2bd2 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -1,12 +1,9 @@ -use std::env; use std::ffi::{OsStr, OsString}; use std::io::Write; -use std::net; -use std::ops::Not; -use std::ops::Range; +use std::ops::{Not, Range}; use std::path::PathBuf; -use std::process; use std::time::Duration; +use std::{env, net, process}; use anyhow::{Context, Result, anyhow, bail}; use path_macro::path; diff --git a/src/tools/miri/rustfmt.toml b/src/tools/miri/rustfmt.toml index 3f9311d9d1a50..49650d8486c40 100644 --- a/src/tools/miri/rustfmt.toml +++ b/src/tools/miri/rustfmt.toml @@ -1,5 +1,10 @@ +# This matches rustc style_edition = "2024" use_small_heuristics = "Max" +group_imports = "StdExternalCrate" +imports_granularity = "Module" + +# Miri-specific settings +force_multiline_blocks = true match_arm_blocks = false match_arm_leading_pipes = "Preserve" -force_multiline_blocks = true diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index c19a962e6523e..f71edafb4b2ad 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -7,14 +7,13 @@ use std::cell::RefCell; use std::cmp::max; use rand::Rng; - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; use rustc_target::abi::{Align, Size}; -use crate::{concurrency::VClock, *}; - use self::reuse_pool::ReusePool; +use crate::concurrency::VClock; +use crate::*; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProvenanceMode { diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs index f6c1675634460..b5366d5ce929c 100644 --- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs +++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs @@ -1,10 +1,10 @@ //! Manages a pool of addresses that can be reused. use rand::Rng; - use rustc_target::abi::{Align, Size}; -use crate::{MemoryKind, MiriConfig, ThreadId, concurrency::VClock}; +use crate::concurrency::VClock; +use crate::{MemoryKind, MiriConfig, ThreadId}; const MAX_POOL_SIZE: usize = 64; diff --git a/src/tools/miri/src/alloc_bytes.rs b/src/tools/miri/src/alloc_bytes.rs index 84225d83b6aec..6ada8c66fde69 100644 --- a/src/tools/miri/src/alloc_bytes.rs +++ b/src/tools/miri/src/alloc_bytes.rs @@ -1,7 +1,6 @@ -use std::alloc; use std::alloc::Layout; use std::borrow::Cow; -use std::slice; +use std::{alloc, slice}; use rustc_middle::mir::interpret::AllocBytes; use rustc_target::abi::{Align, Size}; diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 14f43f576d3a4..8d3ae97e0e9fa 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -28,31 +28,27 @@ use std::num::NonZero; use std::path::PathBuf; use std::str::FromStr; -use tracing::debug; - +use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields, ValidationMode}; use rustc_data_structures::sync::Lrc; use rustc_driver::Compilation; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{self as hir, Node}; use rustc_hir_analysis::check::check_function_signature; use rustc_interface::interface::Config; -use rustc_middle::{ - middle::{ - codegen_fn_attrs::CodegenFnAttrFlags, - exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel}, - }, - query::LocalCrate, - traits::{ObligationCause, ObligationCauseCode}, - ty::{self, Ty, TyCtxt}, - util::Providers, +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::exported_symbols::{ + ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, }; +use rustc_middle::query::LocalCrate; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::util::Providers; use rustc_session::config::{CrateType, EntryFnType, ErrorOutputType, OptLevel}; use rustc_session::search_paths::PathKind; use rustc_session::{CtfeBacktrace, EarlyDiagCtxt}; use rustc_span::def_id::DefId; use rustc_target::spec::abi::Abi; - -use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields, ValidationMode}; +use tracing::debug; struct MiriCompilerCalls { miri_config: miri::MiriConfig, diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 9e205cd0064aa..89fd8c28af2ae 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -2,11 +2,10 @@ use std::cell::RefCell; use std::fmt; use std::num::NonZero; -use smallvec::SmallVec; - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::RetagKind; use rustc_target::abi::Size; +use smallvec::SmallVec; use crate::*; pub mod stacked_borrows; diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index 12eeaae0eff47..146f9902f6fe0 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -1,9 +1,9 @@ -use smallvec::SmallVec; use std::fmt; use rustc_data_structures::fx::FxHashSet; use rustc_span::{Span, SpanData}; use rustc_target::abi::Size; +use smallvec::SmallVec; use crate::borrow_tracker::{GlobalStateInner, ProtectorKind}; use crate::*; diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 7eb6773038388..b270e484bdba4 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -6,25 +6,24 @@ mod item; mod stack; use std::cell::RefCell; -use std::cmp; use std::fmt::Write; -use std::mem; +use std::{cmp, mem}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{Mutability, RetagKind}; -use rustc_middle::ty::{self, Ty, layout::HasParamEnv}; +use rustc_middle::ty::layout::HasParamEnv; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Abi, Size}; -use crate::borrow_tracker::{ - GlobalStateInner, ProtectorKind, - stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder}, -}; -use crate::concurrency::data_race::{NaReadType, NaWriteType}; -use crate::*; - use self::diagnostics::{RetagCause, RetagInfo}; pub use self::item::{Item, Permission}; pub use self::stack::Stack; +use crate::borrow_tracker::stacked_borrows::diagnostics::{ + AllocHistory, DiagnosticCx, DiagnosticCxBuilder, +}; +use crate::borrow_tracker::{GlobalStateInner, ProtectorKind}; +use crate::concurrency::data_race::{NaReadType, NaWriteType}; +use crate::*; pub type AllocState = Stacks; diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index 5c040983142f8..6b5ef7d59c554 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -5,10 +5,8 @@ use rustc_data_structures::fx::FxHashSet; use tracing::trace; use crate::ProvenanceExtra; -use crate::borrow_tracker::{ - AccessKind, BorTag, - stacked_borrows::{Item, Permission}, -}; +use crate::borrow_tracker::stacked_borrows::{Item, Permission}; +use crate::borrow_tracker::{AccessKind, BorTag}; /// Exactly what cache size we should use is a difficult trade-off. There will always be some /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up 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 44ea7533b00cb..af9ec129c69f7 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -5,11 +5,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_span::{Span, SpanData}; use crate::borrow_tracker::ProtectorKind; -use crate::borrow_tracker::tree_borrows::{ - perms::{PermTransition, Permission}, - tree::LocationState, - unimap::UniIndex, -}; +use crate::borrow_tracker::tree_borrows::perms::{PermTransition, Permission}; +use crate::borrow_tracker::tree_borrows::tree::LocationState; +use crate::borrow_tracker::tree_borrows::unimap::UniIndex; use crate::*; /// Cause of an access: either a real access or one diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 2afe02dc2c7f8..30e940b4439a4 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -1,15 +1,12 @@ -use rustc_middle::{ - mir::{Mutability, RetagKind}, - ty::{self, Ty, layout::HasParamEnv}, -}; +use rustc_middle::mir::{Mutability, RetagKind}; +use rustc_middle::ty::layout::HasParamEnv; +use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefId; use rustc_target::abi::{Abi, Size}; +use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}; +use crate::concurrency::data_race::NaReadType; use crate::*; -use crate::{ - borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}, - concurrency::data_race::NaReadType, -}; pub mod diagnostics; mod perms; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index a99c71d96b4b1..9fe134ed34bc9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -12,18 +12,17 @@ use std::{fmt, mem}; -use smallvec::SmallVec; - use rustc_data_structures::fx::FxHashSet; use rustc_span::Span; use rustc_target::abi::Size; +use smallvec::SmallVec; -use crate::borrow_tracker::tree_borrows::{ - Permission, - diagnostics::{self, NodeDebugInfo, TbError, TransitionError}, - perms::PermTransition, - unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap}, +use crate::borrow_tracker::tree_borrows::Permission; +use crate::borrow_tracker::tree_borrows::diagnostics::{ + self, NodeDebugInfo, TbError, TransitionError, }; +use crate::borrow_tracker::tree_borrows::perms::PermTransition; +use crate::borrow_tracker::tree_borrows::unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap}; use crate::borrow_tracker::{GlobalState, ProtectorKind}; use crate::*; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index 5cd5040f80776..5d51a72852ca9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -1,9 +1,10 @@ //! Tests for the tree #![cfg(test)] +use std::fmt; + use super::*; use crate::borrow_tracker::tree_borrows::exhaustive::{Exhaustive, precondition}; -use std::fmt; impl Exhaustive for LocationState { fn exhaustive() -> Box> { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs index cbc25724cb685..7874721c0ac37 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs @@ -12,7 +12,8 @@ #![allow(dead_code)] -use std::{hash::Hash, mem}; +use std::hash::Hash; +use std::mem; use rustc_data_structures::fx::FxHashMap; diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index b5b43f589f699..8ee039b6ddc89 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -40,28 +40,23 @@ //! code some atomic operations may increment the timestamp when not necessary but this has no effect //! on the data-race detection code. -use std::{ - cell::{Cell, Ref, RefCell, RefMut}, - fmt::Debug, - mem, -}; +use std::cell::{Cell, Ref, RefCell, RefMut}; +use std::fmt::Debug; +use std::mem; use rustc_ast::Mutability; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::{Idx, IndexVec}; -use rustc_middle::{mir, ty::Ty}; +use rustc_middle::mir; +use rustc_middle::ty::Ty; use rustc_span::Span; use rustc_target::abi::{Align, HasDataLayout, Size}; +use super::vector_clock::{VClock, VTimestamp, VectorIdx}; +use super::weak_memory::EvalContextExt as _; use crate::diagnostics::RacingOp; use crate::*; -use super::{ - vector_clock::{VClock, VTimestamp, VectorIdx}, - weak_memory::EvalContextExt as _, -}; - pub type AllocState = VClockAlloc; /// Valid atomic read-write orderings, alias of atomic::Ordering (not non-exhaustive). diff --git a/src/tools/miri/src/concurrency/range_object_map.rs b/src/tools/miri/src/concurrency/range_object_map.rs index 859eb4bbb60d2..d36ed36ac1a6c 100644 --- a/src/tools/miri/src/concurrency/range_object_map.rs +++ b/src/tools/miri/src/concurrency/range_object_map.rs @@ -2,10 +2,10 @@ //! ranges and data are discrete and non-splittable -- they represent distinct "objects". An //! allocation in the map will always have the same range until explicitly removed -use rustc_target::abi::Size; use std::ops::{Index, IndexMut, Range}; use rustc_const_eval::interpret::AllocRange; +use rustc_target::abi::Size; #[derive(Clone, Debug)] struct Elem { diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 2b34db047f379..7fe67e5c6727e 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -1,5 +1,6 @@ use std::any::Any; -use std::collections::{VecDeque, hash_map::Entry}; +use std::collections::VecDeque; +use std::collections::hash_map::Entry; use std::ops::Not; use std::time::Duration; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 681e9211246dd..f6b453efcbeb7 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -7,7 +7,6 @@ use std::task::Poll; use std::time::{Duration, SystemTime}; use either::Either; - use rustc_const_eval::CTRL_C_RECEIVED; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs index 901b097c1bd77..f9025e06c684e 100644 --- a/src/tools/miri/src/concurrency/vector_clock.rs +++ b/src/tools/miri/src/concurrency/vector_clock.rs @@ -1,11 +1,10 @@ +use std::cmp::Ordering; +use std::fmt::Debug; +use std::ops::{Index, Shr}; + use rustc_index::Idx; use rustc_span::{DUMMY_SP, Span, SpanData}; use smallvec::SmallVec; -use std::{ - cmp::Ordering, - fmt::Debug, - ops::{Index, Shr}, -}; use super::data_race::NaReadType; @@ -430,10 +429,12 @@ impl Index for VClock { /// test suite #[cfg(test)] mod tests { + use std::cmp::Ordering; + + use rustc_span::DUMMY_SP; + use super::{VClock, VTimestamp, VectorIdx}; use crate::concurrency::data_race::NaReadType; - use rustc_span::DUMMY_SP; - use std::cmp::Ordering; #[test] fn test_equal() { diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 0605b744e6afd..307695436c61e 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -76,21 +76,16 @@ // (https://github.com/ChrisLidbury/tsan11/blob/ecbd6b81e9b9454e01cba78eb9d88684168132c7/lib/tsan/rtl/tsan_relaxed.cc#L295) // and here. -use std::{ - cell::{Ref, RefCell}, - collections::VecDeque, -}; +use std::cell::{Ref, RefCell}; +use std::collections::VecDeque; use rustc_data_structures::fx::FxHashMap; +use super::data_race::{GlobalState as DataRaceState, ThreadClockSet}; +use super::range_object_map::{AccessType, RangeObjectMap}; +use super::vector_clock::{VClock, VTimestamp, VectorIdx}; use crate::*; -use super::{ - data_race::{GlobalState as DataRaceState, ThreadClockSet}, - range_object_map::{AccessType, RangeObjectMap}, - vector_clock::{VClock, VTimestamp, VectorIdx}, -}; - pub type AllocState = StoreBufferAlloc; // Each store buffer must be bounded otherwise it will grow indefinitely. diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 8c8ed9c4ddc3b..3739d46bd60f0 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -1,25 +1,21 @@ //! Main evaluator loop and setting up the initial stack frame. use std::ffi::{OsStr, OsString}; -use std::iter; use std::panic::{self, AssertUnwindSafe}; use std::path::PathBuf; use std::task::Poll; -use std::thread; +use std::{iter, thread}; -use crate::concurrency::thread::TlsAllocAction; -use crate::diagnostics::report_leaks; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; -use rustc_middle::ty::{ - self, Ty, TyCtxt, - layout::{LayoutCx, LayoutOf}, -}; -use rustc_target::spec::abi::Abi; - +use rustc_middle::ty::layout::{LayoutCx, LayoutOf}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config::EntryFnType; +use rustc_target::spec::abi::Abi; +use crate::concurrency::thread::TlsAllocAction; +use crate::diagnostics::report_leaks; use crate::shims::tls; use crate::*; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 10e5882b5ba89..0cd6ba69aac9d 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1,29 +1,22 @@ -use std::cmp; use std::collections::BTreeSet; -use std::iter; use std::num::NonZero; use std::sync::Mutex; use std::time::Duration; +use std::{cmp, iter}; use rand::RngCore; - use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_hir::{ - Safety, - def::{DefKind, Namespace}, - def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE}, -}; +use rustc_hir::Safety; +use rustc_hir::def::{DefKind, Namespace}; +use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE}; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::mir; -use rustc_middle::ty::layout::{FnAbiOf, MaybeResult}; -use rustc_middle::ty::{ - self, FloatTy, IntTy, Ty, TyCtxt, UintTy, - layout::{LayoutOf, TyAndLayout}, -}; +use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, MaybeResult, TyAndLayout}; +use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants}; diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index f38c3b821742a..b6fd9ba1e825c 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -1,4 +1,5 @@ -use rustc_middle::{mir, mir::BinOp, ty}; +use rustc_middle::mir::BinOp; +use rustc_middle::{mir, ty}; use self::helpers::check_arg_count; use crate::*; diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index d8d267f5ba693..23ce2f8ed1f91 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -5,10 +5,8 @@ mod simd; use rand::Rng; use rustc_apfloat::{Float, Round}; -use rustc_middle::{ - mir, - ty::{self, FloatTy}, -}; +use rustc_middle::mir; +use rustc_middle::ty::{self, FloatTy}; use rustc_span::{Symbol, sym}; use rustc_target::abi::Size; diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 2bc11d63a3951..802a70e5a54aa 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -1,8 +1,8 @@ use either::Either; - use rustc_apfloat::{Float, Round}; +use rustc_middle::ty::FloatTy; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; -use rustc_middle::{mir, ty, ty::FloatTy}; +use rustc_middle::{mir, ty}; use rustc_span::{Symbol, sym}; use rustc_target::abi::{Endian, HasDataLayout}; diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 4d0606113f9ea..78e7bf704552d 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -92,15 +92,14 @@ mod range_map; mod shims; // Establish a "crate-wide prelude": we often import `crate::*`. -use rustc_middle::{bug, span_bug}; -use tracing::{info, trace}; - // Make all those symbols available in the same place as our own. #[doc(no_inline)] pub use rustc_const_eval::interpret::*; // Resolve ambiguity. #[doc(no_inline)] pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _}; +use rustc_middle::{bug, span_bug}; +use tracing::{info, trace}; // Type aliases that set the provenance parameter. pub type Pointer = interpret::Pointer>; @@ -111,15 +110,6 @@ pub type OpTy<'tcx> = interpret::OpTy<'tcx, machine::Provenance>; pub type PlaceTy<'tcx> = interpret::PlaceTy<'tcx, machine::Provenance>; pub type MPlaceTy<'tcx> = interpret::MPlaceTy<'tcx, machine::Provenance>; -pub use crate::intrinsics::EvalContextExt as _; -pub use crate::shims::EmulateItemResult; -pub use crate::shims::env::{EnvVars, EvalContextExt as _}; -pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; -pub use crate::shims::os_str::EvalContextExt as _; -pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; -pub use crate::shims::time::EvalContextExt as _; -pub use crate::shims::tls::TlsData; - pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode}; pub use crate::alloc_bytes::MiriAllocBytes; pub use crate::borrow_tracker::stacked_borrows::{ @@ -128,15 +118,17 @@ pub use crate::borrow_tracker::stacked_borrows::{ pub use crate::borrow_tracker::tree_borrows::{EvalContextExt as _, Tree}; pub use crate::borrow_tracker::{BorTag, BorrowTrackerMethod, EvalContextExt as _, RetagFields}; pub use crate::clock::{Clock, Instant}; -pub use crate::concurrency::{ - cpu_affinity::MAX_CPUS, - data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _}, - init_once::{EvalContextExt as _, InitOnceId}, - sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects}, - thread::{ - BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, - TimeoutAnchor, TimeoutClock, UnblockCallback, - }, +pub use crate::concurrency::cpu_affinity::MAX_CPUS; +pub use crate::concurrency::data_race::{ + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _, +}; +pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceId}; +pub use crate::concurrency::sync::{ + CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects, +}; +pub use crate::concurrency::thread::{ + BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor, + TimeoutClock, UnblockCallback, }; pub use crate::diagnostics::{ EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error, @@ -146,6 +138,7 @@ pub use crate::eval::{ create_ecx, eval_entry, }; pub use crate::helpers::{AccessKind, EvalContextExt as _}; +pub use crate::intrinsics::EvalContextExt as _; pub use crate::machine::{ AllocExtra, FrameExtra, MemoryKind, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra, @@ -154,6 +147,13 @@ pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as _; pub use crate::provenance_gc::{EvalContextExt as _, LiveAllocs, VisitProvenance, VisitWith}; pub use crate::range_map::RangeMap; +pub use crate::shims::EmulateItemResult; +pub use crate::shims::env::{EnvVars, EvalContextExt as _}; +pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; +pub use crate::shims::os_str::EvalContextExt as _; +pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; +pub use crate::shims::time::EvalContextExt as _; +pub use crate::shims::tls::TlsData; /// Insert rustc arguments at the beginning of the argument list that Miri wants to be /// set per default, for maximal validation power. diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index b93feeeee3365..fb5ccbcd4fe22 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -4,40 +4,29 @@ use std::borrow::Cow; use std::cell::RefCell; use std::collections::hash_map::Entry; -use std::fmt; use std::path::Path; -use std::process; +use std::{fmt, process}; -use rand::Rng; -use rand::SeedableRng; use rand::rngs::StdRng; - +use rand::{Rng, SeedableRng}; use rustc_attr::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; -use rustc_middle::{ - mir, - query::TyCtxtAt, - ty::{ - self, Instance, Ty, TyCtxt, - layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout}, - }, -}; +use rustc_middle::mir; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::config::InliningThreshold; use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{Span, SpanData, Symbol}; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; -use crate::{ - concurrency::{ - cpu_affinity::{self, CpuAffinityMask}, - data_race::{self, NaReadType, NaWriteType}, - weak_memory, - }, - *, -}; +use crate::concurrency::cpu_affinity::{self, CpuAffinityMask}; +use crate::concurrency::data_race::{self, NaReadType, NaWriteType}; +use crate::concurrency::weak_memory; +use crate::*; /// First real-time signal. /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35 diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index 1b6a7255eefbe..6b8c0fe87adb3 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -1,6 +1,7 @@ use std::iter; -use rand::{Rng, seq::IteratorRandom}; +use rand::Rng; +use rand::seq::IteratorRandom; use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir; use rustc_target::abi::Size; diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs index d4bed69c6707a..c5a35bc14f591 100644 --- a/src/tools/miri/src/provenance_gc.rs +++ b/src/tools/miri/src/provenance_gc.rs @@ -1,5 +1,4 @@ use either::Either; - use rustc_data_structures::fx::FxHashSet; use crate::*; diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index edff17c0514ce..1d20189b6fd8f 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -1,9 +1,11 @@ -use crate::*; use rustc_ast::ast::Mutability; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{BytePos, Loc, Symbol, hygiene}; -use rustc_target::{abi::Size, spec::abi::Abi}; +use rustc_target::abi::Size; +use rustc_target::spec::abi::Abi; + +use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index c465ef3d28bc8..557d648682c7c 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -2,7 +2,8 @@ use std::ffi::{OsStr, OsString}; use rustc_data_structures::fx::FxHashMap; -use self::shims::{unix::UnixEnvVars, windows::WindowsEnvVars}; +use self::shims::unix::UnixEnvVars; +use self::shims::windows::WindowsEnvVars; use crate::*; #[derive(Default)] diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index c0484dde59b5b..6a835098e6048 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -1,16 +1,17 @@ -use std::{collections::hash_map::Entry, io::Write, iter, path::Path}; +use std::collections::hash_map::Entry; +use std::io::Write; +use std::iter; +use std::path::Path; use rustc_apfloat::Float; use rustc_ast::expand::allocator::alloc_error_handler_name; -use rustc_hir::{def::DefKind, def_id::CrateNum}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::CrateNum; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir; -use rustc_middle::ty; +use rustc_middle::{mir, ty}; use rustc_span::Symbol; -use rustc_target::{ - abi::{Align, AlignFromBytesError, Size}, - spec::abi::Abi, -}; +use rustc_target::abi::{Align, AlignFromBytesError, Size}; +use rustc_target::spec::abi::Abi; use self::helpers::{ToHost, ToSoft}; use super::alloc::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index e4998c37f3fe6..2b5acee244cba 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -1,7 +1,8 @@ //! Implements calling functions from a native library. -use libffi::{high::call as ffi, low::CodePtr}; use std::ops::Deref; +use libffi::high::call as ffi; +use libffi::low::CodePtr; use rustc_middle::ty::{self as ty, IntTy, UintTy}; use rustc_span::Symbol; use rustc_target::abi::{Abi, HasDataLayout}; diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index a1be2ae8b588b..17c451059208f 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -1,11 +1,10 @@ use std::borrow::Cow; use std::ffi::{OsStr, OsString}; -use std::path::{Path, PathBuf}; - #[cfg(unix)] use std::os::unix::ffi::{OsStrExt, OsStringExt}; #[cfg(windows)] use std::os::windows::ffi::{OsStrExt, OsStringExt}; +use std::path::{Path, PathBuf}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 324607cc1eda3..184a6c238b392 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -1,7 +1,6 @@ -use std::env; use std::ffi::{OsStr, OsString}; use std::io::ErrorKind; -use std::mem; +use std::{env, mem}; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::Ty; diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 5fae746af2b63..6bd753d0d6bee 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -5,8 +5,7 @@ use std::any::Any; use std::collections::BTreeMap; use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write}; use std::ops::Deref; -use std::rc::Rc; -use std::rc::Weak; +use std::rc::{Rc, Weak}; use rustc_target::abi::Size; diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 470f13442f92d..528d068ea92c0 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -6,16 +6,15 @@ use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; -use crate::concurrency::cpu_affinity::CpuAffinityMask; -use crate::shims::alloc::EvalContextExt as _; -use crate::shims::unix::*; -use crate::*; - use self::shims::unix::android::foreign_items as android; use self::shims::unix::freebsd::foreign_items as freebsd; use self::shims::unix::linux::foreign_items as linux; use self::shims::unix::macos::foreign_items as macos; use self::shims::unix::solarish::foreign_items as solarish; +use crate::concurrency::cpu_affinity::CpuAffinityMask; +use crate::shims::alloc::EvalContextExt as _; +use crate::shims::unix::*; +use crate::*; pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { match name { diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 1b657db5cccc0..92514b67beab1 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -11,14 +11,13 @@ use std::time::SystemTime; use rustc_data_structures::fx::FxHashMap; use rustc_target::abi::Size; +use self::fd::FlockOp; use self::shims::time::system_time_to_duration; use crate::shims::os_str::bytes_to_os_str; use crate::shims::unix::fd::FileDescriptionRef; use crate::shims::unix::*; use crate::*; -use self::fd::FlockOp; - #[derive(Debug)] struct FileHandle { file: File, @@ -186,12 +185,14 @@ impl FileDescription for FileHandle { #[cfg(target_family = "windows")] { use std::os::windows::io::AsRawHandle; - use windows_sys::Win32::{ - Foundation::{ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE}, - Storage::FileSystem::{ - LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile, - }, + + use windows_sys::Win32::Foundation::{ + ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE, + }; + use windows_sys::Win32::Storage::FileSystem::{ + LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile, }; + let fh = self.file.as_raw_handle() as HANDLE; use FlockOp::*; diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index 9fbabbd964180..ab7652ca721ca 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -3,10 +3,11 @@ use std::cell::{Cell, RefCell}; use std::io; use std::io::{Error, ErrorKind}; +use crate::concurrency::VClock; use crate::shims::unix::fd::FileDescriptionRef; use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _}; use crate::shims::unix::*; -use crate::{concurrency::VClock, *}; +use crate::*; /// Maximum value that the eventfd counter can hold. const MAX_COUNTER: u64 = u64::MAX - 1; diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 07527a9d6e332..e2e76aad7cd6e 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -5,8 +5,7 @@ use self::shims::unix::linux::epoll::EvalContextExt as _; use self::shims::unix::linux::eventfd::EvalContextExt as _; use self::shims::unix::linux::mem::EvalContextExt as _; use self::shims::unix::linux::sync::futex; -use crate::machine::SIGRTMAX; -use crate::machine::SIGRTMIN; +use crate::machine::{SIGRTMAX, SIGRTMIN}; use crate::shims::unix::*; use crate::*; diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 3b32612e8baf0..b11f60048a070 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -1,9 +1,10 @@ //! This follows the pattern in src/shims/unix/mem.rs: We only support uses of mremap that would //! correspond to valid uses of realloc. -use crate::*; use rustc_target::abi::Size; +use crate::*; + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn mremap( diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 33ed0e2698226..0397ba3b91ad9 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -14,9 +14,10 @@ //! munmap shim which would partially unmap a region of address space previously mapped by mmap will //! report UB. -use crate::*; use rustc_target::abi::Size; +use crate::*; + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn mmap( diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs index a95b4d3d30793..9bc310e8d0a5d 100644 --- a/src/tools/miri/src/shims/unix/mod.rs +++ b/src/tools/miri/src/shims/unix/mod.rs @@ -14,14 +14,11 @@ mod linux; mod macos; mod solarish; -pub use self::env::UnixEnvVars; -pub use self::fd::{FdTable, FileDescription}; -pub use self::fs::DirTable; -pub use self::linux::epoll::EpollInterestTable; // All the Unix-specific extension traits -pub use self::env::EvalContextExt as _; -pub use self::fd::EvalContextExt as _; -pub use self::fs::EvalContextExt as _; +pub use self::env::{EvalContextExt as _, UnixEnvVars}; +pub use self::fd::{EvalContextExt as _, FdTable, FileDescription}; +pub use self::fs::{DirTable, EvalContextExt as _}; +pub use self::linux::epoll::EpollInterestTable; pub use self::mem::EvalContextExt as _; pub use self::sync::EvalContextExt as _; pub use self::thread::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 56e8270aa62ac..96af4ae51efaa 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -1,6 +1,7 @@ -use crate::*; use rustc_target::spec::abi::Abi; +use crate::*; + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_create( diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index db6872319ea48..d1bfa563387dd 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -9,10 +9,11 @@ use std::io::{Error, ErrorKind, Read}; use rustc_target::abi::Size; +use crate::concurrency::VClock; use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef}; use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _}; use crate::shims::unix::*; -use crate::{concurrency::VClock, *}; +use crate::*; /// The maximum capacity of the socketpair buffer in bytes. /// This number is arbitrary as the value can always diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index cd8f3e59015a3..22634c509bed0 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -1,8 +1,6 @@ use std::ffi::OsStr; -use std::io; -use std::iter; use std::path::{self, Path, PathBuf}; -use std::str; +use std::{io, iter, str}; use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index ec461a4cd3681..0a8e0e8fdfb75 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -1,6 +1,7 @@ -use rustc_target::abi::HasDataLayout; use std::mem::variant_count; +use rustc_target::abi::HasDataLayout; + use crate::*; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/src/tools/miri/src/shims/windows/mod.rs b/src/tools/miri/src/shims/windows/mod.rs index 537c724e52665..892bd6924fc93 100644 --- a/src/tools/miri/src/shims/windows/mod.rs +++ b/src/tools/miri/src/shims/windows/mod.rs @@ -5,9 +5,8 @@ mod handle; mod sync; mod thread; -pub use self::env::WindowsEnvVars; // All the Windows-specific extension traits -pub use self::env::EvalContextExt as _; +pub use self::env::{EvalContextExt as _, WindowsEnvVars}; pub use self::handle::EvalContextExt as _; pub use self::sync::EvalContextExt as _; pub use self::thread::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index bdc4fc9446915..169f8f8111adb 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -1,4 +1,4 @@ -use rustc_apfloat::{ieee::Double, ieee::Single}; +use rustc_apfloat::ieee::{Double, Single}; use rustc_middle::mir; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf as _; diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 211bd168af28b..8132de7d64697 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -1,6 +1,6 @@ use rand::Rng as _; - -use rustc_apfloat::{Float, ieee::Single}; +use rustc_apfloat::Float; +use rustc_apfloat::ieee::Single; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::{mir, ty}; diff --git a/src/tools/miri/test-cargo-miri/src/main.rs b/src/tools/miri/test-cargo-miri/src/main.rs index d171ec1c0d1c4..efe95bf3abab7 100644 --- a/src/tools/miri/test-cargo-miri/src/main.rs +++ b/src/tools/miri/test-cargo-miri/src/main.rs @@ -1,9 +1,10 @@ -use byteorder::{BigEndian, ByteOrder}; use std::env; #[cfg(unix)] use std::io::{self, BufRead}; use std::path::PathBuf; +use byteorder::{BigEndian, ByteOrder}; + fn main() { // Check env var set by `build.rs`. assert_eq!(env!("MIRITESTVAR"), "testval"); diff --git a/src/tools/miri/test-cargo-miri/subcrate/test.rs b/src/tools/miri/test-cargo-miri/subcrate/test.rs index b60cf20339b77..e663643f20b42 100644 --- a/src/tools/miri/test-cargo-miri/subcrate/test.rs +++ b/src/tools/miri/test-cargo-miri/subcrate/test.rs @@ -1,5 +1,4 @@ use std::env; - use std::path::PathBuf; use byteorder::{ByteOrder, LittleEndian}; diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs index 6a49625d75d60..b2a398e0a198e 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs @@ -2,8 +2,7 @@ // Joining the same thread from multiple threads is undefined behavior. -use std::thread; -use std::{mem, ptr}; +use std::{mem, ptr, thread}; extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { // Yield the thread several times so that other threads can join it. diff --git a/src/tools/miri/tests/fail-dep/libc/affinity.rs b/src/tools/miri/tests/fail-dep/libc/affinity.rs index d7d5c59e1bc17..09f096e46f1e6 100644 --- a/src/tools/miri/tests/fail-dep/libc/affinity.rs +++ b/src/tools/miri/tests/fail-dep/libc/affinity.rs @@ -3,10 +3,10 @@ //@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4 fn main() { - use libc::{cpu_set_t, sched_setaffinity}; - use std::mem::size_of; + use libc::{cpu_set_t, sched_setaffinity}; + // If pid is zero, then the calling thread is used. const PID: i32 = 0; diff --git a/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs index c7d8bacd37950..3a832bb0ce0c1 100644 --- a/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs @@ -1,8 +1,7 @@ //@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 //@ignore-target: windows # No libc env support on Windows -use std::env; -use std::thread; +use std::{env, thread}; fn main() { let t = thread::spawn(|| unsafe { diff --git a/src/tools/miri/tests/fail/coroutine-pinned-moved.rs b/src/tools/miri/tests/fail/coroutine-pinned-moved.rs index 46ec58810a62d..abed638e44359 100644 --- a/src/tools/miri/tests/fail/coroutine-pinned-moved.rs +++ b/src/tools/miri/tests/fail/coroutine-pinned-moved.rs @@ -1,10 +1,8 @@ //@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] -use std::{ - ops::{Coroutine, CoroutineState}, - pin::Pin, -}; +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; fn firstn() -> impl Coroutine { #[coroutine] diff --git a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs index 8bceba9380adf..c67ce65eb3404 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs @@ -3,8 +3,7 @@ // Avoid accidental synchronization via address reuse inside `thread::spawn`. //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; #[derive(Copy, Clone)] diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs index 1a2746a26f4d8..5e328740e8517 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs @@ -3,8 +3,7 @@ // Avoid accidental synchronization via address reuse inside `thread::spawn`. //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; #[derive(Copy, Clone)] diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs index b494bd3a003d3..fdc0f9e20f079 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs @@ -3,8 +3,7 @@ // Avoid accidental synchronization via address reuse inside `thread::spawn`. //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; #[derive(Copy, Clone)] diff --git a/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs b/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs index 036267dc11e9d..d3f272dfbf81d 100644 --- a/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs +++ b/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs @@ -8,8 +8,7 @@ #![feature(ptr_internals)] -use core::ptr::Unique; -use core::ptr::addr_of_mut; +use core::ptr::{Unique, addr_of_mut}; fn main() { let mut data = 0u8; diff --git a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs index 255a93226a958..20e642a0a2952 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs @@ -6,8 +6,7 @@ use std::mem::MaybeUninit; use std::ptr::{self, addr_of}; -use std::sync::atomic::AtomicI32; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicI32, Ordering}; use std::thread; use std::time::{Duration, Instant}; diff --git a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs index 2dc09709b8eed..b57386000404f 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs @@ -8,8 +8,7 @@ // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] -use std::mem; -use std::ptr; +use std::{mem, ptr}; pub type Key = libc::pthread_key_t; diff --git a/src/tools/miri/tests/pass-dep/libc/gettid.rs b/src/tools/miri/tests/pass-dep/libc/gettid.rs index ca352e0109a47..51f1a5ed70898 100644 --- a/src/tools/miri/tests/pass-dep/libc/gettid.rs +++ b/src/tools/miri/tests/pass-dep/libc/gettid.rs @@ -2,9 +2,10 @@ //@revisions: with_isolation without_isolation //@[without_isolation] compile-flags: -Zmiri-disable-isolation -use libc::{getpid, gettid}; use std::thread; +use libc::{getpid, gettid}; + fn main() { thread::spawn(|| { // Test that in isolation mode a deterministic value will be returned. 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 ff152eaea5c50..8aa8c7dcb8e6a 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs @@ -5,9 +5,10 @@ #![feature(pointer_is_aligned_to)] #![feature(strict_provenance)] -use libc::{cpu_set_t, sched_getaffinity, sched_setaffinity}; use std::mem::{size_of, size_of_val}; +use libc::{cpu_set_t, sched_getaffinity, sched_setaffinity}; + // If pid is zero, then the calling thread is used. const PID: i32 = 0; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs index be11f65a1e081..99d6d2b38f8de 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs @@ -1,7 +1,9 @@ //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation -use std::{fs::File, io::Error, os::fd::AsRawFd}; +use std::fs::File; +use std::io::Error; +use std::os::fd::AsRawFd; #[path = "../../utils/mod.rs"] mod utils; 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 17e6e507c2750..f85abe2cc4377 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -230,8 +230,7 @@ fn test_posix_mkstemp() { /// Test allocating variant of `realpath`. fn test_posix_realpath_alloc() { - use std::os::unix::ffi::OsStrExt; - use std::os::unix::ffi::OsStringExt; + use std::os::unix::ffi::{OsStrExt, OsStringExt}; let buf; let path = utils::tmp().join("miri_test_libc_posix_realpath_alloc"); diff --git a/src/tools/miri/tests/pass-dep/tokio/file-io.rs b/src/tools/miri/tests/pass-dep/tokio/file-io.rs index 14c27285a6ae5..6e88b907f5dc8 100644 --- a/src/tools/miri/tests/pass-dep/tokio/file-io.rs +++ b/src/tools/miri/tests/pass-dep/tokio/file-io.rs @@ -2,6 +2,7 @@ //@only-target: linux # We only support tokio on Linux use std::fs::remove_file; + use tokio::fs::{File, OpenOptions}; use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; diff --git a/src/tools/miri/tests/pass/async-niche-aliasing.rs b/src/tools/miri/tests/pass/async-niche-aliasing.rs index ab82c929a94c2..fe4ddd9d80088 100644 --- a/src/tools/miri/tests/pass/async-niche-aliasing.rs +++ b/src/tools/miri/tests/pass/async-niche-aliasing.rs @@ -1,13 +1,11 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -use std::{ - future::Future, - mem::MaybeUninit, - pin::Pin, - sync::Arc, - task::{Context, Poll, Wake}, -}; +use std::future::Future; +use std::mem::MaybeUninit; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll, Wake}; struct ThingAdder<'a> { // Using `MaybeUninit` to ensure there are no niches here. diff --git a/src/tools/miri/tests/pass/atomic-compare-exchange-weak-never-fail.rs b/src/tools/miri/tests/pass/atomic-compare-exchange-weak-never-fail.rs index 8d3d71869f42e..0a236377a9c61 100644 --- a/src/tools/miri/tests/pass/atomic-compare-exchange-weak-never-fail.rs +++ b/src/tools/miri/tests/pass/atomic-compare-exchange-weak-never-fail.rs @@ -1,5 +1,6 @@ //@compile-flags: -Zmiri-compare-exchange-weak-failure-rate=0.0 -use std::sync::atomic::{AtomicBool, Ordering::*}; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::*; // Ensure that compare_exchange_weak never fails. fn main() { diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs index 781cc9bd30942..f84fe825d0106 100644 --- a/src/tools/miri/tests/pass/atomic.rs +++ b/src/tools/miri/tests/pass/atomic.rs @@ -6,9 +6,8 @@ // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] -use std::sync::atomic::{ - AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, Ordering::*, compiler_fence, fence, -}; +use std::sync::atomic::Ordering::*; +use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, compiler_fence, fence}; fn main() { atomic_bool(); diff --git a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs index a1371242f60d9..0a61db960c1d4 100644 --- a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs +++ b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs @@ -7,13 +7,11 @@ #![feature(allocator_api)] #![feature(strict_provenance)] -use std::{ - alloc::{AllocError, Allocator, Layout}, - cell::{Cell, UnsafeCell}, - mem, - ptr::{self, NonNull, addr_of}, - thread::{self, ThreadId}, -}; +use std::alloc::{AllocError, Allocator, Layout}; +use std::cell::{Cell, UnsafeCell}; +use std::mem; +use std::ptr::{self, NonNull, addr_of}; +use std::thread::{self, ThreadId}; const BIN_SIZE: usize = 8; diff --git a/src/tools/miri/tests/pass/box-custom-alloc.rs b/src/tools/miri/tests/pass/box-custom-alloc.rs index 8d6da0733fe17..71ce019187c44 100644 --- a/src/tools/miri/tests/pass/box-custom-alloc.rs +++ b/src/tools/miri/tests/pass/box-custom-alloc.rs @@ -3,8 +3,7 @@ #![allow(incomplete_features)] // for trait upcasting #![feature(allocator_api, trait_upcasting)] -use std::alloc::Layout; -use std::alloc::{AllocError, Allocator}; +use std::alloc::{AllocError, Allocator, Layout}; use std::cell::Cell; use std::mem::MaybeUninit; use std::ptr::{self, NonNull}; diff --git a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs index cfc1ef7ae455a..255f4061abce7 100644 --- a/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs +++ b/src/tools/miri/tests/pass/concurrency/address_reuse_happens_before.rs @@ -5,7 +5,8 @@ #![feature(sync_unsafe_cell)] use std::cell::SyncUnsafeCell; -use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::Relaxed; use std::thread; static ADDR: AtomicUsize = AtomicUsize::new(0); diff --git a/src/tools/miri/tests/pass/concurrency/sync_singlethread.rs b/src/tools/miri/tests/pass/concurrency/sync_singlethread.rs index 5663e1c1426c9..e52dbd3186e1e 100644 --- a/src/tools/miri/tests/pass/concurrency/sync_singlethread.rs +++ b/src/tools/miri/tests/pass/concurrency/sync_singlethread.rs @@ -1,6 +1,5 @@ use std::hint; -use std::sync::atomic; -use std::sync::{Mutex, TryLockError}; +use std::sync::{Mutex, TryLockError, atomic}; fn main() { test_mutex_stdlib(); diff --git a/src/tools/miri/tests/pass/coroutine.rs b/src/tools/miri/tests/pass/coroutine.rs index 7822c136d91b6..9ec9b1fc5bc48 100644 --- a/src/tools/miri/tests/pass/coroutine.rs +++ b/src/tools/miri/tests/pass/coroutine.rs @@ -4,10 +4,8 @@ use std::fmt::Debug; use std::mem::ManuallyDrop; -use std::ops::{ - Coroutine, - CoroutineState::{self, *}, -}; +use std::ops::Coroutine; +use std::ops::CoroutineState::{self, *}; use std::pin::Pin; use std::ptr; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/src/tools/miri/tests/pass/dyn-arbitrary-self.rs b/src/tools/miri/tests/pass/dyn-arbitrary-self.rs index dc2b9e4491b27..d993e5ad68ccc 100644 --- a/src/tools/miri/tests/pass/dyn-arbitrary-self.rs +++ b/src/tools/miri/tests/pass/dyn-arbitrary-self.rs @@ -21,7 +21,9 @@ fn pin_box_dyn() { } fn stdlib_pointers() { - use std::{pin::Pin, rc::Rc, sync::Arc}; + use std::pin::Pin; + use std::rc::Rc; + use std::sync::Arc; trait Trait { fn by_rc(self: Rc) -> i64; @@ -60,10 +62,8 @@ fn stdlib_pointers() { } fn pointers_and_wrappers() { - use std::{ - marker::Unsize, - ops::{CoerceUnsized, Deref, DispatchFromDyn}, - }; + use std::marker::Unsize; + use std::ops::{CoerceUnsized, Deref, DispatchFromDyn}; struct Ptr(Box); diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs index 0cfcd532ff4b2..b5feac8c67726 100644 --- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs +++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs @@ -1,7 +1,5 @@ -use std::mem; -use std::num; -use std::ptr; use std::rc::Rc; +use std::{mem, num, ptr}; #[derive(Copy, Clone, Default)] struct Zst; diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index cd606a5282a98..f560669dd6351 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -11,7 +11,8 @@ #![allow(incomplete_features, internal_features)] use std::intrinsics::simd as intrinsics; use std::ptr; -use std::simd::{StdFloat, prelude::*}; +use std::simd::StdFloat; +use std::simd::prelude::*; extern "rust-intrinsic" { #[rustc_nounwind] diff --git a/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs index c46031de2d84f..123fe6ed6425c 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs @@ -2,7 +2,8 @@ // This test is the result of minimizing the `emplacable` crate to reproduce // . -use std::{ops::FnMut, ptr::Pointee}; +use std::ops::FnMut; +use std::ptr::Pointee; pub type EmplacerFn<'a, T> = dyn for<'b> FnMut(::Metadata) + 'a; diff --git a/src/tools/miri/tests/pass/leak-in-static.rs b/src/tools/miri/tests/pass/leak-in-static.rs index 9523394408806..6f08b64ada45f 100644 --- a/src/tools/miri/tests/pass/leak-in-static.rs +++ b/src/tools/miri/tests/pass/leak-in-static.rs @@ -1,7 +1,5 @@ -use std::{ - ptr, - sync::atomic::{AtomicPtr, Ordering}, -}; +use std::ptr; +use std::sync::atomic::{AtomicPtr, Ordering}; static mut LEAKER: Option>> = None; diff --git a/src/tools/miri/tests/pass/ptr_int_casts.rs b/src/tools/miri/tests/pass/ptr_int_casts.rs index 684e8f6ec7bdd..4e274f62981d1 100644 --- a/src/tools/miri/tests/pass/ptr_int_casts.rs +++ b/src/tools/miri/tests/pass/ptr_int_casts.rs @@ -2,8 +2,7 @@ // Tree Borrows doesn't support int2ptr casts, but let's make sure we don't immediately crash either. //@[tree]compile-flags: -Zmiri-tree-borrows //@[stack]compile-flags: -Zmiri-permissive-provenance -use std::mem; -use std::ptr; +use std::{mem, ptr}; fn eq_ref(x: &T, y: &T) -> bool { x as *const _ == y as *const _ diff --git a/src/tools/miri/tests/pass/shims/env/var.rs b/src/tools/miri/tests/pass/shims/env/var.rs index a576c1fc8bb8c..655b29674e340 100644 --- a/src/tools/miri/tests/pass/shims/env/var.rs +++ b/src/tools/miri/tests/pass/shims/env/var.rs @@ -1,6 +1,5 @@ //@compile-flags: -Zmiri-preemption-rate=0 -use std::env; -use std::thread; +use std::{env, thread}; fn main() { // Test that miri environment is isolated when communication is disabled. diff --git a/src/tools/miri/tests/pass/shims/windows-threadname.rs b/src/tools/miri/tests/pass/shims/windows-threadname.rs index 29c3fa5d5f3aa..f1c05fa37905b 100644 --- a/src/tools/miri/tests/pass/shims/windows-threadname.rs +++ b/src/tools/miri/tests/pass/shims/windows-threadname.rs @@ -1,9 +1,8 @@ //@only-target: windows # this directly tests windows-only functions +use core::ffi::c_void; use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; - -use core::ffi::c_void; type HANDLE = *mut c_void; type PWSTR = *mut u16; type PCWSTR = *const u16; diff --git a/src/tools/miri/tests/pass/slices.rs b/src/tools/miri/tests/pass/slices.rs index 459d04d6761c1..39e1d1cb97f26 100644 --- a/src/tools/miri/tests/pass/slices.rs +++ b/src/tools/miri/tests/pass/slices.rs @@ -6,8 +6,7 @@ #![feature(layout_for_ptr)] #![feature(strict_provenance)] -use std::ptr; -use std::slice; +use std::{ptr, slice}; fn slice_of_zst() { fn foo(v: &[T]) -> Option<&[T]> { diff --git a/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs b/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs index 259fc72d3929e..72e360fe19a1f 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs @@ -2,10 +2,8 @@ // this fails when Stacked Borrows is strictly applied even to `!Unpin` types. #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] -use std::{ - ops::{Coroutine, CoroutineState}, - pin::Pin, -}; +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; fn firstn() -> impl Coroutine { #[coroutine] diff --git a/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs b/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs index 9c9db4d1b3b53..1478b21d6c180 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/stack-printing.rs @@ -3,10 +3,8 @@ //@compile-flags: -Zmiri-permissive-provenance -Zmiri-provenance-gc=0 #![feature(strict_provenance)] -use std::{ - alloc::{self, Layout}, - mem::ManuallyDrop, -}; +use std::alloc::{self, Layout}; +use std::mem::ManuallyDrop; extern "Rust" { fn miri_get_alloc_id(ptr: *const u8) -> u64; diff --git a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs index c741e4de6d5b1..b9d5ca06ed058 100644 --- a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs +++ b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs @@ -3,8 +3,7 @@ //@[uniq]compile-flags: -Zmiri-unique-is-unique #![feature(allocator_api)] -use std::mem; -use std::ptr; +use std::{mem, ptr}; fn main() { aliasing_read_only_mutable_refs(); diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 2430140eea1d4..9553a37c9a82f 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -1,8 +1,9 @@ +use std::env; use std::ffi::OsString; use std::num::NonZero; use std::path::{Path, PathBuf}; +use std::process::Command; use std::sync::OnceLock; -use std::{env, process::Command}; use colored::*; use regex::bytes::Regex; From 762a91b40e9f37f1b649f3a879e4639af32da2ed Mon Sep 17 00:00:00 2001 From: Asuna Date: Tue, 24 Sep 2024 01:28:02 +0800 Subject: [PATCH 1080/1301] Simplify negative `Option::{is_some_and,is_none_or}` --- clippy_config/src/msrvs.rs | 1 + clippy_lints/src/booleans.rs | 78 ++++++++++--- clippy_lints/src/lib.rs | 2 +- tests/ui/nonminimal_bool_methods.fixed | 62 ++++++++++ tests/ui/nonminimal_bool_methods.rs | 62 ++++++++++ tests/ui/nonminimal_bool_methods.stderr | 110 +++++++++++++++++- tests/ui/nonminimal_bool_methods_unfixable.rs | 9 ++ .../nonminimal_bool_methods_unfixable.stderr | 17 +++ 8 files changed, 321 insertions(+), 20 deletions(-) create mode 100644 tests/ui/nonminimal_bool_methods_unfixable.rs create mode 100644 tests/ui/nonminimal_bool_methods_unfixable.stderr diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index e30df3d32341a..68a3b11d3848f 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -19,6 +19,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,83,0 { CONST_EXTERN_FN } 1,83,0 { CONST_FLOAT_BITS_CONV } + 1,82,0 { IS_NONE_OR } 1,81,0 { LINT_REASONS_STABILIZATION } 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 3c2af72624f60..160f0c6cf100e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,3 +1,5 @@ +use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::eq_expr_value; use clippy_utils::source::SpanRangeExt; @@ -7,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; -use rustc_session::declare_lint_pass; +use rustc_session::{RustcVersion, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; @@ -69,9 +71,25 @@ declare_clippy_lint! { } // For each pairs, both orders are considered. -const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")]; +const METHODS_WITH_NEGATION: [(Option, &str, &str); 3] = [ + (None, "is_some", "is_none"), + (None, "is_err", "is_ok"), + (Some(msrvs::IS_NONE_OR), "is_some_and", "is_none_or"), +]; + +pub struct NonminimalBool { + msrv: Msrv, +} + +impl NonminimalBool { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} -declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); +impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); impl<'tcx> LateLintPass<'tcx> for NonminimalBool { fn check_fn( @@ -83,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { _: Span, _: LocalDefId, ) { - NonminimalBoolVisitor { cx }.visit_body(body); + NonminimalBoolVisitor { cx, msrv: &self.msrv }.visit_body(body); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -100,6 +118,8 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { _ => {}, } } + + extract_msrv_attr!(LateContext); } fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> { @@ -176,11 +196,11 @@ fn check_inverted_bool_in_condition( ); } -fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) { +fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) { if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind && !expr.span.from_expansion() && !inner.span.from_expansion() - && let Some(suggestion) = simplify_not(cx, inner) + && let Some(suggestion) = simplify_not(cx, msrv, inner) && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow { span_lint_and_sugg( @@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) { struct NonminimalBoolVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + msrv: &'a Msrv, } use quine_mc_cluskey::Bool; @@ -289,6 +310,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { struct SuggestContext<'a, 'tcx, 'v> { terminals: &'v [&'v Expr<'v>], cx: &'a LateContext<'tcx>, + msrv: &'a Msrv, output: String, } @@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { }, Term(n) => { let terminal = self.terminals[n as usize]; - if let Some(str) = simplify_not(self.cx, terminal) { + if let Some(str) = simplify_not(self.cx, self.msrv, terminal) { self.output.push_str(&str); } else { self.output.push('!'); @@ -358,7 +380,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { } } -fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Option { match &expr.kind { ExprKind::Binary(binop, lhs, rhs) => { if !implements_ord(cx, lhs) { @@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { Some(format!("{lhs_snippet}{op}{rhs_snippet}")) }) }, - ExprKind::MethodCall(path, receiver, [], _) => { + ExprKind::MethodCall(path, receiver, args, _) => { let type_of_receiver = cx.typeck_results().expr_ty(receiver); if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option) && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result) @@ -399,21 +421,41 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { METHODS_WITH_NEGATION .iter() .copied() - .flat_map(|(a, b)| vec![(a, b), (b, a)]) - .find(|&(a, _)| { - let path: &str = path.ident.name.as_str(); - a == path + .flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)]) + .find(|&(msrv, a, _)| msrv.is_none_or(|msrv| curr_msrv.meets(msrv)) && a == path.ident.name.as_str()) + .and_then(|(_, _, neg_method)| { + let negated_args = args + .iter() + .map(|arg| simplify_not(cx, curr_msrv, arg)) + .collect::>>()? + .join(", "); + Some(format!( + "{}.{neg_method}({negated_args})", + receiver.span.get_source_text(cx)? + )) }) - .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?))) }, + ExprKind::Closure(closure) => { + let body = cx.tcx.hir().body(closure.body); + let params = body + .params + .iter() + .map(|param| param.span.get_source_text(cx).map(|t| t.to_string())) + .collect::>>()? + .join(", "); + let negated = simplify_not(cx, curr_msrv, body.value)?; + Some(format!("|{params}| {negated}")) + }, + ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()), _ => None, } } -fn suggest(cx: &LateContext<'_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String { +fn suggest(cx: &LateContext<'_>, msrv: &Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String { let mut suggest_context = SuggestContext { terminals, cx, + msrv, output: String::new(), }; suggest_context.recurse(suggestion); @@ -526,7 +568,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { diag.span_suggestion( e.span, "it would look like the following", - suggest(self.cx, suggestion, &h2q.terminals), + suggest(self.cx, self.msrv, suggestion, &h2q.terminals), // nonminimal_bool can produce minimal but // not human readable expressions (#3141) Applicability::Unspecified, @@ -569,12 +611,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { } }; if improvements.is_empty() { - check_simplify_not(self.cx, e); + check_simplify_not(self.cx, self.msrv, e); } else { nonminimal_bool_lint( improvements .into_iter() - .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals)) + .map(|suggestion| suggest(self.cx, self.msrv, suggestion, &h2q.terminals)) .collect(), ); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1d41f568f3785..2eb6d99b761f7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -609,7 +609,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))); store.register_late_pass(|_| Box::new(serde_api::SerdeApi)); store.register_late_pass(move |_| Box::new(types::Types::new(conf))); - store.register_late_pass(|_| Box::new(booleans::NonminimalBool)); + store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf))); store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); store.register_late_pass(|_| Box::new(float_literal::FloatLiteral)); store.register_late_pass(|_| Box::new(ptr::Ptr)); diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed index cc91ba6ec66e1..a23310c1ad9fd 100644 --- a/tests/ui/nonminimal_bool_methods.fixed +++ b/tests/ui/nonminimal_bool_methods.fixed @@ -115,4 +115,66 @@ fn issue_12625() { if a as u64 > b {} //~ ERROR: this boolean expression can be simplified } +fn issue_13436() { + fn not_zero(x: i32) -> bool { + x != 0 + } + + let opt = Some(500); + _ = opt.is_some_and(|x| x < 1000); + _ = opt.is_some_and(|x| x <= 1000); + _ = opt.is_some_and(|x| x > 1000); + _ = opt.is_some_and(|x| x >= 1000); + _ = opt.is_some_and(|x| x == 1000); + _ = opt.is_some_and(|x| x != 1000); + _ = opt.is_some_and(not_zero); + _ = opt.is_none_or(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x > 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x < 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x != 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x == 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(not_zero); + _ = opt.is_none_or(|x| x < 1000); + _ = opt.is_none_or(|x| x <= 1000); + _ = opt.is_none_or(|x| x > 1000); + _ = opt.is_none_or(|x| x >= 1000); + _ = opt.is_none_or(|x| x == 1000); + _ = opt.is_none_or(|x| x != 1000); + _ = opt.is_none_or(not_zero); + _ = opt.is_some_and(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_some_and(|x| x > 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_some_and(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_some_and(|x| x < 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_some_and(|x| x != 1000); //~ ERROR: this boolean expression can be simplified + _ = opt.is_some_and(|x| x == 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(not_zero); + + let opt = Some(true); + _ = opt.is_some_and(|x| x); + _ = opt.is_some_and(|x| !x); + _ = !opt.is_some_and(|x| x); + _ = opt.is_none_or(|x| x); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x); + _ = opt.is_none_or(|x| !x); + _ = !opt.is_none_or(|x| x); + _ = opt.is_some_and(|x| x); //~ ERROR: this boolean expression can be simplified + + let opt: Option> = Some(Ok(123)); + _ = opt.is_some_and(|x| x.is_ok()); + _ = opt.is_some_and(|x| x.is_err()); + _ = opt.is_none_or(|x| x.is_ok()); + _ = opt.is_none_or(|x| x.is_err()); + _ = opt.is_none_or(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified + _ = opt.is_some_and(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified + _ = opt.is_some_and(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified + + #[clippy::msrv = "1.81"] + fn before_stabilization() { + let opt = Some(500); + _ = !opt.is_some_and(|x| x < 1000); + } +} + fn main() {} diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs index c812f6f0ca4f5..6c844373af709 100644 --- a/tests/ui/nonminimal_bool_methods.rs +++ b/tests/ui/nonminimal_bool_methods.rs @@ -115,4 +115,66 @@ fn issue_12625() { if !(a as u64 <= b) {} //~ ERROR: this boolean expression can be simplified } +fn issue_13436() { + fn not_zero(x: i32) -> bool { + x != 0 + } + + let opt = Some(500); + _ = opt.is_some_and(|x| x < 1000); + _ = opt.is_some_and(|x| x <= 1000); + _ = opt.is_some_and(|x| x > 1000); + _ = opt.is_some_and(|x| x >= 1000); + _ = opt.is_some_and(|x| x == 1000); + _ = opt.is_some_and(|x| x != 1000); + _ = opt.is_some_and(not_zero); + _ = !opt.is_some_and(|x| x < 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(|x| x > 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(|x| x == 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(|x| x != 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(not_zero); + _ = opt.is_none_or(|x| x < 1000); + _ = opt.is_none_or(|x| x <= 1000); + _ = opt.is_none_or(|x| x > 1000); + _ = opt.is_none_or(|x| x >= 1000); + _ = opt.is_none_or(|x| x == 1000); + _ = opt.is_none_or(|x| x != 1000); + _ = opt.is_none_or(not_zero); + _ = !opt.is_none_or(|x| x < 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(|x| x > 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(|x| x == 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(|x| x != 1000); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(not_zero); + + let opt = Some(true); + _ = opt.is_some_and(|x| x); + _ = opt.is_some_and(|x| !x); + _ = !opt.is_some_and(|x| x); + _ = !opt.is_some_and(|x| !x); //~ ERROR: this boolean expression can be simplified + _ = opt.is_none_or(|x| x); + _ = opt.is_none_or(|x| !x); + _ = !opt.is_none_or(|x| x); + _ = !opt.is_none_or(|x| !x); //~ ERROR: this boolean expression can be simplified + + let opt: Option> = Some(Ok(123)); + _ = opt.is_some_and(|x| x.is_ok()); + _ = opt.is_some_and(|x| x.is_err()); + _ = opt.is_none_or(|x| x.is_ok()); + _ = opt.is_none_or(|x| x.is_err()); + _ = !opt.is_some_and(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_some_and(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified + _ = !opt.is_none_or(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified + + #[clippy::msrv = "1.81"] + fn before_stabilization() { + let opt = Some(500); + _ = !opt.is_some_and(|x| x < 1000); + } +} + fn main() {} diff --git a/tests/ui/nonminimal_bool_methods.stderr b/tests/ui/nonminimal_bool_methods.stderr index d7adc0638b373..52803e828aed0 100644 --- a/tests/ui/nonminimal_bool_methods.stderr +++ b/tests/ui/nonminimal_bool_methods.stderr @@ -97,5 +97,113 @@ error: this boolean expression can be simplified LL | if !(a as u64 <= b) {} | ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b` -error: aborting due to 16 previous errors +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:131:9 + | +LL | _ = !opt.is_some_and(|x| x < 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x >= 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:132:9 + | +LL | _ = !opt.is_some_and(|x| x <= 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x > 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:133:9 + | +LL | _ = !opt.is_some_and(|x| x > 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x <= 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:134:9 + | +LL | _ = !opt.is_some_and(|x| x >= 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x < 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:135:9 + | +LL | _ = !opt.is_some_and(|x| x == 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x != 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:136:9 + | +LL | _ = !opt.is_some_and(|x| x != 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x == 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:145:9 + | +LL | _ = !opt.is_none_or(|x| x < 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x >= 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:146:9 + | +LL | _ = !opt.is_none_or(|x| x <= 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x > 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:147:9 + | +LL | _ = !opt.is_none_or(|x| x > 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x <= 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:148:9 + | +LL | _ = !opt.is_none_or(|x| x >= 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x < 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:149:9 + | +LL | _ = !opt.is_none_or(|x| x == 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x != 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:150:9 + | +LL | _ = !opt.is_none_or(|x| x != 1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x == 1000)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:157:9 + | +LL | _ = !opt.is_some_and(|x| !x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:161:9 + | +LL | _ = !opt.is_none_or(|x| !x); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:168:9 + | +LL | _ = !opt.is_some_and(|x| x.is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_err())` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:169:9 + | +LL | _ = !opt.is_some_and(|x| x.is_err()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_ok())` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:170:9 + | +LL | _ = !opt.is_none_or(|x| x.is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_err())` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:171:9 + | +LL | _ = !opt.is_none_or(|x| x.is_err()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_ok())` + +error: aborting due to 34 previous errors diff --git a/tests/ui/nonminimal_bool_methods_unfixable.rs b/tests/ui/nonminimal_bool_methods_unfixable.rs new file mode 100644 index 0000000000000..60b8da30a2f13 --- /dev/null +++ b/tests/ui/nonminimal_bool_methods_unfixable.rs @@ -0,0 +1,9 @@ +#![warn(clippy::nonminimal_bool)] +//@no-rustfix + +fn issue_13436() { + let opt_opt = Some(Some(500)); + _ = !opt_opt.is_some_and(|x| !x.is_some_and(|y| y != 1000)); //~ ERROR: this boolean expression can be simplified +} + +fn main() {} diff --git a/tests/ui/nonminimal_bool_methods_unfixable.stderr b/tests/ui/nonminimal_bool_methods_unfixable.stderr new file mode 100644 index 0000000000000..5a90155844cd3 --- /dev/null +++ b/tests/ui/nonminimal_bool_methods_unfixable.stderr @@ -0,0 +1,17 @@ +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods_unfixable.rs:6:9 + | +LL | _ = !opt_opt.is_some_and(|x| !x.is_some_and(|y| y != 1000)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt_opt.is_none_or(|x| x.is_some_and(|y| y != 1000))` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods_unfixable.rs:6:34 + | +LL | _ = !opt_opt.is_some_and(|x| !x.is_some_and(|y| y != 1000)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_none_or(|y| y == 1000)` + +error: aborting due to 2 previous errors + From 5726000b2de7052915e808e42a75d6b60b3db540 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sun, 29 Sep 2024 15:15:29 +0000 Subject: [PATCH 1081/1301] Copy correct path to clipboard for modules/keywords/primitives --- src/librustdoc/html/static/js/main.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 0d556e0a68274..984b0877d8de2 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1828,11 +1828,15 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } but.onclick = () => { - const path = []; - onEachLazy(document.querySelectorAll(".rustdoc-breadcrumbs a"), a => { - path.push(a.textContent); - }); - path.push(document.querySelector("title").textContent.split(" ")[0]); + // Most page titles are ' in - Rust', except + // modules (which don't have the first part) and keywords/primitives + // (which don't have a module path) + const title = document.querySelector("title").textContent.replace(" - Rust", ""); + const [item, module] = title.split(" in "); + const path = [item]; + if (module !== undefined) { + path.unshift(module); + } copyContentToClipboard(path.join("::")); copyButtonAnimation(but); From 6d1a25ad7e8ce026a0a2b0c85046afc81ef529ba Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Mon, 30 Sep 2024 03:40:48 +0800 Subject: [PATCH 1082/1301] preserve brackets around if-lets and skip while-lets --- compiler/rustc_lint/src/if_let_rescope.rs | 29 +++++++++++++++++++++-- tests/ui/drop/lint-if-let-rescope.fixed | 15 +++++++++++- tests/ui/drop/lint-if-let-rescope.rs | 15 +++++++++++- tests/ui/drop/lint-if-let-rescope.stderr | 22 ++++++++++++++++- 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 229d0c3642174..8cb63d5cb0542 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -122,7 +122,11 @@ impl IfLetRescope { } let tcx = cx.tcx; let source_map = tcx.sess.source_map(); - let expr_end = expr.span.shrink_to_hi(); + let expr_end = match expr.kind { + hir::ExprKind::If(_cond, conseq, None) => conseq.span.shrink_to_hi(), + hir::ExprKind::If(_cond, _conseq, Some(alt)) => alt.span.shrink_to_hi(), + _ => return, + }; let mut add_bracket_to_match_head = match_head_needs_bracket(tcx, expr); let mut significant_droppers = vec![]; let mut lifetime_ends = vec![]; @@ -145,7 +149,10 @@ impl IfLetRescope { recovered: Recovered::No, }) = cond.kind { - let if_let_pat = expr.span.shrink_to_lo().between(init.span); + // Peel off round braces + let if_let_pat = source_map + .span_take_while(expr.span, |&ch| ch == '(' || ch.is_whitespace()) + .between(init.span); // The consequent fragment is always a block. let before_conseq = conseq.span.shrink_to_lo(); let lifetime_end = source_map.end_point(conseq.span); @@ -159,6 +166,8 @@ impl IfLetRescope { if ty_ascription.is_some() || !expr.span.can_be_used_for_suggestions() || !pat.span.can_be_used_for_suggestions() + || !if_let_pat.can_be_used_for_suggestions() + || !before_conseq.can_be_used_for_suggestions() { // Our `match` rewrites does not support type ascription, // so we just bail. @@ -240,6 +249,22 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope { if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) { return; } + if let hir::ExprKind::Loop(block, _label, hir::LoopSource::While, _span) = expr.kind + && let Some(value) = block.expr + && let hir::ExprKind::If(cond, _conseq, _alt) = value.kind + && let hir::ExprKind::Let(..) = cond.kind + { + // Recall that `while let` is lowered into this: + // ``` + // loop { + // if let .. { body } else { break; } + // } + // ``` + // There is no observable from the `{ break; }` block so the edition change + // means nothing substantial to this `while` statement. + self.skip.insert(value.hir_id); + return; + } if expr_parent_is_stmt(cx.tcx, expr.hir_id) && matches!(expr.kind, hir::ExprKind::If(_cond, _conseq, None)) { diff --git a/tests/ui/drop/lint-if-let-rescope.fixed b/tests/ui/drop/lint-if-let-rescope.fixed index f228783f88bc5..d579ce9823691 100644 --- a/tests/ui/drop/lint-if-let-rescope.fixed +++ b/tests/ui/drop/lint-if-let-rescope.fixed @@ -2,7 +2,7 @@ #![deny(if_let_rescope)] #![feature(if_let_rescope)] -#![allow(irrefutable_let_patterns)] +#![allow(irrefutable_let_patterns, unused_parens)] fn droppy() -> Droppy { Droppy @@ -68,4 +68,17 @@ fn main() { //~| HELP: the value is now dropped here in Edition 2024 //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } + + if (match droppy().get() { Some(_value) => { true } _ => { false }}) { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| HELP: the value is now dropped here in Edition 2024 + // do something + } + + while let Some(_value) = droppy().get() { + // Should not lint + break; + } } diff --git a/tests/ui/drop/lint-if-let-rescope.rs b/tests/ui/drop/lint-if-let-rescope.rs index 241fb897fce17..aab293cdcfc77 100644 --- a/tests/ui/drop/lint-if-let-rescope.rs +++ b/tests/ui/drop/lint-if-let-rescope.rs @@ -2,7 +2,7 @@ #![deny(if_let_rescope)] #![feature(if_let_rescope)] -#![allow(irrefutable_let_patterns)] +#![allow(irrefutable_let_patterns, unused_parens)] fn droppy() -> Droppy { Droppy @@ -68,4 +68,17 @@ fn main() { //~| HELP: the value is now dropped here in Edition 2024 //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } + + if (if let Some(_value) = droppy().get() { true } else { false }) { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| HELP: the value is now dropped here in Edition 2024 + // do something + } + + while let Some(_value) = droppy().get() { + // Should not lint + break; + } } diff --git a/tests/ui/drop/lint-if-let-rescope.stderr b/tests/ui/drop/lint-if-let-rescope.stderr index 25ca3cf1ca873..50801ae166ff0 100644 --- a/tests/ui/drop/lint-if-let-rescope.stderr +++ b/tests/ui/drop/lint-if-let-rescope.stderr @@ -131,5 +131,25 @@ help: a `match` with a single arm can preserve the drop order up to Edition 2021 LL | if let () = { match Droppy.get() { Some(_value) => {} _ => {}} } { | ~~~~~ +++++++++++++++++ ++++++++ -error: aborting due to 5 previous errors +error: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/lint-if-let-rescope.rs:72:12 + | +LL | if (if let Some(_value) = droppy().get() { true } else { false }) { + | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: the value is now dropped here in Edition 2024 + --> $DIR/lint-if-let-rescope.rs:72:53 + | +LL | if (if let Some(_value) = droppy().get() { true } else { false }) { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL | if (match droppy().get() { Some(_value) => { true } _ => { false }}) { + | ~~~~~ +++++++++++++++++ ~~~~ + + +error: aborting due to 6 previous errors From 8c0adc6716ff1c5b1ef3de0e8972d45cb1053b27 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 23:08:25 +0200 Subject: [PATCH 1083/1301] update lockfile --- Cargo.lock | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e9808c806884..7a9701a9cad53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "chrono-tz" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" dependencies = [ "chrono", "chrono-tz-build", @@ -443,12 +443,11 @@ dependencies = [ [[package]] name = "chrono-tz-build" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" dependencies = [ "parse-zoneinfo", - "phf", "phf_codegen", ] @@ -3119,9 +3118,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2471f8f296262437d7e848e527b4210b44a96e53a3b4435b890227ce3e6da106" +checksum = "d6d984a9db43148467059309bd1e5ad577085162f695d9fe2cf3543aeb25cd38" dependencies = [ "anyhow", "rustc_version", From 54e4761d0526cd314140f1899c54129270295f5e Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 29 Sep 2024 18:21:47 -0400 Subject: [PATCH 1084/1301] Address review comment https://github.com/rust-lang/rust-clippy/pull/13286#discussion_r1780038602 --- clippy_lints/src/lifetimes.rs | 38 ++++++++++++----------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 37d244ff53d30..034cab7202523 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -506,8 +506,17 @@ struct LifetimeChecker<'cx, 'tcx, F> { } impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { - fn new(cx: &'cx LateContext<'tcx>, def_ids: Vec) -> LifetimeChecker<'cx, 'tcx, F> { - let map = def_ids.into_iter().map(|def_id| (def_id, Vec::new())).collect(); + fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'_>) -> LifetimeChecker<'cx, 'tcx, F> { + let map = generics + .params + .iter() + .filter_map(|par| match par.kind { + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit, + } => Some((par.def_id, Vec::new())), + _ => None, + }) + .collect(); Self { cx, map, @@ -556,17 +565,7 @@ where } fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { - let def_ids = generics - .params - .iter() - .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { - kind: LifetimeParamKind::Explicit, - } => Some(par.def_id), - _ => None, - }) - .collect(); - let mut checker = LifetimeChecker::::new(cx, def_ids); + let mut checker = LifetimeChecker::::new(cx, generics); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); @@ -587,18 +586,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, } fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { - let def_ids = impl_ - .generics - .params - .iter() - .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { - kind: LifetimeParamKind::Explicit, - } => Some(par.def_id), - _ => None, - }) - .collect(); - let mut checker = LifetimeChecker::::new(cx, def_ids); + let mut checker = LifetimeChecker::::new(cx, impl_.generics); walk_generics(&mut checker, impl_.generics); if let Some(ref trait_ref) = impl_.of_trait { From bc2a913a9bb89b137a43cc761793ae70886dfd14 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 29 Sep 2024 18:27:33 -0400 Subject: [PATCH 1085/1301] add has_enzyme/needs-enzyme to the test infra --- src/bootstrap/src/core/build_steps/test.rs | 4 ++++ src/tools/compiletest/src/command-list.rs | 1 + src/tools/compiletest/src/common.rs | 3 +++ src/tools/compiletest/src/header.rs | 3 +++ src/tools/compiletest/src/header/needs.rs | 5 +++++ src/tools/compiletest/src/lib.rs | 3 +++ 6 files changed, 19 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 870fe6a9f1658..7283b0e9574c5 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1792,6 +1792,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--host").arg(&*compiler.host.triple); cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.build)); + if builder.build.config.llvm_enzyme { + cmd.arg("--has-enzyme"); + } + if builder.config.cmd.bless() { cmd.arg("--bless"); } diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index 865aa76ddb0c7..a4cedbf66e2ad 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -139,6 +139,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-deterministic-layouts", "needs-dlltool", "needs-dynamic-linking", + "needs-enzyme", "needs-force-clang-based-tests", "needs-git-hash", "needs-llvm-components", diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 2d8c0c3fa5eb6..adc89cad72f89 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -349,6 +349,9 @@ pub struct Config { /// whether to run `tidy` when a rustdoc test fails pub has_tidy: bool, + /// whether to run `enzyme` autodiff tests + pub has_enzyme: bool, + /// The current Rust channel pub channel: String, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 6a889d277934b..83a10c5620864 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -218,6 +218,8 @@ pub struct TestProps { pub filecheck_flags: Vec, /// Don't automatically insert any `--check-cfg` args pub no_auto_check_cfg: bool, + /// Run tests which require enzyme being build + pub has_enzyme: bool, } mod directives { @@ -322,6 +324,7 @@ impl TestProps { llvm_cov_flags: vec![], filecheck_flags: vec![], no_auto_check_cfg: false, + has_enzyme: false, } } diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index 99c0e850f1a24..f5dd722ed3711 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -79,6 +79,11 @@ pub(super) fn handle_needs( condition: cache.sanitizer_safestack, ignore_reason: "ignored on targets without SafeStack support", }, + Need { + name: "needs-enzyme", + condition: config.has_enzyme, + ignore_reason: "ignored when LLVM Enzyme is disabled", + }, Need { name: "needs-run-enabled", condition: config.run_enabled(), diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index cfc619f934208..a8355ee9590d6 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -83,6 +83,7 @@ pub fn parse_config(args: Vec) -> Config { ) .optopt("", "run", "whether to execute run-* tests", "auto | always | never") .optflag("", "ignored", "run tests marked as ignored") + .optflag("", "has-enzyme", "run tests that require enzyme") .optflag("", "with-debug-assertions", "whether to run tests with `ignore-debug` header") .optmulti( "", @@ -233,6 +234,7 @@ pub fn parse_config(args: Vec) -> Config { // Avoid spawning an external command when we know tidy won't be used. false }; + let has_enzyme = matches.opt_present("has-enzyme"); let filters = if mode == Mode::RunMake { matches .free @@ -331,6 +333,7 @@ pub fn parse_config(args: Vec) -> Config { .map(|s| s.parse().expect("invalid --compare-mode provided")), rustfix_coverage: matches.opt_present("rustfix-coverage"), has_tidy, + has_enzyme, channel: matches.opt_str("channel").unwrap(), git_hash: matches.opt_present("git-hash"), edition: matches.opt_str("edition"), From c5598d6a9e0e8a46775f2c69e1278efee8223425 Mon Sep 17 00:00:00 2001 From: Barrett Ray Date: Sun, 29 Sep 2024 15:28:11 -0500 Subject: [PATCH 1086/1301] fix(hir_analysis/wfcheck): don't leak {type error} avoid `{type error}` being leaked in user-facing messages, particularly when using the `adt_const_params` feature --- .../rustc_hir_analysis/src/check/wfcheck.rs | 17 ++-- .../suggest_feature_only_when_possible.stderr | 18 ++-- .../const-param-elided-lifetime.full.stderr | 8 +- .../const-param-elided-lifetime.min.stderr | 90 ++----------------- .../const-param-elided-lifetime.rs | 5 -- ...ram-type-depends-on-const-param.min.stderr | 4 +- .../const-generics/default-ty-closure.stderr | 2 +- .../float-generic.simple.stderr | 2 +- .../fn-const-param-call.min.stderr | 4 +- .../fn-const-param-infer.min.stderr | 2 +- ...ay-size-in-generic-struct-param.min.stderr | 2 +- .../generic_const_exprs/error_in_ty.stderr | 2 +- .../unevaluated-const-ice-119731.stderr | 2 +- .../ice-118285-fn-ptr-value.stderr | 2 +- ...ics-type_name-as-const-argument.min.stderr | 2 +- .../issues/issue-56445-1.min.stderr | 18 +--- .../ui/const-generics/issues/issue-56445-1.rs | 1 - .../issues/issue-62878.min.stderr | 2 +- .../issues/issue-63322-forbid-dyn.min.stderr | 2 +- .../issues/issue-68366.full.stderr | 2 +- .../issues/issue-68366.min.stderr | 2 +- .../issues/issue-68615-adt.min.stderr | 2 +- .../issues/issue-68615-array.min.stderr | 2 +- .../issues/issue-71169.min.stderr | 2 +- .../issues/issue-71381.min.stderr | 4 +- .../issues/issue-71382.min.stderr | 2 +- .../issues/issue-71611.min.stderr | 2 +- .../issues/issue-72352.min.stderr | 2 +- .../issues/issue-73491.min.stderr | 2 +- ...tic-reference-array-const-param.min.stderr | 2 +- .../issues/issue-74101.min.stderr | 4 +- .../issues/issue-74255.min.stderr | 2 +- .../issues/issue-74950.min.stderr | 8 +- .../issues/issue-75047.min.stderr | 2 +- .../const-generics/lifetime-in-const-param.rs | 1 - .../lifetime-in-const-param.stderr | 14 +-- .../min_const_generics/complex-types.stderr | 14 +-- .../ui/const-generics/nested-type.min.stderr | 2 +- .../const-generics/not_wf_param_in_rpitit.rs | 1 - .../not_wf_param_in_rpitit.stderr | 16 +--- tests/ui/const-generics/opaque_types.stderr | 2 +- .../projection-as-arg-const.stderr | 2 +- .../raw-ptr-const-param-deref.min.stderr | 4 +- .../raw-ptr-const-param.min.stderr | 2 +- .../slice-const-param-mismatch.min.stderr | 4 +- .../std/const-generics-range.min.stderr | 12 +-- ...te-const-param-static-reference.min.stderr | 2 +- .../type-dependent/issue-71348.min.stderr | 4 +- .../type-dependent/issue-71382.stderr | 2 +- tests/ui/consts/issue-103790.rs | 1 - tests/ui/consts/issue-103790.stderr | 16 +--- .../feature-gate-adt_const_params.stderr | 2 +- .../feature-gate-unsized-const-params.stderr | 2 +- .../generic-const-items/elided-lifetimes.rs | 1 - .../elided-lifetimes.stderr | 20 +---- .../wfcheck_err_leak_issue_118179.rs | 7 ++ .../wfcheck_err_leak_issue_118179.stderr | 11 +++ .../ui/lifetimes/unusual-rib-combinations.rs | 2 - .../lifetimes/unusual-rib-combinations.stderr | 32 +------ .../effects/mismatched_generic_args.stderr | 6 +- ...oj-ty-as-type-of-const-issue-125757.stderr | 2 +- ...st-region-infer-to-static-in-binder.stderr | 4 +- .../const_generic_type.infer.stderr | 2 +- .../const_generic_type.no_infer.stderr | 2 +- .../ice-unexpected-region-123863.stderr | 4 +- ...ce-hir-wf-check-anon-const-issue-122199.rs | 2 - ...ir-wf-check-anon-const-issue-122199.stderr | 44 +++------ ...ir-wf-check-anon-const-issue-122989.stderr | 4 +- 68 files changed, 147 insertions(+), 324 deletions(-) create mode 100644 tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.rs create mode 100644 tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.stderr diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index a71e14ce463f6..70b0b3f5788ed 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -961,13 +961,20 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), hir_ty.span, "using raw pointers as const generic parameters is forbidden", ), - _ => tcx.dcx().struct_span_err( - hir_ty.span, - format!("`{}` is forbidden as the type of a const generic parameter", ty), - ), + _ => { + // Avoid showing "{type error}" to users. See #118179. + ty.error_reported()?; + + tcx.dcx().struct_span_err( + hir_ty.span, + format!( + "`{ty}` is forbidden as the type of a const generic parameter", + ), + ) + } }; - diag.note("the only supported types are integers, `bool` and `char`"); + diag.note("the only supported types are integers, `bool`, and `char`"); let cause = ObligationCause::misc(hir_ty.span, param.def_id); let adt_const_params_feature_string = diff --git a/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr b/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr index aafc0640dd2da..8c54aef36cac0 100644 --- a/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr +++ b/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr @@ -4,7 +4,7 @@ error: `&'static mut ()` is forbidden as the type of a const generic parameter LL | fn uwu_0() {} | ^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: `&'static u32` is forbidden as the type of a const generic parameter --> $DIR/suggest_feature_only_when_possible.rs:15:19 @@ -12,7 +12,7 @@ error: `&'static u32` is forbidden as the type of a const generic parameter LL | fn owo_0() {} | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -28,7 +28,7 @@ error: `Meow` is forbidden as the type of a const generic parameter LL | fn meow_0() {} | ^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -40,7 +40,7 @@ error: `&'static Meow` is forbidden as the type of a const generic parameter LL | fn meow_1() {} | ^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -56,7 +56,7 @@ error: `[Meow; 100]` is forbidden as the type of a const generic parameter LL | fn meow_2() {} | ^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: `(Meow, u8)` is forbidden as the type of a const generic parameter --> $DIR/suggest_feature_only_when_possible.rs:29:20 @@ -64,7 +64,7 @@ error: `(Meow, u8)` is forbidden as the type of a const generic parameter LL | fn meow_3() {} | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: `(Meow, String)` is forbidden as the type of a const generic parameter --> $DIR/suggest_feature_only_when_possible.rs:34:20 @@ -72,7 +72,7 @@ error: `(Meow, String)` is forbidden as the type of a const generic parameter LL | fn meow_4() {} | ^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: `String` is forbidden as the type of a const generic parameter --> $DIR/suggest_feature_only_when_possible.rs:38:19 @@ -80,7 +80,7 @@ error: `String` is forbidden as the type of a const generic parameter LL | fn nya_0() {} | ^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: `Vec` is forbidden as the type of a const generic parameter --> $DIR/suggest_feature_only_when_possible.rs:40:19 @@ -88,7 +88,7 @@ error: `Vec` is forbidden as the type of a const generic parameter LL | fn nya_1>() {} | ^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 9 previous errors diff --git a/tests/ui/const-generics/const-param-elided-lifetime.full.stderr b/tests/ui/const-generics/const-param-elided-lifetime.full.stderr index d6753a74f8574..34510f546c874 100644 --- a/tests/ui/const-generics/const-param-elided-lifetime.full.stderr +++ b/tests/ui/const-generics/const-param-elided-lifetime.full.stderr @@ -5,25 +5,25 @@ LL | struct A; | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:14:15 + --> $DIR/const-param-elided-lifetime.rs:13:15 | LL | impl A { | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:17:21 + --> $DIR/const-param-elided-lifetime.rs:15:21 | LL | fn foo(&self) {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:22:15 + --> $DIR/const-param-elided-lifetime.rs:19:15 | LL | impl B for A {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:26:17 + --> $DIR/const-param-elided-lifetime.rs:22:17 | LL | fn bar() {} | ^ explicit lifetime name needed here diff --git a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr index 62267224738d0..34510f546c874 100644 --- a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr +++ b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr @@ -5,109 +5,29 @@ LL | struct A; | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:14:15 + --> $DIR/const-param-elided-lifetime.rs:13:15 | LL | impl A { | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:17:21 + --> $DIR/const-param-elided-lifetime.rs:15:21 | LL | fn foo(&self) {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:22:15 + --> $DIR/const-param-elided-lifetime.rs:19:15 | LL | impl B for A {} | ^ explicit lifetime name needed here error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:26:17 + --> $DIR/const-param-elided-lifetime.rs:22:17 | LL | fn bar() {} | ^ explicit lifetime name needed here -error: `&u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:9:19 - | -LL | struct A; - | ^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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: `&u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:14:15 - | -LL | impl A { - | ^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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: `&u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:22:15 - | -LL | impl B for A {} - | ^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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: `&u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:26:17 - | -LL | fn bar() {} - | ^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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: `&u8` is forbidden as the type of a const generic parameter - --> $DIR/const-param-elided-lifetime.rs:17:21 - | -LL | fn foo(&self) {} - | ^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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 10 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0637`. diff --git a/tests/ui/const-generics/const-param-elided-lifetime.rs b/tests/ui/const-generics/const-param-elided-lifetime.rs index e75073de98d30..b1cdb6a46c552 100644 --- a/tests/ui/const-generics/const-param-elided-lifetime.rs +++ b/tests/ui/const-generics/const-param-elided-lifetime.rs @@ -8,23 +8,18 @@ struct A; //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&u8` is forbidden trait B {} impl A { //~^ ERROR `&` without an explicit lifetime name cannot be used here - //[min]~^^ ERROR `&u8` is forbidden fn foo(&self) {} //~^ ERROR `&` without an explicit lifetime name cannot be used here - //[min]~^^ ERROR `&u8` is forbidden } impl B for A {} //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&u8` is forbidden fn bar() {} //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&u8` is forbidden fn main() {} diff --git a/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr b/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr index fcc86b9ac33f2..18b8996326782 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr +++ b/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr @@ -20,7 +20,7 @@ error: `[u8; N]` is forbidden as the type of a const generic parameter LL | pub struct Dependent([(); N]); | ^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -32,7 +32,7 @@ error: `[u8; N]` is forbidden as the type of a const generic parameter LL | pub struct SelfDependent; | ^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/default-ty-closure.stderr b/tests/ui/const-generics/default-ty-closure.stderr index 9c737c1a19def..3f6ca9aea4ee8 100644 --- a/tests/ui/const-generics/default-ty-closure.stderr +++ b/tests/ui/const-generics/default-ty-closure.stderr @@ -4,7 +4,7 @@ error: using function pointers as const generic parameters is forbidden LL | struct X; | ^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/float-generic.simple.stderr b/tests/ui/const-generics/float-generic.simple.stderr index 2999bce32d6ad..abc5e7c5a9926 100644 --- a/tests/ui/const-generics/float-generic.simple.stderr +++ b/tests/ui/const-generics/float-generic.simple.stderr @@ -4,7 +4,7 @@ error: `f32` is forbidden as the type of a const generic parameter LL | fn foo() {} | ^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/fn-const-param-call.min.stderr b/tests/ui/const-generics/fn-const-param-call.min.stderr index d37766b28c9ce..b5b809d278703 100644 --- a/tests/ui/const-generics/fn-const-param-call.min.stderr +++ b/tests/ui/const-generics/fn-const-param-call.min.stderr @@ -4,7 +4,7 @@ error: using function pointers as const generic parameters is forbidden LL | struct Wrapper u32>; | ^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: using function pointers as const generic parameters is forbidden --> $DIR/fn-const-param-call.rs:15:15 @@ -12,7 +12,7 @@ error: using function pointers as const generic parameters is forbidden LL | impl u32> Wrapper { | ^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/fn-const-param-infer.min.stderr b/tests/ui/const-generics/fn-const-param-infer.min.stderr index 4da503d344a1a..5e08f71a26701 100644 --- a/tests/ui/const-generics/fn-const-param-infer.min.stderr +++ b/tests/ui/const-generics/fn-const-param-infer.min.stderr @@ -4,7 +4,7 @@ error: using function pointers as const generic parameters is forbidden LL | struct Checked bool>; | ^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error[E0308]: mismatched types --> $DIR/fn-const-param-infer.rs:33:25 diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr index 1f67a5c09f134..a8b6f06ab1c2f 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr @@ -22,7 +22,7 @@ error: `Config` is forbidden as the type of a const generic parameter LL | struct B { | ^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/generic_const_exprs/error_in_ty.stderr b/tests/ui/const-generics/generic_const_exprs/error_in_ty.stderr index 0e40255bcf59b..d822fa5894a5b 100644 --- a/tests/ui/const-generics/generic_const_exprs/error_in_ty.stderr +++ b/tests/ui/const-generics/generic_const_exprs/error_in_ty.stderr @@ -12,7 +12,7 @@ error: `[usize; x]` is forbidden as the type of a const generic parameter LL | pub struct A {} | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr index 15d3c47258525..45be31c7ba35f 100644 --- a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr +++ b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr @@ -66,7 +66,7 @@ error: `[[usize; v4]; v4]` is forbidden as the type of a const generic parameter LL | pub struct v17 { | ^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/ice-118285-fn-ptr-value.stderr b/tests/ui/const-generics/ice-118285-fn-ptr-value.stderr index 46a8a975d50d7..4ff386c81635a 100644 --- a/tests/ui/const-generics/ice-118285-fn-ptr-value.stderr +++ b/tests/ui/const-generics/ice-118285-fn-ptr-value.stderr @@ -4,7 +4,7 @@ error: using function pointers as const generic parameters is forbidden LL | struct Checked bool>; | ^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr b/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr index 2a6d9f533174e..506f7d05fa63a 100644 --- a/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr +++ b/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr @@ -13,7 +13,7 @@ error: `&'static str` is forbidden as the type of a const generic parameter LL | trait Trait {} | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-56445-1.min.stderr b/tests/ui/const-generics/issues/issue-56445-1.min.stderr index ff0a1bfc0b555..86eb57355bd61 100644 --- a/tests/ui/const-generics/issues/issue-56445-1.min.stderr +++ b/tests/ui/const-generics/issues/issue-56445-1.min.stderr @@ -6,22 +6,6 @@ LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); | = note: lifetime parameters may not be used in the type of const parameters -error: `&str` is forbidden as the type of a const generic parameter - --> $DIR/issue-56445-1.rs:9:25 - | -LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); - | ^^^^^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-56445-1.rs b/tests/ui/const-generics/issues/issue-56445-1.rs index 53aab40b0ad4a..681e0e147cfc8 100644 --- a/tests/ui/const-generics/issues/issue-56445-1.rs +++ b/tests/ui/const-generics/issues/issue-56445-1.rs @@ -8,6 +8,5 @@ use std::marker::PhantomData; struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); //~^ ERROR: the type of const parameters must not depend on other generic parameters -//[min]~| ERROR: `&str` is forbidden as the type of a const generic parameter impl Bug<'_, ""> {} diff --git a/tests/ui/const-generics/issues/issue-62878.min.stderr b/tests/ui/const-generics/issues/issue-62878.min.stderr index 5205726d73845..bd17d70a50ba5 100644 --- a/tests/ui/const-generics/issues/issue-62878.min.stderr +++ b/tests/ui/const-generics/issues/issue-62878.min.stderr @@ -12,7 +12,7 @@ error: `[u8; N]` is forbidden as the type of a const generic parameter LL | fn foo() {} | ^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr index 101ca456cd90f..f14485a4976e7 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr @@ -4,7 +4,7 @@ error: `&'static (dyn A + 'static)` is forbidden as the type of a const generic LL | fn test() { | ^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-68366.full.stderr b/tests/ui/const-generics/issues/issue-68366.full.stderr index 3363a895e4725..caed3c1bf3f7d 100644 --- a/tests/ui/const-generics/issues/issue-68366.full.stderr +++ b/tests/ui/const-generics/issues/issue-68366.full.stderr @@ -4,7 +4,7 @@ error: `Option` is forbidden as the type of a const generic parameter LL | struct Collatz>; | ^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-68366.min.stderr b/tests/ui/const-generics/issues/issue-68366.min.stderr index 276f91e76dd42..10b5a06682f53 100644 --- a/tests/ui/const-generics/issues/issue-68366.min.stderr +++ b/tests/ui/const-generics/issues/issue-68366.min.stderr @@ -13,7 +13,7 @@ error: `Option` is forbidden as the type of a const generic parameter LL | struct Collatz>; | ^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-68615-adt.min.stderr b/tests/ui/const-generics/issues/issue-68615-adt.min.stderr index 2f95eef98c01e..d25b34435ede8 100644 --- a/tests/ui/const-generics/issues/issue-68615-adt.min.stderr +++ b/tests/ui/const-generics/issues/issue-68615-adt.min.stderr @@ -4,7 +4,7 @@ error: `[usize; 0]` is forbidden as the type of a const generic parameter LL | struct Const {} | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-68615-array.min.stderr b/tests/ui/const-generics/issues/issue-68615-array.min.stderr index 6d18f8195d242..60cbc9b4eab14 100644 --- a/tests/ui/const-generics/issues/issue-68615-array.min.stderr +++ b/tests/ui/const-generics/issues/issue-68615-array.min.stderr @@ -4,7 +4,7 @@ error: `[usize; 0]` is forbidden as the type of a const generic parameter LL | struct Foo {} | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-71169.min.stderr b/tests/ui/const-generics/issues/issue-71169.min.stderr index 94d11f969ff8a..2ecbc3379516b 100644 --- a/tests/ui/const-generics/issues/issue-71169.min.stderr +++ b/tests/ui/const-generics/issues/issue-71169.min.stderr @@ -12,7 +12,7 @@ error: `[u8; LEN]` is forbidden as the type of a const generic parameter LL | fn foo() {} | ^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-71381.min.stderr b/tests/ui/const-generics/issues/issue-71381.min.stderr index e16d3b7a8a469..38d2cbe636831 100644 --- a/tests/ui/const-generics/issues/issue-71381.min.stderr +++ b/tests/ui/const-generics/issues/issue-71381.min.stderr @@ -20,7 +20,7 @@ error: using function pointers as const generic parameters is forbidden LL | pub fn call_me(&self) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: using function pointers as const generic parameters is forbidden --> $DIR/issue-71381.rs:23:19 @@ -28,7 +28,7 @@ error: using function pointers as const generic parameters is forbidden LL | const FN: unsafe extern "C" fn(Args), | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/issues/issue-71382.min.stderr b/tests/ui/const-generics/issues/issue-71382.min.stderr index 0c58b10c0b7c8..f70e1733a9770 100644 --- a/tests/ui/const-generics/issues/issue-71382.min.stderr +++ b/tests/ui/const-generics/issues/issue-71382.min.stderr @@ -4,7 +4,7 @@ error: using function pointers as const generic parameters is forbidden LL | fn test(&self) { | ^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issues/issue-71611.min.stderr b/tests/ui/const-generics/issues/issue-71611.min.stderr index b01936f4d2513..7252bfd1d6a72 100644 --- a/tests/ui/const-generics/issues/issue-71611.min.stderr +++ b/tests/ui/const-generics/issues/issue-71611.min.stderr @@ -12,7 +12,7 @@ error: using function pointers as const generic parameters is forbidden LL | fn func(outer: A) { | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/issues/issue-72352.min.stderr b/tests/ui/const-generics/issues/issue-72352.min.stderr index ede0faec7c67c..17ccfa8a0dabb 100644 --- a/tests/ui/const-generics/issues/issue-72352.min.stderr +++ b/tests/ui/const-generics/issues/issue-72352.min.stderr @@ -4,7 +4,7 @@ error: using function pointers as const generic parameters is forbidden LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const c_char) -> usize { | ^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issues/issue-73491.min.stderr b/tests/ui/const-generics/issues/issue-73491.min.stderr index 8fdd65894ef2b..2cdbeea2fd6e5 100644 --- a/tests/ui/const-generics/issues/issue-73491.min.stderr +++ b/tests/ui/const-generics/issues/issue-73491.min.stderr @@ -4,7 +4,7 @@ error: `[u32; LEN]` is forbidden as the type of a const generic parameter LL | fn hoge() {} | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr b/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr index cba03b1cb1f0f..256636c0628cd 100644 --- a/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr +++ b/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr @@ -4,7 +4,7 @@ error: `&'static [u32]` is forbidden as the type of a const generic parameter LL | fn a() {} | ^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-74101.min.stderr b/tests/ui/const-generics/issues/issue-74101.min.stderr index 236556addcefd..65fb51d7df99f 100644 --- a/tests/ui/const-generics/issues/issue-74101.min.stderr +++ b/tests/ui/const-generics/issues/issue-74101.min.stderr @@ -4,7 +4,7 @@ error: `[u8; 1 + 2]` is forbidden as the type of a const generic parameter LL | fn test() {} | ^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -16,7 +16,7 @@ error: `[u8; 1 + 2]` is forbidden as the type of a const generic parameter LL | struct Foo; | ^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-74255.min.stderr b/tests/ui/const-generics/issues/issue-74255.min.stderr index 800902860a763..3b30227a9a673 100644 --- a/tests/ui/const-generics/issues/issue-74255.min.stderr +++ b/tests/ui/const-generics/issues/issue-74255.min.stderr @@ -4,7 +4,7 @@ error: `IceEnum` is forbidden as the type of a const generic parameter LL | fn ice_struct_fn() {} | ^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/issues/issue-74950.min.stderr b/tests/ui/const-generics/issues/issue-74950.min.stderr index 086176d9959c7..22537af786b14 100644 --- a/tests/ui/const-generics/issues/issue-74950.min.stderr +++ b/tests/ui/const-generics/issues/issue-74950.min.stderr @@ -4,7 +4,7 @@ error: `Inner` is forbidden as the type of a const generic parameter LL | struct Outer; | ^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -16,7 +16,7 @@ error: `Inner` is forbidden as the type of a const generic parameter LL | struct Outer; | ^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | @@ -29,7 +29,7 @@ error: `Inner` is forbidden as the type of a const generic parameter LL | struct Outer; | ^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | @@ -42,7 +42,7 @@ error: `Inner` is forbidden as the type of a const generic parameter LL | struct Outer; | ^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | diff --git a/tests/ui/const-generics/issues/issue-75047.min.stderr b/tests/ui/const-generics/issues/issue-75047.min.stderr index f2cc76b9bed03..d78ab6718201a 100644 --- a/tests/ui/const-generics/issues/issue-75047.min.stderr +++ b/tests/ui/const-generics/issues/issue-75047.min.stderr @@ -4,7 +4,7 @@ error: `[u8; Bar::::value()]` is forbidden as the type of a const generic p LL | struct Foo::value()]>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/lifetime-in-const-param.rs b/tests/ui/const-generics/lifetime-in-const-param.rs index be90dbb213eef..ce6b52b229f4b 100644 --- a/tests/ui/const-generics/lifetime-in-const-param.rs +++ b/tests/ui/const-generics/lifetime-in-const-param.rs @@ -4,6 +4,5 @@ struct S2<'b>(&'b ()); struct S<'a, const N: S2>(&'a ()); //~^ ERROR missing lifetime specifier [E0106] -//~| ERROR `S2<'_>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/lifetime-in-const-param.stderr b/tests/ui/const-generics/lifetime-in-const-param.stderr index 4096725c52a1b..7c9d27c1e284e 100644 --- a/tests/ui/const-generics/lifetime-in-const-param.stderr +++ b/tests/ui/const-generics/lifetime-in-const-param.stderr @@ -4,18 +4,6 @@ error[E0106]: missing lifetime specifier LL | struct S<'a, const N: S2>(&'a ()); | ^^ expected named lifetime parameter -error: `S2<'_>` is forbidden as the type of a const generic parameter - --> $DIR/lifetime-in-const-param.rs:5:23 - | -LL | struct S<'a, const N: S2>(&'a ()); - | ^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(adt_const_params)] - | - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/const-generics/min_const_generics/complex-types.stderr b/tests/ui/const-generics/min_const_generics/complex-types.stderr index 0211770f9e51b..bca68982c399d 100644 --- a/tests/ui/const-generics/min_const_generics/complex-types.stderr +++ b/tests/ui/const-generics/min_const_generics/complex-types.stderr @@ -4,7 +4,7 @@ error: `[u8; 0]` is forbidden as the type of a const generic parameter LL | struct Foo; | ^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -16,7 +16,7 @@ error: `()` is forbidden as the type of a const generic parameter LL | struct Bar; | ^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -28,7 +28,7 @@ error: `No` is forbidden as the type of a const generic parameter LL | struct Fez; | ^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -40,7 +40,7 @@ error: `&'static u8` is forbidden as the type of a const generic parameter LL | struct Faz; | ^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -56,7 +56,7 @@ error: `!` is forbidden as the type of a const generic parameter LL | struct Fiz; | ^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: `()` is forbidden as the type of a const generic parameter --> $DIR/complex-types.rs:20:19 @@ -64,7 +64,7 @@ error: `()` is forbidden as the type of a const generic parameter LL | enum Goo { A, B } | ^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -76,7 +76,7 @@ error: `()` is forbidden as the type of a const generic parameter LL | union Boo { a: () } | ^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/nested-type.min.stderr b/tests/ui/const-generics/nested-type.min.stderr index 0da2b30e3f1a9..8696be3faf37b 100644 --- a/tests/ui/const-generics/nested-type.min.stderr +++ b/tests/ui/const-generics/nested-type.min.stderr @@ -29,7 +29,7 @@ LL | | LL | | }]>; | |__^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/not_wf_param_in_rpitit.rs b/tests/ui/const-generics/not_wf_param_in_rpitit.rs index 5471dc9022f90..eb672194340e8 100644 --- a/tests/ui/const-generics/not_wf_param_in_rpitit.rs +++ b/tests/ui/const-generics/not_wf_param_in_rpitit.rs @@ -6,7 +6,6 @@ trait Trait { //~| ERROR: the trait `Trait` cannot be made into an object //~| ERROR: the trait `Trait` cannot be made into an object //~| ERROR: the trait `Trait` cannot be made into an object - //~| ERROR: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter //~| ERROR: trait objects must include the `dyn` keyword async fn a() {} } diff --git a/tests/ui/const-generics/not_wf_param_in_rpitit.stderr b/tests/ui/const-generics/not_wf_param_in_rpitit.stderr index 82e251f1306c2..ade40550c7389 100644 --- a/tests/ui/const-generics/not_wf_param_in_rpitit.stderr +++ b/tests/ui/const-generics/not_wf_param_in_rpitit.stderr @@ -25,7 +25,7 @@ LL | trait Trait { | ^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/not_wf_param_in_rpitit.rs:11:14 + --> $DIR/not_wf_param_in_rpitit.rs:10:14 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -48,7 +48,7 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/not_wf_param_in_rpitit.rs:11:14 + --> $DIR/not_wf_param_in_rpitit.rs:10:14 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -64,14 +64,6 @@ help: alternatively, consider constraining `a` so it does not apply to trait obj LL | async fn a() where Self: Sized {} | +++++++++++++++++ -error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/not_wf_param_in_rpitit.rs:3:22 - | -LL | trait Trait { - | ^^^^^ - | - = note: the only supported types are integers, `bool` and `char` - error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/not_wf_param_in_rpitit.rs:3:13 | @@ -79,7 +71,7 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/not_wf_param_in_rpitit.rs:11:14 + --> $DIR/not_wf_param_in_rpitit.rs:10:14 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -107,7 +99,7 @@ help: add `dyn` keyword before this trait LL | trait Trait { | +++ -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0038, E0391, E0425, E0782. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/opaque_types.stderr b/tests/ui/const-generics/opaque_types.stderr index 3947d645fcbe2..847f1da16f612 100644 --- a/tests/ui/const-generics/opaque_types.stderr +++ b/tests/ui/const-generics/opaque_types.stderr @@ -74,7 +74,7 @@ error: `Foo` is forbidden as the type of a const generic parameter LL | fn foo() {} | ^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error[E0391]: cycle detected when computing type of opaque `Foo::{opaque#0}` --> $DIR/opaque_types.rs:3:12 diff --git a/tests/ui/const-generics/projection-as-arg-const.stderr b/tests/ui/const-generics/projection-as-arg-const.stderr index 88672bce0a7ee..f6e7620db7d9b 100644 --- a/tests/ui/const-generics/projection-as-arg-const.stderr +++ b/tests/ui/const-generics/projection-as-arg-const.stderr @@ -4,7 +4,7 @@ error: `::Identity` is forbidden as the type of a const generic LL | pub fn foo::Identity>() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/raw-ptr-const-param-deref.min.stderr b/tests/ui/const-generics/raw-ptr-const-param-deref.min.stderr index 6027dbb01cddb..fca2966e3a30a 100644 --- a/tests/ui/const-generics/raw-ptr-const-param-deref.min.stderr +++ b/tests/ui/const-generics/raw-ptr-const-param-deref.min.stderr @@ -4,7 +4,7 @@ error: using raw pointers as const generic parameters is forbidden LL | struct Const; | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: using raw pointers as const generic parameters is forbidden --> $DIR/raw-ptr-const-param-deref.rs:13:15 @@ -12,7 +12,7 @@ error: using raw pointers as const generic parameters is forbidden LL | impl Const

{ | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/raw-ptr-const-param.min.stderr b/tests/ui/const-generics/raw-ptr-const-param.min.stderr index c48eea069e0da..5694b12f2d52a 100644 --- a/tests/ui/const-generics/raw-ptr-const-param.min.stderr +++ b/tests/ui/const-generics/raw-ptr-const-param.min.stderr @@ -4,7 +4,7 @@ error: using raw pointers as const generic parameters is forbidden LL | struct Const; | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error[E0308]: mismatched types --> $DIR/raw-ptr-const-param.rs:11:40 diff --git a/tests/ui/const-generics/slice-const-param-mismatch.min.stderr b/tests/ui/const-generics/slice-const-param-mismatch.min.stderr index 3b2410c989477..594f8b9b79a33 100644 --- a/tests/ui/const-generics/slice-const-param-mismatch.min.stderr +++ b/tests/ui/const-generics/slice-const-param-mismatch.min.stderr @@ -4,7 +4,7 @@ error: `&'static str` is forbidden as the type of a const generic parameter LL | struct ConstString; | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -20,7 +20,7 @@ error: `&'static [u8]` is forbidden as the type of a const generic parameter LL | struct ConstBytes; | ^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/std/const-generics-range.min.stderr b/tests/ui/const-generics/std/const-generics-range.min.stderr index 67f137cf1a077..fd23b9b248a06 100644 --- a/tests/ui/const-generics/std/const-generics-range.min.stderr +++ b/tests/ui/const-generics/std/const-generics-range.min.stderr @@ -4,7 +4,7 @@ error: `std::ops::Range` is forbidden as the type of a const generic para LL | struct _Range>; | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -16,7 +16,7 @@ error: `RangeFrom` is forbidden as the type of a const generic parameter LL | struct _RangeFrom>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -28,7 +28,7 @@ error: `RangeFull` is forbidden as the type of a const generic parameter LL | struct _RangeFull; | ^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -40,7 +40,7 @@ error: `RangeInclusive` is forbidden as the type of a const generic param LL | struct _RangeInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -52,7 +52,7 @@ error: `RangeTo` is forbidden as the type of a const generic parameter LL | struct _RangeTo>; | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -64,7 +64,7 @@ error: `RangeToInclusive` is forbidden as the type of a const generic par LL | struct _RangeToInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr b/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr index cf236487cf0ec..911afa3391d16 100644 --- a/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr +++ b/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr @@ -4,7 +4,7 @@ error: `&'static ()` is forbidden as the type of a const generic parameter LL | struct Const; | ^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr index 5aee282952aa2..8995c4158635f 100644 --- a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr +++ b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr @@ -12,7 +12,7 @@ error: `&'static str` is forbidden as the type of a const generic parameter LL | trait Get<'a, const N: &'static str> { | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -28,7 +28,7 @@ error: `&'static str` is forbidden as the type of a const generic parameter LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/const-generics/type-dependent/issue-71382.stderr b/tests/ui/const-generics/type-dependent/issue-71382.stderr index 69fd6f1c7d53e..3830b1527c372 100644 --- a/tests/ui/const-generics/type-dependent/issue-71382.stderr +++ b/tests/ui/const-generics/type-dependent/issue-71382.stderr @@ -4,7 +4,7 @@ error: using function pointers as const generic parameters is forbidden LL | fn test u8>(&self) -> u8 { | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/consts/issue-103790.rs b/tests/ui/consts/issue-103790.rs index d19115ede74bf..869a43e4018b3 100644 --- a/tests/ui/consts/issue-103790.rs +++ b/tests/ui/consts/issue-103790.rs @@ -6,6 +6,5 @@ struct S; //~| ERROR missing generics for struct `S` //~| ERROR cycle detected when computing type of `S::S` //~| ERROR `()` is forbidden as the type of a const generic parameter -//~| ERROR `S<{const error}, {const error}>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/consts/issue-103790.stderr b/tests/ui/consts/issue-103790.stderr index c671f078cb5c0..1515fa60a5c08 100644 --- a/tests/ui/consts/issue-103790.stderr +++ b/tests/ui/consts/issue-103790.stderr @@ -42,25 +42,13 @@ error: `()` is forbidden as the type of a const generic parameter LL | struct S; | ^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] | -error: `S<{const error}, {const error}>` is forbidden as the type of a const generic parameter - --> $DIR/issue-103790.rs:4:32 - | -LL | struct S; - | ^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(adt_const_params)] - | - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0107, E0391, E0403. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr index 649e936888b22..18d514f8cb5f8 100644 --- a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr +++ b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr @@ -4,7 +4,7 @@ error: `&'static str` is forbidden as the type of a const generic parameter LL | struct Foo; | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr index 0a87f34f4f51f..85ca2f59cb639 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr @@ -4,7 +4,7 @@ error: `[u8]` is forbidden as the type of a const generic parameter LL | struct Foo; | ^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/generic-const-items/elided-lifetimes.rs b/tests/ui/generic-const-items/elided-lifetimes.rs index cca73e2e81e8d..90899de5af618 100644 --- a/tests/ui/generic-const-items/elided-lifetimes.rs +++ b/tests/ui/generic-const-items/elided-lifetimes.rs @@ -9,7 +9,6 @@ where const I: &str = ""; //~^ ERROR `&` without an explicit lifetime name cannot be used here -//~| ERROR `&str` is forbidden as the type of a const generic parameter const B>: () = (); //~ ERROR `'_` cannot be used here diff --git a/tests/ui/generic-const-items/elided-lifetimes.stderr b/tests/ui/generic-const-items/elided-lifetimes.stderr index 85807a1b631a6..2b543d02b5d0f 100644 --- a/tests/ui/generic-const-items/elided-lifetimes.stderr +++ b/tests/ui/generic-const-items/elided-lifetimes.stderr @@ -16,27 +16,11 @@ LL | const I: &str = ""; | ^ explicit lifetime name needed here error[E0637]: `'_` cannot be used here - --> $DIR/elided-lifetimes.rs:14:18 + --> $DIR/elided-lifetimes.rs:13:18 | LL | const B>: () = (); | ^^ `'_` is a reserved lifetime name -error: `&str` is forbidden as the type of a const generic parameter - --> $DIR/elided-lifetimes.rs:10:18 - | -LL | const I: &str = ""; - | ^^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0637`. diff --git a/tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.rs b/tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.rs new file mode 100644 index 0000000000000..856f7b622d7ef --- /dev/null +++ b/tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.rs @@ -0,0 +1,7 @@ +//! Regression test for #118179: `adt_const_params` feature shouldn't leak +//! `{type error}` in error messages. + +struct G>(T); +//~^ ERROR the type of const parameters must not depend on other generic parameters + +fn main() {} diff --git a/tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.stderr b/tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.stderr new file mode 100644 index 0000000000000..654004571dbbe --- /dev/null +++ b/tests/ui/generic-const-items/wfcheck_err_leak_issue_118179.stderr @@ -0,0 +1,11 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/wfcheck_err_leak_issue_118179.rs:4:26 + | +LL | struct G>(T); + | ^ the type must not depend on the parameter `T` + | + = note: type parameters may not be used in the type of const parameters + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/lifetimes/unusual-rib-combinations.rs b/tests/ui/lifetimes/unusual-rib-combinations.rs index 0708a00d37157..0e92b41ae1ee2 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.rs +++ b/tests/ui/lifetimes/unusual-rib-combinations.rs @@ -20,11 +20,9 @@ fn c() {} // Elided lifetime in path in ConstGeneric fn d() {} //~^ ERROR missing lifetime specifier -//~| ERROR `S<'_>` is forbidden as the type of a const generic parameter trait Foo<'a> {} struct Bar Foo<'a>)>; //~^ ERROR the type of const parameters must not depend on other generic parameters -//~| ERROR `&dyn for<'a> Foo<'a>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/lifetimes/unusual-rib-combinations.stderr b/tests/ui/lifetimes/unusual-rib-combinations.stderr index ebf6f6ca403cb..b7effdc8d61d0 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.stderr +++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr @@ -5,7 +5,7 @@ LL | fn d() {} | ^ expected named lifetime parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/unusual-rib-combinations.rs:26:22 + --> $DIR/unusual-rib-combinations.rs:25:22 | LL | struct Bar Foo<'a>)>; | ^^ the type must not depend on the parameter `'a` @@ -40,35 +40,7 @@ error[E0308]: mismatched types LL | fn a() -> [u8; foo()] { | ^^^^^ expected `usize`, found `()` -error: `S<'_>` is forbidden as the type of a const generic parameter - --> $DIR/unusual-rib-combinations.rs:21:15 - | -LL | fn d() {} - | ^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(adt_const_params)] - | - -error: `&dyn for<'a> Foo<'a>` is forbidden as the type of a const generic parameter - --> $DIR/unusual-rib-combinations.rs:26:21 - | -LL | struct Bar Foo<'a>)>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: the only supported types are integers, `bool` and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(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 8 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0106, E0214, E0308, E0770. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/mismatched_generic_args.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/mismatched_generic_args.stderr index 8c814295de465..8e12b40381fd2 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/mismatched_generic_args.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/mismatched_generic_args.stderr @@ -22,7 +22,7 @@ error: `Dimension` is forbidden as the type of a const generic parameter LL | pub struct Quantity(S); | ^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -40,7 +40,7 @@ error: `Dimension` is forbidden as the type of a const generic parameter LL | impl Add for Quantity {} | ^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -52,7 +52,7 @@ error: `Dimension` is forbidden as the type of a const generic parameter LL | pub fn add(x: Quantity) -> Quantity { | ^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr index 7094ee8c67ca0..8df76b296acd6 100644 --- a/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr +++ b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr @@ -4,7 +4,7 @@ error: `::Type` is forbidden as the type of a const generic parame LL | struct Wrapper::Type> {} | ^^^^^^^^^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: the constant `C` is not of type `::Type` --> $DIR/default-proj-ty-as-type-of-const-issue-125757.rs:15:22 diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr index e0cbee88aa100..377dfc8b52916 100644 --- a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr +++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr @@ -10,7 +10,7 @@ error: using function pointers as const generic parameters is forbidden LL | struct X; | ^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: using function pointers as const generic parameters is forbidden --> $DIR/const-region-infer-to-static-in-binder.rs:4:20 @@ -18,7 +18,7 @@ error: using function pointers as const generic parameters is forbidden LL | struct X; | ^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 3 previous errors diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr b/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr index 3b6999cabc416..b7999a695e7f3 100644 --- a/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr +++ b/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr @@ -4,7 +4,7 @@ error: `Bar` is forbidden as the type of a const generic parameter LL | async fn test() { | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr index 55a5a3d20006b..b526ab49d8d4a 100644 --- a/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr +++ b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr @@ -45,7 +45,7 @@ error: `Bar` is forbidden as the type of a const generic parameter LL | async fn test() { | ^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 4 previous errors diff --git a/tests/ui/typeck/ice-unexpected-region-123863.stderr b/tests/ui/typeck/ice-unexpected-region-123863.stderr index 0479f134a3897..742096f3861f6 100644 --- a/tests/ui/typeck/ice-unexpected-region-123863.stderr +++ b/tests/ui/typeck/ice-unexpected-region-123863.stderr @@ -4,7 +4,7 @@ error: `&'static str` is forbidden as the type of a const generic parameter LL | const fn concat_strs() -> &'static str { | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] @@ -20,7 +20,7 @@ error: `&'static str` is forbidden as the type of a const generic parameter LL | struct Inner; | ^^^^^^^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs index 3f43fbfc0cf41..53363319ba0e6 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs @@ -4,7 +4,6 @@ trait Trait { //~| ERROR the trait `Trait` cannot be made into an object //~| ERROR the trait `Trait` cannot be made into an object //~| ERROR the trait `Trait` cannot be made into an object - //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] @@ -15,7 +14,6 @@ trait Trait { //~| ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions //~| ERROR associated item referring to unboxed trait object for its own trait //~| ERROR the trait `Trait` cannot be made into an object - //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr index 6b309c223afff..fefb788fac79c 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr @@ -1,5 +1,5 @@ error[E0403]: the name `N` is already used for a generic parameter in this item's generic parameters - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:18 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:18 | LL | trait Trait { | - first use of `N` @@ -14,13 +14,13 @@ LL | trait Trait { | ^^^ not found in this scope error[E0423]: expected value, found builtin type `u32` - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:29 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:29 | LL | fn fnc(&self) -> Trait { | ^^^ not a value error[E0425]: cannot find value `bar` in this scope - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:25:9 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:23:9 | LL | bar | ^^^ not found in this scope @@ -54,13 +54,13 @@ LL | trait Trait { = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:12 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:12 | LL | fn fnc(&self) -> Trait { | ^^^^^^^^^^^^^^^^^^^^ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:21 | LL | fn fnc(&self) -> Trait { | ^^^^^ @@ -73,7 +73,7 @@ LL | fn fnc(&self) -> Trait { | +++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:44 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:44 | LL | fn fnc(&self) -> Trait { | ^^^^^ @@ -106,7 +106,7 @@ LL | trait Trait { | ^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -122,7 +122,7 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -131,16 +131,8 @@ LL | fn fnc(&self) -> Trait { | ^^^ ...because method `fnc` has generic type parameters = help: consider moving `fnc` to another trait -error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 - | -LL | trait Trait { - | ^^^^^ - | - = note: the only supported types are integers, `bool` and `char` - error: associated item referring to unboxed trait object for its own trait - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:44 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:44 | LL | trait Trait { | ----- in this trait @@ -154,7 +146,7 @@ LL | fn fnc(&self) -> Self { | ~~~~ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:21 | LL | fn fnc(&self) -> Trait { | ^^^^^ @@ -168,13 +160,13 @@ LL | fn fnc(&self) -> Trait { | +++ error[E0038]: the trait `Trait` cannot be made into an object - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:21 | LL | fn fnc(&self) -> Trait { | ^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -190,7 +182,7 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:11:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -200,15 +192,7 @@ LL | fn fnc(&self) -> Trait { = help: consider moving `fnc` to another trait = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 - | -LL | fn fnc(&self) -> Trait { - | ^^^^^ - | - = note: the only supported types are integers, `bool` and `char` - -error: aborting due to 13 previous errors; 5 warnings emitted +error: aborting due to 11 previous errors; 5 warnings emitted Some errors have detailed explanations: E0038, E0391, E0403, E0423, E0425. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr index d2b0e2d92e01f..f8905437c6ea6 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr @@ -73,7 +73,7 @@ error: `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic para LL | trait Foo> { | ^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:11 @@ -104,7 +104,7 @@ error: `(dyn Foo<2> + 'static)` is forbidden as the type of a const generic para LL | trait Bar> {} | ^^^^^^ | - = note: the only supported types are integers, `bool` and `char` + = note: the only supported types are integers, `bool`, and `char` error: aborting due to 5 previous errors; 2 warnings emitted From 4b8a5bd511814ed97640c46ab2232acc0e0ade41 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 26 Sep 2024 16:48:32 +0200 Subject: [PATCH 1087/1301] panic when an interpreter error gets unintentionally discarded --- .../rustc_const_eval/src/interpret/place.rs | 19 ++- .../src/interpret/validity.rs | 38 +++--- .../src/util/check_validity_requirement.rs | 4 +- .../rustc_middle/src/mir/interpret/error.rs | 61 +++++++++- .../rustc_middle/src/mir/interpret/mod.rs | 12 +- .../src/dataflow_const_prop.rs | 61 ++++++---- compiler/rustc_mir_transform/src/gvn.rs | 115 ++++++++++-------- .../rustc_mir_transform/src/jump_threading.rs | 53 +++++--- .../src/known_panics_lint.rs | 35 +++--- src/tools/miri/src/concurrency/data_race.rs | 2 +- src/tools/miri/src/diagnostics.rs | 6 +- src/tools/miri/src/intrinsics/simd.rs | 3 +- 12 files changed, 262 insertions(+), 147 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 32f90254a9470..05264d32c6ba8 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size}; use tracing::{instrument, trace}; use super::{ - AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, - Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance, - Scalar, alloc_range, mir_assign_valid_types, + AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, DiscardInterpError, ImmTy, Immediate, + InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, + Projectable, Provenance, Scalar, alloc_range, mir_assign_valid_types, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -490,9 +490,16 @@ where // If an access is both OOB and misaligned, we want to see the bounds error. // However we have to call `check_misalign` first to make the borrow checker happy. let misalign_err = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn); - let a = self.get_ptr_alloc_mut(mplace.ptr(), size)?; - misalign_err?; - Ok(a) + match self.get_ptr_alloc_mut(mplace.ptr(), size) { + Ok(a) => { + misalign_err?; + Ok(a) + } + Err(e) => { + misalign_err.discard_interp_err(); + Err(e) + } + } } /// Turn a local in the current frame into a place. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 203cceccd9d1f..3c9fdd73100c6 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -17,8 +17,8 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, - UnsupportedOpInfo, ValidationErrorInfo, alloc_range, + ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind, + Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -95,16 +95,19 @@ macro_rules! try_validation { Ok(x) => x, // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - Err(e) => match e.kind() { - $( - $($p)|+ => - throw_validation_failure!( - $where, - $kind - ) - ),+, - #[allow(unreachable_patterns)] - _ => Err::(e)?, + Err(e) => { + let (kind, backtrace) = e.into_parts(); + match kind { + $( + $($p)|+ => { + throw_validation_failure!( + $where, + $kind + ) + } + ),+, + _ => Err::(InterpErrorInfo::from_parts(kind, backtrace))?, + } } } }}; @@ -510,7 +513,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance { ptr_kind, // FIXME this says "null pointer" when null but we need translate - pointer: format!("{}", Pointer::>::from_addr_invalid(*i)) + pointer: format!("{}", Pointer::>::from_addr_invalid(i)) }, Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds { ptr_kind @@ -1231,7 +1234,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, Err(err) => { // For some errors we might be able to provide extra information. // (This custom logic does not fit the `try_validation!` macro.) - match err.kind() { + let (kind, backtrace) = err.into_parts(); + match kind { Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { // Some byte was uninitialized, determine which // element that byte belongs to so we can @@ -1242,7 +1246,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, .unwrap(); self.path.push(PathElem::ArrayElem(i)); - if matches!(err.kind(), Ub(InvalidUninitBytes(_))) { + if matches!(kind, Ub(InvalidUninitBytes(_))) { throw_validation_failure!(self.path, Uninit { expected }) } else { throw_validation_failure!(self.path, PointerAsInt { expected }) @@ -1250,7 +1254,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, } // Propagate upwards (that will also check for unexpected errors). - _ => return Err(err), + _ => return Err(InterpErrorInfo::from_parts(kind, backtrace)), } } } @@ -1282,7 +1286,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // It's not great to catch errors here, since we can't give a very good path, // but it's better than ICEing. Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => { - InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type: *expected_dyn_type } + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } }, ); } diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 19393188c9adf..421648d9e7b2a 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,4 +1,5 @@ use rustc_middle::bug; +use rustc_middle::mir::interpret::DiscardInterpError; use rustc_middle::ty::layout::{ HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement, }; @@ -75,7 +76,8 @@ fn check_validity_requirement_strict<'tcx>( /*recursive*/ false, /*reset_provenance_and_padding*/ false, ) - .is_ok()) + .discard_interp_err() + .is_some()) } /// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 46646e759d59e..06fdb9e1ecdee 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::backtrace::Backtrace; use std::borrow::Cow; -use std::fmt; +use std::{fmt, mem}; use either::Either; use rustc_ast_ir::Mutability; @@ -104,13 +104,57 @@ rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); /// These should always be constructed by calling `.into()` on /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*` /// macros for this. +/// +/// Interpreter errors must *not* be silently discarded (that will lead to a panic). Instead, +/// explicitly call `discard_interp_err` if this is really the right thing to do. Note that if +/// this happens during const-eval or in Miri, it could lead to a UB error being lost! #[derive(Debug)] pub struct InterpErrorInfo<'tcx>(Box>); +/// Calling `.ok()` on an `InterpResult` leads to a panic because of the guard. +/// To still let people opt-in to discarding interpreter errors, we have this extension trait. +pub trait DiscardInterpError { + type Output; + + fn discard_interp_err(self) -> Option; +} + +impl<'tcx, T> DiscardInterpError for InterpResult<'tcx, T> { + type Output = T; + + fn discard_interp_err(self) -> Option { + match self { + Ok(v) => Some(v), + Err(e) => { + // Disarm the guard. + mem::forget(e.0.guard); + None + } + } + } +} + +#[derive(Debug)] +struct Guard; + +impl Drop for Guard { + fn drop(&mut self) { + // We silence the guard if we are already panicking, to avoid double-panics. + if !std::thread::panicking() { + panic!( + "an interpreter error got improperly discarded; use `discard_interp_err()` instead of `ok()` if this is intentional" + ); + } + } +} + #[derive(Debug)] struct InterpErrorInfoInner<'tcx> { kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace, + /// This makes us panic on drop. This is used to catch + /// accidentally discarding an interpreter error. + guard: Guard, } #[derive(Debug)] @@ -151,15 +195,25 @@ impl InterpErrorBacktrace { impl<'tcx> InterpErrorInfo<'tcx> { pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { - let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; + let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace, guard }) = self; + mem::forget(guard); // The error got explicitly discarded right here. (kind, backtrace) } pub fn into_kind(self) -> InterpError<'tcx> { - let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self; + let InterpErrorInfo(box InterpErrorInfoInner { kind, guard, .. }) = self; + mem::forget(guard); // The error got explicitly discarded right here. kind } + pub fn discard_interp_err(self) { + mem::forget(self.0.guard); // The error got explicitly discarded right here. + } + + pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { + Self(Box::new(InterpErrorInfoInner { kind, backtrace, guard: Guard })) + } + #[inline] pub fn kind(&self) -> &InterpError<'tcx> { &self.0.kind @@ -191,6 +245,7 @@ impl<'tcx> From> for InterpErrorInfo<'tcx> { InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace: InterpErrorBacktrace::new(), + guard: Guard, })) } } diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index d7809cc4343d2..8ce6f9d230052 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -34,12 +34,12 @@ pub use self::allocation::{ InitChunkIter, alloc_range, }; pub use self::error::{ - BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, - EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, - InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo, - MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, - ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, - ValidationErrorKind, + BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, DiscardInterpError, ErrorHandled, + EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, + EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, + InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, + ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, + ValidationErrorInfo, ValidationErrorKind, }; pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance}; pub use self::value::Scalar; diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 88dc8e74a8c29..905143b2efbc2 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -3,7 +3,9 @@ //! Currently, this pass only propagates scalar values. use rustc_const_eval::const_eval::{DummyMachine, throw_machine_stop_str}; -use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable}; +use rustc_const_eval::interpret::{ + DiscardInterpError, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable, +}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_middle::bug; @@ -364,8 +366,10 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } Operand::Constant(box constant) => { - if let Ok(constant) = - self.ecx.eval_mir_constant(&constant.const_, constant.span, None) + if let Some(constant) = self + .ecx + .eval_mir_constant(&constant.const_, constant.span, None) + .discard_interp_err() { self.assign_constant(state, place, constant, &[]); } @@ -387,7 +391,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { for &(mut proj_elem) in projection { if let PlaceElem::Index(index) = proj_elem { if let FlatSet::Elem(index) = state.get(index.into(), &self.map) - && let Ok(offset) = index.to_target_usize(&self.tcx) + && let Some(offset) = index.to_target_usize(&self.tcx).discard_interp_err() && let Some(min_length) = offset.checked_add(1) { proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false }; @@ -395,35 +399,40 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return; } } - operand = if let Ok(operand) = self.ecx.project(&operand, proj_elem) { - operand - } else { - return; - } + operand = + if let Some(operand) = self.ecx.project(&operand, proj_elem).discard_interp_err() { + operand + } else { + return; + } } self.map.for_each_projection_value( place, operand, &mut |elem, op| match elem { - TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(), - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), + TrackElem::Field(idx) => { + self.ecx.project_field(op, idx.as_usize()).discard_interp_err() + } + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_interp_err(), TrackElem::Discriminant => { - let variant = self.ecx.read_discriminant(op).ok()?; - let discr_value = - self.ecx.discriminant_for_variant(op.layout.ty, variant).ok()?; + let variant = self.ecx.read_discriminant(op).discard_interp_err()?; + let discr_value = self + .ecx + .discriminant_for_variant(op.layout.ty, variant) + .discard_interp_err()?; Some(discr_value.into()) } TrackElem::DerefLen => { - let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into(); - let len_usize = op.len(&self.ecx).ok()?; + let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_interp_err()?.into(); + let len_usize = op.len(&self.ecx).discard_interp_err()?; let layout = self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).unwrap(); Some(ImmTy::from_uint(len_usize, layout).into()) } }, &mut |place, op| { - if let Ok(imm) = self.ecx.read_immediate_raw(op) + if let Some(imm) = self.ecx.read_immediate_raw(op).discard_interp_err() && let Some(imm) = imm.right() { let elem = self.wrap_immediate(*imm); @@ -447,11 +456,11 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom), // Both sides are known, do the actual computation. (FlatSet::Elem(left), FlatSet::Elem(right)) => { - match self.ecx.binary_op(op, &left, &right) { + match self.ecx.binary_op(op, &left, &right).discard_interp_err() { // Ideally this would return an Immediate, since it's sometimes // a pair and sometimes not. But as a hack we always return a pair // and just make the 2nd component `Bottom` when it does not exist. - Ok(val) => { + Some(val) => { if matches!(val.layout.abi, Abi::ScalarPair(..)) { let (val, overflow) = val.to_scalar_pair(); (FlatSet::Elem(val), FlatSet::Elem(overflow)) @@ -470,7 +479,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } let arg_scalar = const_arg.to_scalar(); - let Ok(arg_value) = arg_scalar.to_bits(layout.size) else { + let Some(arg_value) = arg_scalar.to_bits(layout.size).discard_interp_err() else { return (FlatSet::Top, FlatSet::Top); }; @@ -518,8 +527,10 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return None; } let enum_ty_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?; - let discr_value = - self.ecx.discriminant_for_variant(enum_ty_layout.ty, variant_index).ok()?; + let discr_value = self + .ecx + .discriminant_for_variant(enum_ty_layout.ty, variant_index) + .discard_interp_err()?; Some(discr_value.to_scalar()) } @@ -573,7 +584,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { map: &Map<'tcx>, ) -> Option> { let ty = place.ty(self.local_decls, self.patch.tcx).ty; - let layout = ecx.layout_of(ty).ok()?; + let layout = ecx.layout_of(ty).discard_interp_err()?; if layout.is_zst() { return Some(Const::zero_sized(ty)); @@ -595,7 +606,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { .intern_with_temp_alloc(layout, |ecx, dest| { try_write_constant(ecx, dest, place, ty, state, map) }) - .ok()?; + .discard_interp_err()?; return Some(Const::Val(ConstValue::Indirect { alloc_id, offset: Size::ZERO }, ty)); } @@ -830,7 +841,7 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> { if let PlaceElem::Index(local) = elem { let offset = self.before_effect.get(&(location, local.into()))?; let offset = offset.try_to_scalar()?; - let offset = offset.to_target_usize(&self.tcx).ok()?; + let offset = offset.to_target_usize(&self.tcx).discard_interp_err()?; let min_length = offset.checked_add(1)?; Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false }) } else { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index f735d08fca573..4dbcc6af9bde4 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -87,8 +87,8 @@ use std::borrow::Cow; use either::Either; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ - ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, - intern_const_alloc_for_constprop, + DiscardInterpError, ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, + Scalar, intern_const_alloc_for_constprop, }; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; @@ -393,7 +393,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Repeat(..) => return None, Constant { ref value, disambiguator: _ } => { - self.ecx.eval_mir_constant(value, DUMMY_SP, None).ok()? + self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_interp_err()? } Aggregate(kind, variant, ref fields) => { let fields = fields @@ -414,34 +414,39 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty, }; let variant = if ty.is_enum() { Some(variant) } else { None }; - let ty = self.ecx.layout_of(ty).ok()?; + let ty = self.ecx.layout_of(ty).discard_interp_err()?; if ty.is_zst() { ImmTy::uninit(ty).into() } else if matches!(kind, AggregateTy::RawPtr { .. }) { // Pointers don't have fields, so don't `project_field` them. - let data = self.ecx.read_pointer(fields[0]).ok()?; + let data = self.ecx.read_pointer(fields[0]).discard_interp_err()?; let meta = if fields[1].layout.is_zst() { MemPlaceMeta::None } else { - MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).ok()?) + MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_interp_err()?) }; let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); ImmTy::from_immediate(ptr_imm, ty).into() } else if matches!(ty.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) { - let dest = self.ecx.allocate(ty, MemoryKind::Stack).ok()?; + let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_interp_err()?; let variant_dest = if let Some(variant) = variant { - self.ecx.project_downcast(&dest, variant).ok()? + self.ecx.project_downcast(&dest, variant).discard_interp_err()? } else { dest.clone() }; for (field_index, op) in fields.into_iter().enumerate() { - let field_dest = self.ecx.project_field(&variant_dest, field_index).ok()?; - self.ecx.copy_op(op, &field_dest).ok()?; + let field_dest = self + .ecx + .project_field(&variant_dest, field_index) + .discard_interp_err()?; + self.ecx.copy_op(op, &field_dest).discard_interp_err()?; } - self.ecx.write_discriminant(variant.unwrap_or(FIRST_VARIANT), &dest).ok()?; + self.ecx + .write_discriminant(variant.unwrap_or(FIRST_VARIANT), &dest) + .discard_interp_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) - .ok()?; + .discard_interp_err()?; dest.into() } else { return None; @@ -467,7 +472,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // This should have been replaced by a `ConstantIndex` earlier. ProjectionElem::Index(_) => return None, }; - self.ecx.project(value, elem).ok()? + self.ecx.project(value, elem).discard_interp_err()? } Address { place, kind, provenance: _ } => { if !place.is_indirect_first_projection() { @@ -475,14 +480,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let local = self.locals[place.local]?; let pointer = self.evaluated[local].as_ref()?; - let mut mplace = self.ecx.deref_pointer(pointer).ok()?; + let mut mplace = self.ecx.deref_pointer(pointer).discard_interp_err()?; for proj in place.projection.iter().skip(1) { // We have no call stack to associate a local with a value, so we cannot // interpret indexing. if matches!(proj, ProjectionElem::Index(_)) { return None; } - mplace = self.ecx.project(&mplace, proj).ok()?; + mplace = self.ecx.project(&mplace, proj).discard_interp_err()?; } let pointer = mplace.to_ref(&self.ecx); let ty = match kind { @@ -494,26 +499,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ), AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), }; - let layout = self.ecx.layout_of(ty).ok()?; + let layout = self.ecx.layout_of(ty).discard_interp_err()?; ImmTy::from_immediate(pointer, layout).into() } Discriminant(base) => { let base = self.evaluated[base].as_ref()?; - let variant = self.ecx.read_discriminant(base).ok()?; - let discr_value = - self.ecx.discriminant_for_variant(base.layout.ty, variant).ok()?; + let variant = self.ecx.read_discriminant(base).discard_interp_err()?; + let discr_value = self + .ecx + .discriminant_for_variant(base.layout.ty, variant) + .discard_interp_err()?; discr_value.into() } Len(slice) => { let slice = self.evaluated[slice].as_ref()?; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); - let len = slice.len(&self.ecx).ok()?; + let len = slice.len(&self.ecx).discard_interp_err()?; let imm = ImmTy::from_uint(len, usize_layout); imm.into() } NullaryOp(null_op, ty) => { - let layout = self.ecx.layout_of(ty).ok()?; + let layout = self.ecx.layout_of(ty).discard_interp_err()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op && layout.is_unsized() { @@ -535,36 +542,36 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } UnaryOp(un_op, operand) => { let operand = self.evaluated[operand].as_ref()?; - let operand = self.ecx.read_immediate(operand).ok()?; - let val = self.ecx.unary_op(un_op, &operand).ok()?; + let operand = self.ecx.read_immediate(operand).discard_interp_err()?; + let val = self.ecx.unary_op(un_op, &operand).discard_interp_err()?; val.into() } BinaryOp(bin_op, lhs, rhs) => { let lhs = self.evaluated[lhs].as_ref()?; - let lhs = self.ecx.read_immediate(lhs).ok()?; + let lhs = self.ecx.read_immediate(lhs).discard_interp_err()?; let rhs = self.evaluated[rhs].as_ref()?; - let rhs = self.ecx.read_immediate(rhs).ok()?; - let val = self.ecx.binary_op(bin_op, &lhs, &rhs).ok()?; + let rhs = self.ecx.read_immediate(rhs).discard_interp_err()?; + let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_interp_err()?; val.into() } Cast { kind, value, from: _, to } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.evaluated[value].as_ref()?; - let value = self.ecx.read_immediate(value).ok()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.int_to_int_or_float(&value, to).ok()?; + let value = self.ecx.read_immediate(value).discard_interp_err()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; + let res = self.ecx.int_to_int_or_float(&value, to).discard_interp_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.evaluated[value].as_ref()?; - let value = self.ecx.read_immediate(value).ok()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.float_to_float_or_int(&value, to).ok()?; + let value = self.ecx.read_immediate(value).discard_interp_err()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; + let res = self.ecx.float_to_float_or_int(&value, to).discard_interp_err()?; res.into() } CastKind::Transmute => { let value = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).ok()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; // `offset` for immediates only supports scalar/scalar-pair ABIs, // so bail out if the target is not one. if value.as_mplace_or_imm().is_right() { @@ -574,29 +581,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { _ => return None, } } - value.offset(Size::ZERO, to, &self.ecx).ok()? + value.offset(Size::ZERO, to, &self.ecx).discard_interp_err()? } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => { let src = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).ok()?; - let dest = self.ecx.allocate(to, MemoryKind::Stack).ok()?; - self.ecx.unsize_into(src, to, &dest.clone().into()).ok()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; + let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_interp_err()?; + self.ecx.unsize_into(src, to, &dest.clone().into()).discard_interp_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) - .ok()?; + .discard_interp_err()?; dest.into() } CastKind::FnPtrToPtr | CastKind::PtrToPtr => { let src = self.evaluated[value].as_ref()?; - let src = self.ecx.read_immediate(src).ok()?; - let to = self.ecx.layout_of(to).ok()?; - let ret = self.ecx.ptr_to_ptr(&src, to).ok()?; + let src = self.ecx.read_immediate(src).discard_interp_err()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; + let ret = self.ecx.ptr_to_ptr(&src, to).discard_interp_err()?; ret.into() } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => { let src = self.evaluated[value].as_ref()?; - let src = self.ecx.read_immediate(src).ok()?; - let to = self.ecx.layout_of(to).ok()?; + let src = self.ecx.read_immediate(src).discard_interp_err()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; ImmTy::from_immediate(*src, to).into() } _ => return None, @@ -708,7 +715,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let Some(idx) = self.locals[idx_local] { if let Some(offset) = self.evaluated[idx].as_ref() - && let Ok(offset) = self.ecx.read_target_usize(offset) + && let Some(offset) = self.ecx.read_target_usize(offset).discard_interp_err() && let Some(min_length) = offset.checked_add(1) { projection.to_mut()[i] = @@ -868,7 +875,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let DefKind::Enum = self.tcx.def_kind(enum_did) { let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args); - let discr = self.ecx.discriminant_for_variant(enum_ty, variant).ok()?; + let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_interp_err()?; return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty)); } @@ -1218,13 +1225,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } - let layout = self.ecx.layout_of(lhs_ty).ok()?; + let layout = self.ecx.layout_of(lhs_ty).discard_interp_err()?; let as_bits = |value| { let constant = self.evaluated[value].as_ref()?; if layout.abi.is_scalar() { - let scalar = self.ecx.read_scalar(constant).ok()?; - scalar.to_bits(constant.layout.size).ok() + let scalar = self.ecx.read_scalar(constant).discard_interp_err()?; + scalar.to_bits(constant.layout.size).discard_interp_err() } else { // `constant` is a wide pointer. Do not evaluate to bits. None @@ -1484,7 +1491,7 @@ fn op_to_prop_const<'tcx>( // If this constant has scalar ABI, return it as a `ConstValue::Scalar`. if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi - && let Ok(scalar) = ecx.read_scalar(op) + && let Some(scalar) = ecx.read_scalar(op).discard_interp_err() { if !scalar.try_to_scalar_int().is_ok() { // Check that we do not leak a pointer. @@ -1498,12 +1505,12 @@ fn op_to_prop_const<'tcx>( // If this constant is already represented as an `Allocation`, // try putting it into global memory to return it. if let Either::Left(mplace) = op.as_mplace_or_imm() { - let (size, _align) = ecx.size_and_align_of_mplace(&mplace).ok()??; + let (size, _align) = ecx.size_and_align_of_mplace(&mplace).discard_interp_err()??; // Do not try interning a value that contains provenance. // Due to https://github.com/rust-lang/rust/issues/79738, doing so could lead to bugs. // FIXME: remove this hack once that issue is fixed. - let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).ok()??; + let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_interp_err()??; if alloc_ref.has_provenance() { return None; } @@ -1511,7 +1518,7 @@ fn op_to_prop_const<'tcx>( let pointer = mplace.ptr().into_pointer_or_addr().ok()?; let (prov, offset) = pointer.into_parts(); let alloc_id = prov.alloc_id(); - intern_const_alloc_for_constprop(ecx, alloc_id).ok()?; + intern_const_alloc_for_constprop(ecx, alloc_id).discard_interp_err()?; // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything // by `GlobalAlloc::Memory`, so do fall through to copying if needed. @@ -1526,7 +1533,9 @@ fn op_to_prop_const<'tcx>( } // Everything failed: create a new allocation to hold the data. - let alloc_id = ecx.intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)).ok()?; + let alloc_id = ecx + .intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)) + .discard_interp_err()?; let value = ConstValue::Indirect { alloc_id, offset: Size::ZERO }; // Check that we do not leak a pointer. diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 91fbc91e1e727..e69a7e552f606 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -37,7 +37,9 @@ use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; -use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; +use rustc_const_eval::interpret::{ + DiscardInterpError, ImmTy, Immediate, InterpCx, OpTy, Projectable, +}; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; @@ -200,7 +202,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { debug!(?discr, ?bb); let discr_ty = discr.ty(self.body, self.tcx).ty; - let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; + let Some(discr_layout) = self.ecx.layout_of(discr_ty).discard_interp_err() else { + return; + }; let Some(discr) = self.map.find(discr.as_ref()) else { return }; debug!(?discr); @@ -388,24 +392,28 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { lhs, constant, &mut |elem, op| match elem { - TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(), - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), + TrackElem::Field(idx) => { + self.ecx.project_field(op, idx.as_usize()).discard_interp_err() + } + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_interp_err(), TrackElem::Discriminant => { - let variant = self.ecx.read_discriminant(op).ok()?; - let discr_value = - self.ecx.discriminant_for_variant(op.layout.ty, variant).ok()?; + let variant = self.ecx.read_discriminant(op).discard_interp_err()?; + let discr_value = self + .ecx + .discriminant_for_variant(op.layout.ty, variant) + .discard_interp_err()?; Some(discr_value.into()) } TrackElem::DerefLen => { - let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into(); - let len_usize = op.len(&self.ecx).ok()?; + let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_interp_err()?.into(); + let len_usize = op.len(&self.ecx).discard_interp_err()?; let layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); Some(ImmTy::from_uint(len_usize, layout).into()) } }, &mut |place, op| { if let Some(conditions) = state.try_get_idx(place, &self.map) - && let Ok(imm) = self.ecx.read_immediate_raw(op) + && let Some(imm) = self.ecx.read_immediate_raw(op).discard_interp_err() && let Some(imm) = imm.right() && let Immediate::Scalar(Scalar::Int(int)) = *imm { @@ -429,8 +437,10 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { match rhs { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { - let Ok(constant) = - self.ecx.eval_mir_constant(&constant.const_, constant.span, None) + let Some(constant) = self + .ecx + .eval_mir_constant(&constant.const_, constant.span, None) + .discard_interp_err() else { return; }; @@ -469,8 +479,10 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { AggregateKind::Adt(.., Some(_)) => return, AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant) - && let Ok(discr_value) = - self.ecx.discriminant_for_variant(agg_ty, *variant_index) + && let Some(discr_value) = self + .ecx + .discriminant_for_variant(agg_ty, *variant_index) + .discard_interp_err() { self.process_immediate(bb, discr_target, discr_value, state); } @@ -555,7 +567,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant // of a niche encoding. If we cannot ensure that we write to the discriminant, do // nothing. - let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else { return }; + let Some(enum_layout) = self.ecx.layout_of(enum_ty).discard_interp_err() else { + return; + }; let writes_discriminant = match enum_layout.variants { Variants::Single { index } => { assert_eq!(index, *variant_index); @@ -568,7 +582,10 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } => *variant_index != untagged_variant, }; if writes_discriminant { - let Ok(discr) = self.ecx.discriminant_for_variant(enum_ty, *variant_index) + let Some(discr) = self + .ecx + .discriminant_for_variant(enum_ty, *variant_index) + .discard_interp_err() else { return; }; @@ -645,7 +662,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let Some(discr) = discr.place() else { return }; let discr_ty = discr.ty(self.body, self.tcx).ty; - let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; + let Some(discr_layout) = self.ecx.layout_of(discr_ty).discard_interp_err() else { + return; + }; let Some(conditions) = state.try_get(discr.as_ref(), &self.map) else { return }; if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) { diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 783e7aabe8543..b165ea53440e0 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ - ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, + DiscardInterpError, ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, }; use rustc_data_structures::fx::FxHashSet; use rustc_hir::HirId; @@ -101,7 +101,8 @@ impl<'tcx> Value<'tcx> { } (PlaceElem::Index(idx), Value::Aggregate { fields, .. }) => { let idx = prop.get_const(idx.into())?.immediate()?; - let idx = prop.ecx.read_target_usize(idx).ok()?.try_into().ok()?; + let idx = + prop.ecx.read_target_usize(idx).discard_interp_err()?.try_into().ok()?; if idx <= FieldIdx::MAX_AS_U32 { fields.get(FieldIdx::from_u32(idx)).unwrap_or(&Value::Uninit) } else { @@ -243,6 +244,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { "known panics lint encountered formatting error: {}", format_interp_error(self.ecx.tcx.dcx(), error), ); + error.discard_interp_err(); None } } @@ -347,9 +349,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // We need the type of the LHS. We cannot use `place_layout` as that is the type // of the result, which for checked binops is not the same! let left_ty = left.ty(self.local_decls(), self.tcx); - let left_size = self.ecx.layout_of(left_ty).ok()?.size; + let left_size = self.ecx.layout_of(left_ty).discard_interp_err()?.size; let right_size = r.layout.size; - let r_bits = r.to_scalar().to_bits(right_size).ok(); + let r_bits = r.to_scalar().to_bits(right_size).discard_interp_err(); if r_bits.is_some_and(|b| b >= left_size.bits() as u128) { debug!("check_binary_op: reporting assert for {:?}", location); let panic = AssertKind::Overflow( @@ -496,7 +498,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // This can be `None` if the lhs wasn't const propagated and we just // triggered the assert on the value of the rhs. self.eval_operand(op) - .and_then(|op| self.ecx.read_immediate(&op).ok()) + .and_then(|op| self.ecx.read_immediate(&op).discard_interp_err()) .map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int())) }; let msg = match msg { @@ -540,7 +542,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } use rustc_middle::mir::Rvalue::*; - let layout = self.ecx.layout_of(dest.ty(self.body, self.tcx).ty).ok()?; + let layout = self.ecx.layout_of(dest.ty(self.body, self.tcx).ty).discard_interp_err()?; trace!(?layout); let val: Value<'_> = match *rvalue { @@ -602,7 +604,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Len(place) => { let len = match self.get_const(place)? { - Value::Immediate(src) => src.len(&self.ecx).ok()?, + Value::Immediate(src) => src.len(&self.ecx).discard_interp_err()?, Value::Aggregate { fields, .. } => fields.len() as u64, Value::Uninit => match place.ty(self.local_decls(), self.tcx).ty.kind() { ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, @@ -633,21 +635,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Cast(ref kind, ref value, to) => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.eval_operand(value)?; - let value = self.ecx.read_immediate(&value).ok()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.int_to_int_or_float(&value, to).ok()?; + let value = self.ecx.read_immediate(&value).discard_interp_err()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; + let res = self.ecx.int_to_int_or_float(&value, to).discard_interp_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.eval_operand(value)?; - let value = self.ecx.read_immediate(&value).ok()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.float_to_float_or_int(&value, to).ok()?; + let value = self.ecx.read_immediate(&value).discard_interp_err()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; + let res = self.ecx.float_to_float_or_int(&value, to).discard_interp_err()?; res.into() } CastKind::Transmute => { let value = self.eval_operand(value)?; - let to = self.ecx.layout_of(to).ok()?; + let to = self.ecx.layout_of(to).discard_interp_err()?; // `offset` for immediates only supports scalar/scalar-pair ABIs, // so bail out if the target is not one. match (value.layout.abi, to.abi) { @@ -656,7 +658,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { _ => return None, } - value.offset(Size::ZERO, to, &self.ecx).ok()?.into() + value.offset(Size::ZERO, to, &self.ecx).discard_interp_err()?.into() } _ => return None, }, @@ -781,7 +783,8 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { TerminatorKind::SwitchInt { ref discr, ref targets } => { if let Some(ref value) = self.eval_operand(discr) && let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) - && let Ok(constant) = value_const.to_bits(value_const.size()) + && let Some(constant) = + value_const.to_bits(value_const.size()).discard_interp_err() { // We managed to evaluate the discriminant, so we know we only need to visit // one target. diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index d1cd15aab3dde..5910be22a690a 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -641,7 +641,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // The program didn't actually do a read, so suppress the memory access hooks. // This is also a very special exception where we just ignore an error -- if this read // was UB e.g. because the memory is uninitialized, we don't want to know! - let old_val = this.run_for_validation(|this| this.read_scalar(dest)).ok(); + let old_val = this.run_for_validation(|this| this.read_scalar(dest)).discard_interp_err(); this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; this.validate_atomic_store(dest, atomic)?; this.buffered_atomic_write(val, dest, atomic, old_val) diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 4445550512b49..ee087ece9d833 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -223,7 +223,10 @@ pub fn report_error<'tcx>( let info = info.downcast_ref::().expect("invalid MachineStop payload"); use TerminationInfo::*; let title = match info { - Exit { code, leak_check } => return Some((*code, *leak_check)), + &Exit { code, leak_check } => { + e.discard_interp_err(); + return Some((code, leak_check)); + } Abort(_) => Some("abnormal termination"), UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) => Some("unsupported operation"), @@ -375,6 +378,7 @@ pub fn report_error<'tcx>( InvalidProgramInfo::AlreadyReported(_) ) => { // This got already reported. No point in reporting it again. + e.discard_interp_err(); return None; } _ => diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 802a70e5a54aa..7f43c61cd66d8 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -247,7 +247,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This does NaN adjustments. let val = this.binary_op(mir_op, &left, &right).map_err(|err| { match err.kind() { - InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { + &InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { + err.discard_interp_err(); // This resets the interpreter backtrace, but it's not worth avoiding that. let shift_amount = match shift_amount { Either::Left(v) => v.to_string(), From 15ac6983930a0d49b921c0330dbbb5a4f8f1d34a Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 30 Sep 2024 10:18:27 +0200 Subject: [PATCH 1088/1301] canonicalizer: rm region uniquification, add caching --- .../src/canonicalizer.rs | 247 ++++++++++-------- 1 file changed, 138 insertions(+), 109 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 196ddeb244302..5c3c35b7b3e46 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -1,5 +1,6 @@ use std::cmp::Ordering; +use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; @@ -44,8 +45,15 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { canonicalize_mode: CanonicalizeMode, variables: &'a mut Vec, + variable_lookup_table: HashMap, + primitive_var_infos: Vec>, binder_index: ty::DebruijnIndex, + + /// We only use the debruijn index during lookup as all other fields + /// should not be impacted by whether a type is folded once or multiple + /// times. + cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>, } impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { @@ -60,12 +68,14 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { canonicalize_mode, variables, + variable_lookup_table: Default::default(), primitive_var_infos: Vec::new(), binder_index: ty::INNERMOST, + + cache: Default::default(), }; let value = value.fold_with(&mut canonicalizer); - // FIXME: Restore these assertions. Should we uplift type flags? assert!(!value.has_infer(), "unexpected infer in {value:?}"); assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); @@ -75,6 +85,37 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { Canonical { defining_opaque_types, max_universe, variables, value } } + fn get_or_insert_bound_var( + &mut self, + arg: impl Into, + canonical_var_info: CanonicalVarInfo, + ) -> ty::BoundVar { + // FIXME: 16 is made up and arbitrary. We should look at some + // perf data here. + let arg = arg.into(); + let idx = if self.variables.len() > 16 { + if self.variable_lookup_table.is_empty() { + self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..)); + } + + *self.variable_lookup_table.entry(arg).or_insert_with(|| { + let var = self.variables.len(); + self.variables.push(arg); + self.primitive_var_infos.push(canonical_var_info); + var + }) + } else { + self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| { + let var = self.variables.len(); + self.variables.push(arg); + self.primitive_var_infos.push(canonical_var_info); + var + }) + }; + + ty::BoundVar::from(idx) + } + fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) { let mut var_infos = self.primitive_var_infos; // See the rustc-dev-guide section about how we deal with universes @@ -124,8 +165,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6 // - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: - // - // This algorithm runs in `O(n²)` where `n` is the number of different universe - // indices in the input. This should be fine as `n` is expected to be small. + // This algorithm runs in `O(mn)` where `n` is the number of different universes and + // `m` the number of variables. This should be fine as both are expected to be small. let mut curr_compressed_uv = ty::UniverseIndex::ROOT; let mut existential_in_new_uv = None; let mut next_orig_uv = Some(ty::UniverseIndex::ROOT); @@ -185,14 +226,16 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { for var in var_infos.iter_mut() { // We simply put all regions from the input into the highest // compressed universe, so we only deal with them at the end. - if !var.is_region() && is_existential == var.is_existential() { - update_uv(var, orig_uv, is_existential) + if !var.is_region() { + if is_existential == var.is_existential() { + update_uv(var, orig_uv, is_existential) + } } } } } - // We uniquify regions and always put them into their own universe + // We put all regions into a separate universe. let mut first_region = true; for var in var_infos.iter_mut() { if var.is_region() { @@ -208,93 +251,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos); (curr_compressed_uv, var_infos) } -} - -impl, I: Interner> TypeFolder for Canonicalizer<'_, D, I> { - fn cx(&self) -> I { - self.delegate.cx() - } - - fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder - where - T: TypeFoldable, - { - self.binder_index.shift_in(1); - let t = t.super_fold_with(self); - self.binder_index.shift_out(1); - t - } - - fn fold_region(&mut self, r: I::Region) -> I::Region { - let kind = match r.kind() { - ty::ReBound(..) => return r, - - // We may encounter `ReStatic` in item signatures or the hidden type - // of an opaque. `ReErased` should only be encountered in the hidden - // type of an opaque for regions that are ignored for the purposes of - // captures. - // - // FIXME: We should investigate the perf implications of not uniquifying - // `ReErased`. We may be able to short-circuit registering region - // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => return r, - }, - - ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => { - panic!("unexpected region in response: {r:?}") - } - }, - - ty::RePlaceholder(placeholder) => match self.canonicalize_mode { - // We canonicalize placeholder regions as existentials in query inputs. - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { max_input_universe } => { - // If we have a placeholder region inside of a query, it must be from - // a new universe. - if max_input_universe.can_name(placeholder.universe()) { - panic!("new placeholder in universe {max_input_universe:?}: {r:?}"); - } - CanonicalVarKind::PlaceholderRegion(placeholder) - } - }, - - ty::ReVar(vid) => { - assert_eq!( - self.delegate.opportunistic_resolve_lt_var(vid), - r, - "region vid should have been resolved fully before canonicalization" - ); - match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => { - CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) - } - } - } - }; - - let existing_bound_var = match self.canonicalize_mode { - CanonicalizeMode::Input => None, - CanonicalizeMode::Response { .. } => { - self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from) - } - }; - - let var = existing_bound_var.unwrap_or_else(|| { - let var = ty::BoundVar::from(self.variables.len()); - self.variables.push(r.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }); - Region::new_anon_bound(self.cx(), self.binder_index, var) - } - - fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { @@ -368,20 +326,98 @@ impl, I: Interner> TypeFolder for Canonicaliz | ty::Tuple(_) | ty::Alias(_, _) | ty::Bound(_, _) - | ty::Error(_) => return t.super_fold_with(self), + | ty::Error(_) => { + return t.super_fold_with(self); + } }; - let var = ty::BoundVar::from( - self.variables.iter().position(|&v| v == t.into()).unwrap_or_else(|| { - let var = self.variables.len(); - self.variables.push(t.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }), - ); + let var = self.get_or_insert_bound_var(t, CanonicalVarInfo { kind }); Ty::new_anon_bound(self.cx(), self.binder_index, var) } +} + +impl, I: Interner> TypeFolder for Canonicalizer<'_, D, I> { + fn cx(&self) -> I { + self.delegate.cx() + } + + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder + where + T: TypeFoldable, + { + self.binder_index.shift_in(1); + let t = t.super_fold_with(self); + self.binder_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + let kind = match r.kind() { + ty::ReBound(..) => return r, + + // We may encounter `ReStatic` in item signatures or the hidden type + // of an opaque. `ReErased` should only be encountered in the hidden + // type of an opaque for regions that are ignored for the purposes of + // captures. + // + // FIXME: We should investigate the perf implications of not uniquifying + // `ReErased`. We may be able to short-circuit registering region + // obligations if we encounter a `ReErased` on one side, for example. + ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => return r, + }, + + ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => { + panic!("unexpected region in response: {r:?}") + } + }, + + ty::RePlaceholder(placeholder) => match self.canonicalize_mode { + // We canonicalize placeholder regions as existentials in query inputs. + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { max_input_universe } => { + // If we have a placeholder region inside of a query, it must be from + // a new universe. + if max_input_universe.can_name(placeholder.universe()) { + panic!("new placeholder in universe {max_input_universe:?}: {r:?}"); + } + CanonicalVarKind::PlaceholderRegion(placeholder) + } + }, + + ty::ReVar(vid) => { + assert_eq!( + self.delegate.opportunistic_resolve_lt_var(vid), + r, + "region vid should have been resolved fully before canonicalization" + ); + match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => { + CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) + } + } + } + }; + + let var = self.get_or_insert_bound_var(r, CanonicalVarInfo { kind }); + + Region::new_anon_bound(self.cx(), self.binder_index, var) + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + ty + } else { + let res = self.cached_fold_ty(t); + assert!(self.cache.insert((self.binder_index, t), res).is_none()); + res + } + } fn fold_const(&mut self, c: I::Const) -> I::Const { let kind = match c.kind() { @@ -419,14 +455,7 @@ impl, I: Interner> TypeFolder for Canonicaliz | ty::ConstKind::Expr(_) => return c.super_fold_with(self), }; - let var = ty::BoundVar::from( - self.variables.iter().position(|&v| v == c.into()).unwrap_or_else(|| { - let var = self.variables.len(); - self.variables.push(c.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }), - ); + let var = self.get_or_insert_bound_var(c, CanonicalVarInfo { kind }); Const::new_anon_bound(self.cx(), self.binder_index, var) } From 020bd6d323f20fa3c723320ee5b8484a8bcaa6cb Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 30 Sep 2024 11:15:05 +0200 Subject: [PATCH 1089/1301] Improve `--print=check-cfg` documentation --- .../src/compiler-flags/print-check-cfg.md | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/doc/unstable-book/src/compiler-flags/print-check-cfg.md b/src/doc/unstable-book/src/compiler-flags/print-check-cfg.md index ab63c986e856e..8d314aa62d4cc 100644 --- a/src/doc/unstable-book/src/compiler-flags/print-check-cfg.md +++ b/src/doc/unstable-book/src/compiler-flags/print-check-cfg.md @@ -4,23 +4,28 @@ The tracking issue for this feature is: [#125704](https://github.com/rust-lang/r ------------------------ -This option of the `--print` flag print the list of expected cfgs. +This option of the `--print` flag print the list of all the expected cfgs. -This is related to the `--check-cfg` flag which allows specifying arbitrary expected +This is related to the [`--check-cfg` flag][check-cfg] which allows specifying arbitrary expected names and values. -This print option works similarly to `--print=cfg` (modulo check-cfg specifics): - - *check_cfg syntax*: *output of --print=check-cfg* - - `cfg(windows)`: `windows` - - `cfg(feature, values("foo", "bar"))`: `feature="foo"` and `feature="bar"` - - `cfg(feature, values(none(), ""))`: `feature` and `feature=""` - - `cfg(feature, values(any()))`: `feature=any()` - - `cfg(feature, values())`: `feature=` - - `cfg(any())`: `any()` - - *nothing*: `any()=any()` +This print option works similarly to `--print=cfg` (modulo check-cfg specifics). + +| `--check-cfg` | `--print=check-cfg` | +|-----------------------------------|-----------------------------| +| `cfg(foo)` | `foo` | +| `cfg(foo, values("bar"))` | `foo="bar"` | +| `cfg(foo, values(none(), "bar"))` | `foo` & `foo="bar"` | +| | *check-cfg specific syntax* | +| `cfg(foo, values(any())` | `foo=any()` | +| `cfg(foo, values())` | `foo=` | +| `cfg(any())` | `any()` | +| *none* | `any()=any()` | To be used like this: ```bash rustc --print=check-cfg -Zunstable-options lib.rs ``` + +[check-cfg]: https://doc.rust-lang.org/nightly/rustc/check-cfg.html From 874d55cea8af386d23663dcea1821d9697e028ff Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 30 Sep 2024 13:05:44 +0300 Subject: [PATCH 1090/1301] enable compiler fingerprint logs in verbose mode Signed-off-by: onur-ozkan --- src/bootstrap/src/core/builder.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 47420f8fe72fb..122eb50dd946e 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -2012,6 +2012,11 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); } + if self.is_verbose() { + // This provides very useful logs especially when debugging build cache-related stuff. + cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); + } + cargo.env("RUSTC_VERBOSE", self.verbosity.to_string()); // Downstream forks of the Rust compiler might want to use a custom libc to add support for From 9cb540a13cb2249754ea3e755cb1472151d061db Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 30 Sep 2024 12:13:17 +0200 Subject: [PATCH 1091/1301] Reject leading unsafe in `cfg!(...)` and `--check-cfg`. --- compiler/rustc_builtin_macros/src/cfg.rs | 2 +- compiler/rustc_interface/src/interface.rs | 2 +- .../unsafe/extraneous-unsafe-attributes.rs | 6 +++++- .../extraneous-unsafe-attributes.stderr | 20 ++++++++++++++++++- tests/ui/check-cfg/invalid-arguments.rs | 3 ++- .../invalid-arguments.unsafe_attr.stderr | 5 +++++ 6 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 tests/ui/check-cfg/invalid-arguments.unsafe_attr.stderr diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index de198115fa00b..cf1d5c68eadd0 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -43,7 +43,7 @@ fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span })); } - let cfg = p.parse_meta_item(AllowLeadingUnsafe::Yes)?; + let cfg = p.parse_meta_item(AllowLeadingUnsafe::No)?; let _ = p.eat(&token::Comma); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c2241773c8ccb..780693f893290 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -174,7 +174,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch } }; - let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::Yes) { + let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(meta_item) if parser.token == token::Eof => meta_item, Ok(..) => expected_error(), Err(err) => { diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs index b561550c19843..273b127bf9cb4 100644 --- a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs @@ -27,4 +27,8 @@ mod inner { #[unsafe(used)] //~ ERROR: is not an unsafe attribute static FOO: usize = 0; -fn main() {} +fn main() { + let _a = cfg!(unsafe(foo)); + //~^ ERROR: expected identifier, found keyword `unsafe` + //~^^ ERROR: invalid predicate `r#unsafe` +} diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr index 9fb7f062b912b..445d239d86729 100644 --- a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr @@ -22,6 +22,23 @@ LL | #[unsafe(test)] | = note: extraneous unsafe is not allowed in attributes +error: expected identifier, found keyword `unsafe` + --> $DIR/extraneous-unsafe-attributes.rs:31:19 + | +LL | let _a = cfg!(unsafe(foo)); + | ^^^^^^ expected identifier, found keyword + | +help: escape `unsafe` to use it as an identifier + | +LL | let _a = cfg!(r#unsafe(foo)); + | ++ + +error[E0537]: invalid predicate `r#unsafe` + --> $DIR/extraneous-unsafe-attributes.rs:31:19 + | +LL | let _a = cfg!(unsafe(foo)); + | ^^^^^^^^^^^ + error: `ignore` is not an unsafe attribute --> $DIR/extraneous-unsafe-attributes.rs:13:3 | @@ -62,5 +79,6 @@ LL | #[unsafe(used)] | = note: extraneous unsafe is not allowed in attributes -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors +For more information about this error, try `rustc --explain E0537`. diff --git a/tests/ui/check-cfg/invalid-arguments.rs b/tests/ui/check-cfg/invalid-arguments.rs index b8588ecb4ff68..c6b1218ce27c9 100644 --- a/tests/ui/check-cfg/invalid-arguments.rs +++ b/tests/ui/check-cfg/invalid-arguments.rs @@ -8,7 +8,7 @@ //@ revisions: values_any_missing_values values_any_before_ident ident_in_values_1 //@ revisions: ident_in_values_2 unknown_meta_item_1 unknown_meta_item_2 unknown_meta_item_3 //@ revisions: mixed_values_any mixed_any any_values giberich unterminated -//@ revisions: none_not_empty cfg_none +//@ revisions: none_not_empty cfg_none unsafe_attr // //@ [anything_else]compile-flags: --check-cfg=anything_else(...) //@ [boolean]compile-flags: --check-cfg=cfg(true) @@ -33,5 +33,6 @@ //@ [cfg_none]compile-flags: --check-cfg=cfg(none()) //@ [giberich]compile-flags: --check-cfg=cfg(...) //@ [unterminated]compile-flags: --check-cfg=cfg( +//@ [unsafe_attr]compile-flags: --check-cfg=unsafe(cfg(foo)) fn main() {} diff --git a/tests/ui/check-cfg/invalid-arguments.unsafe_attr.stderr b/tests/ui/check-cfg/invalid-arguments.unsafe_attr.stderr new file mode 100644 index 0000000000000..5236ed6f60547 --- /dev/null +++ b/tests/ui/check-cfg/invalid-arguments.unsafe_attr.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `unsafe(cfg(foo))` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + From 5a7058c5a542ec42d1fa9b524f7b4f7d6845d1e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:07:24 +0800 Subject: [PATCH 1092/1301] Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags These conditionally applied flags trigger rebuilds because they can invalidate previous cargo build cache. --- src/bootstrap/src/core/build_steps/compile.rs | 4 ---- src/bootstrap/src/core/build_steps/tool.rs | 11 ++++------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index eaa982d4e2bbd..bb1d8d279288d 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1053,10 +1053,6 @@ pub fn rustc_cargo( cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)"); - // If the rustc output is piped to e.g. `head -n1` we want the process to be - // killed, rather than having an error bubble up and cause a panic. - cargo.rustflag("-Zon-broken-pipe=kill"); - if builder.config.llvm_enzyme { cargo.rustflag("-l").rustflag("Enzyme-19"); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 64dfe054d9c77..fa2c1b8360f4c 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -200,6 +200,10 @@ pub fn prepare_tool_cargo( cargo.arg("--features").arg(features.join(", ")); } + // Warning: be very careful with RUSTFLAGS or command-line options, as conditionally applied + // RUSTFLAGS or cli flags can lead to hard-to-diagnose rebuilds due to flag differences, causing + // previous tool build artifacts to get invalidated. + // Enable internal lints for clippy and rustdoc // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]` // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 @@ -209,13 +213,6 @@ pub fn prepare_tool_cargo( // See https://github.com/rust-lang/rust/issues/116538 cargo.rustflag("-Zunstable-options"); - // `-Zon-broken-pipe=kill` breaks cargo tests - if !path.ends_with("cargo") { - // If the output is piped to e.g. `head -n1` we want the process to be killed, - // rather than having an error bubble up and cause a panic. - cargo.rustflag("-Zon-broken-pipe=kill"); - } - cargo } From fd1429a56b5c6eadb6205c982f4945b631f55db9 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 30 Sep 2024 14:20:12 +0300 Subject: [PATCH 1093/1301] replace manual verbose checks with `Config::is_verbose` Signed-off-by: onur-ozkan --- src/bootstrap/src/core/builder.rs | 4 ++-- src/bootstrap/src/core/config/config.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 47420f8fe72fb..5653b29fda8fa 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1562,8 +1562,8 @@ impl<'a> Builder<'a> { let libdir = self.rustc_libdir(compiler); let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8"); - if !matches!(self.config.dry_run, DryRun::SelfCheck) { - self.verbose_than(0, || println!("using sysroot {sysroot_str}")); + if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) { + println!("using sysroot {sysroot_str}"); } let mut rustflags = Rustflags::new(target); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 77e0ece31047f..0689a4caa9a31 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2428,7 +2428,7 @@ impl Config { /// Runs a function if verbosity is greater than 0 pub fn verbose(&self, f: impl Fn()) { - if self.verbose > 0 { + if self.is_verbose() { f() } } @@ -2713,7 +2713,7 @@ impl Config { .success(); if has_changes { if if_unchanged { - if self.verbose > 0 { + if self.is_verbose() { println!( "WARNING: saw changes to compiler/ or library/ since {commit}; \ ignoring `download-rustc`" @@ -2810,7 +2810,7 @@ impl Config { let has_changes = !t!(git.as_command_mut().status()).success(); if has_changes { if if_unchanged { - if self.verbose > 0 { + if self.is_verbose() { println!( "warning: saw changes to one of {modified_paths:?} since {commit}; \ ignoring `{option_name}`" From ac2e3180344bdd6092fe2b845df65c4ff4a5aa10 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 26 Sep 2024 20:16:40 +0200 Subject: [PATCH 1094/1301] make type-check-4 asm tests about non-const expressions --- tests/ui/asm/aarch64/type-check-4.rs | 27 ------------------------ tests/ui/asm/aarch64/type-check-4.stderr | 21 ------------------ tests/ui/asm/non-const.rs | 11 ++++++++++ tests/ui/asm/non-const.stderr | 11 ++++++++++ tests/ui/asm/x86_64/type-check-4.rs | 27 ------------------------ tests/ui/asm/x86_64/type-check-4.stderr | 21 ------------------ 6 files changed, 22 insertions(+), 96 deletions(-) delete mode 100644 tests/ui/asm/aarch64/type-check-4.rs delete mode 100644 tests/ui/asm/aarch64/type-check-4.stderr create mode 100644 tests/ui/asm/non-const.rs create mode 100644 tests/ui/asm/non-const.stderr delete mode 100644 tests/ui/asm/x86_64/type-check-4.rs delete mode 100644 tests/ui/asm/x86_64/type-check-4.stderr diff --git a/tests/ui/asm/aarch64/type-check-4.rs b/tests/ui/asm/aarch64/type-check-4.rs deleted file mode 100644 index c24567bd5b002..0000000000000 --- a/tests/ui/asm/aarch64/type-check-4.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ only-aarch64 -//@ build-fail - -use std::arch::global_asm; - -fn main() {} - -// Constants must be... constant - -static mut S: i32 = 1; -const fn const_foo(x: i32) -> i32 { - x -} -const fn const_bar(x: T) -> T { - x -} -global_asm!("{}", const unsafe { S }); -//~^ ERROR: evaluation of constant value failed -//~| mutable global memory -global_asm!("{}", const const_foo(0)); -global_asm!("{}", const const_foo(unsafe { S })); -//~^ ERROR: evaluation of constant value failed -//~| mutable global memory -global_asm!("{}", const const_bar(0)); -global_asm!("{}", const const_bar(unsafe { S })); -//~^ ERROR: evaluation of constant value failed -//~| mutable global memory diff --git a/tests/ui/asm/aarch64/type-check-4.stderr b/tests/ui/asm/aarch64/type-check-4.stderr deleted file mode 100644 index 8c22dfcdb5ed6..0000000000000 --- a/tests/ui/asm/aarch64/type-check-4.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0080]: evaluation of constant value failed - --> $DIR/type-check-4.rs:17:34 - | -LL | global_asm!("{}", const unsafe { S }); - | ^ constant accesses mutable global memory - -error[E0080]: evaluation of constant value failed - --> $DIR/type-check-4.rs:21:44 - | -LL | global_asm!("{}", const const_foo(unsafe { S })); - | ^ constant accesses mutable global memory - -error[E0080]: evaluation of constant value failed - --> $DIR/type-check-4.rs:25:44 - | -LL | global_asm!("{}", const const_bar(unsafe { S })); - | ^ constant accesses mutable global memory - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/asm/non-const.rs b/tests/ui/asm/non-const.rs new file mode 100644 index 0000000000000..63c4656322610 --- /dev/null +++ b/tests/ui/asm/non-const.rs @@ -0,0 +1,11 @@ +//@ needs-asm-support + +use std::arch::global_asm; + +fn main() {} + +// Constants must be... constant +fn non_const_fn(x: i32) -> i32 { x } + +global_asm!("/* {} */", const non_const_fn(0)); +//~^ERROR: cannot call non-const fn diff --git a/tests/ui/asm/non-const.stderr b/tests/ui/asm/non-const.stderr new file mode 100644 index 0000000000000..5fae2ac984378 --- /dev/null +++ b/tests/ui/asm/non-const.stderr @@ -0,0 +1,11 @@ +error[E0015]: cannot call non-const fn `non_const_fn` in constants + --> $DIR/non-const.rs:10:31 + | +LL | global_asm!("/* {} */", const non_const_fn(0)); + | ^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/asm/x86_64/type-check-4.rs b/tests/ui/asm/x86_64/type-check-4.rs deleted file mode 100644 index ebc6edc07e4cd..0000000000000 --- a/tests/ui/asm/x86_64/type-check-4.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ only-x86_64 -//@ build-fail - -use std::arch::global_asm; - -fn main() {} - -// Constants must be... constant - -static mut S: i32 = 1; -const fn const_foo(x: i32) -> i32 { - x -} -const fn const_bar(x: T) -> T { - x -} -global_asm!("{}", const unsafe { S }); -//~^ ERROR evaluation of constant value failed -//~| mutable global memory -global_asm!("{}", const const_foo(0)); -global_asm!("{}", const const_foo(unsafe { S })); -//~^ ERROR evaluation of constant value failed -//~| mutable global memory -global_asm!("{}", const const_bar(0)); -global_asm!("{}", const const_bar(unsafe { S })); -//~^ ERROR evaluation of constant value failed -//~| mutable global memory diff --git a/tests/ui/asm/x86_64/type-check-4.stderr b/tests/ui/asm/x86_64/type-check-4.stderr deleted file mode 100644 index 8c22dfcdb5ed6..0000000000000 --- a/tests/ui/asm/x86_64/type-check-4.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0080]: evaluation of constant value failed - --> $DIR/type-check-4.rs:17:34 - | -LL | global_asm!("{}", const unsafe { S }); - | ^ constant accesses mutable global memory - -error[E0080]: evaluation of constant value failed - --> $DIR/type-check-4.rs:21:44 - | -LL | global_asm!("{}", const const_foo(unsafe { S })); - | ^ constant accesses mutable global memory - -error[E0080]: evaluation of constant value failed - --> $DIR/type-check-4.rs:25:44 - | -LL | global_asm!("{}", const const_bar(unsafe { S })); - | ^ constant accesses mutable global memory - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0080`. From ed5443fcdf3b4d02a89aec929cd62aa97586096f Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Mon, 30 Sep 2024 22:21:45 +0800 Subject: [PATCH 1095/1301] apply suggestions --- compiler/rustc_lint/src/if_let_rescope.rs | 3 +- tests/ui/drop/lint-if-let-rescope.fixed | 17 ++++++++- tests/ui/drop/lint-if-let-rescope.rs | 17 ++++++++- tests/ui/drop/lint-if-let-rescope.stderr | 46 +++++++++++++++++++++-- 4 files changed, 75 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 8cb63d5cb0542..8f643de836a02 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -260,7 +260,8 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope { // if let .. { body } else { break; } // } // ``` - // There is no observable from the `{ break; }` block so the edition change + // There is no observable change in drop order on the overall `if let` expression + // given that the `{ break; }` block is trivial so the edition change // means nothing substantial to this `while` statement. self.skip.insert(value.hir_id); return; diff --git a/tests/ui/drop/lint-if-let-rescope.fixed b/tests/ui/drop/lint-if-let-rescope.fixed index d579ce9823691..199068d0fd265 100644 --- a/tests/ui/drop/lint-if-let-rescope.fixed +++ b/tests/ui/drop/lint-if-let-rescope.fixed @@ -1,7 +1,7 @@ //@ run-rustfix #![deny(if_let_rescope)] -#![feature(if_let_rescope)] +#![feature(if_let_rescope, stmt_expr_attributes)] #![allow(irrefutable_let_patterns, unused_parens)] fn droppy() -> Droppy { @@ -69,16 +69,29 @@ fn main() { //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } + #[rustfmt::skip] if (match droppy().get() { Some(_value) => { true } _ => { false }}) { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 // do something + } else if (((match droppy().get() { Some(_value) => { true } _ => { false }}))) { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } while let Some(_value) = droppy().get() { // Should not lint break; } + + while (match droppy().get() { Some(_value) => { false } _ => { true }}) { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 + } } diff --git a/tests/ui/drop/lint-if-let-rescope.rs b/tests/ui/drop/lint-if-let-rescope.rs index aab293cdcfc77..4c043c0266cc4 100644 --- a/tests/ui/drop/lint-if-let-rescope.rs +++ b/tests/ui/drop/lint-if-let-rescope.rs @@ -1,7 +1,7 @@ //@ run-rustfix #![deny(if_let_rescope)] -#![feature(if_let_rescope)] +#![feature(if_let_rescope, stmt_expr_attributes)] #![allow(irrefutable_let_patterns, unused_parens)] fn droppy() -> Droppy { @@ -69,16 +69,29 @@ fn main() { //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } + #[rustfmt::skip] if (if let Some(_value) = droppy().get() { true } else { false }) { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 // do something + } else if (((if let Some(_value) = droppy().get() { true } else { false }))) { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } while let Some(_value) = droppy().get() { // Should not lint break; } + + while (if let Some(_value) = droppy().get() { false } else { true }) { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 + } } diff --git a/tests/ui/drop/lint-if-let-rescope.stderr b/tests/ui/drop/lint-if-let-rescope.stderr index 50801ae166ff0..ef60d141b7988 100644 --- a/tests/ui/drop/lint-if-let-rescope.stderr +++ b/tests/ui/drop/lint-if-let-rescope.stderr @@ -132,7 +132,7 @@ LL | if let () = { match Droppy.get() { Some(_value) => {} _ => {}} } { | ~~~~~ +++++++++++++++++ ++++++++ error: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/lint-if-let-rescope.rs:72:12 + --> $DIR/lint-if-let-rescope.rs:73:12 | LL | if (if let Some(_value) = droppy().get() { true } else { false }) { | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ @@ -142,7 +142,7 @@ LL | if (if let Some(_value) = droppy().get() { true } else { false }) { = warning: this changes meaning in Rust 2024 = note: for more information, see issue #124085 help: the value is now dropped here in Edition 2024 - --> $DIR/lint-if-let-rescope.rs:72:53 + --> $DIR/lint-if-let-rescope.rs:73:53 | LL | if (if let Some(_value) = droppy().get() { true } else { false }) { | ^ @@ -151,5 +151,45 @@ help: a `match` with a single arm can preserve the drop order up to Edition 2021 LL | if (match droppy().get() { Some(_value) => { true } _ => { false }}) { | ~~~~~ +++++++++++++++++ ~~~~ + -error: aborting due to 6 previous errors +error: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/lint-if-let-rescope.rs:79:21 + | +LL | } else if (((if let Some(_value) = droppy().get() { true } else { false }))) { + | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: the value is now dropped here in Edition 2024 + --> $DIR/lint-if-let-rescope.rs:79:62 + | +LL | } else if (((if let Some(_value) = droppy().get() { true } else { false }))) { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL | } else if (((match droppy().get() { Some(_value) => { true } _ => { false }}))) { + | ~~~~~ +++++++++++++++++ ~~~~ + + +error: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/lint-if-let-rescope.rs:91:15 + | +LL | while (if let Some(_value) = droppy().get() { false } else { true }) { + | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: the value is now dropped here in Edition 2024 + --> $DIR/lint-if-let-rescope.rs:91:57 + | +LL | while (if let Some(_value) = droppy().get() { false } else { true }) { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL | while (match droppy().get() { Some(_value) => { false } _ => { true }}) { + | ~~~~~ +++++++++++++++++ ~~~~ + + +error: aborting due to 8 previous errors From eb75d20a55e7c6416592a6e9d4d2e7e21e08ce14 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 28 Sep 2024 14:16:05 -0400 Subject: [PATCH 1096/1301] Relax a debug assertion in codegen --- Cargo.lock | 1 + .../rustc_codegen_cranelift/src/unsize.rs | 17 ++------ compiler/rustc_codegen_ssa/Cargo.toml | 1 + compiler/rustc_codegen_ssa/src/base.rs | 42 ++++++++++++++++--- tests/ui/codegen/sub-principals-in-codegen.rs | 8 ++++ 5 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 tests/ui/codegen/sub-principals-in-codegen.rs diff --git a/Cargo.lock b/Cargo.lock index 582b5a763e6f7..7ea8c30008816 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3448,6 +3448,7 @@ dependencies = [ "rustc_span", "rustc_symbol_mangling", "rustc_target", + "rustc_trait_selection", "rustc_type_ir", "serde_json", "smallvec", diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 339628053a953..5c297ebfadbfd 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -2,6 +2,7 @@ //! //! [`PointerCoercion::Unsize`]: `rustc_middle::ty::adjustment::PointerCoercion::Unsize` +use rustc_codegen_ssa::base::validate_trivial_unsize; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use crate::base::codegen_panic_nounwind; @@ -34,20 +35,8 @@ pub(crate) fn unsized_info<'tcx>( let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // Codegen takes advantage of the additional assumption, where if the - // principal trait def id of what's being casted doesn't change, - // then we don't need to adjust the vtable at all. This - // corresponds to the fact that `dyn Tr: Unsize>` - // requires that `A = B`; we don't allow *upcasting* objects - // between the same trait with different args. If we, for - // some reason, were to relax the `Unsize` trait, it could become - // unsound, so let's assert here that the trait refs are *equal*. - // - // We can use `assert_eq` because the binders should have been anonymized, - // and because higher-ranked equality now requires the binders are equal. - debug_assert_eq!( - data_a.principal(), - data_b.principal(), + debug_assert!( + validate_trivial_unsize(fx.tcx, data_a, data_b), "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" ); return old_info; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 81590674ec7c1..346f07dfa0a62 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -34,6 +34,7 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } serde_json = "1.0.59" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 5c67600e4eec1..1d39125498335 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -27,6 +27,8 @@ use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Symbol}; use rustc_target::abi::FIRST_VARIANT; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; use crate::assert_module_sources::CguReuse; @@ -101,6 +103,38 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.sext(cmp, ret_ty) } +/// Codegen takes advantage of the additional assumption, where if the +/// principal trait def id of what's being casted doesn't change, +/// then we don't need to adjust the vtable at all. This +/// corresponds to the fact that `dyn Tr: Unsize>` +/// requires that `A = B`; we don't allow *upcasting* objects +/// between the same trait with different args. If we, for +/// some reason, were to relax the `Unsize` trait, it could become +/// unsound, so let's validate here that the trait refs are subtypes. +pub fn validate_trivial_unsize<'tcx>( + tcx: TyCtxt<'tcx>, + data_a: &'tcx ty::List>, + data_b: &'tcx ty::List>, +) -> bool { + match (data_a.principal(), data_b.principal()) { + (Some(principal_a), Some(principal_b)) => { + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let Ok(()) = ocx.sub( + &ObligationCause::dummy(), + ty::ParamEnv::reveal_all(), + principal_a, + principal_b, + ) else { + return false; + }; + ocx.select_all_or_error().is_empty() + } + (None, None) => true, + _ => false, + } +} + /// Retrieves the information we are losing (making dynamic) in an unsizing /// adjustment. /// @@ -133,12 +167,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // between the same trait with different args. If we, for // some reason, were to relax the `Unsize` trait, it could become // unsound, so let's assert here that the trait refs are *equal*. - // - // We can use `assert_eq` because the binders should have been anonymized, - // and because higher-ranked equality now requires the binders are equal. - debug_assert_eq!( - data_a.principal(), - data_b.principal(), + debug_assert!( + validate_trivial_unsize(cx.tcx(), data_a, data_b), "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" ); diff --git a/tests/ui/codegen/sub-principals-in-codegen.rs b/tests/ui/codegen/sub-principals-in-codegen.rs new file mode 100644 index 0000000000000..178c10da5968f --- /dev/null +++ b/tests/ui/codegen/sub-principals-in-codegen.rs @@ -0,0 +1,8 @@ +//@ build-pass + +// Regression test for an overly aggressive assertion in #130855. + +fn main() { + let subtype: &(dyn for<'a> Fn(&'a i32) -> &'a i32) = &|x| x; + let supertype: &(dyn Fn(&'static i32) -> &'static i32) = subtype; +} From cbb5047d35d76cd34ccf238220ccc70316f6ab77 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Sep 2024 12:42:29 -0400 Subject: [PATCH 1097/1301] Relate binders explicitly, do a leak check too --- compiler/rustc_codegen_ssa/src/base.rs | 45 +++++++++++++------ .../src/traits/engine.rs | 17 ++++++- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 1d39125498335..d91c0f0790d9d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -27,7 +27,8 @@ use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Symbol}; use rustc_target::abi::FIRST_VARIANT; -use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_trait_selection::infer::at::ToTrace; +use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; @@ -113,22 +114,38 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// unsound, so let's validate here that the trait refs are subtypes. pub fn validate_trivial_unsize<'tcx>( tcx: TyCtxt<'tcx>, - data_a: &'tcx ty::List>, - data_b: &'tcx ty::List>, + source_data: &'tcx ty::List>, + target_data: &'tcx ty::List>, ) -> bool { - match (data_a.principal(), data_b.principal()) { - (Some(principal_a), Some(principal_b)) => { + match (source_data.principal(), target_data.principal()) { + (Some(hr_source_principal), Some(hr_target_principal)) => { let infcx = tcx.infer_ctxt().build(); + let universe = infcx.universe(); let ocx = ObligationCtxt::new(&infcx); - let Ok(()) = ocx.sub( - &ObligationCause::dummy(), - ty::ParamEnv::reveal_all(), - principal_a, - principal_b, - ) else { - return false; - }; - ocx.select_all_or_error().is_empty() + infcx.enter_forall(hr_target_principal, |target_principal| { + let source_principal = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + hr_source_principal, + ); + let Ok(()) = ocx.eq_trace( + &ObligationCause::dummy(), + ty::ParamEnv::reveal_all(), + ToTrace::to_trace( + &ObligationCause::dummy(), + hr_target_principal, + hr_source_principal, + ), + target_principal, + source_principal, + ) else { + return false; + }; + if !ocx.select_all_or_error().is_empty() { + return false; + } + infcx.leak_check(universe, None).is_ok() + }) } (None, None) => true, _ => false, diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index de1d4ef15aced..d562692c1a865 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -9,12 +9,13 @@ use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance}; +use rustc_type_ir::relate::Relate; use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine}; use crate::error_reporting::InferCtxtErrorExt; @@ -133,6 +134,20 @@ where .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + pub fn eq_trace>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + trace: TypeTrace<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. pub fn sub>( &self, From af3f212453f667981bb07c0029f86bc607f3baa9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 29 Sep 2024 17:51:38 -0400 Subject: [PATCH 1098/1301] Instantiate binders in supertrait_vtable_slot --- .../src/traits/vtable.rs | 40 ++++++++++++++----- .../higher-ranked-upcasting-ok.rs | 15 +++---- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index a2760fe6049b0..5aed55ef2d27d 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -2,6 +2,8 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_hir::def_id::DefId; +use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt}; +use rustc_infer::traits::ObligationCause; use rustc_infer::traits::util::PredicateSet; use rustc_middle::bug; use rustc_middle::query::Providers; @@ -13,7 +15,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::debug; use crate::errors::DumpVTableEntries; -use crate::traits::{impossible_predicates, is_vtable_safe_method}; +use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method}; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { @@ -383,17 +385,37 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let ty::Dynamic(target, _, _) = *target.kind() else { bug!(); }; - let target_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal()?) - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let target_principal = target.principal()?.with_self_ty(tcx, tcx.types.trait_object_dummy_self); // Given that we have a target principal, it is a bug for there not to be a source principal. let ty::Dynamic(source, _, _) = *source.kind() else { bug!(); }; - let source_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = + source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); + + let infcx = tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::reveal_all(); + let trait_refs_are_compatible = + |source: ty::PolyTraitRef<'tcx>, target: ty::PolyTraitRef<'tcx>| { + infcx.probe(|_| { + let ocx = ObligationCtxt::new(&infcx); + let source = ocx.normalize(&ObligationCause::dummy(), param_env, source); + let target = ocx.normalize(&ObligationCause::dummy(), param_env, target); + infcx.enter_forall(target, |target| { + let source = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + source, + ); + let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target, source) + else { + return false; + }; + ocx.select_all_or_error().is_empty() + }) + }) + }; let vtable_segment_callback = { let mut vptr_offset = 0; @@ -404,9 +426,7 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); - if tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), trait_ref) - == target_principal - { + if trait_refs_are_compatible(trait_ref, target_principal) { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs index 7f793e1269fd7..c4c070e49fdb7 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs @@ -1,21 +1,22 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@ check-pass +//@ build-pass -// We should be able to instantiate a binder during trait upcasting. -// This test could be `check-pass`, but we should make sure that we -// do so in both trait solvers. +// Check that we are able to instantiate a binder during trait upcasting, +// and that it doesn't cause any issues with codegen either. #![feature(trait_upcasting)] trait Supertrait<'a, 'b> {} trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {} -impl<'a> Supertrait<'a, 'a> for () {} -impl<'a> Subtrait<'a, 'a> for () {} +impl Supertrait<'_, '_> for () {} +impl Subtrait<'_, '_> for () {} fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> { x } -fn main() {} +fn main() { + ok(&()); +} From d87e0ca497e70fe1c3112de0dce87fbd777f6451 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Sep 2024 13:14:44 -0400 Subject: [PATCH 1099/1301] Extract trait_refs_are_compatible, make it instantiate binders --- .../src/traits/engine.rs | 17 ++++- .../src/traits/vtable.rs | 75 ++++++++++++------- 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index de1d4ef15aced..d562692c1a865 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -9,12 +9,13 @@ use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance}; +use rustc_type_ir::relate::Relate; use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine}; use crate::error_reporting::InferCtxtErrorExt; @@ -133,6 +134,20 @@ where .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + pub fn eq_trace>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + trace: TypeTrace<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. pub fn sub>( &self, diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 5aed55ef2d27d..4f13524706b2d 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_hir::def_id::DefId; +use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::util::PredicateSet; @@ -24,6 +25,8 @@ pub enum VtblSegment<'tcx> { } /// Prepare the segments for a vtable +// FIXME: This should take a `PolyExistentialTraitRef`, since we don't care +// about our `Self` type here. pub fn prepare_vtable_segments<'tcx, T>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, @@ -385,7 +388,7 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let ty::Dynamic(target, _, _) = *target.kind() else { bug!(); }; - let target_principal = target.principal()?.with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let target_principal = target.principal()?; // Given that we have a target principal, it is a bug for there not to be a source principal. let ty::Dynamic(source, _, _) = *source.kind() else { @@ -394,29 +397,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let source_principal = source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); - let infcx = tcx.infer_ctxt().build(); - let param_env = ty::ParamEnv::reveal_all(); - let trait_refs_are_compatible = - |source: ty::PolyTraitRef<'tcx>, target: ty::PolyTraitRef<'tcx>| { - infcx.probe(|_| { - let ocx = ObligationCtxt::new(&infcx); - let source = ocx.normalize(&ObligationCause::dummy(), param_env, source); - let target = ocx.normalize(&ObligationCause::dummy(), param_env, target); - infcx.enter_forall(target, |target| { - let source = infcx.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - source, - ); - let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target, source) - else { - return false; - }; - ocx.select_all_or_error().is_empty() - }) - }) - }; - let vtable_segment_callback = { let mut vptr_offset = 0; move |segment| { @@ -424,9 +404,15 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( VtblSegment::MetadataDSA => { vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); - if trait_refs_are_compatible(trait_ref, target_principal) { + VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { + vptr_offset += + tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); + if trait_refs_are_compatible( + tcx, + vtable_principal + .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), + target_principal, + ) { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { @@ -446,6 +432,41 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap() } +fn trait_refs_are_compatible<'tcx>( + tcx: TyCtxt<'tcx>, + hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>, + hr_target_principal: ty::PolyExistentialTraitRef<'tcx>, +) -> bool { + if hr_vtable_principal.def_id() != hr_target_principal.def_id() { + return false; + } + + let infcx = tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::reveal_all(); + let ocx = ObligationCtxt::new(&infcx); + let hr_source_principal = + ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal); + let hr_target_principal = + ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal); + infcx.enter_forall(hr_target_principal, |target_principal| { + let source_principal = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + hr_source_principal, + ); + let Ok(()) = ocx.eq_trace( + &ObligationCause::dummy(), + param_env, + ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal), + target_principal, + source_principal, + ) else { + return false; + }; + ocx.select_all_or_error().is_empty() + }) +} + pub(super) fn provide(providers: &mut Providers) { *providers = Providers { own_existential_vtable_entries, From 7c552d56b2234018e6aa729cda6bdfbb7d533d28 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Sep 2024 13:17:31 -0400 Subject: [PATCH 1100/1301] Also fix first_method_vtable_slot --- .../src/traits/vtable.rs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 4f13524706b2d..6e6f948a2cdc4 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -332,14 +332,10 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { bug!(); }; - let source_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = + source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); - let target_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), key) - // We don't care about the self type, since it will always be the same thing. - .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key)); let vtable_segment_callback = { let mut vptr_offset = 0; @@ -348,15 +344,18 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe VtblSegment::MetadataDSA => { vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - if tcx - .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref) - == target_principal - { + VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { + if trait_refs_are_compatible( + tcx, + vtable_principal + .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), + target_principal, + ) { return ControlFlow::Break(vptr_offset); } - vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); + vptr_offset += + tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); if emit_vptr { vptr_offset += 1; From eaaa94318b57e64a1a502a082aae2ed45e343af0 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 30 Sep 2024 13:26:02 -0400 Subject: [PATCH 1101/1301] Unpin `cc` and upgrade to the latest version `cc` was previously pinned because version 1.1.106 dropped support for Visual Studio 12 (2013), and we wanted to decouple that from the rest of the automated updates. As noted in [2], there is no longer anything indicating we support VS2013, so it should be okay to unpin it. `cc` 1.1.22 contains a fix that may help improve the high MSVC CI failure rate [3], so we also have motivation to update to that point. [1]: https://github.com/rust-lang/rust/issues/129307 [2]: https://github.com/rust-lang/rust/issues/129307#issuecomment-2383749868 [3]: https://github.com/rust-lang/rust/issues/127883 --- Cargo.lock | 7 +++++-- compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_llvm/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 582b5a763e6f7..70731b198fc96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -393,9 +393,12 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.105" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5208975e568d83b6b05cc0a063c8e7e9acc2b43bee6da15616a5b73e109d7437" +checksum = "3bbb537bb4a30b90362caddba8f360c0a56bc13d3a5570028e7197204cb54a17" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 81590674ec7c1..f316a0a6cfc89 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" ar_archive_writer = "0.4.2" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" -cc = "=1.0.105" # FIXME(cc): pinned to keep support for VS2013 +cc = "1.1.23" either = "1.5.0" itertools = "0.12" jobserver = "0.1.28" diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 1f74aaf9965a7..b29d6b79250f3 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,5 +10,5 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -cc = "=1.0.105" # FIXME(cc): pinned to keep support for VS2013 +cc = "1.1.23" # tidy-alphabetical-end From 041e76b7cd7c499837fcea63f7f780fdf774a1a5 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Sat, 22 Jun 2024 22:17:57 +0300 Subject: [PATCH 1102/1301] Add multi-producer, multi-consumer channel (mpmc) --- library/std/src/lib.rs | 4 +- library/std/src/sync/mod.rs | 9 +- library/std/src/sync/mpmc/error.rs | 5 + library/std/src/sync/mpmc/mod.rs | 1035 ++++++++++++++++- library/std/src/sync/mpmc/tests.rs | 728 ++++++++++++ .../const-generics/issues/issue-82956.stderr | 2 +- tests/ui/issues/issue-12041.stderr | 2 +- tests/ui/lint/use_suggestion_json.stderr | 25 +- 8 files changed, 1758 insertions(+), 52 deletions(-) create mode 100644 library/std/src/sync/mpmc/tests.rs diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b81e7c18abbb0..65a9aa66c7cc6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -153,7 +153,7 @@ //! the [`io`], [`fs`], and [`net`] modules. //! //! The [`thread`] module contains Rust's threading abstractions. [`sync`] -//! contains further primitive shared memory types, including [`atomic`] and +//! contains further primitive shared memory types, including [`atomic`], [`mpmc`] and //! [`mpsc`], which contains the channel types for message passing. //! //! # Use before and after `main()` @@ -177,6 +177,7 @@ //! - after-main use of thread-locals, which also affects additional features: //! - [`thread::current()`] //! - [`thread::scope()`] +//! - [`sync::mpmc`] //! - [`sync::mpsc`] //! - before-main stdio file descriptors are not guaranteed to be open on unix platforms //! @@ -202,6 +203,7 @@ //! [`atomic`]: sync::atomic //! [`for`]: ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for //! [`str`]: prim@str +//! [`mpmc`]: sync::mpmc //! [`mpsc`]: sync::mpsc //! [`std::cmp`]: cmp //! [`std::slice`]: mod@slice diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 0fb8e669bf86e..0fb77331293fe 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -133,6 +133,11 @@ //! inter-thread synchronisation mechanism, at the cost of some //! extra memory. //! +//! - [`mpmc`]: Multi-producer, multi-consumer queues, used for +//! message-based communication. Can provide a lightweight +//! inter-thread synchronisation mechanism, at the cost of some +//! extra memory. +//! //! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at //! most one thread at a time is able to access some data. //! @@ -153,6 +158,7 @@ //! [`Arc`]: crate::sync::Arc //! [`Barrier`]: crate::sync::Barrier //! [`Condvar`]: crate::sync::Condvar +//! [`mpmc`]: crate::sync::mpmc //! [`mpsc`]: crate::sync::mpsc //! [`Mutex`]: crate::sync::Mutex //! [`Once`]: crate::sync::Once @@ -193,12 +199,13 @@ pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +#[unstable(feature = "mpmc_channel", issue = "126840")] +pub mod mpmc; pub mod mpsc; mod barrier; mod condvar; mod lazy_lock; -mod mpmc; mod mutex; pub(crate) mod once; mod once_lock; diff --git a/library/std/src/sync/mpmc/error.rs b/library/std/src/sync/mpmc/error.rs index e3aec7e76232f..e34b56d08312b 100644 --- a/library/std/src/sync/mpmc/error.rs +++ b/library/std/src/sync/mpmc/error.rs @@ -7,6 +7,7 @@ use crate::{error, fmt}; /// /// [`send_timeout`]: super::Sender::send_timeout #[derive(PartialEq, Eq, Clone, Copy)] +#[unstable(feature = "mpmc_channel", issue = "126840")] pub enum SendTimeoutError { /// The message could not be sent because the channel is full and the operation timed out. /// @@ -18,12 +19,14 @@ pub enum SendTimeoutError { Disconnected(T), } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for SendTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "SendTimeoutError(..)".fmt(f) } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Display for SendTimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -33,8 +36,10 @@ impl fmt::Display for SendTimeoutError { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl error::Error for SendTimeoutError {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl From> for SendTimeoutError { fn from(err: SendError) -> SendTimeoutError { match err { diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index c640e07348ea0..77a67f4fd386d 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -1,8 +1,112 @@ -//! Multi-producer multi-consumer channels. +//! Multi-producer, multi-consumer FIFO queue communication primitives. +//! +//! This module provides message-based communication over channels, concretely +//! defined by two types: +//! +//! * [`Sender`] +//! * [`Receiver`] +//! +//! [`Sender`]s are used to send data to a set of [`Receiver`]s. Both +//! sender and receiver are cloneable (multi-producer) such that many threads can send +//! simultaneously to receivers (multi-consumer). +//! +//! These channels come in two flavors: +//! +//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function +//! will return a `(Sender, Receiver)` tuple where all sends will be +//! **asynchronous** (they never block). The channel conceptually has an +//! infinite buffer. +//! +//! 2. A synchronous, bounded channel. The [`sync_channel`] function will +//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! messages is a pre-allocated buffer of a fixed size. All sends will be +//! **synchronous** by blocking until there is buffer space available. Note +//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" +//! channel where each sender atomically hands off a message to a receiver. +//! +//! [`send`]: Sender::send +//! +//! ## Disconnection +//! +//! The send and receive operations on channels will all return a [`Result`] +//! indicating whether the operation succeeded or not. An unsuccessful operation +//! is normally indicative of the other half of a channel having "hung up" by +//! being dropped in its corresponding thread. +//! +//! Once half of a channel has been deallocated, most operations can no longer +//! continue to make progress, so [`Err`] will be returned. Many applications +//! will continue to [`unwrap`] the results returned from this module, +//! instigating a propagation of failure among threads if one unexpectedly dies. +//! +//! [`unwrap`]: Result::unwrap +//! +//! # Examples +//! +//! Simple usage: +//! +//! ``` +//! #![feature(mpmc_channel)] +//! +//! use std::thread; +//! use std::sync::mpmc::channel; +//! +//! // Create a simple streaming channel +//! let (tx, rx) = channel(); +//! thread::spawn(move || { +//! tx.send(10).unwrap(); +//! }); +//! assert_eq!(rx.recv().unwrap(), 10); +//! ``` +//! +//! Shared usage: +//! +//! ``` +//! #![feature(mpmc_channel)] +//! +//! use std::thread; +//! use std::sync::mpmc::channel; +//! +//! // Create a shared channel that can be sent along from many threads +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). +//! let (tx, rx) = channel(); +//! for i in 0..10 { +//! let tx = tx.clone(); +//! thread::spawn(move || { +//! tx.send(i).unwrap(); +//! }); +//! } +//! +//! for _ in 0..5 { +//! let rx1 = rx.clone(); +//! let rx2 = rx.clone(); +//! thread::spawn(move || { +//! let j = rx1.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! thread::spawn(move || { +//! let j = rx2.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! } +//! ``` +//! +//! Propagating panics: +//! +//! ``` +//! #![feature(mpmc_channel)] +//! +//! use std::sync::mpmc::channel; +//! +//! // The call to recv() will return an error because the channel has already +//! // hung up (or been deallocated) +//! let (tx, rx) = channel::(); +//! drop(tx); +//! assert!(rx.recv().is_err()); +//! ``` -// This module is not currently exposed publicly, but is used -// as the implementation for the channels in `sync::mpsc`. The -// implementation comes from the crossbeam-channel crate: +// This module is used as the implementation for the channels in `sync::mpsc`. +// The implementation comes from the crossbeam-channel crate: // // Copyright (c) 2019 The Crossbeam Project Developers // @@ -46,9 +150,47 @@ use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::time::{Duration, Instant}; -/// Creates a channel of unbounded capacity. +/// Creates a new asynchronous channel, returning the sender/receiver halves. +/// All data sent on the [`Sender`] will become available on the [`Receiver`] in +/// the same order as it was sent, and no [`send`] will block the calling thread +/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will +/// block after its buffer limit is reached). [`recv`] will block until a message +/// is available while there is at least one [`Sender`] alive (including clones). /// -/// This channel has a growable buffer that can hold any number of messages at a time. +/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times. +/// The [`Receiver`] also can be cloned to have multi receivers. +/// +/// If the [`Receiver`] is disconnected while trying to [`send`] with the +/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the +/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will +/// return a [`RecvError`]. +/// +/// [`send`]: Sender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// +/// // Spawn off an expensive computation +/// thread::spawn(move || { +/// # fn expensive_computation() {} +/// sender.send(expensive_computation()).unwrap(); +/// }); +/// +/// // Do some useful work for awhile +/// +/// // Let's see what that answer was +/// println!("{:?}", receiver.recv().unwrap()); +/// ``` +#[must_use] +#[unstable(feature = "mpmc_channel", issue = "126840")] pub fn channel() -> (Sender, Receiver) { let (s, r) = counter::new(list::Channel::new()); let s = Sender { flavor: SenderFlavor::List(s) }; @@ -56,12 +198,50 @@ pub fn channel() -> (Sender, Receiver) { (s, r) } -/// Creates a channel of bounded capacity. +/// Creates a new synchronous, bounded channel. +/// All data sent on the [`Sender`] will become available on the [`Receiver`] +/// in the same order as it was sent. Like asynchronous [`channel`]s, the +/// [`Receiver`] will block until a message becomes available. `sync_channel` +/// differs greatly in the semantics of the sender, however. +/// +/// This channel has an internal buffer on which messages will be queued. +/// `bound` specifies the buffer size. When the internal buffer becomes full, +/// future sends will *block* waiting for the buffer to open up. Note that a +/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" +/// where each [`send`] will not return until a [`recv`] is paired with it. +/// +/// The [`Sender`] can be cloned to [`send`] to the same channel multiple +/// times. The [`Receiver`] also can be cloned to have multi receivers. +/// +/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying +/// to [`send`] with the [`Sender`], the [`send`] method will return a +/// [`SendError`]. Similarly, If the [`Sender`] is disconnected while trying +/// to [`recv`], the [`recv`] method will return a [`RecvError`]. +/// +/// [`send`]: Sender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// use std::sync::mpsc::sync_channel; +/// use std::thread; +/// +/// let (sender, receiver) = sync_channel(1); /// -/// This channel has a buffer that can hold at most `cap` messages at a time. +/// // this returns immediately +/// sender.send(1).unwrap(); /// -/// A special case is zero-capacity channel, which cannot hold any messages. Instead, send and -/// receive operations must appear at the same time in order to pair up and pass the message over. +/// thread::spawn(move || { +/// // this will block until the previous message has been received +/// sender.send(2).unwrap(); +/// }); +/// +/// assert_eq!(receiver.recv().unwrap(), 1); +/// assert_eq!(receiver.recv().unwrap(), 2); +/// ``` +#[must_use] +#[unstable(feature = "mpmc_channel", issue = "126840")] pub fn sync_channel(cap: usize) -> (Sender, Receiver) { if cap == 0 { let (s, r) = counter::new(zero::Channel::new()); @@ -76,7 +256,42 @@ pub fn sync_channel(cap: usize) -> (Sender, Receiver) { } } -/// The sending side of a channel. +/// The sending-half of Rust's synchronous [`channel`] type. +/// +/// Messages can be sent through this channel with [`send`]. +/// +/// Note: all senders (the original and its clones) need to be dropped for the receiver +/// to stop blocking to receive messages with [`Receiver::recv`]. +/// +/// [`send`]: Sender::send +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// let sender2 = sender.clone(); +/// +/// // First thread owns sender +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// }); +/// +/// // Second thread owns sender2 +/// thread::spawn(move || { +/// sender2.send(2).unwrap(); +/// }); +/// +/// let msg = receiver.recv().unwrap(); +/// let msg2 = receiver.recv().unwrap(); +/// +/// assert_eq!(3, msg + msg2); +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] pub struct Sender { flavor: SenderFlavor, } @@ -93,10 +308,14 @@ enum SenderFlavor { Zero(counter::Sender>), } +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Send for Sender {} +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Sync for Sender {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl UnwindSafe for Sender {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl RefUnwindSafe for Sender {} impl Sender { @@ -107,6 +326,19 @@ impl Sender { /// /// If called on a zero-capacity channel, this method will send the message only if there /// happens to be a receive operation on the other side of the channel at the same time. + /// + /// # Examples + /// + /// ```rust + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::{channel, Receiver, Sender}; + /// + /// let (sender, _receiver): (Sender, Receiver) = channel(); + /// + /// assert!(sender.try_send(1).is_ok()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { match &self.flavor { SenderFlavor::Array(chan) => chan.try_send(msg), @@ -115,14 +347,36 @@ impl Sender { } } - /// Blocks the current thread until a message is sent or the channel is disconnected. + /// Attempts to send a value on this channel, returning it back if it could + /// not be sent. /// - /// If the channel is full and not disconnected, this call will block until the send operation - /// can proceed. If the channel becomes disconnected, this call will wake up and return an - /// error. The returned error contains the original message. + /// A successful send occurs when it is determined that the other end of + /// the channel has not hung up already. An unsuccessful send would be one + /// where the corresponding receiver has already been deallocated. Note + /// that a return value of [`Err`] means that the data will never be + /// received, but a return value of [`Ok`] does *not* mean that the data + /// will be received. It is possible for the corresponding receiver to + /// hang up immediately after this function returns [`Ok`]. /// - /// If called on a zero-capacity channel, this method will wait for a receive operation to - /// appear on the other side of the channel. + /// This method will never block the current thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// + /// let (tx, rx) = channel(); + /// + /// // This send is always successful + /// tx.send(1).unwrap(); + /// + /// // This send will fail because the receiver is gone + /// drop(rx); + /// assert!(tx.send(1).is_err()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn send(&self, msg: T) -> Result<(), SendError> { match &self.flavor { SenderFlavor::Array(chan) => chan.send(msg, None), @@ -136,10 +390,6 @@ impl Sender { } } -// The methods below are not used by `sync::mpsc`, but -// are useful and we'll likely want to expose them -// eventually -#[allow(unused)] impl Sender { /// Waits for a message to be sent into the channel, but only for a limited time. /// @@ -149,6 +399,20 @@ impl Sender { /// /// If called on a zero-capacity channel, this method will wait for a receive operation to /// appear on the other side of the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::time::Duration; + /// + /// let (tx, rx) = channel(); + /// + /// tx.send_timeout(1, Duration::from_millis(400)).unwrap(); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn send_timeout(&self, msg: T, timeout: Duration) -> Result<(), SendTimeoutError> { match Instant::now().checked_add(timeout) { Some(deadline) => self.send_deadline(msg, deadline), @@ -165,6 +429,21 @@ impl Sender { /// /// If called on a zero-capacity channel, this method will wait for a receive operation to /// appear on the other side of the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::time::{Duration, Instant}; + /// + /// let (tx, rx) = channel(); + /// + /// let t = Instant::now() + Duration::from_millis(400); + /// tx.send_deadline(1, t).unwrap(); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn send_deadline(&self, msg: T, deadline: Instant) -> Result<(), SendTimeoutError> { match &self.flavor { SenderFlavor::Array(chan) => chan.send(msg, Some(deadline)), @@ -176,6 +455,31 @@ impl Sender { /// Returns `true` if the channel is empty. /// /// Note: Zero-capacity channels are always empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::channel(); + /// + /// let tx1 = send.clone(); + /// let tx2 = send.clone(); + /// + /// assert!(tx1.is_empty()); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(!tx1.is_empty()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_empty(&self) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.is_empty(), @@ -187,6 +491,29 @@ impl Sender { /// Returns `true` if the channel is full. /// /// Note: Zero-capacity channels are always full. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::sync_channel(1); + /// + /// let (tx1, tx2) = (send.clone(), send.clone()); + /// assert!(!tx1.is_full()); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(tx1.is_full()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_full(&self) -> bool { match &self.flavor { SenderFlavor::Array(chan) => chan.is_full(), @@ -196,6 +523,29 @@ impl Sender { } /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::channel(); + /// let (tx1, tx2) = (send.clone(), send.clone()); + /// + /// assert_eq!(tx1.len(), 0); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(tx1.len(), 1); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn len(&self) -> usize { match &self.flavor { SenderFlavor::Array(chan) => chan.len(), @@ -205,6 +555,29 @@ impl Sender { } /// If the channel is bounded, returns its capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, _recv) = mpmc::sync_channel(3); + /// let (tx1, tx2) = (send.clone(), send.clone()); + /// + /// assert_eq!(tx1.capacity(), Some(3)); + /// + /// let handle = thread::spawn(move || { + /// tx2.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(tx1.capacity(), Some(3)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn capacity(&self) -> Option { match &self.flavor { SenderFlavor::Array(chan) => chan.capacity(), @@ -214,6 +587,21 @@ impl Sender { } /// Returns `true` if senders belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// + /// let (tx1, _) = mpmc::channel::(); + /// let (tx2, _) = mpmc::channel::(); + /// + /// assert!(tx1.same_channel(&tx1)); + /// assert!(!tx1.same_channel(&tx2)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn same_channel(&self, other: &Sender) -> bool { match (&self.flavor, &other.flavor) { (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b, @@ -224,6 +612,7 @@ impl Sender { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Drop for Sender { fn drop(&mut self) { unsafe { @@ -236,6 +625,7 @@ impl Drop for Sender { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Clone for Sender { fn clone(&self) -> Self { let flavor = match &self.flavor { @@ -248,17 +638,216 @@ impl Clone for Sender { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Sender { .. }") } } -/// The receiving side of a channel. +/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. +/// Different threads can share this [`Sender`] by cloning it. +/// +/// Messages sent to the channel can be retrieved using [`recv`]. +/// +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (send, recv) = channel(); +/// +/// let tx_thread = thread::spawn(move || { +/// send.send("Hello world!").unwrap(); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// send.send("Delayed for 2 seconds").unwrap(); +/// }); +/// +/// let (rx1, rx2) = (recv.clone(), recv.clone()); +/// let rx_thread_1 = thread::spawn(move || { +/// println!("{}", rx1.recv().unwrap()); // Received immediately +/// }); +/// let rx_thread_2 = thread::spawn(move || { +/// println!("{}", rx2.recv().unwrap()); // Received after 2 seconds +/// }); +/// +/// tx_thread.join().unwrap(); +/// rx_thread_1.join().unwrap(); +/// rx_thread_2.join().unwrap(); +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] pub struct Receiver { flavor: ReceiverFlavor, } +/// An iterator over messages on a [`Receiver`], created by [`iter`]. +/// +/// This iterator will block whenever [`next`] is called, +/// waiting for a new message, and [`None`] will be returned +/// when the corresponding channel has hung up. +/// +/// [`iter`]: Receiver::iter +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] +#[derive(Debug)] +pub struct Iter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An iterator that attempts to yield all pending values for a [`Receiver`], +/// created by [`try_iter`]. +/// +/// [`None`] will be returned when there are no pending values remaining or +/// if the corresponding channel has hung up. +/// +/// This iterator will never block the caller in order to wait for data to +/// become available. Instead, it will return [`None`]. +/// +/// [`try_iter`]: Receiver::try_iter +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (sender, receiver) = channel(); +/// +/// // Nothing is in the buffer yet +/// assert!(receiver.try_iter().next().is_none()); +/// println!("Nothing in the buffer..."); +/// +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// sender.send(2).unwrap(); +/// sender.send(3).unwrap(); +/// }); +/// +/// println!("Going to sleep..."); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// +/// for x in receiver.try_iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] +#[derive(Debug)] +pub struct TryIter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An owning iterator over messages on a [`Receiver`], +/// created by [`into_iter`]. +/// +/// This iterator will block whenever [`next`] +/// is called, waiting for a new message, and [`None`] will be +/// returned if the corresponding channel has hung up. +/// +/// [`into_iter`]: Receiver::into_iter +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// #![feature(mpmc_channel)] +/// +/// use std::sync::mpmc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.into_iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[unstable(feature = "mpmc_channel", issue = "126840")] +#[derive(Debug)] +pub struct IntoIter { + rx: Receiver, +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl<'a, T> Iterator for TryIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.try_recv().ok() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl<'a, T> IntoIterator for &'a Receiver { + type Item = T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[unstable(feature = "mpmc_channel", issue = "126840")] +impl IntoIterator for Receiver { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter { rx: self } + } +} + /// Receiver flavors. enum ReceiverFlavor { /// Bounded channel based on a preallocated array. @@ -271,20 +860,46 @@ enum ReceiverFlavor { Zero(counter::Receiver>), } +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Send for Receiver {} +#[unstable(feature = "mpmc_channel", issue = "126840")] unsafe impl Sync for Receiver {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl UnwindSafe for Receiver {} +#[unstable(feature = "mpmc_channel", issue = "126840")] impl RefUnwindSafe for Receiver {} impl Receiver { /// Attempts to receive a message from the channel without blocking. /// - /// This method will either receive a message from the channel immediately or return an error - /// if the channel is empty. + /// This method will never block the caller in order to wait for data to + /// become available. Instead, this will always return immediately with a + /// possible option of pending data on the channel. /// /// If called on a zero-capacity channel, this method will receive a message only if there /// happens to be a send operation on the other side of the channel at the same time. + /// + /// This is useful for a flavor of "optimistic check" before deciding to + /// block on a receiver. + /// + /// Compared with [`recv`], this function has two failure cases instead of one + /// (one for disconnection, one for an empty buffer). + /// + /// [`recv`]: Self::recv + /// + /// # Examples + /// + /// ```rust + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::{Receiver, channel}; + /// + /// let (_, receiver): (_, Receiver) = channel(); + /// + /// assert!(receiver.try_recv().is_err()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn try_recv(&self) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.try_recv(), @@ -293,15 +908,64 @@ impl Receiver { } } - /// Blocks the current thread until a message is received or the channel is empty and - /// disconnected. + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up. /// - /// If the channel is empty and not disconnected, this call will block until the receive - /// operation can proceed. If the channel is empty and becomes disconnected, this call will - /// wake up and return an error. + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent (at least one sender + /// still exists). Once a message is sent to the corresponding [`Sender`], + /// this receiver will wake up and return that message. /// - /// If called on a zero-capacity channel, this method will wait for a send operation to appear - /// on the other side of the channel. + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// ``` + /// + /// Buffering behavior: + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// use std::sync::mpmc::RecvError; + /// + /// let (send, recv) = mpmc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// drop(send); + /// }); + /// + /// // wait for the thread to join so we ensure the sender is dropped + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// assert_eq!(Ok(2), recv.recv()); + /// assert_eq!(Ok(3), recv.recv()); + /// assert_eq!(Err(RecvError), recv.recv()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn recv(&self) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.recv(None), @@ -311,14 +975,65 @@ impl Receiver { .map_err(|_| RecvError) } - /// Waits for a message to be received from the channel, but only for a limited time. + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if it waits more than `timeout`. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent (at least one sender + /// still exists). Once a message is sent to the corresponding [`Sender`], + /// this receiver will wake up and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// Successfully receiving value before encountering timeout: /// - /// If the channel is empty and not disconnected, this call will block until the receive - /// operation can proceed or the operation times out. If the channel is empty and becomes - /// disconnected, this call will wake up and return an error. + /// ```no_run + /// #![feature(mpmc_channel)] /// - /// If called on a zero-capacity channel, this method will wait for a send operation to appear - /// on the other side of the channel. + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpmc; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching timeout: + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpmc; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Err(mpmc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn recv_timeout(&self, timeout: Duration) -> Result { match Instant::now().checked_add(timeout) { Some(deadline) => self.recv_deadline(deadline), @@ -327,14 +1042,65 @@ impl Receiver { } } - /// Waits for a message to be received from the channel, but only for a limited time. + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if `deadline` is reached. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding [`Sender`], then this receiver will wake up + /// and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// Successfully receiving value before reaching deadline: + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpmc; /// - /// If the channel is empty and not disconnected, this call will block until the receive - /// operation can proceed or the operation times out. If the channel is empty and becomes - /// disconnected, this call will wake up and return an error. + /// let (send, recv) = mpmc::channel(); /// - /// If called on a zero-capacity channel, this method will wait for a send operation to appear - /// on the other side of the channel. + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching deadline: + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpmc; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Err(mpmc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn recv_deadline(&self, deadline: Instant) -> Result { match &self.flavor { ReceiverFlavor::Array(chan) => chan.recv(Some(deadline)), @@ -342,16 +1108,77 @@ impl Receiver { ReceiverFlavor::Zero(chan) => chan.recv(Some(deadline)), } } + + /// Returns an iterator that will attempt to yield all pending values. + /// It will return `None` if there are no more pending values or if the + /// channel has hung up. The iterator will never [`panic!`] or block the + /// user by waiting for values. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (sender, receiver) = channel(); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_secs(1)); + /// sender.send(1).unwrap(); + /// sender.send(2).unwrap(); + /// sender.send(3).unwrap(); + /// }); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// // block for two seconds + /// thread::sleep(Duration::from_secs(2)); + /// + /// let mut iter = receiver.try_iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] + pub fn try_iter(&self) -> TryIter<'_, T> { + TryIter { rx: self } + } } -// The methods below are not used by `sync::mpsc`, but -// are useful and we'll likely want to expose them -// eventually -#[allow(unused)] impl Receiver { /// Returns `true` if the channel is empty. /// /// Note: Zero-capacity channels are always empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// assert!(recv.is_empty()); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(!recv.is_empty()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_empty(&self) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.is_empty(), @@ -363,6 +1190,28 @@ impl Receiver { /// Returns `true` if the channel is full. /// /// Note: Zero-capacity channels are always full. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::sync_channel(1); + /// + /// assert!(!recv.is_full()); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert!(recv.is_full()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn is_full(&self) -> bool { match &self.flavor { ReceiverFlavor::Array(chan) => chan.is_full(), @@ -372,6 +1221,28 @@ impl Receiver { } /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::channel(); + /// + /// assert_eq!(recv.len(), 0); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(recv.len(), 1); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn len(&self) -> usize { match &self.flavor { ReceiverFlavor::Array(chan) => chan.len(), @@ -381,6 +1252,28 @@ impl Receiver { } /// If the channel is bounded, returns its capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// use std::thread; + /// + /// let (send, recv) = mpmc::sync_channel(3); + /// + /// assert_eq!(recv.capacity(), Some(3)); + /// + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(recv.capacity(), Some(3)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn capacity(&self) -> Option { match &self.flavor { ReceiverFlavor::Array(chan) => chan.capacity(), @@ -390,6 +1283,21 @@ impl Receiver { } /// Returns `true` if receivers belong to the same channel. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc; + /// + /// let (_, rx1) = mpmc::channel::(); + /// let (_, rx2) = mpmc::channel::(); + /// + /// assert!(rx1.same_channel(&rx1)); + /// assert!(!rx1.same_channel(&rx2)); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn same_channel(&self, other: &Receiver) -> bool { match (&self.flavor, &other.flavor) { (ReceiverFlavor::Array(a), ReceiverFlavor::Array(b)) => a == b, @@ -398,8 +1306,39 @@ impl Receiver { _ => false, } } + + /// Returns an iterator that will block waiting for messages, but never + /// [`panic!`]. It will return [`None`] when the channel has hung up. + /// + /// # Examples + /// + /// ```rust + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// use std::thread; + /// + /// let (send, recv) = channel(); + /// + /// thread::spawn(move || { + /// send.send(1).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// }); + /// + /// let mut iter = recv.iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { rx: self } + } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Drop for Receiver { fn drop(&mut self) { unsafe { @@ -412,6 +1351,7 @@ impl Drop for Receiver { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl Clone for Receiver { fn clone(&self) -> Self { let flavor = match &self.flavor { @@ -424,6 +1364,7 @@ impl Clone for Receiver { } } +#[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Receiver { .. }") diff --git a/library/std/src/sync/mpmc/tests.rs b/library/std/src/sync/mpmc/tests.rs new file mode 100644 index 0000000000000..ab14050df6c98 --- /dev/null +++ b/library/std/src/sync/mpmc/tests.rs @@ -0,0 +1,728 @@ +use super::*; +use crate::{env, thread}; + +pub fn stress_factor() -> usize { + match env::var("RUST_TEST_STRESS") { + Ok(val) => val.parse().unwrap(), + Err(..) => 1, + } +} + +#[test] +fn smoke() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn drop_full() { + let (tx, _rx) = channel::>(); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn drop_full_shared() { + let (tx, _rx) = channel::>(); + drop(tx.clone()); + drop(tx.clone()); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn smoke_shared() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); + let tx = tx.clone(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_threads() { + let (tx, rx) = channel::(); + let t1 = thread::spawn(move || { + for i in 0..2 { + tx.send(i).unwrap(); + } + }); + let t2 = thread::spawn(move || { + assert_eq!(rx.recv().unwrap(), 0); + assert_eq!(rx.recv().unwrap(), 1); + }); + t1.join().unwrap(); + t2.join().unwrap(); +} + +#[test] +fn smoke_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()); +} + +#[test] +fn smoke_shared_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()) +} + +#[test] +fn smoke_shared_port_gone2() { + let (tx, rx) = channel::(); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + assert!(tx2.send(1).is_err()); +} + +#[test] +fn port_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() {} +} + +#[test] +fn port_gone_concurrent_shared() { + let (tx, rx) = channel::(); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() && tx2.send(1).is_ok() {} +} + +#[test] +fn smoke_chan_gone() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn smoke_chan_gone_shared() { + let (tx, rx) = channel::<()>(); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + assert!(rx.recv().is_err()); +} + +#[test] +fn chan_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + tx.send(1).unwrap(); + }); + while rx.recv().is_ok() {} +} + +#[test] +fn stress() { + let count = if cfg!(miri) { 100 } else { 10000 }; + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..count { + tx.send(1).unwrap(); + } + }); + for _ in 0..count { + assert_eq!(rx.recv().unwrap(), 1); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn stress_shared() { + const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; + const NTHREADS: u32 = 8; + let (tx, rx) = channel::(); + + let t = thread::spawn(move || { + for _ in 0..AMT * NTHREADS { + assert_eq!(rx.recv().unwrap(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + drop(tx); + t.join().ok().expect("thread panicked"); +} + +#[test] +fn send_from_outside_runtime() { + let (tx1, rx1) = channel::<()>(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + tx1.send(()).unwrap(); + for _ in 0..40 { + assert_eq!(rx2.recv().unwrap(), 1); + } + }); + rx1.recv().unwrap(); + let t2 = thread::spawn(move || { + for _ in 0..40 { + tx2.send(1).unwrap(); + } + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn recv_from_outside_runtime() { + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..40 { + assert_eq!(rx.recv().unwrap(), 1); + } + }); + for _ in 0..40 { + tx.send(1).unwrap(); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn no_runtime() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + assert_eq!(rx1.recv().unwrap(), 1); + tx2.send(2).unwrap(); + }); + let t2 = thread::spawn(move || { + tx1.send(1).unwrap(); + assert_eq!(rx2.recv().unwrap(), 2); + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = channel::(); + drop(rx); +} + +#[test] +fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = channel::(); + drop(tx); +} + +#[test] +fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = channel::>(); + drop(rx); + assert!(tx.send(Box::new(0)).is_err()); +} + +#[test] +fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = thread::spawn(move || { + let (tx, rx) = channel::(); + drop(tx); + rx.recv().unwrap(); + }) + .join(); + // What is our res? + assert!(res.is_err()); +} + +#[test] +fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = channel::>(); + tx.send(Box::new(10)).unwrap(); + assert!(*rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_open() { + let (tx, rx) = channel::(); + assert!(tx.send(10).is_ok()); + assert!(rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(10).is_err()); +} + +#[test] +fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = channel::(); + tx.send(10).unwrap(); + assert!(rx.recv() == Ok(10)); +} + +#[test] +fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn oneshot_single_thread_peek_data() { + let (tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + tx.send(10).unwrap(); + assert_eq!(rx.try_recv(), Ok(10)); +} + +#[test] +fn oneshot_single_thread_peek_close() { + let (tx, rx) = channel::(); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_open() { + let (_tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }); + + tx.send(Box::new(10)).unwrap(); +} + +#[test] +fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + drop(tx); + }); + let res = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }) + .join(); + assert!(res.is_err()); +} + +#[test] +fn oneshot_multi_thread_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + drop(tx); + } +} + +#[test] +fn oneshot_multi_thread_send_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + let _ = thread::spawn(move || { + tx.send(1).unwrap(); + }) + .join(); + } +} + +#[test] +fn oneshot_multi_thread_recv_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + thread::spawn(move || { + let res = thread::spawn(move || { + rx.recv().unwrap(); + }) + .join(); + assert!(res.is_err()); + }); + let _t = thread::spawn(move || { + thread::spawn(move || { + drop(tx); + }); + }); + } +} + +#[test] +fn oneshot_multi_thread_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + tx.send(Box::new(10)).unwrap(); + }); + assert!(*rx.recv().unwrap() == 10); + } +} + +#[test] +fn stream_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel(); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: Sender>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + tx.send(Box::new(i)).unwrap(); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1); + }); + } + } +} + +#[test] +fn oneshot_single_thread_recv_timeout() { + let (tx, rx) = channel(); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn stress_recv_timeout_two_threads() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + let timeout = Duration::from_millis(100); + + thread::spawn(move || { + for i in 0..stress { + if i % 2 == 0 { + thread::sleep(timeout * 2); + } + tx.send(1usize).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(timeout) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn recv_timeout_upgrade() { + let (tx, rx) = channel::<()>(); + let timeout = Duration::from_millis(1); + let _tx_clone = tx.clone(); + + let start = Instant::now(); + assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); + assert!(Instant::now() >= start + timeout); +} + +#[test] +fn stress_recv_timeout_shared() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + + for i in 0..stress { + let tx = tx.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(i as u64 * 10)); + tx.send(1usize).unwrap(); + }); + } + + drop(tx); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn very_long_recv_timeout_wont_panic() { + let (tx, rx) = channel::<()>(); + let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); + thread::sleep(Duration::from_secs(1)); + assert!(tx.send(()).is_ok()); + assert_eq!(join_handle.join().unwrap(), Ok(())); +} + +#[test] +fn recv_a_lot() { + let count = if cfg!(miri) { 1000 } else { 10000 }; + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = channel(); + for _ in 0..count { + tx.send(()).unwrap(); + } + for _ in 0..count { + rx.recv().unwrap(); + } +} + +#[test] +fn shared_recv_timeout() { + let (tx, rx) = channel(); + let total = 5; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } + + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn shared_chan_stress() { + let (tx, rx) = channel(); + let total = stress_factor() + 100; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } +} + +#[test] +fn test_nested_recv_iter() { + let (tx, rx) = channel::(); + let (total_tx, total_rx) = channel::(); + + let _t = thread::spawn(move || { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc).unwrap(); + }); + + tx.send(3).unwrap(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + assert_eq!(total_rx.recv().unwrap(), 6); +} + +#[test] +fn test_recv_iter_break() { + let (tx, rx) = channel::(); + let (count_tx, count_rx) = channel(); + + let _t = thread::spawn(move || { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count).unwrap(); + }); + + tx.send(2).unwrap(); + tx.send(2).unwrap(); + tx.send(2).unwrap(); + let _ = tx.send(2); + drop(tx); + assert_eq!(count_rx.recv().unwrap(), 4); +} + +#[test] +fn test_recv_try_iter() { + let (request_tx, request_rx) = channel(); + let (response_tx, response_rx) = channel(); + + // Request `x`s until we have `6`. + let t = thread::spawn(move || { + let mut count = 0; + loop { + for x in response_rx.try_iter() { + count += x; + if count == 6 { + return count; + } + } + request_tx.send(()).unwrap(); + } + }); + + for _ in request_rx.iter() { + if response_tx.send(2).is_err() { + break; + } + } + + assert_eq!(t.join().unwrap(), 6); +} + +#[test] +fn test_recv_into_iter_owned() { + let mut iter = { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + + rx.into_iter() + }; + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn test_recv_into_iter_borrowed() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + let mut iter = (&rx).into_iter(); + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn try_recv_states() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::<()>(); + let (tx3, rx3) = channel::<()>(); + let _t = thread::spawn(move || { + rx2.recv().unwrap(); + tx1.send(1).unwrap(); + tx3.send(()).unwrap(); + rx2.recv().unwrap(); + drop(tx1); + tx3.send(()).unwrap(); + }); + + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); +} + +// This bug used to end up in a livelock inside of the Receiver destructor +// because the internal state of the Shared packet was corrupted +#[test] +fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()).unwrap(); + }); + // make sure the other thread has gone to sleep + for _ in 0..5000 { + thread::yield_now(); + } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()).unwrap(); + + // wait for the child thread to exit before we exit + rx2.recv().unwrap(); +} + +#[test] +fn issue_32114() { + let (tx, _) = channel(); + let _ = tx.send(123); + assert_eq!(tx.send(123), Err(SendError(123))); +} + +#[test] +fn issue_39364() { + let (tx, rx) = channel::<()>(); + let t = thread::spawn(move || { + thread::sleep(Duration::from_millis(300)); + let _ = tx.clone(); + // Don't drop; hand back to caller. + tx + }); + + let _ = rx.recv_timeout(Duration::from_millis(500)); + let _tx = t.join().unwrap(); // delay dropping until end of test + let _ = rx.recv_timeout(Duration::from_millis(500)); +} diff --git a/tests/ui/const-generics/issues/issue-82956.stderr b/tests/ui/const-generics/issues/issue-82956.stderr index a956fc741f4d4..5e380eea81cab 100644 --- a/tests/ui/const-generics/issues/issue-82956.stderr +++ b/tests/ui/const-generics/issues/issue-82956.stderr @@ -14,7 +14,7 @@ LL + use std::collections::btree_map::IntoIter; | LL + use std::collections::btree_set::IntoIter; | - and 8 other candidates + and 9 other candidates error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-12041.stderr b/tests/ui/issues/issue-12041.stderr index 51061c0262eb1..f2c10b833836b 100644 --- a/tests/ui/issues/issue-12041.stderr +++ b/tests/ui/issues/issue-12041.stderr @@ -4,7 +4,7 @@ error[E0382]: use of moved value: `tx` LL | let tx = tx; | ^^ value moved here, in previous iteration of loop | - = note: move occurs because `tx` has type `Sender`, which does not implement the `Copy` trait + = note: move occurs because `tx` has type `std::sync::mpsc::Sender`, which does not implement the `Copy` trait error: aborting due to 1 previous error diff --git a/tests/ui/lint/use_suggestion_json.stderr b/tests/ui/lint/use_suggestion_json.stderr index 4683e5dd8f3c2..0d4304e2e2e40 100644 --- a/tests/ui/lint/use_suggestion_json.stderr +++ b/tests/ui/lint/use_suggestion_json.stderr @@ -348,6 +348,29 @@ mod foo { "label": null, "suggested_replacement": "use std::slice::Iter; +", + "suggestion_applicability": "MaybeIncorrect", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 541, + "byte_end": 541, + "line_start": 11, + "line_end": 11, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::sync::mpmc::Iter; + ", "suggestion_applicability": "MaybeIncorrect", "expansion": null @@ -396,7 +419,7 @@ mod foo { \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[38;5;10m+ use std::collections::hash_map::Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m -\u001b[0m and 8 other candidates\u001b[0m +\u001b[0m and 9 other candidates\u001b[0m " } From 7650307c0e9d10c6c0f5e1470629a23218fd7422 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 30 Sep 2024 20:49:35 +0200 Subject: [PATCH 1103/1301] add test ensuring we cannot call const-stable unstable functions --- .../auxiliary/unstable_but_const_stable.rs | 13 ++++++ tests/ui/consts/unstable-const-stable.rs | 14 ++++++ tests/ui/consts/unstable-const-stable.stderr | 43 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 tests/ui/consts/auxiliary/unstable_but_const_stable.rs create mode 100644 tests/ui/consts/unstable-const-stable.rs create mode 100644 tests/ui/consts/unstable-const-stable.stderr diff --git a/tests/ui/consts/auxiliary/unstable_but_const_stable.rs b/tests/ui/consts/auxiliary/unstable_but_const_stable.rs new file mode 100644 index 0000000000000..88044b0272c78 --- /dev/null +++ b/tests/ui/consts/auxiliary/unstable_but_const_stable.rs @@ -0,0 +1,13 @@ +#![feature(staged_api, rustc_attrs, intrinsics)] +#![stable(since="1.0.0", feature = "stable")] + +extern "rust-intrinsic" { + #[unstable(feature = "unstable", issue = "42")] + #[rustc_const_stable(feature = "stable", since = "1.0.0")] + #[rustc_nounwind] + pub fn write_bytes(dst: *mut T, val: u8, count: usize); +} + +#[unstable(feature = "unstable", issue = "42")] +#[rustc_const_stable(feature = "stable", since = "1.0.0")] +pub const fn some_unstable_fn() {} diff --git a/tests/ui/consts/unstable-const-stable.rs b/tests/ui/consts/unstable-const-stable.rs new file mode 100644 index 0000000000000..f69e8d0efe593 --- /dev/null +++ b/tests/ui/consts/unstable-const-stable.rs @@ -0,0 +1,14 @@ +//@ aux-build:unstable_but_const_stable.rs + +extern crate unstable_but_const_stable; +use unstable_but_const_stable::*; + +fn main() { + some_unstable_fn(); //~ERROR use of unstable library feature + unsafe { write_bytes(4 as *mut u8, 0, 0) }; //~ERROR use of unstable library feature +} + +const fn const_main() { + some_unstable_fn(); //~ERROR use of unstable library feature + unsafe { write_bytes(4 as *mut u8, 0, 0) }; //~ERROR use of unstable library feature +} diff --git a/tests/ui/consts/unstable-const-stable.stderr b/tests/ui/consts/unstable-const-stable.stderr new file mode 100644 index 0000000000000..c4ffbbb60db3c --- /dev/null +++ b/tests/ui/consts/unstable-const-stable.stderr @@ -0,0 +1,43 @@ +error[E0658]: use of unstable library feature 'unstable' + --> $DIR/unstable-const-stable.rs:7:5 + | +LL | some_unstable_fn(); + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #42 for more information + = help: add `#![feature(unstable)]` 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 'unstable' + --> $DIR/unstable-const-stable.rs:8:14 + | +LL | unsafe { write_bytes(4 as *mut u8, 0, 0) }; + | ^^^^^^^^^^^ + | + = note: see issue #42 for more information + = help: add `#![feature(unstable)]` 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 'unstable' + --> $DIR/unstable-const-stable.rs:12:5 + | +LL | some_unstable_fn(); + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #42 for more information + = help: add `#![feature(unstable)]` 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 'unstable' + --> $DIR/unstable-const-stable.rs:13:14 + | +LL | unsafe { write_bytes(4 as *mut u8, 0, 0) }; + | ^^^^^^^^^^^ + | + = note: see issue #42 for more information + = help: add `#![feature(unstable)]` 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 4 previous errors + +For more information about this error, try `rustc --explain E0658`. From df6c27db4e0f6060339ea1289ce87b8cbb3aa729 Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 1 Oct 2024 03:00:14 +0800 Subject: [PATCH 1104/1301] Refactor return_read_bytes_and_count and return_written_byte_count_or_error --- src/tools/miri/src/helpers.rs | 13 +++- src/tools/miri/src/shims/unix/fd.rs | 65 ++++++++++--------- src/tools/miri/src/shims/unix/fs.rs | 20 ++++-- .../miri/src/shims/unix/linux/eventfd.rs | 19 ++---- .../miri/src/shims/unix/unnamed_socket.rs | 26 +++----- 5 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 0cd6ba69aac9d..5324e4bec5b22 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use std::num::NonZero; use std::sync::Mutex; use std::time::Duration; -use std::{cmp, iter}; +use std::{cmp, io, iter}; use rand::RngCore; use rustc_apfloat::Float; @@ -839,6 +839,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.set_last_error(self.io_error_to_errnum(err)?) } + /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. + fn set_last_error_and_return( + &mut self, + err: impl Into, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + self.set_last_error(self.io_error_to_errnum(err.into())?)?; + self.write_int(-1, dest)?; + Ok(()) + } + /// Helper function that consumes an `std::io::Result` and returns an /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns /// `Ok(-1)` and sets the last OS error accordingly. diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 6bd753d0d6bee..f64d0ed1467bc 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -150,7 +150,10 @@ impl FileDescription for io::Stdin { helpers::isolation_abort_error("`read` from stdin")?; } let result = Read::read(&mut { self }, &mut bytes); - ecx.return_read_bytes_and_count(ptr, &bytes, result, dest) + match result { + Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest), + Err(e) => ecx.set_last_error_and_return(e, dest), + } } fn is_tty(&self, communicate_allowed: bool) -> bool { @@ -181,7 +184,10 @@ impl FileDescription for io::Stdout { // the host -- there is no good in adding extra buffering // here. io::stdout().flush().unwrap(); - ecx.return_written_byte_count_or_error(result, dest) + match result { + Ok(write_size) => ecx.return_write_success(write_size, dest), + Err(e) => ecx.set_last_error_and_return(e, dest), + } } fn is_tty(&self, communicate_allowed: bool) -> bool { @@ -207,7 +213,10 @@ impl FileDescription for io::Stderr { // We allow writing to stderr even with isolation enabled. // No need to flush, stderr is not buffered. let result = Write::write(&mut { self }, bytes); - ecx.return_written_byte_count_or_error(result, dest) + match result { + Ok(write_size) => ecx.return_write_success(write_size, dest), + Err(e) => ecx.set_last_error_and_return(e, dest), + } } fn is_tty(&self, communicate_allowed: bool) -> bool { @@ -234,8 +243,7 @@ impl FileDescription for NullOutput { ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx> { // We just don't write anything, but report to the user that we did. - let result = Ok(len); - ecx.return_written_byte_count_or_error(result, dest) + ecx.return_write_success(len, dest) } } @@ -655,46 +663,39 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Helper to implement `FileDescription::read`: - /// `result` should be the return value of some underlying `read` call that used `bytes` as its output buffer. + /// This is only used when `read` is successful. + /// `actual_read_size` should be the return value of some underlying `read` call that used + /// `bytes` as its output buffer. /// The length of `bytes` must not exceed either the host's or the target's `isize`. - /// If `Result` indicates success, `bytes` is written to `buf` and the size is written to `dest`. - /// Otherwise, `-1` is written to `dest` and the last libc error is set appropriately. - fn return_read_bytes_and_count( + /// `bytes` is written to `buf` and the size is written to `dest`. + fn return_read_success( &mut self, buf: Pointer, bytes: &[u8], - result: io::Result, + actual_read_size: usize, dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - match result { - Ok(read_bytes) => { - // If reading to `bytes` did not fail, we write those bytes to the buffer. - // Crucially, if fewer than `bytes.len()` bytes were read, only write - // that much into the output buffer! - this.write_bytes_ptr(buf, bytes[..read_bytes].iter().copied())?; - // The actual read size is always less than what got originally requested so this cannot fail. - this.write_int(u64::try_from(read_bytes).unwrap(), dest)?; - Ok(()) - } - Err(e) => { - this.set_last_error_from_io_error(e)?; - this.write_int(-1, dest)?; - Ok(()) - } - } + // If reading to `bytes` did not fail, we write those bytes to the buffer. + // Crucially, if fewer than `bytes.len()` bytes were read, only write + // that much into the output buffer! + this.write_bytes_ptr(buf, bytes[..actual_read_size].iter().copied())?; + + // The actual read size is always less than what got originally requested so this cannot fail. + this.write_int(u64::try_from(actual_read_size).unwrap(), dest)?; + Ok(()) } - /// This function writes the number of written bytes (given in `result`) to `dest`, or sets the - /// last libc error and writes -1 to dest. - fn return_written_byte_count_or_error( + /// Helper to implement `FileDescription::write`: + /// This function is only used when `write` is successful, and writes `actual_write_size` to `dest` + fn return_write_success( &mut self, - result: io::Result, + actual_write_size: usize, dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let result = this.try_unwrap_io_result(result.map(|c| i64::try_from(c).unwrap()))?; - this.write_int(result, dest)?; + // The actual write size is always less than what got originally requested so this cannot fail. + this.write_int(u64::try_from(actual_write_size).unwrap(), dest)?; Ok(()) } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 92514b67beab1..2fa1729ad9c8a 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -41,7 +41,10 @@ impl FileDescription for FileHandle { assert!(communicate_allowed, "isolation should have prevented even opening a file"); let mut bytes = vec![0; len]; let result = (&mut &self.file).read(&mut bytes); - ecx.return_read_bytes_and_count(ptr, &bytes, result, dest) + match result { + Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest), + Err(e) => ecx.set_last_error_and_return(e, dest), + } } fn write<'tcx>( @@ -56,7 +59,10 @@ impl FileDescription for FileHandle { assert!(communicate_allowed, "isolation should have prevented even opening a file"); let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; let result = (&mut &self.file).write(bytes); - ecx.return_written_byte_count_or_error(result, dest) + match result { + Ok(write_size) => ecx.return_write_success(write_size, dest), + Err(e) => ecx.set_last_error_and_return(e, dest), + } } fn pread<'tcx>( @@ -84,7 +90,10 @@ impl FileDescription for FileHandle { res }; let result = f(); - ecx.return_read_bytes_and_count(ptr, &bytes, result, dest) + match result { + Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest), + Err(e) => ecx.set_last_error_and_return(e, dest), + } } fn pwrite<'tcx>( @@ -112,7 +121,10 @@ impl FileDescription for FileHandle { res }; let result = f(); - ecx.return_written_byte_count_or_error(result, dest) + match result { + Ok(write_size) => ecx.return_write_success(write_size, dest), + Err(e) => ecx.set_last_error_and_return(e, dest), + } } fn seek<'tcx>( diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index ab7652ca721ca..2e485d2f4e165 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -1,7 +1,7 @@ //! Linux `eventfd` implementation. use std::cell::{Cell, RefCell}; use std::io; -use std::io::{Error, ErrorKind}; +use std::io::ErrorKind; use crate::concurrency::VClock; use crate::shims::unix::fd::FileDescriptionRef; @@ -66,9 +66,7 @@ impl FileDescription for Event { 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.size.bytes_usize() { - ecx.set_last_error_from_io_error(Error::from(ErrorKind::InvalidInput))?; - ecx.write_int(-1, dest)?; - return Ok(()); + return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); } // eventfd read at the size of u64. @@ -78,9 +76,7 @@ impl FileDescription for Event { let counter = self.counter.get(); if counter == 0 { if self.is_nonblock { - ecx.set_last_error_from_io_error(Error::from(ErrorKind::WouldBlock))?; - ecx.write_int(-1, dest)?; - return Ok(()); + return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); } throw_unsup_format!("eventfd: blocking is unsupported"); @@ -128,8 +124,7 @@ impl FileDescription for Event { 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() { - let result = Err(Error::from(ErrorKind::InvalidInput)); - return ecx.return_written_byte_count_or_error(result, dest); + return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); } // Read the user supplied value from the pointer. @@ -138,8 +133,7 @@ impl FileDescription for Event { // u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1. if num == u64::MAX { - let result = Err(Error::from(ErrorKind::InvalidInput)); - return ecx.return_written_byte_count_or_error(result, dest); + return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); } // If the addition does not let the counter to exceed the maximum value, update the counter. // Else, block. @@ -153,8 +147,7 @@ impl FileDescription for Event { } None | Some(u64::MAX) => if self.is_nonblock { - let result = Err(Error::from(ErrorKind::WouldBlock)); - return ecx.return_written_byte_count_or_error(result, dest); + return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); } else { throw_unsup_format!("eventfd: blocking is unsupported"); }, diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index d1bfa563387dd..56327d1461aed 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -5,7 +5,7 @@ use std::cell::{Cell, OnceCell, RefCell}; use std::collections::VecDeque; use std::io; -use std::io::{Error, ErrorKind, Read}; +use std::io::{ErrorKind, Read}; use rustc_target::abi::Size; @@ -138,8 +138,7 @@ impl FileDescription for AnonSocket { // Always succeed on read size 0. if len == 0 { - let result = Ok(0); - return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest); + return ecx.return_read_success(ptr, &bytes, 0, dest); } let Some(readbuf) = &self.readbuf else { @@ -152,8 +151,7 @@ impl FileDescription for AnonSocket { if self.peer_fd().upgrade().is_none() { // Socketpair with no peer and empty buffer. // 0 bytes successfully read indicates end-of-file. - let result = Ok(0); - return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest); + return ecx.return_read_success(ptr, &bytes, 0, dest); } else { if self.is_nonblock { // Non-blocking socketpair with writer and empty buffer. @@ -161,8 +159,7 @@ impl FileDescription for AnonSocket { // EAGAIN or EWOULDBLOCK can be returned for socket, // POSIX.1-2001 allows either error to be returned for this case. // Since there is no ErrorKind for EAGAIN, WouldBlock is used. - let result = Err(Error::from(ErrorKind::WouldBlock)); - return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest); + return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); } else { // Blocking socketpair with writer and empty buffer. // FIXME: blocking is currently not supported @@ -194,8 +191,7 @@ impl FileDescription for AnonSocket { ecx.check_and_update_readiness(&peer_fd)?; } - let result = Ok(actual_read_size); - ecx.return_read_bytes_and_count(ptr, &bytes, result, dest) + ecx.return_read_success(ptr, &bytes, actual_read_size, dest) } fn write<'tcx>( @@ -210,16 +206,14 @@ impl FileDescription for AnonSocket { // Always succeed on write size 0. // ("If count is zero and fd refers to a file other than a regular file, the results are not specified.") if len == 0 { - let result = Ok(0); - return ecx.return_written_byte_count_or_error(result, dest); + return ecx.return_write_success(0, dest); } // We are writing to our peer's readbuf. let Some(peer_fd) = self.peer_fd().upgrade() else { // If the upgrade from Weak to Rc fails, it indicates that all read ends have been // closed. - let result = Err(Error::from(ErrorKind::BrokenPipe)); - return ecx.return_written_byte_count_or_error(result, dest); + return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest); }; let Some(writebuf) = &peer_fd.downcast::().unwrap().readbuf else { @@ -233,8 +227,7 @@ impl FileDescription for AnonSocket { if available_space == 0 { if self.is_nonblock { // Non-blocking socketpair with a full buffer. - let result = Err(Error::from(ErrorKind::WouldBlock)); - return ecx.return_written_byte_count_or_error(result, dest); + return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); } else { // Blocking socketpair with a full buffer. throw_unsup_format!("socketpair write: blocking isn't supported yet"); @@ -256,8 +249,7 @@ impl FileDescription for AnonSocket { // The kernel does this even if the fd was already readable before, so we follow suit. ecx.check_and_update_readiness(&peer_fd)?; - let result = Ok(actual_write_size); - ecx.return_written_byte_count_or_error(result, dest) + ecx.return_write_success(actual_write_size, dest) } } From 19252bde65c8587bcf43ae464d49c56791696afa Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sun, 29 Sep 2024 22:11:59 +0000 Subject: [PATCH 1105/1301] add `stable_since` convenience --- compiler/rustc_attr/src/builtin.rs | 10 ++++++++++ src/librustdoc/clean/types.rs | 5 +---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 762ebc47ca9d0..28d73fbe9f374 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -80,6 +80,10 @@ impl Stability { pub fn is_stable(&self) -> bool { self.level.is_stable() } + + pub fn stable_since(&self) -> Option { + self.level.stable_since() + } } /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. @@ -170,6 +174,12 @@ impl StabilityLevel { pub fn is_stable(&self) -> bool { matches!(self, StabilityLevel::Stable { .. }) } + pub fn stable_since(&self) -> Option { + match *self { + StabilityLevel::Stable { since, .. } => Some(since), + StabilityLevel::Unstable { .. } => None, + } + } } #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index b9c5e8e787b29..2fccdb923d4ad 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -638,10 +638,7 @@ impl Item { } pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { - match self.stability(tcx)?.level { - StabilityLevel::Stable { since, .. } => Some(since), - StabilityLevel::Unstable { .. } => None, - } + self.stability(tcx).and_then(|stability| stability.stable_since()) } pub(crate) fn is_non_exhaustive(&self) -> bool { From cd31b3acb3c963aff8d226030ac02379d741f3bd Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Mon, 30 Sep 2024 01:20:17 +0000 Subject: [PATCH 1106/1301] rustdoc: rewrite stability inheritance as a pass --- src/librustdoc/clean/auto_trait.rs | 1 + src/librustdoc/clean/blanket_impl.rs | 1 + src/librustdoc/clean/inline.rs | 1 + src/librustdoc/clean/types.rs | 55 ++++----------- src/librustdoc/html/render/print_item.rs | 31 ++++----- src/librustdoc/passes/mod.rs | 5 ++ src/librustdoc/passes/propagate_stability.rs | 72 ++++++++++++++++++++ tests/rustdoc-ui/issues/issue-91713.stdout | 2 + tests/rustdoc/stability.rs | 49 ++++++++++--- 9 files changed, 149 insertions(+), 68 deletions(-) create mode 100644 src/librustdoc/passes/propagate_stability.rs diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 08c88fc950d7f..d966f9931045d 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -117,6 +117,7 @@ fn synthesize_auto_trait_impl<'tcx>( name: None, inner: Box::new(clean::ItemInner { attrs: Default::default(), + stability: None, kind: clean::ImplItem(Box::new(clean::Impl { safety: hir::Safety::Safe, generics, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 3682129488598..1f3cb4a61b895 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -87,6 +87,7 @@ pub(crate) fn synthesize_blanket_impls( item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, inner: Box::new(clean::ItemInner { attrs: Default::default(), + stability: None, kind: clean::ImplItem(Box::new(clean::Impl { safety: hir::Safety::Safe, generics: clean_ty_generics( diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index d3c4ef4dc906c..e7f921eef7fca 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -672,6 +672,7 @@ fn build_module_items( item_id: ItemId::DefId(did), inner: Box::new(clean::ItemInner { attrs: Default::default(), + stability: None, kind: clean::ImportItem(clean::Import::new_simple( item.ident.name, clean::ImportSource { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2fccdb923d4ad..a3277e8ca921e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -6,7 +6,7 @@ use std::{fmt, iter}; use arrayvec::ArrayVec; use rustc_ast_pretty::pprust; -use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince}; +use rustc_attr::{ConstStability, Deprecation, Stability, StableSince}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -333,6 +333,8 @@ pub(crate) struct ItemInner { /// E.g., struct vs enum vs function. pub(crate) kind: ItemKind, pub(crate) attrs: Attributes, + /// The effective stability, filled out by the `propagate-stability` pass. + pub(crate) stability: Option, } impl std::ops::Deref for Item { @@ -381,46 +383,17 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } impl Item { + /// Returns the effective stability of the item. + /// + /// This method should only be called after the `propagate-stability` pass has been run. pub(crate) fn stability(&self, tcx: TyCtxt<'_>) -> Option { - let (mut def_id, mut stability) = if let Some(inlined) = self.inline_stmt_id { - let inlined_def_id = inlined.to_def_id(); - if let Some(stability) = tcx.lookup_stability(inlined_def_id) { - (inlined_def_id, stability) - } else { - // For re-exports into crates without `staged_api`, reuse the original stability. - // This is necessary, because we always want to mark unstable items. - let def_id = self.def_id()?; - return tcx.lookup_stability(def_id); - } - } else { - let def_id = self.def_id()?; - let stability = tcx.lookup_stability(def_id)?; - (def_id, stability) - }; - - let StabilityLevel::Stable { mut since, allowed_through_unstable_modules: false } = - stability.level - else { - return Some(stability); - }; - - // If any of the item's ancestors was stabilized later or is still unstable, - // then report the ancestor's stability instead. - while let Some(parent_def_id) = tcx.opt_parent(def_id) { - if let Some(parent_stability) = tcx.lookup_stability(parent_def_id) { - match parent_stability.level { - StabilityLevel::Unstable { .. } => return Some(parent_stability), - StabilityLevel::Stable { since: parent_since, .. } => { - if parent_since > since { - stability = parent_stability; - since = parent_since; - } - } - } - } - def_id = parent_def_id; - } - Some(stability) + let stability = self.inner.stability; + debug_assert!( + stability.is_some() + || self.def_id().is_none_or(|did| tcx.lookup_stability(did).is_none()), + "missing stability for cleaned item: {self:?}", + ); + stability } pub(crate) fn const_stability(&self, tcx: TyCtxt<'_>) -> Option { @@ -502,7 +475,7 @@ impl Item { Item { item_id: def_id.into(), - inner: Box::new(ItemInner { kind, attrs }), + inner: Box::new(ItemInner { kind, attrs, stability: None }), name, cfg, inline_stmt_id: None, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index d120e7f36eb6a..38276e4d20c26 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -436,16 +436,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: } clean::ImportItem(ref import) => { - let stab_tags = if let Some(import_def_id) = import.source.did { - // Just need an item with the correct def_id and attrs - let import_item = - clean::Item { item_id: import_def_id.into(), ..(*myitem).clone() }; - - let stab_tags = Some(extra_info_tags(&import_item, item, tcx).to_string()); - stab_tags - } else { - None - }; + let stab_tags = import.source.did.map_or_else(String::new, |import_def_id| { + extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string() + }); w.write_str(ITEM_TABLE_ROW_OPEN); let id = match import.kind { @@ -454,7 +447,6 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: } clean::ImportKind::Glob => String::new(), }; - let stab_tags = stab_tags.unwrap_or_default(); let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() { ("", "") } else { @@ -521,7 +513,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: {docs_before}{docs}{docs_after}", name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()), visibility_and_hidden = visibility_and_hidden, - stab_tags = extra_info_tags(myitem, item, tcx), + stab_tags = extra_info_tags(tcx, myitem, item, None), class = myitem.type_(), unsafety_flag = unsafety_flag, href = item_path(myitem.type_(), myitem.name.unwrap().as_str()), @@ -544,9 +536,10 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: /// Render the stability, deprecation and portability tags that are displayed in the item's summary /// at the module level. fn extra_info_tags<'a, 'tcx: 'a>( + tcx: TyCtxt<'tcx>, item: &'a clean::Item, parent: &'a clean::Item, - tcx: TyCtxt<'tcx>, + import_def_id: Option, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { fn tag_html<'a>( @@ -564,18 +557,18 @@ fn extra_info_tags<'a, 'tcx: 'a>( } // The trailing space after each tag is to space it properly against the rest of the docs. - if let Some(depr) = &item.deprecation(tcx) { + let deprecation = import_def_id + .map_or_else(|| item.deprecation(tcx), |import_did| tcx.lookup_deprecation(import_did)); + if let Some(depr) = deprecation { let message = if depr.is_in_effect() { "Deprecated" } else { "Deprecation planned" }; write!(f, "{}", tag_html("deprecated", "", message))?; } // The "rustc_private" crates are permanently unstable so it makes no sense // to render "unstable" everywhere. - if item - .stability(tcx) - .as_ref() - .is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private) - { + let stability = import_def_id + .map_or_else(|| item.stability(tcx), |import_did| tcx.lookup_stability(import_did)); + if stability.is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private) { write!(f, "{}", tag_html("unstable", "", "Experimental"))?; } diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index b1dc766049d53..f5b7802372149 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -23,6 +23,9 @@ pub(crate) use self::strip_priv_imports::STRIP_PRIV_IMPORTS; mod propagate_doc_cfg; pub(crate) use self::propagate_doc_cfg::PROPAGATE_DOC_CFG; +mod propagate_stability; +pub(crate) use self::propagate_stability::PROPAGATE_STABILITY; + pub(crate) mod collect_intra_doc_links; pub(crate) use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS; @@ -75,6 +78,7 @@ pub(crate) const PASSES: &[Pass] = &[ STRIP_PRIVATE, STRIP_PRIV_IMPORTS, PROPAGATE_DOC_CFG, + PROPAGATE_STABILITY, COLLECT_INTRA_DOC_LINKS, COLLECT_TRAIT_IMPLS, CALCULATE_DOC_COVERAGE, @@ -91,6 +95,7 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[ ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate), ConditionalPass::always(COLLECT_INTRA_DOC_LINKS), ConditionalPass::always(PROPAGATE_DOC_CFG), + ConditionalPass::always(PROPAGATE_STABILITY), ConditionalPass::always(RUN_LINTS), ]; diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs new file mode 100644 index 0000000000000..f51e993bfa5d0 --- /dev/null +++ b/src/librustdoc/passes/propagate_stability.rs @@ -0,0 +1,72 @@ +//! Propagates stability to child items. +//! +//! The purpose of this pass is to make items whose parents are "more unstable" +//! than the item itself inherit the parent's stability. +//! For example, [`core::error::Error`] is marked as stable since 1.0.0, but the +//! [`core::error`] module is marked as stable since 1.81.0, so we want to show +//! [`core::error::Error`] as stable since 1.81.0 as well. + +use rustc_attr::{Stability, StabilityLevel}; +use rustc_hir::def_id::CRATE_DEF_ID; + +use crate::clean::{Crate, Item, ItemId}; +use crate::core::DocContext; +use crate::fold::DocFolder; +use crate::passes::Pass; + +pub(crate) const PROPAGATE_STABILITY: Pass = Pass { + name: "propagate-stability", + run: propagate_stability, + description: "propagates stability to child items", +}; + +pub(crate) fn propagate_stability(cr: Crate, cx: &mut DocContext<'_>) -> Crate { + let crate_stability = cx.tcx.lookup_stability(CRATE_DEF_ID); + StabilityPropagator { parent_stability: crate_stability, cx }.fold_crate(cr) +} + +struct StabilityPropagator<'a, 'tcx> { + parent_stability: Option, + cx: &'a mut DocContext<'tcx>, +} + +impl<'a, 'tcx> DocFolder for StabilityPropagator<'a, 'tcx> { + fn fold_item(&mut self, mut item: Item) -> Option { + let parent_stability = self.parent_stability; + + let stability = match item.item_id { + ItemId::DefId(def_id) => { + let own_stability = self.cx.tcx.lookup_stability(def_id); + + // If any of the item's parents was stabilized later or is still unstable, + // then use the parent's stability instead. + if let Some(own_stab) = own_stability + && let StabilityLevel::Stable { + since: own_since, + allowed_through_unstable_modules: false, + } = own_stab.level + && let Some(parent_stab) = parent_stability + && (parent_stab.is_unstable() + || parent_stab + .stable_since() + .is_some_and(|parent_since| parent_since > own_since)) + { + parent_stability + } else { + own_stability + } + } + ItemId::Auto { .. } | ItemId::Blanket { .. } => { + // For now, we do now show stability for synthesized impls. + None + } + }; + + item.inner.stability = stability; + self.parent_stability = stability; + let item = self.fold_item_recur(item); + self.parent_stability = parent_stability; + + Some(item) + } +} diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout index 1ea3dbfb59f31..790e58b0df9f9 100644 --- a/tests/rustdoc-ui/issues/issue-91713.stdout +++ b/tests/rustdoc-ui/issues/issue-91713.stdout @@ -5,6 +5,7 @@ strip-aliased-non-local - strips all non-local private aliased items from the ou strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items + propagate-stability - propagates stability to child items collect-intra-doc-links - resolves intra-doc links collect-trait-impls - retrieves trait impls for items in the crate calculate-doc-coverage - counts the number of items with and without documentation @@ -19,6 +20,7 @@ strip-aliased-non-local strip-priv-imports (when --document-private-items) collect-intra-doc-links propagate-doc-cfg + propagate-stability run-lints Passes run with `--show-coverage`: diff --git a/tests/rustdoc/stability.rs b/tests/rustdoc/stability.rs index de855b43ba538..fc72154cad864 100644 --- a/tests/rustdoc/stability.rs +++ b/tests/rustdoc/stability.rs @@ -25,28 +25,61 @@ pub struct ZzStable; #[unstable(feature = "unstable", issue = "none")] pub mod unstable { - //@ !hasraw stability/unstable/struct.Foo.html '//span[@class="since"]' + //@ !hasraw stability/unstable/struct.StableInUnstable.html \ + // '//span[@class="since"]' //@ has - '//div[@class="stab unstable"]' 'experimental' #[stable(feature = "rust1", since = "1.0.0")] - pub struct Foo; + pub struct StableInUnstable; + + #[stable(feature = "rust1", since = "1.0.0")] + pub mod stable_in_unstable { + //@ !hasraw stability/unstable/stable_in_unstable/struct.Inner.html \ + // '//span[@class="since"]' + //@ has - '//div[@class="stab unstable"]' 'experimental' + #[stable(feature = "rust1", since = "1.0.0")] + pub struct Inner; + } } #[stable(feature = "rust2", since = "2.2.2")] pub mod stable_later { - //@ has stability/stable_later/struct.Bar.html '//span[@class="since"]' '2.2.2' + //@ has stability/stable_later/struct.StableInLater.html \ + // '//span[@class="since"]' '2.2.2' #[stable(feature = "rust1", since = "1.0.0")] - pub struct Bar; + pub struct StableInLater; + + #[stable(feature = "rust1", since = "1.0.0")] + pub mod stable_in_later { + //@ has stability/stable_later/stable_in_later/struct.Inner.html \ + // '//span[@class="since"]' '2.2.2' + #[stable(feature = "rust1", since = "1.0.0")] + pub struct Inner; + } } #[stable(feature = "rust1", since = "1.0.0")] pub mod stable_earlier { - //@ has stability/stable_earlier/struct.Foo.html '//span[@class="since"]' '1.0.0' + //@ has stability/stable_earlier/struct.StableInUnstable.html \ + // '//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::unstable::StableInUnstable; + + //@ has stability/stable_earlier/stable_in_unstable/struct.Inner.html \ + // '//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::unstable::stable_in_unstable; + + //@ has stability/stable_earlier/struct.StableInLater.html \ + // '//span[@class="since"]' '1.0.0' #[doc(inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::unstable::Foo; + pub use crate::stable_later::StableInLater; - //@ has stability/stable_earlier/struct.Bar.html '//span[@class="since"]' '1.0.0' + //@ has stability/stable_earlier/stable_in_later/struct.Inner.html \ + // '//span[@class="since"]' '1.0.0' #[doc(inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::stable_later::Bar; + pub use crate::stable_later::stable_in_later; } From 91d0752e1698ac393a90b8f3ad58e59d904070d9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 30 Sep 2024 16:56:40 -0700 Subject: [PATCH 1107/1301] Update wasm-component-ld to 0.5.9 This updates the `wasm-component-ld` linker binary for the `wasm32-wasip2` target to 0.5.9, pulling in a few bug fixes and recent updates. --- Cargo.lock | 48 +++++++++++++------------- src/tools/wasm-component-ld/Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 582b5a763e6f7..6c1071ccbc226 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5768,16 +5768,16 @@ checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-component-ld" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb17cdbc91766d4ea0bcd6026c36ba77a21b5c8199aeb1f0993461fe6a6bec2b" +checksum = "fde17bc96539700198e12516230c76095cc215c84ef39ad206e1af3f84243e0f" dependencies = [ "anyhow", "clap", "lexopt", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.217.0", + "wasmparser 0.218.0", "wat", "wit-component", "wit-parser", @@ -5801,19 +5801,19 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b88b0814c9a2b323a9b46c687e726996c255ac8b64aa237dd11c81ed4854760" +checksum = "22b896fa8ceb71091ace9bcb81e853f54043183a1c9667cf93422c40252ffa0a" dependencies = [ "leb128", - "wasmparser 0.217.0", + "wasmparser 0.218.0", ] [[package]] name = "wasm-metadata" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65a146bf9a60e9264f0548a2599aa9656dba9a641eff9ab88299dc2a637e483c" +checksum = "aa5eeb071abe8a2132fdd5565dabffee70775ee8c24fc7e300ac43f51f4a8a91" dependencies = [ "anyhow", "indexmap", @@ -5821,8 +5821,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.217.0", - "wasmparser 0.217.0", + "wasm-encoder 0.218.0", + "wasmparser 0.218.0", ] [[package]] @@ -5837,9 +5837,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca917a21307d3adf2b9857b94dd05ebf8496bdcff4437a9b9fb3899d3e6c74e7" +checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc" dependencies = [ "ahash", "bitflags 2.6.0", @@ -5851,22 +5851,22 @@ dependencies = [ [[package]] name = "wast" -version = "217.0.0" +version = "218.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79004ecebded92d3c710d4841383368c7f04b63d0992ddd6b0c7d5029b7629b7" +checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.217.0", + "wasm-encoder 0.218.0", ] [[package]] name = "wat" -version = "1.217.0" +version = "1.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c126271c3d92ca0f7c63e4e462e40c69cca52fd4245fcda730d1cf558fb55088" +checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391" dependencies = [ "wast", ] @@ -6143,9 +6143,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7117809905e49db716d81e794f79590c052bf2fdbbcda1731ca0fb28f6f3ddf" +checksum = "aa53aa7e6bf2b3e8ccaffbcc963fbdb672a603dc0af393a481b6cec24c266406" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -6154,17 +6154,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.217.0", + "wasm-encoder 0.218.0", "wasm-metadata", - "wasmparser 0.217.0", + "wasmparser 0.218.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.217.0" +version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb893dcd6d370cfdf19a0d9adfcd403efb8e544e1a0ea3a8b81a21fe392eaa78" +checksum = "0d3d1066ab761b115f97fef2b191090faabcb0f37b555b758d3caf42d4ed9e55" dependencies = [ "anyhow", "id-arena", @@ -6175,7 +6175,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.217.0", + "wasmparser 0.218.0", ] [[package]] diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index 91ff19ad9fcb0..49f4e59650e71 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.4" +wasm-component-ld = "0.5.9" From 6b09a93566cc891a56a8f9bb68a25d16ca4d91d3 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 27 Sep 2024 18:35:30 -0400 Subject: [PATCH 1108/1301] Enable `f16` tests on non-GNU Windows There is a MinGW ABI bug that prevents `f16` and `f128` from being usable on `windows-gnu` targets. This does not affect MSVC; however, we have `f16` and `f128` tests disabled on all Windows targets. Update the gating to only affect `windows-gnu`, which means `f16` tests will be enabled. There is no effect for `f128` since the default fallback is `false`. --- library/std/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/build.rs b/library/std/build.rs index 5da470c6462aa..7d37d4e9d7d83 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -101,7 +101,7 @@ fn main() { // Unsupported ("arm64ec", _) => false, // MinGW ABI bugs - ("x86_64", "windows") => false, + ("x86_64", "windows") if target_env == "gnu" => false, // Infinite recursion ("csky", _) => false, ("hexagon", _) => false, @@ -129,10 +129,10 @@ fn main() { // ABI unsupported ("sparc", _) => false, // MinGW ABI bugs - ("x86_64", "windows") => false, + ("x86_64", "windows") if target_env == "gnu" => false, // 64-bit Linux is about the only platform to have f128 symbols by default (_, "linux") if target_pointer_width == 64 => true, - // Same as for f16, except MacOS is also missing f128 symbols. + // Almost all OSs are missing symbol. compiler-builtins will have to add them. _ => false, }; From 88c74529c1210bdf06e30b70aa51721218512090 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 08:41:54 +0200 Subject: [PATCH 1109/1301] move io error handling helpers to their own file --- src/tools/miri/src/helpers.rs | 185 +------------------------- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/shims/io_error.rs | 190 +++++++++++++++++++++++++++ src/tools/miri/src/shims/mod.rs | 1 + 4 files changed, 193 insertions(+), 184 deletions(-) create mode 100644 src/tools/miri/src/shims/io_error.rs diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 5324e4bec5b22..da0581ea86254 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use std::num::NonZero; use std::sync::Mutex; use std::time::Duration; -use std::{cmp, io, iter}; +use std::{cmp, iter}; use rand::RngCore; use rustc_apfloat::Float; @@ -31,65 +31,6 @@ pub enum AccessKind { Write, } -// This mapping should match `decode_error_kind` in -// . -const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { - use std::io::ErrorKind::*; - &[ - ("E2BIG", ArgumentListTooLong), - ("EADDRINUSE", AddrInUse), - ("EADDRNOTAVAIL", AddrNotAvailable), - ("EBUSY", ResourceBusy), - ("ECONNABORTED", ConnectionAborted), - ("ECONNREFUSED", ConnectionRefused), - ("ECONNRESET", ConnectionReset), - ("EDEADLK", Deadlock), - ("EDQUOT", FilesystemQuotaExceeded), - ("EEXIST", AlreadyExists), - ("EFBIG", FileTooLarge), - ("EHOSTUNREACH", HostUnreachable), - ("EINTR", Interrupted), - ("EINVAL", InvalidInput), - ("EISDIR", IsADirectory), - ("ELOOP", FilesystemLoop), - ("ENOENT", NotFound), - ("ENOMEM", OutOfMemory), - ("ENOSPC", StorageFull), - ("ENOSYS", Unsupported), - ("EMLINK", TooManyLinks), - ("ENAMETOOLONG", InvalidFilename), - ("ENETDOWN", NetworkDown), - ("ENETUNREACH", NetworkUnreachable), - ("ENOTCONN", NotConnected), - ("ENOTDIR", NotADirectory), - ("ENOTEMPTY", DirectoryNotEmpty), - ("EPIPE", BrokenPipe), - ("EROFS", ReadOnlyFilesystem), - ("ESPIPE", NotSeekable), - ("ESTALE", StaleNetworkFileHandle), - ("ETIMEDOUT", TimedOut), - ("ETXTBSY", ExecutableFileBusy), - ("EXDEV", CrossesDevices), - // The following have two valid options. We have both for the forwards mapping; only the - // first one will be used for the backwards mapping. - ("EPERM", PermissionDenied), - ("EACCES", PermissionDenied), - ("EWOULDBLOCK", WouldBlock), - ("EAGAIN", WouldBlock), - ] -}; -// This mapping should match `decode_error_kind` in -// . -const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { - use std::io::ErrorKind::*; - // FIXME: this is still incomplete. - &[ - ("ERROR_ACCESS_DENIED", PermissionDenied), - ("ERROR_FILE_NOT_FOUND", NotFound), - ("ERROR_INVALID_PARAMETER", InvalidInput), - ] -}; - /// Gets an instance for a path. /// /// A `None` namespace indicates we are looking for a module. @@ -745,130 +686,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix") } - /// Get last error variable as a place, lazily allocating thread-local storage for it if - /// necessary. - fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let this = self.eval_context_mut(); - if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { - Ok(errno_place.clone()) - } else { - // Allocate new place, set initial value to 0. - let errno_layout = this.machine.layouts.u32; - let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?; - this.write_scalar(Scalar::from_u32(0), &errno_place)?; - this.active_thread_mut().last_error = Some(errno_place.clone()); - Ok(errno_place) - } - } - - /// Sets the last error variable. - fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let errno_place = this.last_error_place()?; - this.write_scalar(scalar, &errno_place) - } - - /// Gets the last error variable. - fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - let errno_place = this.last_error_place()?; - this.read_scalar(&errno_place) - } - - /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` - /// as a platform-specific errnum. - fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_ref(); - let target = &this.tcx.sess.target; - if target.families.iter().any(|f| f == "unix") { - for &(name, kind) in UNIX_IO_ERROR_TABLE { - if err.kind() == kind { - return Ok(this.eval_libc(name)); - } - } - throw_unsup_format!("unsupported io error: {err}") - } else if target.families.iter().any(|f| f == "windows") { - for &(name, kind) in WINDOWS_IO_ERROR_TABLE { - if err.kind() == kind { - return Ok(this.eval_windows("c", name)); - } - } - throw_unsup_format!("unsupported io error: {err}"); - } else { - throw_unsup_format!( - "converting io::Error into errnum is unsupported for OS {}", - target.os - ) - } - } - - /// The inverse of `io_error_to_errnum`. - #[allow(clippy::needless_return)] - fn try_errnum_to_io_error( - &self, - errnum: Scalar, - ) -> InterpResult<'tcx, Option> { - let this = self.eval_context_ref(); - let target = &this.tcx.sess.target; - if target.families.iter().any(|f| f == "unix") { - let errnum = errnum.to_i32()?; - for &(name, kind) in UNIX_IO_ERROR_TABLE { - if errnum == this.eval_libc_i32(name) { - return Ok(Some(kind)); - } - } - return Ok(None); - } else if target.families.iter().any(|f| f == "windows") { - let errnum = errnum.to_u32()?; - for &(name, kind) in WINDOWS_IO_ERROR_TABLE { - if errnum == this.eval_windows("c", name).to_u32()? { - return Ok(Some(kind)); - } - } - return Ok(None); - } else { - throw_unsup_format!( - "converting errnum into io::Error is unsupported for OS {}", - target.os - ) - } - } - - /// Sets the last OS error using a `std::io::ErrorKind`. - fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err)?) - } - - /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. - fn set_last_error_and_return( - &mut self, - err: impl Into, - dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err.into())?)?; - self.write_int(-1, dest)?; - Ok(()) - } - - /// Helper function that consumes an `std::io::Result` and returns an - /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns - /// `Ok(-1)` and sets the last OS error accordingly. - /// - /// This function uses `T: From` instead of `i32` directly because some IO related - /// functions return different integer types (like `read`, that returns an `i64`). - fn try_unwrap_io_result>( - &mut self, - result: std::io::Result, - ) -> InterpResult<'tcx, T> { - match result { - Ok(ok) => Ok(ok), - Err(e) => { - self.eval_context_mut().set_last_error_from_io_error(e)?; - Ok((-1).into()) - } - } - } - /// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type fn deref_pointer_as( &self, diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 78e7bf704552d..c4adf38299c81 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -150,6 +150,7 @@ pub use crate::range_map::RangeMap; pub use crate::shims::EmulateItemResult; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; +pub use crate::shims::io_error::EvalContextExt as _; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::time::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs new file mode 100644 index 0000000000000..8c727f4c894ed --- /dev/null +++ b/src/tools/miri/src/shims/io_error.rs @@ -0,0 +1,190 @@ +use std::io; + +use crate::*; + +// This mapping should match `decode_error_kind` in +// . +const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { + use std::io::ErrorKind::*; + &[ + ("E2BIG", ArgumentListTooLong), + ("EADDRINUSE", AddrInUse), + ("EADDRNOTAVAIL", AddrNotAvailable), + ("EBUSY", ResourceBusy), + ("ECONNABORTED", ConnectionAborted), + ("ECONNREFUSED", ConnectionRefused), + ("ECONNRESET", ConnectionReset), + ("EDEADLK", Deadlock), + ("EDQUOT", FilesystemQuotaExceeded), + ("EEXIST", AlreadyExists), + ("EFBIG", FileTooLarge), + ("EHOSTUNREACH", HostUnreachable), + ("EINTR", Interrupted), + ("EINVAL", InvalidInput), + ("EISDIR", IsADirectory), + ("ELOOP", FilesystemLoop), + ("ENOENT", NotFound), + ("ENOMEM", OutOfMemory), + ("ENOSPC", StorageFull), + ("ENOSYS", Unsupported), + ("EMLINK", TooManyLinks), + ("ENAMETOOLONG", InvalidFilename), + ("ENETDOWN", NetworkDown), + ("ENETUNREACH", NetworkUnreachable), + ("ENOTCONN", NotConnected), + ("ENOTDIR", NotADirectory), + ("ENOTEMPTY", DirectoryNotEmpty), + ("EPIPE", BrokenPipe), + ("EROFS", ReadOnlyFilesystem), + ("ESPIPE", NotSeekable), + ("ESTALE", StaleNetworkFileHandle), + ("ETIMEDOUT", TimedOut), + ("ETXTBSY", ExecutableFileBusy), + ("EXDEV", CrossesDevices), + // The following have two valid options. We have both for the forwards mapping; only the + // first one will be used for the backwards mapping. + ("EPERM", PermissionDenied), + ("EACCES", PermissionDenied), + ("EWOULDBLOCK", WouldBlock), + ("EAGAIN", WouldBlock), + ] +}; +// This mapping should match `decode_error_kind` in +// . +const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { + use std::io::ErrorKind::*; + // FIXME: this is still incomplete. + &[ + ("ERROR_ACCESS_DENIED", PermissionDenied), + ("ERROR_FILE_NOT_FOUND", NotFound), + ("ERROR_INVALID_PARAMETER", InvalidInput), + ] +}; + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Get last error variable as a place, lazily allocating thread-local storage for it if + /// necessary. + fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> { + let this = self.eval_context_mut(); + if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { + Ok(errno_place.clone()) + } else { + // Allocate new place, set initial value to 0. + let errno_layout = this.machine.layouts.u32; + let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?; + this.write_scalar(Scalar::from_u32(0), &errno_place)?; + this.active_thread_mut().last_error = Some(errno_place.clone()); + Ok(errno_place) + } + } + + /// Sets the last error variable. + fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let errno_place = this.last_error_place()?; + this.write_scalar(scalar, &errno_place) + } + + /// Gets the last error variable. + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let errno_place = this.last_error_place()?; + this.read_scalar(&errno_place) + } + + /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` + /// as a platform-specific errnum. + fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_ref(); + let target = &this.tcx.sess.target; + if target.families.iter().any(|f| f == "unix") { + for &(name, kind) in UNIX_IO_ERROR_TABLE { + if err.kind() == kind { + return Ok(this.eval_libc(name)); + } + } + throw_unsup_format!("unsupported io error: {err}") + } else if target.families.iter().any(|f| f == "windows") { + for &(name, kind) in WINDOWS_IO_ERROR_TABLE { + if err.kind() == kind { + return Ok(this.eval_windows("c", name)); + } + } + throw_unsup_format!("unsupported io error: {err}"); + } else { + throw_unsup_format!( + "converting io::Error into errnum is unsupported for OS {}", + target.os + ) + } + } + + /// The inverse of `io_error_to_errnum`. + #[allow(clippy::needless_return)] + fn try_errnum_to_io_error( + &self, + errnum: Scalar, + ) -> InterpResult<'tcx, Option> { + let this = self.eval_context_ref(); + let target = &this.tcx.sess.target; + if target.families.iter().any(|f| f == "unix") { + let errnum = errnum.to_i32()?; + for &(name, kind) in UNIX_IO_ERROR_TABLE { + if errnum == this.eval_libc_i32(name) { + return Ok(Some(kind)); + } + } + return Ok(None); + } else if target.families.iter().any(|f| f == "windows") { + let errnum = errnum.to_u32()?; + for &(name, kind) in WINDOWS_IO_ERROR_TABLE { + if errnum == this.eval_windows("c", name).to_u32()? { + return Ok(Some(kind)); + } + } + return Ok(None); + } else { + throw_unsup_format!( + "converting errnum into io::Error is unsupported for OS {}", + target.os + ) + } + } + + /// Sets the last OS error using a `std::io::ErrorKind`. + fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { + self.set_last_error(self.io_error_to_errnum(err)?) + } + + /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. + fn set_last_error_and_return( + &mut self, + err: impl Into, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + this.set_last_error(this.io_error_to_errnum(err.into())?)?; + this.write_int(-1, dest)?; + Ok(()) + } + + /// Helper function that consumes an `std::io::Result` and returns an + /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns + /// `Ok(-1)` and sets the last OS error accordingly. + /// + /// This function uses `T: From` instead of `i32` directly because some IO related + /// functions return different integer types (like `read`, that returns an `i64`). + fn try_unwrap_io_result>( + &mut self, + result: std::io::Result, + ) -> InterpResult<'tcx, T> { + match result { + Ok(ok) => Ok(ok), + Err(e) => { + self.eval_context_mut().set_last_error_from_io_error(e)?; + Ok((-1).into()) + } + } + } +} diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index a689ac2b3784e..b9317ac1a15fa 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -12,6 +12,7 @@ mod x86; pub mod env; pub mod extern_static; pub mod foreign_items; +pub mod io_error; pub mod os_str; pub mod panic; pub mod time; From 9c21fd4b93540d841e11ba4f975660666a9bfc3f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 09:02:33 +0200 Subject: [PATCH 1110/1301] make set_last_error directly callable on a bunch of ways to represent errors --- src/tools/miri/src/lib.rs | 2 +- src/tools/miri/src/shims/io_error.rs | 48 +++++++++++++++---- src/tools/miri/src/shims/unix/env.rs | 15 +++--- src/tools/miri/src/shims/unix/fd.rs | 8 ++-- .../miri/src/shims/unix/foreign_items.rs | 22 ++++----- src/tools/miri/src/shims/unix/fs.rs | 48 ++++++++----------- src/tools/miri/src/shims/unix/linux/epoll.rs | 6 +-- src/tools/miri/src/shims/unix/linux/sync.rs | 9 ++-- src/tools/miri/src/shims/windows/env.rs | 8 ++-- .../miri/src/shims/windows/foreign_items.rs | 2 +- 10 files changed, 88 insertions(+), 80 deletions(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index c4adf38299c81..330147c8f1cf8 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -150,7 +150,7 @@ pub use crate::range_map::RangeMap; pub use crate::shims::EmulateItemResult; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; -pub use crate::shims::io_error::EvalContextExt as _; +pub use crate::shims::io_error::{EvalContextExt as _, LibcError}; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::time::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index 8c727f4c894ed..61b943b599d93 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -2,6 +2,34 @@ use std::io; use crate::*; +/// A representation of an IO error: either a libc error name, +/// or a host error. +#[derive(Debug)] +pub enum IoError { + LibcError(&'static str), + HostError(io::Error), + Raw(Scalar), +} +pub use self::IoError::*; + +impl From for IoError { + fn from(value: io::Error) -> Self { + IoError::HostError(value) + } +} + +impl From for IoError { + fn from(value: io::ErrorKind) -> Self { + IoError::HostError(value.into()) + } +} + +impl From for IoError { + fn from(value: Scalar) -> Self { + IoError::Raw(value) + } +} + // This mapping should match `decode_error_kind` in // . const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { @@ -80,10 +108,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Sets the last error variable. - fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { + fn set_last_error(&mut self, err: impl Into) -> InterpResult<'tcx> { let this = self.eval_context_mut(); + let errno = match err.into() { + HostError(err) => this.io_error_to_errnum(err)?, + LibcError(name) => this.eval_libc(name), + Raw(val) => val, + }; let errno_place = this.last_error_place()?; - this.write_scalar(scalar, &errno_place) + this.write_scalar(errno, &errno_place) } /// Gets the last error variable. @@ -152,19 +185,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - /// Sets the last OS error using a `std::io::ErrorKind`. - fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err)?) - } - /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. fn set_last_error_and_return( &mut self, - err: impl Into, + err: impl Into, dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - this.set_last_error(this.io_error_to_errnum(err.into())?)?; + this.set_last_error(err)?; this.write_int(-1, dest)?; Ok(()) } @@ -182,7 +210,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match result { Ok(ok) => Ok(ok), Err(e) => { - self.eval_context_mut().set_last_error_from_io_error(e)?; + self.eval_context_mut().set_last_error(e)?; Ok((-1).into()) } } diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 184a6c238b392..923de15d2dafa 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -177,8 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) // return zero on success } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } @@ -203,8 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } @@ -218,7 +216,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`getcwd`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Pointer::null()); } @@ -228,10 +226,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.write_path_to_c_str(&cwd, buf, size)?.0 { return Ok(buf); } - let erange = this.eval_libc("ERANGE"); - this.set_last_error(erange)?; + this.set_last_error(LibcError("ERANGE"))?; } - Err(e) => this.set_last_error_from_io_error(e)?, + Err(e) => this.set_last_error(e)?, } Ok(Pointer::null()) @@ -245,7 +242,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`chdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index f64d0ed1467bc..fe634de122c46 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -524,7 +524,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fcntl`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -606,8 +606,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => fd.read(&fd, communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; return Ok(()); }; @@ -651,8 +650,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => fd.write(&fd, communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; return Ok(()); }; diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 528d068ea92c0..14af4245148e2 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -355,8 +355,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) { None => { - let einval = this.eval_libc("ENOMEM"); - this.set_last_error(einval)?; + let enmem = this.eval_libc("ENOMEM"); + this.set_last_error(enmem)?; this.write_null(dest)?; } Some(len) => { @@ -646,13 +646,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let chunk_size = CpuAffinityMask::chunk_size(this); if this.ptr_is_null(mask)? { - let einval = this.eval_libc("EFAULT"); - this.set_last_error(einval)?; + let efault = this.eval_libc("EFAULT"); + this.set_last_error(efault)?; this.write_int(-1, dest)?; } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 { // we only copy whole chunks of size_of::() - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) { let cpuset = cpuset.clone(); @@ -662,8 +661,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } else { // The thread whose ID is pid could not be found - let einval = this.eval_libc("ESRCH"); - this.set_last_error(einval)?; + let esrch = this.eval_libc("ESRCH"); + this.set_last_error(esrch)?; this.write_int(-1, dest)?; } } @@ -689,8 +688,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; if this.ptr_is_null(mask)? { - let einval = this.eval_libc("EFAULT"); - this.set_last_error(einval)?; + let efault = this.eval_libc("EFAULT"); + this.set_last_error(efault)?; this.write_int(-1, dest)?; } else { // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`. @@ -707,8 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } None => { // The intersection between the mask and the available CPUs was empty. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 2fa1729ad9c8a..80e45d8439819 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -534,8 +534,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let o_tmpfile = this.eval_libc_i32("O_TMPFILE"); if flag & o_tmpfile == o_tmpfile { // if the flag contains `O_TMPFILE` then we return a graceful error - let eopnotsupp = this.eval_libc("EOPNOTSUPP"); - this.set_last_error(eopnotsupp)?; + this.set_last_error(LibcError("EOPNOTSUPP"))?; return Ok(Scalar::from_i32(-1)); } } @@ -572,7 +571,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`open`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -591,8 +590,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let seek_from = if whence == this.eval_libc_i32("SEEK_SET") { if offset < 0 { // Negative offsets return `EINVAL`. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i64(-1)); } else { SeekFrom::Start(u64::try_from(offset).unwrap()) @@ -602,8 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if whence == this.eval_libc_i32("SEEK_END") { SeekFrom::End(i64::try_from(offset).unwrap()) } else { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i64(-1)); }; @@ -627,7 +624,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -658,7 +655,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -959,7 +956,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rename`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -983,7 +980,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -1011,7 +1008,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -1045,7 +1042,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_target_usize(id, this)) } Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(Scalar::null_ptr(this)) } } @@ -1130,7 +1127,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None } Some(Err(e)) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; None } }; @@ -1314,15 +1311,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(result)) } else { drop(fd); - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } else { drop(fd); // The file is not writable - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } @@ -1400,16 +1395,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let flags = this.read_scalar(flags_op)?.to_i32()?; if offset < 0 || nbytes < 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE") | this.eval_libc_i32("SYNC_FILE_RANGE_WRITE") | this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER"); if flags & allowed_flags != flags { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } @@ -1471,7 +1464,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(path_bytes.len().try_into().unwrap()) } Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(-1) } } @@ -1551,7 +1544,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_maybe_pointer(dest, this)) } Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(Scalar::from_target_usize(0, this)) } } @@ -1603,8 +1596,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If we don't find the suffix, it is an error. if last_six_char_bytes != suffix_bytes { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } @@ -1670,7 +1662,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => { // "On error, -1 is returned, and errno is set to // indicate the error" - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; return Ok(Scalar::from_i32(-1)); } }, @@ -1749,7 +1741,7 @@ impl FileMetadata { let metadata = match metadata { Ok(metadata) => metadata, Err(e) => { - ecx.set_last_error_from_io_error(e)?; + ecx.set_last_error(e)?; return Ok(None); } }; diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 675a404ae117c..ff0709dd5ac1c 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -261,8 +261,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Throw EINVAL if epfd and fd have the same value. if epfd_value == fd { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } @@ -443,8 +442,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let timeout = this.read_scalar(timeout)?.to_i32()?; if epfd_value <= 0 || maxevents <= 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; return Ok(()); } diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index bd21203907419..1d1764b3a1a11 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -75,8 +75,7 @@ pub fn futex<'tcx>( }; if bitset == 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; return Ok(()); } @@ -88,8 +87,7 @@ pub fn futex<'tcx>( let duration = match this.read_timespec(&timeout)? { Some(duration) => duration, None => { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; return Ok(()); } @@ -198,8 +196,7 @@ pub fn futex<'tcx>( u32::MAX }; if bitset == 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; return Ok(()); } diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 9707482b1e2c2..cf99ac758c643 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -150,7 +150,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_u32(0)); } @@ -163,7 +163,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_path_to_wide_str(&cwd, buf, size)?, ))); } - Err(e) => this.set_last_error_from_io_error(e)?, + Err(e) => this.set_last_error(e)?, } Ok(Scalar::from_u32(0)) } @@ -182,7 +182,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(this.eval_windows("c", "FALSE")); } @@ -190,7 +190,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match env::set_current_dir(path) { Ok(()) => Ok(this.eval_windows("c", "TRUE")), Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(this.eval_windows("c", "FALSE")) } } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 22634c509bed0..c51726b05dc5a 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -227,7 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let filename = this.read_path_from_wide_str(filename)?; let result = match win_absolute(&filename)? { Err(err) => { - this.set_last_error_from_io_error(err)?; + this.set_last_error(err)?; Scalar::from_u32(0) // return zero upon failure } Ok(abs_filename) => { From 4f4e1d42b59e96141745ace47ed7a7c96211bf33 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 09:08:10 +0200 Subject: [PATCH 1111/1301] add set_last_error_and_return_i32 helper and use it in a few places --- src/tools/miri/src/shims/io_error.rs | 34 ++++++++++++++++++---------- src/tools/miri/src/shims/unix/env.rs | 10 +++----- src/tools/miri/src/shims/unix/fd.rs | 11 +++------ 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index 61b943b599d93..aa13c405145cc 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -119,6 +119,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(errno, &errno_place) } + /// Sets the last OS error and writes -1 to dest place. + fn set_last_error_and_return( + &mut self, + err: impl Into, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + this.set_last_error(err)?; + this.write_int(-1, dest)?; + Ok(()) + } + + /// Sets the last OS error and return `-1` as a `i32`-typed Scalar + fn set_last_error_and_return_i32( + &mut self, + err: impl Into, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + this.set_last_error(err)?; + Ok(Scalar::from_i32(-1)) + } + /// Gets the last error variable. fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); @@ -185,18 +207,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. - fn set_last_error_and_return( - &mut self, - err: impl Into, - dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - this.set_last_error(err)?; - this.write_int(-1, dest)?; - Ok(()) - } - /// Helper function that consumes an `std::io::Result` and returns an /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns /// `Ok(-1)` and sets the last OS error accordingly. diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 923de15d2dafa..4a9e2313aadf1 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -177,8 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) // return zero on success } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - this.set_last_error(LibcError("EINVAL"))?; - Ok(Scalar::from_i32(-1)) + this.set_last_error_and_return_i32(LibcError("EINVAL")) } } @@ -202,8 +201,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - this.set_last_error(LibcError("EINVAL"))?; - Ok(Scalar::from_i32(-1)) + this.set_last_error_and_return_i32(LibcError("EINVAL")) } } @@ -242,9 +240,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`chdir`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - - return Ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } let result = env::set_current_dir(path).map(|()| 0); diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index fe634de122c46..41a819fb31f8c 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -524,8 +524,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fcntl`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return Ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } this.ffullsync_fd(fd_num) @@ -606,9 +605,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => fd.read(&fd, communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - this.set_last_error(LibcError("EINVAL"))?; - this.write_int(-1, dest)?; - return Ok(()); + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; fd.pread(communicate, offset, buf, count, dest, this)? } @@ -650,9 +647,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => fd.write(&fd, communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - this.set_last_error(LibcError("EINVAL"))?; - this.write_int(-1, dest)?; - return Ok(()); + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; fd.pwrite(communicate, buf, count, offset, dest, this)? } From c7cd55f7c5032c646db22f144dda2f04d8ee6009 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 4 Sep 2024 17:15:13 -0700 Subject: [PATCH 1112/1301] Stabilize expr_2021 fragment in all editions Co-authored-by: Michael Goulet Co-authored-by: Vincenzo Palazzo --- compiler/rustc_expand/src/mbe/macro_check.rs | 4 +- compiler/rustc_expand/src/mbe/quoted.rs | 38 ++----------------- compiler/rustc_feature/src/accepted.rs | 2 + compiler/rustc_feature/src/unstable.rs | 2 - .../unused-macro-with-bad-frag-spec.stderr | 2 +- tests/ui/macros/expr_2021.rs | 14 +++++++ .../macros/expr_2021_cargo_fix_edition.fixed | 2 - .../ui/macros/expr_2021_cargo_fix_edition.rs | 2 - .../macros/expr_2021_cargo_fix_edition.stderr | 6 +-- .../expr_2021_inline_const.edi2021.stderr | 8 ++-- .../expr_2021_inline_const.edi2024.stderr | 4 +- tests/ui/macros/expr_2021_inline_const.rs | 3 -- .../expr_2024_underscore_expr.edi2021.stderr | 8 ++-- .../expr_2024_underscore_expr.edi2024.stderr | 4 +- tests/ui/macros/expr_2024_underscore_expr.rs | 3 -- ...ature-gate-expr_fragment_specifier_2024.rs | 11 ------ ...e-gate-expr_fragment_specifier_2024.stderr | 13 ------- .../macros/invalid-fragment-specifier.stderr | 4 +- tests/ui/macros/issue-21356.stderr | 2 +- .../macro-missing-fragment.e2024.stderr | 6 +-- .../metavar_cross_edition_recursive_macros.rs | 1 - 21 files changed, 44 insertions(+), 95 deletions(-) create mode 100644 tests/ui/macros/expr_2021.rs delete mode 100644 tests/ui/macros/feature-gate-expr_fragment_specifier_2024.rs delete mode 100644 tests/ui/macros/feature-gate-expr_fragment_specifier_2024.stderr diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index b1d898b69494c..1498b9cbd5d62 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -119,7 +119,7 @@ use rustc_span::symbol::{MacroRulesNormalizedIdent, kw}; use rustc_span::{ErrorGuaranteed, Span}; use smallvec::SmallVec; -use super::quoted::VALID_FRAGMENT_NAMES_MSG_2021; +use super::quoted::VALID_FRAGMENT_NAMES_MSG; use crate::errors; use crate::mbe::{KleeneToken, TokenTree}; @@ -274,7 +274,7 @@ fn check_binders( psess.dcx().emit_err(errors::MissingFragmentSpecifier { span, add_span: span.shrink_to_hi(), - valid: VALID_FRAGMENT_NAMES_MSG_2021, + valid: VALID_FRAGMENT_NAMES_MSG, }); } else { psess.buffer_lint( diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index f0a6c841f3121..2edd289625e18 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,4 +1,3 @@ -use rustc_ast::token::NtExprKind::*; use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token}; use rustc_ast::{NodeId, tokenstream}; use rustc_ast_pretty::pprust; @@ -13,12 +12,9 @@ use crate::errors; use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; -const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ - `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, \ - `item` and `vis`"; -pub(crate) const VALID_FRAGMENT_NAMES_MSG_2021: &str = "valid fragment specifiers are \ - `ident`, `block`, `stmt`, `expr`, `expr_2021`, `pat`, `ty`, `lifetime`, `literal`, `path`, \ - `meta`, `tt`, `item` and `vis`"; +pub(crate) const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ + `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, \ + `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility"; /// Takes a `tokenstream::TokenStream` and returns a `Vec`. Specifically, this /// takes a generic `TokenStream`, such as is used in the rest of the compiler, and returns a @@ -92,39 +88,13 @@ pub(super) fn parse( }; let kind = NonterminalKind::from_symbol(fragment.name, edition) .unwrap_or_else(|| { - let help = match fragment.name { - sym::expr_2021 => { - format!( - "fragment specifier `expr_2021` \ - requires Rust 2021 or later\n\ - {VALID_FRAGMENT_NAMES_MSG}" - ) - } - _ if edition().at_least_rust_2021() - && features.expr_fragment_specifier_2024 => - { - VALID_FRAGMENT_NAMES_MSG_2021.into() - } - _ => VALID_FRAGMENT_NAMES_MSG.into(), - }; sess.dcx().emit_err(errors::InvalidFragmentSpecifier { span, fragment, - help, + help: VALID_FRAGMENT_NAMES_MSG.into(), }); NonterminalKind::Ident }); - if kind == NonterminalKind::Expr(Expr2021 { inferred: false }) - && !features.expr_fragment_specifier_2024 - { - rustc_session::parse::feature_err( - sess, - sym::expr_fragment_specifier_2024, - span, - "fragment specifier `expr_2021` is unstable", - ) - .emit(); - } result.push(TokenTree::MetaVarDecl(span, ident, Some(kind))); continue; } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 5ff002dd7d2bf..a850eb956207e 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -189,6 +189,8 @@ declare_features! ( (accepted, exhaustive_integer_patterns, "1.33.0", Some(50907)), /// Allows explicit generic arguments specification with `impl Trait` present. (accepted, explicit_generic_args_with_impl_trait, "1.63.0", Some(83701)), + /// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment. + (accepted, expr_fragment_specifier_2024, "CURRENT_RUSTC_VERSION", Some(123742)), /// Allows arbitrary expressions in key-value attributes at parse time. (accepted, extended_key_value_attributes, "1.54.0", Some(78835)), /// Allows resolving absolute paths as paths from other crates. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1ffd35dbf91d1..c5530097e9606 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -450,8 +450,6 @@ declare_features! ( (unstable, exhaustive_patterns, "1.13.0", Some(51085)), /// Allows explicit tail calls via `become` expression. (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), - /// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment. - (incomplete, expr_fragment_specifier_2024, "1.80.0", Some(123742)), /// Allows using `efiapi`, `sysv64` and `win64` as calling convention /// for functions with varargs. (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)), diff --git a/tests/ui/lint/unused/unused-macro-with-bad-frag-spec.stderr b/tests/ui/lint/unused/unused-macro-with-bad-frag-spec.stderr index f027e169b42bb..003c6975c95c4 100644 --- a/tests/ui/lint/unused/unused-macro-with-bad-frag-spec.stderr +++ b/tests/ui/lint/unused/unused-macro-with-bad-frag-spec.stderr @@ -4,7 +4,7 @@ error: invalid fragment specifier `t_ty` LL | ($wrong:t_ty) => () | ^^^^^^^^^^^ | - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility error: aborting due to 1 previous error diff --git a/tests/ui/macros/expr_2021.rs b/tests/ui/macros/expr_2021.rs new file mode 100644 index 0000000000000..8a274e7753359 --- /dev/null +++ b/tests/ui/macros/expr_2021.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ edition: 2015 + +// Ensures expr_2021 fragment specifier is accepted in old editions + +macro_rules! my_macro { + ($x:expr_2021) => { + println!("Hello, {}!", $x); + }; +} + +fn main() { + my_macro!("world"); +} diff --git a/tests/ui/macros/expr_2021_cargo_fix_edition.fixed b/tests/ui/macros/expr_2021_cargo_fix_edition.fixed index 1becd8a92d62e..061a4b98033c4 100644 --- a/tests/ui/macros/expr_2021_cargo_fix_edition.fixed +++ b/tests/ui/macros/expr_2021_cargo_fix_edition.fixed @@ -1,8 +1,6 @@ //@ run-rustfix //@ check-pass //@ compile-flags: --edition=2021 -#![allow(incomplete_features)] -#![feature(expr_fragment_specifier_2024)] #![warn(edition_2024_expr_fragment_specifier)] macro_rules! m { diff --git a/tests/ui/macros/expr_2021_cargo_fix_edition.rs b/tests/ui/macros/expr_2021_cargo_fix_edition.rs index ec0b86d2c2332..cd9cd965fad22 100644 --- a/tests/ui/macros/expr_2021_cargo_fix_edition.rs +++ b/tests/ui/macros/expr_2021_cargo_fix_edition.rs @@ -1,8 +1,6 @@ //@ run-rustfix //@ check-pass //@ compile-flags: --edition=2021 -#![allow(incomplete_features)] -#![feature(expr_fragment_specifier_2024)] #![warn(edition_2024_expr_fragment_specifier)] macro_rules! m { diff --git a/tests/ui/macros/expr_2021_cargo_fix_edition.stderr b/tests/ui/macros/expr_2021_cargo_fix_edition.stderr index e8a44fed322f9..fe1fd4a26a028 100644 --- a/tests/ui/macros/expr_2021_cargo_fix_edition.stderr +++ b/tests/ui/macros/expr_2021_cargo_fix_edition.stderr @@ -1,5 +1,5 @@ warning: the `expr` fragment specifier will accept more expressions in the 2024 edition - --> $DIR/expr_2021_cargo_fix_edition.rs:9:9 + --> $DIR/expr_2021_cargo_fix_edition.rs:7:9 | LL | ($e:expr) => { | ^^^^ @@ -7,7 +7,7 @@ LL | ($e:expr) => { = warning: this changes meaning in Rust 2024 = note: for more information, see Migration Guide note: the lint level is defined here - --> $DIR/expr_2021_cargo_fix_edition.rs:6:9 + --> $DIR/expr_2021_cargo_fix_edition.rs:4:9 | LL | #![warn(edition_2024_expr_fragment_specifier)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | ($e:expr_2021) => { | ~~~~~~~~~ warning: the `expr` fragment specifier will accept more expressions in the 2024 edition - --> $DIR/expr_2021_cargo_fix_edition.rs:13:11 + --> $DIR/expr_2021_cargo_fix_edition.rs:11:11 | LL | ($($i:expr)*) => { }; | ^^^^ diff --git a/tests/ui/macros/expr_2021_inline_const.edi2021.stderr b/tests/ui/macros/expr_2021_inline_const.edi2021.stderr index b55ae62030c56..22d662aaaf2f9 100644 --- a/tests/ui/macros/expr_2021_inline_const.edi2021.stderr +++ b/tests/ui/macros/expr_2021_inline_const.edi2021.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `const` - --> $DIR/expr_2021_inline_const.rs:26:12 + --> $DIR/expr_2021_inline_const.rs:23:12 | LL | macro_rules! m2021 { | ------------------ when calling this macro @@ -8,13 +8,13 @@ LL | m2021!(const { 1 }); | ^^^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr_2021` - --> $DIR/expr_2021_inline_const.rs:10:6 + --> $DIR/expr_2021_inline_const.rs:7:6 | LL | ($e:expr_2021) => { | ^^^^^^^^^^^^ error: no rules expected the token `const` - --> $DIR/expr_2021_inline_const.rs:27:12 + --> $DIR/expr_2021_inline_const.rs:24:12 | LL | macro_rules! m2024 { | ------------------ when calling this macro @@ -23,7 +23,7 @@ LL | m2024!(const { 1 }); | ^^^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr` - --> $DIR/expr_2021_inline_const.rs:16:6 + --> $DIR/expr_2021_inline_const.rs:13:6 | LL | ($e:expr) => { | ^^^^^^^ diff --git a/tests/ui/macros/expr_2021_inline_const.edi2024.stderr b/tests/ui/macros/expr_2021_inline_const.edi2024.stderr index 285db53d6c834..2555e4f757a52 100644 --- a/tests/ui/macros/expr_2021_inline_const.edi2024.stderr +++ b/tests/ui/macros/expr_2021_inline_const.edi2024.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `const` - --> $DIR/expr_2021_inline_const.rs:26:12 + --> $DIR/expr_2021_inline_const.rs:23:12 | LL | macro_rules! m2021 { | ------------------ when calling this macro @@ -8,7 +8,7 @@ LL | m2021!(const { 1 }); | ^^^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr_2021` - --> $DIR/expr_2021_inline_const.rs:10:6 + --> $DIR/expr_2021_inline_const.rs:7:6 | LL | ($e:expr_2021) => { | ^^^^^^^^^^^^ diff --git a/tests/ui/macros/expr_2021_inline_const.rs b/tests/ui/macros/expr_2021_inline_const.rs index 06b74a466d6e2..39a542fe4d94a 100644 --- a/tests/ui/macros/expr_2021_inline_const.rs +++ b/tests/ui/macros/expr_2021_inline_const.rs @@ -3,9 +3,6 @@ //@[edi2021]compile-flags: --edition=2021 // This test ensures that the inline const match only on edition 2024 -#![feature(expr_fragment_specifier_2024)] -#![allow(incomplete_features)] - macro_rules! m2021 { ($e:expr_2021) => { $e diff --git a/tests/ui/macros/expr_2024_underscore_expr.edi2021.stderr b/tests/ui/macros/expr_2024_underscore_expr.edi2021.stderr index 335b3f613434b..34df20a69ef51 100644 --- a/tests/ui/macros/expr_2024_underscore_expr.edi2021.stderr +++ b/tests/ui/macros/expr_2024_underscore_expr.edi2021.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `_` - --> $DIR/expr_2024_underscore_expr.rs:22:12 + --> $DIR/expr_2024_underscore_expr.rs:19:12 | LL | macro_rules! m2021 { | ------------------ when calling this macro @@ -8,13 +8,13 @@ LL | m2021!(_); | ^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr_2021` - --> $DIR/expr_2024_underscore_expr.rs:10:6 + --> $DIR/expr_2024_underscore_expr.rs:7:6 | LL | ($e:expr_2021) => { | ^^^^^^^^^^^^ error: no rules expected the token `_` - --> $DIR/expr_2024_underscore_expr.rs:23:12 + --> $DIR/expr_2024_underscore_expr.rs:20:12 | LL | macro_rules! m2024 { | ------------------ when calling this macro @@ -23,7 +23,7 @@ LL | m2024!(_); | ^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr` - --> $DIR/expr_2024_underscore_expr.rs:16:6 + --> $DIR/expr_2024_underscore_expr.rs:13:6 | LL | ($e:expr) => { | ^^^^^^^ diff --git a/tests/ui/macros/expr_2024_underscore_expr.edi2024.stderr b/tests/ui/macros/expr_2024_underscore_expr.edi2024.stderr index 9e49f66a89ae8..372c5d8637c9f 100644 --- a/tests/ui/macros/expr_2024_underscore_expr.edi2024.stderr +++ b/tests/ui/macros/expr_2024_underscore_expr.edi2024.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `_` - --> $DIR/expr_2024_underscore_expr.rs:22:12 + --> $DIR/expr_2024_underscore_expr.rs:19:12 | LL | macro_rules! m2021 { | ------------------ when calling this macro @@ -8,7 +8,7 @@ LL | m2021!(_); | ^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr_2021` - --> $DIR/expr_2024_underscore_expr.rs:10:6 + --> $DIR/expr_2024_underscore_expr.rs:7:6 | LL | ($e:expr_2021) => { | ^^^^^^^^^^^^ diff --git a/tests/ui/macros/expr_2024_underscore_expr.rs b/tests/ui/macros/expr_2024_underscore_expr.rs index b2129bf154f7e..86e3137450685 100644 --- a/tests/ui/macros/expr_2024_underscore_expr.rs +++ b/tests/ui/macros/expr_2024_underscore_expr.rs @@ -3,9 +3,6 @@ //@[edi2021]compile-flags: --edition=2021 // This test ensures that the `_` tok is considered an // expression on edition 2024. -#![feature(expr_fragment_specifier_2024)] -#![allow(incomplete_features)] - macro_rules! m2021 { ($e:expr_2021) => { $e = 1; diff --git a/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.rs b/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.rs deleted file mode 100644 index 5a737b298214d..0000000000000 --- a/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ compile-flags: --edition=2024 -Z unstable-options - -macro_rules! m { - ($e:expr_2021) => { //~ ERROR: fragment specifier `expr_2021` is unstable - $e - }; -} - -fn main() { - m!(()); -} diff --git a/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.stderr b/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.stderr deleted file mode 100644 index 273a93877ce13..0000000000000 --- a/tests/ui/macros/feature-gate-expr_fragment_specifier_2024.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: fragment specifier `expr_2021` is unstable - --> $DIR/feature-gate-expr_fragment_specifier_2024.rs:4:6 - | -LL | ($e:expr_2021) => { - | ^^^^^^^^^^^^ - | - = note: see issue #123742 for more information - = help: add `#![feature(expr_fragment_specifier_2024)]` 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/macros/invalid-fragment-specifier.stderr b/tests/ui/macros/invalid-fragment-specifier.stderr index 7516dbc3a0876..a51ea619b3662 100644 --- a/tests/ui/macros/invalid-fragment-specifier.stderr +++ b/tests/ui/macros/invalid-fragment-specifier.stderr @@ -4,7 +4,7 @@ error: invalid fragment specifier `id` LL | ($wrong:id) => {}; | ^^^^^^^^^ | - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility error: invalid fragment specifier `r#if` --> $DIR/invalid-fragment-specifier.rs:7:6 @@ -12,7 +12,7 @@ error: invalid fragment specifier `r#if` LL | ($wrong:r#if) => {}; | ^^^^^^^^^^^ | - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility error: aborting due to 2 previous errors diff --git a/tests/ui/macros/issue-21356.stderr b/tests/ui/macros/issue-21356.stderr index dd09da6df4f43..5ff9264251487 100644 --- a/tests/ui/macros/issue-21356.stderr +++ b/tests/ui/macros/issue-21356.stderr @@ -4,7 +4,7 @@ error: invalid fragment specifier `t_ty` LL | macro_rules! test { ($wrong:t_ty ..) => () } | ^^^^^^^^^^^ | - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility error: aborting due to 1 previous error diff --git a/tests/ui/macros/macro-missing-fragment.e2024.stderr b/tests/ui/macros/macro-missing-fragment.e2024.stderr index 3afa069c170e4..0dc48e0c7b219 100644 --- a/tests/ui/macros/macro-missing-fragment.e2024.stderr +++ b/tests/ui/macros/macro-missing-fragment.e2024.stderr @@ -5,7 +5,7 @@ LL | ( $( any_token $field_rust_type )* ) => {}; | ^^^^^^^^^^^^^^^^ | = note: fragment specifiers must be specified in the 2024 edition - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `expr_2021`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility help: try adding a specifier here | LL | ( $( any_token $field_rust_type:spec )* ) => {}; @@ -18,7 +18,7 @@ LL | ( $name ) => {}; | ^^^^^ | = note: fragment specifiers must be specified in the 2024 edition - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `expr_2021`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility help: try adding a specifier here | LL | ( $name:spec ) => {}; @@ -31,7 +31,7 @@ LL | ( $name ) => {}; | ^^^^^ | = note: fragment specifiers must be specified in the 2024 edition - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `expr_2021`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility help: try adding a specifier here | LL | ( $name:spec ) => {}; diff --git a/tests/ui/macros/metavar_cross_edition_recursive_macros.rs b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs index 3eec1208b89ae..9a5b92f503297 100644 --- a/tests/ui/macros/metavar_cross_edition_recursive_macros.rs +++ b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs @@ -6,7 +6,6 @@ // This test captures the behavior of macro-generating-macros with fragment // specifiers across edition boundaries. -#![feature(expr_fragment_specifier_2024)] #![feature(macro_metavar_expr)] #![allow(incomplete_features)] From 57b9b1f9745a56943cc0aebc4c3ec487945f044e Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 18 Sep 2024 17:44:32 +0200 Subject: [PATCH 1113/1301] Use `ast::NestedMetaItem` when evaluating cfg predicate --- compiler/rustc_attr/src/builtin.rs | 35 +++++++++++++------ compiler/rustc_builtin_macros/src/cfg.rs | 9 +++-- compiler/rustc_codegen_ssa/src/lib.rs | 2 +- compiler/rustc_expand/src/config.rs | 12 ++++--- compiler/rustc_metadata/src/native_libs.rs | 9 +++-- compiler/rustc_parse/src/lib.rs | 4 +-- compiler/rustc_parse/src/parser/attr.rs | 8 +++-- compiler/rustc_session/src/cstore.rs | 2 +- .../traits/on_unimplemented.rs | 12 +++---- tests/ui/macros/cfg.rs | 2 +- tests/ui/macros/cfg.stderr | 4 +-- 11 files changed, 62 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 762ebc47ca9d0..37f70c218698c 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -523,7 +523,7 @@ pub struct Condition { /// Tests if a cfg-pattern matches the cfg set pub fn cfg_matches( - cfg: &ast::MetaItem, + cfg: &ast::NestedMetaItem, sess: &Session, lint_node_id: NodeId, features: Option<&Features>, @@ -594,12 +594,26 @@ pub fn parse_version(s: Symbol) -> Option { /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. pub fn eval_condition( - cfg: &ast::MetaItem, + cfg: &ast::NestedMetaItem, sess: &Session, features: Option<&Features>, eval: &mut impl FnMut(Condition) -> bool, ) -> bool { let dcx = sess.dcx(); + + let cfg = match cfg { + ast::NestedMetaItem::MetaItem(meta_item) => meta_item, + _ => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: cfg.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(cfg.span()), + }); + return false; + } + }; + match &cfg.kind { ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { try_gate_cfg(sym::version, cfg.span, sess, features); @@ -653,23 +667,19 @@ pub fn eval_condition( .iter() // We don't use any() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks - .fold(false, |res, mi| { - res | eval_condition(mi.meta_item().unwrap(), sess, features, eval) - }), + .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), sym::all => mis .iter() // We don't use all() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks - .fold(true, |res, mi| { - res & eval_condition(mi.meta_item().unwrap(), sess, features, eval) - }), + .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), sym::not => { let [mi] = mis.as_slice() else { dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); return false; }; - !eval_condition(mi.meta_item().unwrap(), sess, features, eval) + !eval_condition(mi, sess, features, eval) } sym::target => { if let Some(features) = features @@ -690,7 +700,12 @@ pub fn eval_condition( seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); } - res & eval_condition(&mi, sess, features, eval) + res & eval_condition( + &ast::NestedMetaItem::MetaItem(mi), + sess, + features, + eval, + ) }) } _ => { diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index cf1d5c68eadd0..940c94b1cfc89 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -6,7 +6,6 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; -use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_span::Span; use {rustc_ast as ast, rustc_attr as attr}; @@ -36,14 +35,18 @@ pub(crate) fn expand_cfg( }) } -fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { +fn parse_cfg<'a>( + cx: &ExtCtxt<'a>, + span: Span, + tts: TokenStream, +) -> PResult<'a, ast::NestedMetaItem> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span })); } - let cfg = p.parse_meta_item(AllowLeadingUnsafe::No)?; + let cfg = p.parse_meta_item_inner()?; let _ = p.eat(&token::Comma); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 162d14272a555..3129b9ac20344 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -156,7 +156,7 @@ pub struct NativeLib { pub kind: NativeLibKind, pub name: Symbol, pub filename: Option, - pub cfg: Option, + pub cfg: Option, pub verbatim: bool, pub dll_imports: Vec, } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 3208837427736..8ea4f901368ec 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -5,7 +5,9 @@ use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, }; -use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId}; +use rustc_ast::{ + self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NestedMetaItem, NodeId, +}; use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ @@ -449,7 +451,7 @@ impl<'a> StripUnconfigured<'a> { } } -pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> { +pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a NestedMetaItem> { let span = meta_item.span; match meta_item.meta_item_list() { None => { @@ -464,9 +466,9 @@ pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a Meta sess.dcx().emit_err(InvalidCfg::MultiplePredicates { span: l.span() }); None } - Some([single]) => match single.meta_item() { - Some(meta_item) => Some(meta_item), - None => { + Some([single]) => match single.is_meta_item() { + true => Some(single), + false => { sess.dcx().emit_err(InvalidCfg::PredicateLiteral { span: single.span() }); None } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 82b907d2501fe..373b109189e7b 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use std::path::{Path, PathBuf}; -use rustc_ast::{CRATE_NODE_ID, NestedMetaItem}; +use rustc_ast::CRATE_NODE_ID; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_middle::query::LocalCrate; @@ -304,7 +304,12 @@ impl<'tcx> Collector<'tcx> { sess.dcx().emit_err(errors::LinkCfgForm { span: item.span() }); continue; }; - let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else { + let [link_cfg] = link_cfg else { + sess.dcx() + .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); + continue; + }; + if !link_cfg.is_meta_item() { sess.dcx() .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); continue; diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index f7a8b8780edc4..da02f98d0e5bd 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -18,7 +18,7 @@ use std::path::Path; use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AttrItem, Attribute, MetaItem, token}; +use rustc_ast::{AttrItem, Attribute, NestedMetaItem, token}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, FatalError, PResult}; @@ -160,7 +160,7 @@ pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> Tok pub fn parse_cfg_attr( cfg_attr: &Attribute, psess: &ParseSess, -) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { +) -> Option<(NestedMetaItem, Vec<(AttrItem, Span)>)> { const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ "; diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index c65cf3f40f658..4aa56cb7624a6 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -356,8 +356,10 @@ impl<'a> Parser<'a> { } /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited. - pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> { - let cfg_predicate = self.parse_meta_item(AllowLeadingUnsafe::No)?; + pub fn parse_cfg_attr( + &mut self, + ) -> PResult<'a, (ast::NestedMetaItem, Vec<(ast::AttrItem, Span)>)> { + let cfg_predicate = self.parse_meta_item_inner()?; self.expect(&token::Comma)?; // Presumably, the majority of the time there will only be one attr. @@ -452,7 +454,7 @@ impl<'a> Parser<'a> { /// ```ebnf /// MetaItemInner = UNSUFFIXED_LIT | MetaItem ; /// ``` - fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> { + pub fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> { match self.parse_unsuffixed_meta_item_lit() { Ok(lit) => return Ok(ast::NestedMetaItem::Lit(lit)), Err(err) => err.cancel(), // we provide a better error below diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 66ad70d8d18be..b936a299b0973 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -72,7 +72,7 @@ pub struct NativeLib { pub name: Symbol, /// If packed_bundled_libs enabled, actual filename of library is stored. pub filename: Option, - pub cfg: Option, + pub cfg: Option, pub foreign_module: Option, pub verbatim: Option, pub dll_imports: Vec, 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 2c7ca50f9549c..f2efc5ee47d2b 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 @@ -1,7 +1,7 @@ use std::iter; use std::path::PathBuf; -use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, MetaItem, NestedMetaItem}; +use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; @@ -282,7 +282,7 @@ pub struct OnUnimplementedFormatString { #[derive(Debug)] pub struct OnUnimplementedDirective { - pub condition: Option, + pub condition: Option, pub subcommands: Vec, pub message: Option, pub label: Option, @@ -413,9 +413,7 @@ impl<'tcx> OnUnimplementedDirective { } else { let cond = item_iter .next() - .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))? - .meta_item() - .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?; + .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value && let Err(guar) = parse_value(value, cfg.span) @@ -558,8 +556,8 @@ impl<'tcx> OnUnimplementedDirective { IgnoredDiagnosticOption::maybe_emit_warning( tcx, item_def_id, - directive.condition.as_ref().map(|i| i.span), - aggr.condition.as_ref().map(|i| i.span), + directive.condition.as_ref().map(|i| i.span()), + aggr.condition.as_ref().map(|i| i.span()), "condition", ); IgnoredDiagnosticOption::maybe_emit_warning( diff --git a/tests/ui/macros/cfg.rs b/tests/ui/macros/cfg.rs index 2aac50a9d011a..387cc0ba8e25c 100644 --- a/tests/ui/macros/cfg.rs +++ b/tests/ui/macros/cfg.rs @@ -1,6 +1,6 @@ fn main() { cfg!(); //~ ERROR macro requires a cfg-pattern - cfg!(123); //~ ERROR expected identifier + cfg!(123); //~ ERROR unsupported literal cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string cfg!(foo, bar); //~ ERROR expected 1 cfg-pattern } diff --git a/tests/ui/macros/cfg.stderr b/tests/ui/macros/cfg.stderr index 2633d5f720d70..5c21497dc5dd6 100644 --- a/tests/ui/macros/cfg.stderr +++ b/tests/ui/macros/cfg.stderr @@ -6,11 +6,11 @@ LL | cfg!(); | = note: this error originates in the macro `cfg` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected identifier, found `123` +error[E0565]: unsupported literal --> $DIR/cfg.rs:3:10 | LL | cfg!(123); - | ^^^ expected identifier + | ^^^ error[E0565]: literal in `cfg` predicate value must be a string --> $DIR/cfg.rs:4:16 From c99f29b29fa4115436c352a921f9963b173b5608 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 19 Sep 2024 10:13:14 +0200 Subject: [PATCH 1114/1301] Implement boolean lit support in cfg predicates --- compiler/rustc_ast/src/attr/mod.rs | 10 +++++++ compiler/rustc_attr/src/builtin.rs | 3 +- compiler/rustc_expand/src/config.rs | 6 ++-- compiler/rustc_metadata/src/native_libs.rs | 2 +- .../traits/on_unimplemented.rs | 4 ++- tests/ui/cfg/true-false.rs | 29 +++++++++++++++++++ 6 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 tests/ui/cfg/true-false.rs diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 124d0acfa4912..338d50cb39424 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -527,6 +527,16 @@ impl NestedMetaItem { } } + /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem` or if it's + /// `NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`. + pub fn meta_item_or_bool(&self) -> Option<&NestedMetaItem> { + match self { + NestedMetaItem::MetaItem(_item) => Some(self), + NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self), + _ => None, + } + } + /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`. pub fn meta_item(&self) -> Option<&MetaItem> { match self { diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 37f70c218698c..930fdeb975d87 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -603,6 +603,7 @@ pub fn eval_condition( let cfg = match cfg { ast::NestedMetaItem::MetaItem(meta_item) => meta_item, + ast::NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => return *b, _ => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: cfg.span(), @@ -649,7 +650,7 @@ pub fn eval_condition( } ast::MetaItemKind::List(mis) => { for mi in mis.iter() { - if !mi.is_meta_item() { + if mi.meta_item_or_bool().is_none() { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: mi.span(), reason: UnsupportedLiteralReason::Generic, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 8ea4f901368ec..af56169fd60c9 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -466,9 +466,9 @@ pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a Nest sess.dcx().emit_err(InvalidCfg::MultiplePredicates { span: l.span() }); None } - Some([single]) => match single.is_meta_item() { - true => Some(single), - false => { + Some([single]) => match single.meta_item_or_bool() { + Some(meta_item) => Some(meta_item), + None => { sess.dcx().emit_err(InvalidCfg::PredicateLiteral { span: single.span() }); None } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 373b109189e7b..c7953d50406ac 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -309,7 +309,7 @@ impl<'tcx> Collector<'tcx> { .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); continue; }; - if !link_cfg.is_meta_item() { + let Some(link_cfg) = link_cfg.meta_item_or_bool() else { sess.dcx() .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); continue; 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 f2efc5ee47d2b..e9a2c5b8d8e06 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 @@ -413,7 +413,9 @@ impl<'tcx> OnUnimplementedDirective { } else { let cond = item_iter .next() - .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?; + .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))? + .meta_item_or_bool() + .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value && let Err(guar) = parse_value(value, cfg.span) diff --git a/tests/ui/cfg/true-false.rs b/tests/ui/cfg/true-false.rs new file mode 100644 index 0000000000000..0bd1cf427fafd --- /dev/null +++ b/tests/ui/cfg/true-false.rs @@ -0,0 +1,29 @@ +//@ run-pass + +#![feature(link_cfg)] + +#[cfg(true)] +fn foo() -> bool { + cfg!(true) +} + +#[cfg(false)] +fn foo() -> bool { + cfg!(false) +} + +#[cfg_attr(true, cfg(false))] +fn foo() {} + +#[link(name = "foo", cfg(false))] +extern "C" {} + +fn main() { + assert!(foo()); + assert!(cfg!(true)); + assert!(!cfg!(false)); + assert!(cfg!(not(false))); + assert!(cfg!(all(true))); + assert!(cfg!(any(true))); + assert!(!cfg!(not(true))); +} From 4529b86196836ef1efe5c710a8874877d17b9ef8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 11:01:45 +0200 Subject: [PATCH 1115/1301] make test_lots_of_insertions test take less long in Miri --- library/std/src/collections/hash/map/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 2599060ef62eb..fa8ea95b89142 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -274,7 +274,7 @@ fn test_lots_of_insertions() { for _ in 0..loops { assert!(m.is_empty()); - let count = if cfg!(miri) { 101 } else { 1001 }; + let count = if cfg!(miri) { 66 } else { 1001 }; for i in 1..count { assert!(m.insert(i, i).is_none()); From 125db409ffe4bcec2ce801d401eabd0e607ebf4a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Jul 2024 15:29:36 +0200 Subject: [PATCH 1116/1301] Small optimization for integers Display implementation --- library/core/src/fmt/num.rs | 138 ++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 46 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index e7726da8d91f2..5badc0a024c84 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,11 +208,46 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ 8081828384858687888990919293949596979899"; macro_rules! impl_Display { - ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + ($($t:ident => $size:literal $(as $positive:ident in $other:ident)? => named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { + + $( + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Display for $t { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If it's a signed integer. + $( + let is_nonnegative = *self >= 0; + + #[cfg(not(feature = "optimize_for_size"))] + { + if !is_nonnegative { + // convert the negative num to positive by summing 1 to its 2s complement + return $other((!self as $positive).wrapping_add(1), false, f); + } + } + #[cfg(feature = "optimize_for_size")] + { + if !is_nonnegative { + // convert the negative num to positive by summing 1 to its 2s complement + return $other((!self.$conv_fn()).wrapping_add(1), false, f); + } + } + )? + // If it's a positive integer. + #[cfg(not(feature = "optimize_for_size"))] + { + $name(*self, true, f) + } + #[cfg(feature = "optimize_for_size")] + { + $gen_name(*self, true, f) + } + } + } + #[cfg(not(feature = "optimize_for_size"))] - fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // 2^128 is about 3*10^38, so 39 gives an extra byte of space - let mut buf = [MaybeUninit::::uninit(); 39]; + fn $name(mut n: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut buf = [MaybeUninit::::uninit(); $size]; let mut curr = buf.len(); let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); let lut_ptr = DEC_DIGITS_LUT.as_ptr(); @@ -226,22 +261,26 @@ macro_rules! impl_Display { // is safe to access. unsafe { // need at least 16 bits for the 4-characters-at-a-time to work. - assert!(crate::mem::size_of::<$u>() >= 2); - - // eagerly decode 4 characters at a time - while n >= 10000 { - let rem = (n % 10000) as usize; - n /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.add(d2), buf_ptr.add(curr + 2), 2); + #[allow(overflowing_literals)] + #[allow(unused_comparisons)] + // This block will be removed for smaller types at compile time and in the worst + // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. + if core::mem::size_of::<$t>() >= 2 { + // eagerly decode 4 characters at a time + while n >= 10000 { + let rem = (n % 10000) as usize; + n /= 10000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + + // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since + // otherwise `curr < 0`. But then `n` was originally at least `10000^10` + // which is `10^40 > 2^128 > n`. + ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); + ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); + } } // if we reach here numbers are <= 9999, so at most 4 chars long @@ -255,6 +294,8 @@ macro_rules! impl_Display { ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); } + // if we reach here numbers are <= 100, so at most 2 chars long + // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. // decode last 1 or 2 chars if n < 10 { curr -= 1; @@ -273,11 +314,10 @@ macro_rules! impl_Display { slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) }; f.pad_integral(is_nonnegative, "", buf_slice) - } + })* #[cfg(feature = "optimize_for_size")] - fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // 2^128 is about 3*10^38, so 39 gives an extra byte of space + fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut buf = [MaybeUninit::::uninit(); 39]; let mut curr = buf.len(); let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); @@ -306,21 +346,6 @@ macro_rules! impl_Display { }; f.pad_integral(is_nonnegative, "", buf_slice) } - - $(#[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Display for $t { - #[allow(unused_comparisons)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - self.$conv_fn() - } else { - // convert the negative num to positive by summing 1 to it's 2 complement - (!self.$conv_fn()).wrapping_add(1) - }; - $name(n, is_nonnegative, f) - } - })* }; } @@ -374,7 +399,6 @@ macro_rules! impl_Exp { (n, exponent, exponent, added_precision) }; - // 39 digits (worst case u128) + . = 40 // Since `curr` always decreases by the number of digits copied, this means // that `curr >= 0`. let mut buf = [MaybeUninit::::uninit(); 40]; @@ -469,7 +493,7 @@ macro_rules! impl_Exp { let n = if is_nonnegative { self.$conv_fn() } else { - // convert the negative num to positive by summing 1 to it's 2 complement + // convert the negative num to positive by summing 1 to its 2s complement (!self.$conv_fn()).wrapping_add(1) }; $name(n, is_nonnegative, false, f) @@ -484,7 +508,7 @@ macro_rules! impl_Exp { let n = if is_nonnegative { self.$conv_fn() } else { - // convert the negative num to positive by summing 1 to it's 2 complement + // convert the negative num to positive by summing 1 to its 2s complement (!self.$conv_fn()).wrapping_add(1) }; $name(n, is_nonnegative, true, f) @@ -499,8 +523,17 @@ macro_rules! impl_Exp { mod imp { use super::*; impl_Display!( - i8, u8, i16, u16, i32, u32, i64, u64, usize, isize - as u64 via to_u64 named fmt_u64 + i8 => 3 as u8 in fmt_u8 => named fmt_i8, + u8 => 3 => named fmt_u8, + i16 => 5 as u16 in fmt_u16 => named fmt_i16, + u16 => 5 => named fmt_u16, + i32 => 10 as u32 in fmt_u32 => named fmt_i32, + u32 => 10 => named fmt_u32, + i64 => 19 as u64 in fmt_u64 => named fmt_i64, + u64 => 20 => named fmt_u64, + isize => 19 as usize in fmt_usize => named fmt_isize, + usize => 20 => named fmt_usize, + ; as u64 via to_u64 named fmt_u64 ); impl_Exp!( i8, u8, i16, u16, i32, u32, i64, u64, usize, isize @@ -511,8 +544,21 @@ mod imp { #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] mod imp { use super::*; - impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32); - impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64); + impl_Display!( + i8 => 3 as u8 in fmt_u8 => named fmt_i8, + u8 => 3 => named fmt_u8, + i16 => 5 as u16 in fmt_u16 => named fmt_i16, + u16 => 5 => named fmt_u16, + i32 => 10 as u32 in fmt_u32 => named fmt_i32, + u32 => 10 => named fmt_u32, + isize => 10 as usize in fmt_usize => named fmt_isize, + usize => 10 => named fmt_usize, + ; as u32 via to_u32 named fmt_u32); + impl_Display!( + i64 => 19 as u64 in fmt_u64 => named fmt_i64, + u64 => 20 => named fmt_u64, + ; as u64 via to_u64 named fmt_u64); + impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64); } @@ -619,7 +665,7 @@ impl fmt::Display for i128 { let n = if is_nonnegative { self.to_u128() } else { - // convert the negative num to positive by summing 1 to it's 2 complement + // convert the negative num to positive by summing 1 to its 2s complement (!self.to_u128()).wrapping_add(1) }; fmt_u128(n, is_nonnegative, f) From 884e0f0a6865049355b952b9a14c8b4bce0e73b2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Sep 2024 17:24:31 +0200 Subject: [PATCH 1117/1301] Simplify `impl_Display` macro --- library/core/src/fmt/num.rs | 177 ++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 87 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 5badc0a024c84..bc62f11d09fbb 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,7 +208,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ 8081828384858687888990919293949596979899"; macro_rules! impl_Display { - ($($t:ident => $size:literal $(as $positive:ident in $other:ident)? => named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { + ($($t:ident => $size:literal $(as $positive:ident)? named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { $( #[stable(feature = "rust1", since = "1.0.0")] @@ -222,102 +222,105 @@ macro_rules! impl_Display { { if !is_nonnegative { // convert the negative num to positive by summing 1 to its 2s complement - return $other((!self as $positive).wrapping_add(1), false, f); + return (!self as $positive).wrapping_add(1)._fmt(false, f); } } #[cfg(feature = "optimize_for_size")] { if !is_nonnegative { // convert the negative num to positive by summing 1 to its 2s complement - return $other((!self.$conv_fn()).wrapping_add(1), false, f); + return $gen_name((!self.$conv_fn()).wrapping_add(1), false, f); } } )? // If it's a positive integer. #[cfg(not(feature = "optimize_for_size"))] { - $name(*self, true, f) + self._fmt(true, f) } #[cfg(feature = "optimize_for_size")] { - $gen_name(*self, true, f) + $gen_name(self.$conv_fn(), true, f) } } } #[cfg(not(feature = "optimize_for_size"))] - fn $name(mut n: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buf = [MaybeUninit::::uninit(); $size]; - let mut curr = buf.len(); - let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we - // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show - // that it's OK to copy into `buf_ptr`, notice that at the beginning - // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at - // each step this is kept the same as `n` is divided. Since `n` is always - // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` - // is safe to access. - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - #[allow(overflowing_literals)] - #[allow(unused_comparisons)] - // This block will be removed for smaller types at compile time and in the worst - // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. - if core::mem::size_of::<$t>() >= 2 { - // eagerly decode 4 characters at a time - while n >= 10000 { - let rem = (n % 10000) as usize; - n /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); + impl $t { + fn _fmt(mut self: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut buf = [MaybeUninit::::uninit(); $size]; + let mut curr = $size; + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we + // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show + // that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + // need at least 16 bits for the 4-characters-at-a-time to work. + #[allow(overflowing_literals)] + #[allow(unused_comparisons)] + // This block will be removed for smaller types at compile time and in the worst + // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. + if core::mem::size_of::<$t>() >= 2 { + // eagerly decode 4 characters at a time + while self >= 10000 { + let rem = (self % 10000) as usize; + self /= 10000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + + // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since + // otherwise `curr < 0`. But then `n` was originally at least `10000^10` + // which is `10^40 > 2^128 > n`. + ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); + ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); + } } - } - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = n as usize; // possibly reduce 64bit math + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = self as usize; // possibly reduce 64bit math - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); + } - // if we reach here numbers are <= 100, so at most 2 chars long - // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - *buf_ptr.add(curr) = (n as u8) + b'0'; - } else { - let d1 = n << 1; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); + // if we reach here numbers are <= 100, so at most 2 chars long + // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + *buf_ptr.add(curr) = (n as u8) + b'0'; + } else { + let d1 = n << 1; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); + } } - } - // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid - // UTF-8 since `DEC_DIGITS_LUT` is - let buf_slice = unsafe { - str::from_utf8_unchecked( - slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) - }; - f.pad_integral(is_nonnegative, "", buf_slice) + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid + // UTF-8 since `DEC_DIGITS_LUT` is + let buf_slice = unsafe { + str::from_utf8_unchecked( + slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + }; + f.pad_integral(is_nonnegative, "", buf_slice) + } })* #[cfg(feature = "optimize_for_size")] fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space let mut buf = [MaybeUninit::::uninit(); 39]; let mut curr = buf.len(); let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); @@ -523,16 +526,16 @@ macro_rules! impl_Exp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 in fmt_u8 => named fmt_i8, - u8 => 3 => named fmt_u8, - i16 => 5 as u16 in fmt_u16 => named fmt_i16, - u16 => 5 => named fmt_u16, - i32 => 10 as u32 in fmt_u32 => named fmt_i32, - u32 => 10 => named fmt_u32, - i64 => 19 as u64 in fmt_u64 => named fmt_i64, - u64 => 20 => named fmt_u64, - isize => 19 as usize in fmt_usize => named fmt_isize, - usize => 20 => named fmt_usize, + i8 => 3 as u8 named fmt_i8, + u8 => 3 named fmt_u8, + i16 => 5 as u16 named fmt_i16, + u16 => 5 named fmt_u16, + i32 => 10 as u32 named fmt_i32, + u32 => 10 named fmt_u32, + i64 => 19 as u64 named fmt_i64, + u64 => 20 named fmt_u64, + isize => 19 as usize named fmt_isize, + usize => 20 named fmt_usize, ; as u64 via to_u64 named fmt_u64 ); impl_Exp!( @@ -545,18 +548,18 @@ mod imp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 in fmt_u8 => named fmt_i8, - u8 => 3 => named fmt_u8, - i16 => 5 as u16 in fmt_u16 => named fmt_i16, - u16 => 5 => named fmt_u16, - i32 => 10 as u32 in fmt_u32 => named fmt_i32, - u32 => 10 => named fmt_u32, - isize => 10 as usize in fmt_usize => named fmt_isize, - usize => 10 => named fmt_usize, + i8 => 3 as u8 named fmt_i8, + u8 => 3 named fmt_u8, + i16 => 5 as u16 named fmt_i16, + u16 => 5 named fmt_u16, + i32 => 10 as u32 named fmt_i32, + u32 => 10 named fmt_u32, + isize => 19 as usize named fmt_isize, + usize => 20 named fmt_usize, ; as u32 via to_u32 named fmt_u32); impl_Display!( - i64 => 19 as u64 in fmt_u64 => named fmt_i64, - u64 => 20 => named fmt_u64, + i64 => 19 as u64 named fmt_i64, + u64 => 20 named fmt_u64, ; as u64 via to_u64 named fmt_u64); impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); From 1562bf790974f0061c47d1a83b7a75cc507a744d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Oct 2024 12:01:55 +0200 Subject: [PATCH 1118/1301] Remove the need to provide the maximum number of digits to `impl_Display` macro --- library/core/src/fmt/num.rs | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index bc62f11d09fbb..aecd725eca561 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,7 +208,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ 8081828384858687888990919293949596979899"; macro_rules! impl_Display { - ($($t:ident => $size:literal $(as $positive:ident)? named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { + ($($t:ident $(as $positive:ident)? named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => { $( #[stable(feature = "rust1", since = "1.0.0")] @@ -248,8 +248,9 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $t { fn _fmt(mut self: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buf = [MaybeUninit::::uninit(); $size]; - let mut curr = $size; + const SIZE: usize = $t::MAX.ilog(10) as usize + 1; + let mut buf = [MaybeUninit::::uninit(); SIZE]; + let mut curr = SIZE; let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); let lut_ptr = DEC_DIGITS_LUT.as_ptr(); @@ -526,16 +527,16 @@ macro_rules! impl_Exp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 named fmt_i8, - u8 => 3 named fmt_u8, - i16 => 5 as u16 named fmt_i16, - u16 => 5 named fmt_u16, - i32 => 10 as u32 named fmt_i32, - u32 => 10 named fmt_u32, - i64 => 19 as u64 named fmt_i64, - u64 => 20 named fmt_u64, - isize => 19 as usize named fmt_isize, - usize => 20 named fmt_usize, + i8 as u8 named fmt_i8, + u8 named fmt_u8, + i16 as u16 named fmt_i16, + u16 named fmt_u16, + i32 as u32 named fmt_i32, + u32 named fmt_u32, + i64 as u64 named fmt_i64, + u64 named fmt_u64, + isize as usize named fmt_isize, + usize named fmt_usize, ; as u64 via to_u64 named fmt_u64 ); impl_Exp!( @@ -548,18 +549,18 @@ mod imp { mod imp { use super::*; impl_Display!( - i8 => 3 as u8 named fmt_i8, - u8 => 3 named fmt_u8, - i16 => 5 as u16 named fmt_i16, - u16 => 5 named fmt_u16, - i32 => 10 as u32 named fmt_i32, - u32 => 10 named fmt_u32, - isize => 19 as usize named fmt_isize, - usize => 20 named fmt_usize, + i8 as u8 named fmt_i8, + u8 named fmt_u8, + i16 as u16 named fmt_i16, + u16 named fmt_u16, + i32 as u32 named fmt_i32, + u32 named fmt_u32, + isize as usize named fmt_isize, + usize named fmt_usize, ; as u32 via to_u32 named fmt_u32); impl_Display!( - i64 => 19 as u64 named fmt_i64, - u64 => 20 named fmt_u64, + i64 as u64 named fmt_i64, + u64 named fmt_u64, ; as u64 via to_u64 named fmt_u64); impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); From 50a6a3565c10bdb02834a6f25677a09b9a9b4107 Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 1 Oct 2024 14:16:19 +0300 Subject: [PATCH 1119/1301] add fixme to remove llvm option when minimal version is 19 --- src/bootstrap/src/core/build_steps/llvm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index e4011221286f6..3021358415790 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -344,6 +344,7 @@ impl Step for Llvm { .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") .define("LLVM_INCLUDE_TESTS", enable_tests) + // FIXME: remove this when minimal llvm is 19 .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") From c53d4c78ad54a0eb2a117b5d47aa18b3c2bff484 Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Mon, 30 Sep 2024 21:50:01 +0100 Subject: [PATCH 1120/1301] bootstrap: Add support for ./x setup emacs Add support for automatically setting up the recommended LSP config for Emacs. Additionally, refactor setup.rs to make it easier to add support for more editors in the future. --- src/bootstrap/src/core/build_steps/setup.rs | 182 ++++++++++++------ .../src/core/build_steps/setup/tests.rs | 9 +- src/bootstrap/src/core/builder.rs | 4 +- src/bootstrap/src/core/config/flags.rs | 3 +- src/etc/completions/x.py.sh | 2 +- src/etc/rust_analyzer_eglot.el | 50 ++--- 6 files changed, 161 insertions(+), 89 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 0ee2cb451f39c..7fbc5de06dfdf 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -35,21 +35,6 @@ pub enum Profile { static PROFILE_DIR: &str = "src/bootstrap/defaults"; -/// A list of historical hashes of `src/etc/rust_analyzer_settings.json`. -/// New entries should be appended whenever this is updated so we can detect -/// outdated vs. user-modified settings files. -static SETTINGS_HASHES: &[&str] = &[ - "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", - "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", - "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", - "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", - "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", - "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", - "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", - "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", -]; -static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json"); - impl Profile { fn include_path(&self, src_path: &Path) -> PathBuf { PathBuf::from(format!("{}/{PROFILE_DIR}/config.{}.toml", src_path.display(), self)) @@ -533,46 +518,123 @@ undesirable, simply delete the `pre-push` file from .git/hooks." Ok(()) } -/// Sets up or displays `src/etc/rust_analyzer_settings.json` -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct Vscode; +/// Handles editor-specific setup differences +#[derive(Clone, Debug, Eq, PartialEq)] +enum EditorKind { + Vscode, + Emacs, +} -impl Step for Vscode { - type Output = (); - const DEFAULT: bool = true; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.alias("vscode") +impl EditorKind { + /// A list of historical hashes of each LSP settings file + /// New entries should be appended whenever this is updated so we can detect + /// outdated vs. user-modified settings files. + fn hashes(&self) -> Vec<&str> { + match self { + EditorKind::Vscode => vec![ + "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", + "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", + "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", + "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", + "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", + "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", + "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", + "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", + ], + EditorKind::Emacs => vec![ + "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", + "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", + ], + } } - fn make_run(run: RunConfig<'_>) { - if run.builder.config.dry_run() { - return; + + fn settings_path(&self, config: &Config) -> PathBuf { + config.src.join(self.settings_short_path()) + } + + fn settings_short_path(&self) -> PathBuf { + self.settings_folder().join(match self { + EditorKind::Vscode => "settings.json", + EditorKind::Emacs => ".dir-locals.el", + }) + } + + fn settings_folder(&self) -> PathBuf { + match self { + EditorKind::Vscode => PathBuf::new().join(".vscode"), + EditorKind::Emacs => PathBuf::new(), } - if let [cmd] = &run.paths[..] { - if cmd.assert_single_path().path.as_path().as_os_str() == "vscode" { - run.builder.ensure(Vscode); - } + } + + fn settings_template(&self) -> &str { + match self { + EditorKind::Vscode => include_str!("../../../../etc/rust_analyzer_settings.json"), + EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"), } } - fn run(self, builder: &Builder<'_>) -> Self::Output { - let config = &builder.config; - if config.dry_run() { - return; + + fn backup_extension(&self) -> &str { + match self { + EditorKind::Vscode => "json.bak", + EditorKind::Emacs => "el.bak", } - while !t!(create_vscode_settings_maybe(config)) {} } } -/// Create a `.vscode/settings.json` file for rustc development, or just print it +/// Helper macro for implementing the necessary Step to support editor LSP setup +/// The first argument must match the argument set up in `flags.rs` +/// The second argument must match the name of some `EditorKind` variant +/// After using the macro, the editor needs to be registered in `builder.rs` with describe!() +macro_rules! impl_editor_support { + ( $($editor:ident, $kind:ident),+ ) => {$( + #[doc = concat!(" Sets up or displays the LSP config for ", stringify!($editor), ".")] + #[derive(Clone, Debug, Eq, PartialEq, Hash)] + pub struct $kind; + + impl Step for $kind { + type Output = (); + const DEFAULT: bool = true; + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias(stringify!($editor)) + } + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == stringify!($editor) { + run.builder.ensure($kind); + } + } + } + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + while !t!(create_editor_settings_maybe(config, EditorKind::$kind)) {} + } + } + )+}; +} + +impl_editor_support!(vscode, Vscode); +impl_editor_support!(emacs, Emacs); + +/// Create the recommended editor LSP config file for rustc development, or just print it /// If this method should be re-called, it returns `false`. -fn create_vscode_settings_maybe(config: &Config) -> io::Result { - let (current_hash, historical_hashes) = SETTINGS_HASHES.split_last().unwrap(); - let vscode_settings = config.src.join(".vscode").join("settings.json"); - // If None, no settings.json exists +fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Result { + let hashes = editor.hashes(); + let (current_hash, historical_hashes) = hashes.split_last().unwrap(); + let settings_path = editor.settings_path(config); + let settings_short_path = editor.settings_short_path(); + let settings_filename = settings_short_path.to_str().unwrap(); + // If None, no settings file exists // If Some(true), is a previous version of settings.json // If Some(false), is not a previous version (i.e. user modified) // If it's up to date we can just skip this let mut mismatched_settings = None; - if let Ok(current) = fs::read_to_string(&vscode_settings) { + if let Ok(current) = fs::read_to_string(&settings_path) { let mut hasher = sha2::Sha256::new(); hasher.update(¤t); let hash = hex_encode(hasher.finalize().as_slice()); @@ -585,20 +647,23 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result { } } println!( - "\nx.py can automatically install the recommended `.vscode/settings.json` file for rustc development" + "\nx.py can automatically install the recommended `{settings_filename}` file for rustc development" ); + match mismatched_settings { Some(true) => eprintln!( - "WARNING: existing `.vscode/settings.json` is out of date, x.py will update it" + "WARNING: existing `{}` is out of date, x.py will update it", + settings_filename ), Some(false) => eprintln!( - "WARNING: existing `.vscode/settings.json` has been modified by user, x.py will back it up and replace it" + "WARNING: existing `{}` has been modified by user, x.py will back it up and replace it", + settings_filename ), _ => (), } - let should_create = match prompt_user( - "Would you like to create/update settings.json? (Press 'p' to preview values): [y/N]", - )? { + let should_create = match prompt_user(&format!( + "Would you like to create/update `{settings_filename}`? (Press 'p' to preview values): [y/N]" + ))? { Some(PromptResult::Yes) => true, Some(PromptResult::Print) => false, _ => { @@ -607,9 +672,9 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result { } }; if should_create { - let path = config.src.join(".vscode"); - if !path.exists() { - fs::create_dir(&path)?; + let settings_folder_path = config.src.join(editor.settings_folder()); + if !settings_folder_path.exists() { + fs::create_dir(settings_folder_path)?; } let verb = match mismatched_settings { // exists but outdated, we can replace this @@ -617,18 +682,21 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result { // exists but user modified, back it up Some(false) => { // exists and is not current version or outdated, so back it up - let mut backup = vscode_settings.clone(); - backup.set_extension("json.bak"); - eprintln!("WARNING: copying `settings.json` to `settings.json.bak`"); - fs::copy(&vscode_settings, &backup)?; + let backup = settings_path.clone().with_extension(editor.backup_extension()); + eprintln!( + "WARNING: copying `{}` to `{}`", + settings_path.file_name().unwrap().to_str().unwrap(), + backup.file_name().unwrap().to_str().unwrap(), + ); + fs::copy(&settings_path, &backup)?; "Updated" } _ => "Created", }; - fs::write(&vscode_settings, RUST_ANALYZER_SETTINGS)?; - println!("{verb} `.vscode/settings.json`"); + fs::write(&settings_path, editor.settings_template())?; + println!("{verb} `{}`", settings_filename); } else { - println!("\n{RUST_ANALYZER_SETTINGS}"); + println!("\n{}", editor.settings_template()); } Ok(should_create) } diff --git a/src/bootstrap/src/core/build_steps/setup/tests.rs b/src/bootstrap/src/core/build_steps/setup/tests.rs index 3552224f33b56..f3d4b6aa4db6e 100644 --- a/src/bootstrap/src/core/build_steps/setup/tests.rs +++ b/src/bootstrap/src/core/build_steps/setup/tests.rs @@ -1,16 +1,17 @@ use sha2::Digest; -use super::{RUST_ANALYZER_SETTINGS, SETTINGS_HASHES}; +use super::EditorKind; use crate::utils::helpers::hex_encode; #[test] fn check_matching_settings_hash() { + let editor = EditorKind::Vscode; let mut hasher = sha2::Sha256::new(); - hasher.update(&RUST_ANALYZER_SETTINGS); + hasher.update(&editor.settings_template()); let hash = hex_encode(hasher.finalize().as_slice()); assert_eq!( &hash, - SETTINGS_HASHES.last().unwrap(), - "Update `SETTINGS_HASHES` with the new hash of `src/etc/rust_analyzer_settings.json`" + editor.hashes().last().unwrap(), + "Update `EditorKind::hashes()` with the new hash of `src/etc/rust_analyzer_settings.json`" ); } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 6ba669f3b1034..91ac9cceb38db 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1000,7 +1000,9 @@ impl<'a> Builder<'a> { run::GenerateWindowsSys, run::GenerateCompletions, ), - Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode), + Kind::Setup => { + describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs) + } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), // special-cased in Build::build() diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 87db5f93fb03c..73446382cc63a 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -450,11 +450,12 @@ Arguments: To only set up the git hook, VS Code config or toolchain link, you may use ./x.py setup hook ./x.py setup vscode + ./x.py setup emacs ./x.py setup link", Profile::all_for_help(" ").trim_end()))] Setup { /// Either the profile for `config.toml` or another setup action. /// May be omitted to set up interactively - #[arg(value_name = "|hook|vscode|link")] + #[arg(value_name = "|hook|vscode|emacs|link")] profile: Option, }, /// Suggest a subset of tests to run, based on modified files diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 2aa0d9f69cc87..78d59c229d7dc 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -2741,7 +2741,7 @@ _x.py() { return 0 ;; x.py__setup) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|vscode|link] [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|vscode|emacs|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el index e55d80d98dec3..7b4309f8e188f 100644 --- a/src/etc/rust_analyzer_eglot.el +++ b/src/etc/rust_analyzer_eglot.el @@ -2,28 +2,28 @@ .((eglot-workspace-configuration . (:rust-analyzer ( :check ( :invocationLocation "root" - :invocationStrategy "once" - :overrideCommand ["python3" - "x.py" - "check" - "--json-output"]) - :linkedProjects ["Cargo.toml" - "src/tools/x/Cargo.toml" - "src/bootstrap/Cargo.toml" - "src/tools/rust-analyzer/Cargo.toml" - "compiler/rustc_codegen_cranelift/Cargo.toml" - "compiler/rustc_codegen_gcc/Cargo.toml"] - :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt" - "--edition=2021"]) - :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" - :enable t) - :cargo ( :buildScripts ( :enable t - :invocationLocation "root" - :invocationStrategy "once" - :overrideCommand ["python3" - "x.py" - "check" - "--json-output"]) - :sysrootSrc "./library" - :extraEnv (:RUSTC_BOOTSTRAP "1")) - :rustc ( :source "./Cargo.toml" ))))))) + :invocationStrategy "once" + :overrideCommand ["python3" + "x.py" + "check" + "--json-output"]) + :linkedProjects ["Cargo.toml" + "src/tools/x/Cargo.toml" + "src/bootstrap/Cargo.toml" + "src/tools/rust-analyzer/Cargo.toml" + "compiler/rustc_codegen_cranelift/Cargo.toml" + "compiler/rustc_codegen_gcc/Cargo.toml"] + :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt" + "--edition=2021"]) + :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" + :enable t) + :cargo ( :buildScripts ( :enable t + :invocationLocation "root" + :invocationStrategy "once" + :overrideCommand ["python3" + "x.py" + "check" + "--json-output"]) + :sysrootSrc "./library" + :extraEnv (:RUSTC_BOOTSTRAP "1")) + :rustc ( :source "./Cargo.toml" ))))))) From dfc7e396a6426211078b24fe0a2c8f29adcbe2f2 Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Mon, 30 Sep 2024 22:26:22 +0100 Subject: [PATCH 1121/1301] bootstrap: Add support for ./x setup helix --- src/bootstrap/src/core/build_steps/setup.rs | 9 +++++++++ src/bootstrap/src/core/builder.rs | 2 +- src/bootstrap/src/core/config/flags.rs | 3 ++- src/etc/completions/x.py.sh | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 7fbc5de06dfdf..2b5700699c482 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -523,6 +523,7 @@ undesirable, simply delete the `pre-push` file from .git/hooks." enum EditorKind { Vscode, Emacs, + Helix, } impl EditorKind { @@ -545,6 +546,9 @@ impl EditorKind { "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", ], + EditorKind::Helix => vec![ + "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233" + ] } } @@ -556,6 +560,7 @@ impl EditorKind { self.settings_folder().join(match self { EditorKind::Vscode => "settings.json", EditorKind::Emacs => ".dir-locals.el", + EditorKind::Helix => "languages.toml", }) } @@ -563,6 +568,7 @@ impl EditorKind { match self { EditorKind::Vscode => PathBuf::new().join(".vscode"), EditorKind::Emacs => PathBuf::new(), + EditorKind::Helix => PathBuf::new().join(".helix"), } } @@ -570,6 +576,7 @@ impl EditorKind { match self { EditorKind::Vscode => include_str!("../../../../etc/rust_analyzer_settings.json"), EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"), + EditorKind::Helix => include_str!("../../../../etc/rust_analyzer_helix.toml"), } } @@ -577,6 +584,7 @@ impl EditorKind { match self { EditorKind::Vscode => "json.bak", EditorKind::Emacs => "el.bak", + EditorKind::Helix => "toml.bak", } } } @@ -620,6 +628,7 @@ macro_rules! impl_editor_support { impl_editor_support!(vscode, Vscode); impl_editor_support!(emacs, Emacs); +impl_editor_support!(helix, Helix); /// Create the recommended editor LSP config file for rustc development, or just print it /// If this method should be re-called, it returns `false`. diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 91ac9cceb38db..dcffeccbaff3e 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1001,7 +1001,7 @@ impl<'a> Builder<'a> { run::GenerateCompletions, ), Kind::Setup => { - describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs) + describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs, setup::Helix) } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 73446382cc63a..2957e47c77110 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -451,11 +451,12 @@ Arguments: ./x.py setup hook ./x.py setup vscode ./x.py setup emacs + ./x.py setup helix ./x.py setup link", Profile::all_for_help(" ").trim_end()))] Setup { /// Either the profile for `config.toml` or another setup action. /// May be omitted to set up interactively - #[arg(value_name = "|hook|vscode|emacs|link")] + #[arg(value_name = "|hook|vscode|emacs|helix|link")] profile: Option, }, /// Suggest a subset of tests to run, based on modified files diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 78d59c229d7dc..15475b976ea50 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -2741,7 +2741,7 @@ _x.py() { return 0 ;; x.py__setup) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|vscode|emacs|link] [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|vscode|emacs|helix|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 From 9b24fae0bec325a3aa65e9373d018eb55a327d0e Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Mon, 30 Sep 2024 22:38:36 +0100 Subject: [PATCH 1122/1301] bootstrap: Add support for ./x setup vim --- src/bootstrap/src/core/build_steps/setup.rs | 24 +++++++++++---------- src/bootstrap/src/core/builder.rs | 10 ++++++++- src/bootstrap/src/core/config/flags.rs | 3 ++- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ src/etc/completions/x.py.sh | 2 +- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 2b5700699c482..3aa8d3a67d8bd 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -522,6 +522,7 @@ undesirable, simply delete the `pre-push` file from .git/hooks." #[derive(Clone, Debug, Eq, PartialEq)] enum EditorKind { Vscode, + Vim, Emacs, Helix, } @@ -532,7 +533,7 @@ impl EditorKind { /// outdated vs. user-modified settings files. fn hashes(&self) -> Vec<&str> { match self { - EditorKind::Vscode => vec![ + EditorKind::Vscode | EditorKind::Vim => vec![ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", @@ -546,9 +547,9 @@ impl EditorKind { "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", ], - EditorKind::Helix => vec![ - "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233" - ] + EditorKind::Helix => { + vec!["2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233"] + } } } @@ -559,6 +560,7 @@ impl EditorKind { fn settings_short_path(&self) -> PathBuf { self.settings_folder().join(match self { EditorKind::Vscode => "settings.json", + EditorKind::Vim => "coc-settings.json", EditorKind::Emacs => ".dir-locals.el", EditorKind::Helix => "languages.toml", }) @@ -567,6 +569,7 @@ impl EditorKind { fn settings_folder(&self) -> PathBuf { match self { EditorKind::Vscode => PathBuf::new().join(".vscode"), + EditorKind::Vim => PathBuf::new().join(".vim"), EditorKind::Emacs => PathBuf::new(), EditorKind::Helix => PathBuf::new().join(".helix"), } @@ -574,18 +577,16 @@ impl EditorKind { fn settings_template(&self) -> &str { match self { - EditorKind::Vscode => include_str!("../../../../etc/rust_analyzer_settings.json"), + EditorKind::Vscode | EditorKind::Vim => { + include_str!("../../../../etc/rust_analyzer_settings.json") + } EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"), EditorKind::Helix => include_str!("../../../../etc/rust_analyzer_helix.toml"), } } - fn backup_extension(&self) -> &str { - match self { - EditorKind::Vscode => "json.bak", - EditorKind::Emacs => "el.bak", - EditorKind::Helix => "toml.bak", - } + fn backup_extension(&self) -> String { + format!("{}.bak", self.settings_short_path().extension().unwrap().to_str().unwrap()) } } @@ -627,6 +628,7 @@ macro_rules! impl_editor_support { } impl_editor_support!(vscode, Vscode); +impl_editor_support!(vim, Vim); impl_editor_support!(emacs, Emacs); impl_editor_support!(helix, Helix); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index dcffeccbaff3e..67f9a3ab3edfc 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1001,7 +1001,15 @@ impl<'a> Builder<'a> { run::GenerateCompletions, ), Kind::Setup => { - describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs, setup::Helix) + describe!( + setup::Profile, + setup::Hook, + setup::Link, + setup::Vscode, + setup::Emacs, + setup::Helix, + setup::Vim + ) } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 2957e47c77110..fb27d1ebb182d 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -450,13 +450,14 @@ Arguments: To only set up the git hook, VS Code config or toolchain link, you may use ./x.py setup hook ./x.py setup vscode + ./x.py setup vim ./x.py setup emacs ./x.py setup helix ./x.py setup link", Profile::all_for_help(" ").trim_end()))] Setup { /// Either the profile for `config.toml` or another setup action. /// May be omitted to set up interactively - #[arg(value_name = "|hook|vscode|emacs|helix|link")] + #[arg(value_name = "|hook|vscode|vim|emacs|helix|link")] profile: Option, }, /// Suggest a subset of tests to run, based on modified files diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index e6f7f105fa27a..e6f5363dbf2f9 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -270,4 +270,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "If `llvm.download-ci-llvm` is not defined, it defaults to `true`.", }, + ChangeInfo { + change_id: 131075, + severity: ChangeSeverity::Info, + summary: "New options for ./x setup added - ./x setup [vim|emacs|helix]", + }, ]; diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 15475b976ea50..913b0e820cb51 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -2741,7 +2741,7 @@ _x.py() { return 0 ;; x.py__setup) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|vscode|emacs|helix|link] [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|vscode|vim|emacs|helix|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 From f48194ea5578802c786edff59dd72388da0e6305 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Tue, 1 Oct 2024 08:55:10 +1000 Subject: [PATCH 1123/1301] Replace -Z default-hidden-visibility with -Z default-visibility MCP: https://github.com/rust-lang/compiler-team/issues/782 Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- compiler/rustc_codegen_gcc/src/allocator.rs | 13 +++- compiler/rustc_codegen_llvm/src/allocator.rs | 22 +++--- compiler/rustc_codegen_llvm/src/declare.rs | 13 +--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 11 +++ compiler/rustc_interface/src/tests.rs | 2 +- compiler/rustc_middle/src/mir/mono.rs | 11 +++ .../rustc_monomorphize/src/partitioning.rs | 34 ++++----- compiler/rustc_session/src/config.rs | 4 +- compiler/rustc_session/src/options.rs | 24 +++++-- compiler/rustc_session/src/session.rs | 12 ++-- compiler/rustc_target/src/spec/mod.rs | 69 ++++++++++++++++--- .../default-hidden-visibility.md | 12 ---- .../src/compiler-flags/default-visibility.md | 44 ++++++++++++ ...en-visibility.rs => default-visibility.rs} | 18 ++--- 14 files changed, 208 insertions(+), 81 deletions(-) delete mode 100644 src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md create mode 100644 src/doc/unstable-book/src/compiler-flags/default-visibility.md rename tests/codegen/{default-hidden-visibility.rs => default-visibility.rs} (60%) diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index e9498857fb9a3..f13a75648aea8 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -104,10 +104,17 @@ fn create_wrapper_function( false, ); - if tcx.sess.default_hidden_visibility() { - #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); + #[cfg(feature = "master")] + match tcx.sess.default_visibility() { + rustc_target::spec::SymbolVisibility::Hidden => { + func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)) + } + rustc_target::spec::SymbolVisibility::Protected => { + func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Protected)) + } + rustc_target::spec::SymbolVisibility::Interposable => {} } + if tcx.sess.must_emit_unwind_tables() { // TODO(antoyo): emit unwind tables. } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 2adac278c62f9..89f5305840b94 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -77,18 +77,20 @@ pub(crate) unsafe fn codegen( // __rust_alloc_error_handler_should_panic let name = OomStrategy::SYMBOL; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); - } + llvm::LLVMRustSetVisibility( + ll_g, + llvm::Visibility::from_generic(tcx.sess.default_visibility()), + ); let val = tcx.sess.opts.unstable_opts.oom.should_panic(); let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::LLVMSetInitializer(ll_g, llval); let name = NO_ALLOC_SHIM_IS_UNSTABLE; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); - } + llvm::LLVMRustSetVisibility( + ll_g, + llvm::Visibility::from_generic(tcx.sess.default_visibility()), + ); let llval = llvm::LLVMConstInt(i8, 0, False); llvm::LLVMSetInitializer(ll_g, llval); } @@ -132,9 +134,11 @@ fn create_wrapper_function( None }; - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } + llvm::LLVMRustSetVisibility( + llfn, + llvm::Visibility::from_generic(tcx.sess.default_visibility()), + ); + if tcx.sess.must_emit_unwind_tables() { let uwtable = attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index b0b29ca1280d5..7be44dd51b577 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -22,6 +22,7 @@ use tracing::debug; use crate::abi::{FnAbi, FnAbiLlvmExt}; use crate::context::CodegenCx; use crate::llvm::AttributePlace::Function; +use crate::llvm::Visibility; use crate::type_::Type; use crate::value::Value; use crate::{attributes, llvm}; @@ -84,11 +85,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { fn_type: &'ll Type, ) -> &'ll Value { // Declare C ABI functions with the visibility used by C by default. - let visibility = if self.tcx.sess.default_hidden_visibility() { - llvm::Visibility::Hidden - } else { - llvm::Visibility::Default - }; + let visibility = Visibility::from_generic(self.tcx.sess.default_visibility()); declare_raw_fn(self, name, llvm::CCallConv, unnamed, visibility, fn_type) } @@ -107,11 +104,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { unnamed: llvm::UnnamedAddr, fn_type: &'ll Type, ) -> &'ll Value { - let visibility = if self.tcx.sess.default_hidden_visibility() { - llvm::Visibility::Hidden - } else { - llvm::Visibility::Default - }; + let visibility = Visibility::from_generic(self.tcx.sess.default_visibility()); declare_raw_fn(self, name, callconv, unnamed, visibility, fn_type) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9aabfd794bad7..d3950df91fbd0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t}; +use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ @@ -133,6 +134,16 @@ pub enum Visibility { Protected = 2, } +impl Visibility { + pub fn from_generic(visibility: SymbolVisibility) -> Self { + match visibility { + SymbolVisibility::Hidden => Visibility::Hidden, + SymbolVisibility::Protected => Visibility::Protected, + SymbolVisibility::Interposable => Visibility::Default, + } + } +} + /// LLVMUnnamedAddr #[repr(C)] pub enum UnnamedAddr { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 57aa3deae806b..895897eca6b8b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -770,7 +770,7 @@ fn test_unstable_options_tracking_hash() { tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); - tracked!(default_hidden_visibility, Some(true)); + tracked!(default_visibility, Some(rustc_target::spec::SymbolVisibility::Hidden)); tracked!(dep_info_omit_d_target, true); tracked!(direct_access_external_data, Some(true)); tracked!(dual_proc_macros, true); diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 954f746ce5b61..56ca9167d4daa 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -15,6 +15,7 @@ use rustc_query_system::ich::StableHashingContext; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_span::symbol::Symbol; +use rustc_target::spec::SymbolVisibility; use tracing::debug; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; @@ -305,6 +306,16 @@ pub enum Visibility { Protected, } +impl From for Visibility { + fn from(value: SymbolVisibility) -> Self { + match value { + SymbolVisibility::Hidden => Visibility::Hidden, + SymbolVisibility::Protected => Visibility::Protected, + SymbolVisibility::Interposable => Visibility::Default, + } + } +} + impl<'tcx> CodegenUnit<'tcx> { #[inline] pub fn new(name: Symbol) -> CodegenUnit<'tcx> { diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index ad05cca66cab3..5bd484d7bb002 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -904,26 +904,22 @@ fn mono_item_visibility<'tcx>( } fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { - if !tcx.sess.default_hidden_visibility() { - return Visibility::Default; - } - - // Generic functions never have export-level C. - if is_generic { - return Visibility::Hidden; - } - - // Things with export level C don't get instantiated in - // downstream crates. - if !id.is_local() { - return Visibility::Hidden; - } + let export_level = if is_generic { + // Generic functions never have export-level C. + SymbolExportLevel::Rust + } else { + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C, + _ => SymbolExportLevel::Rust, + } + }; + match export_level { + // C-export level items remain at `Default` to allow C code to + // access and interpose them. + SymbolExportLevel::C => Visibility::Default, - // C-export level items remain at `Default`, all other internal - // items become `Hidden`. - match tcx.reachable_non_generics(id.krate).get(&id) { - Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => Visibility::Default, - _ => Visibility::Hidden, + // For all other symbols, `default_visibility` determines which visibility to use. + SymbolExportLevel::Rust => tcx.sess.default_visibility().into(), } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b052d8d72c7d8..0d293415aa9c7 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3008,7 +3008,8 @@ pub(crate) mod dep_tracking { use rustc_span::edition::Edition; use rustc_target::spec::{ CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, - RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, WasmCAbi, + RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTriple, + TlsModel, WasmCAbi, }; use super::{ @@ -3101,6 +3102,7 @@ pub(crate) mod dep_tracking { StackProtector, SwitchWithOptPath, SymbolManglingVersion, + SymbolVisibility, RemapPathScopeComponents, SourceFileHashAlgorithm, OutFileName, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aac776d2919aa..1de09b8be4d63 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -13,8 +13,8 @@ use rustc_span::edition::Edition; use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, - RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, - WasmCAbi, + RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, + TargetTriple, TlsModel, WasmCAbi, }; use crate::config::*; @@ -416,6 +416,8 @@ mod desc { "one of: `disabled`, `trampolines`, or `aliases`"; pub(crate) const parse_symbol_mangling_version: &str = "one of: `legacy`, `v0` (RFC 2603), or `hashed`"; + pub(crate) const parse_opt_symbol_visibility: &str = + "one of: `hidden`, `protected`, or `interposable`"; pub(crate) const parse_src_file_hash: &str = "either `md5` or `sha1`"; pub(crate) const parse_relocation_model: &str = "one of supported relocation models (`rustc --print relocation-models`)"; @@ -922,6 +924,20 @@ mod parse { true } + pub(crate) fn parse_opt_symbol_visibility( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + if let Some(v) = v { + if let Ok(vis) = SymbolVisibility::from_str(v) { + *slot = Some(vis); + } else { + return false; + } + } + true + } + pub(crate) fn parse_optimization_fuel( slot: &mut Option<(String, u64)>, v: Option<&str>, @@ -1688,8 +1704,8 @@ options! { "compress debug info sections (none, zlib, zstd, default: none)"), deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], "deduplicate identical diagnostics (default: yes)"), - default_hidden_visibility: Option = (None, parse_opt_bool, [TRACKED], - "overrides the `default_hidden_visibility` setting of the target"), + default_visibility: Option = (None, parse_opt_symbol_visibility, [TRACKED], + "overrides the `default_visibility` setting of the target"), dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], "in dep-info output, omit targets for tracking dependencies of the dep-info files \ themselves (default: no)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index adc28e3846254..d67e69fe0fb72 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -31,7 +31,8 @@ use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SmallDataThresholdSupport, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel, + SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, + TargetTriple, TlsModel, }; use crate::code_stats::CodeStats; @@ -617,12 +618,13 @@ impl Session { } } - /// Whether the default visibility of symbols should be "hidden" rather than "default". - pub fn default_hidden_visibility(&self) -> bool { + /// Returns the default symbol visibility. + pub fn default_visibility(&self) -> SymbolVisibility { self.opts .unstable_opts - .default_hidden_visibility - .unwrap_or(self.target.options.default_hidden_visibility) + .default_visibility + .or(self.target.options.default_visibility) + .unwrap_or(SymbolVisibility::Interposable) } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f327c1fd17902..d1fcfe4adef18 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -830,6 +830,46 @@ impl RelroLevel { } } +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum SymbolVisibility { + Hidden, + Protected, + Interposable, +} + +impl SymbolVisibility { + pub fn desc(&self) -> &str { + match *self { + SymbolVisibility::Hidden => "hidden", + SymbolVisibility::Protected => "protected", + SymbolVisibility::Interposable => "interposable", + } + } +} + +impl FromStr for SymbolVisibility { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "hidden" => Ok(SymbolVisibility::Hidden), + "protected" => Ok(SymbolVisibility::Protected), + "interposable" => Ok(SymbolVisibility::Interposable), + _ => Err(()), + } + } +} + +impl ToJson for SymbolVisibility { + fn to_json(&self) -> Json { + match *self { + SymbolVisibility::Hidden => "hidden".to_json(), + SymbolVisibility::Protected => "protected".to_json(), + SymbolVisibility::Interposable => "interposable".to_json(), + } + } +} + impl FromStr for RelroLevel { type Err = (); @@ -2326,13 +2366,12 @@ pub struct TargetOptions { /// for this target unconditionally. pub no_builtins: bool, - /// The default visibility for symbols in this target should be "hidden" - /// rather than "default". + /// The default visibility for symbols in this target. /// - /// This value typically shouldn't be accessed directly, but through - /// the `rustc_session::Session::default_hidden_visibility` method, which - /// allows `rustc` users to override this setting using cmdline flags. - pub default_hidden_visibility: bool, + /// This value typically shouldn't be accessed directly, but through the + /// `rustc_session::Session::default_visibility` method, which allows `rustc` users to override + /// this setting using cmdline flags. + pub default_visibility: Option, /// Whether a .debug_gdb_scripts section will be added to the output object file pub emit_debug_gdb_scripts: bool, @@ -2623,7 +2662,7 @@ impl Default for TargetOptions { requires_lto: false, singlethread: false, no_builtins: false, - default_hidden_visibility: false, + default_visibility: None, emit_debug_gdb_scripts: true, requires_uwtable: false, default_uwtable: false, @@ -2963,6 +3002,18 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, Option) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::() { + Ok(level) => base.$key_name = Some(level), + _ => return Some(Err(format!("'{}' is not a valid value for \ + symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, DebuginfoKind) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { @@ -3353,7 +3404,7 @@ impl Target { key!(requires_lto, bool); key!(singlethread, bool); key!(no_builtins, bool); - key!(default_hidden_visibility, bool); + key!(default_visibility, Option)?; key!(emit_debug_gdb_scripts, bool); key!(requires_uwtable, bool); key!(default_uwtable, bool); @@ -3633,7 +3684,7 @@ impl ToJson for Target { target_option_val!(requires_lto); target_option_val!(singlethread); target_option_val!(no_builtins); - target_option_val!(default_hidden_visibility); + target_option_val!(default_visibility); target_option_val!(emit_debug_gdb_scripts); target_option_val!(requires_uwtable); target_option_val!(default_uwtable); diff --git a/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md deleted file mode 100644 index 579add4a9d985..0000000000000 --- a/src/doc/unstable-book/src/compiler-flags/default-hidden-visibility.md +++ /dev/null @@ -1,12 +0,0 @@ -# `default-hidden-visibility` - -The tracking issue for this feature is: https://github.com/rust-lang/compiler-team/issues/656 - ------------------------- - -This flag can be used to override the target's -[`default_hidden_visibility`](https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/struct.TargetOptions.html#structfield.default_hidden_visibility) -setting. -Using `-Zdefault_hidden_visibility=yes` is roughly equivalent to Clang's -[`-fvisibility=hidden`](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fvisibility) -cmdline flag. diff --git a/src/doc/unstable-book/src/compiler-flags/default-visibility.md b/src/doc/unstable-book/src/compiler-flags/default-visibility.md new file mode 100644 index 0000000000000..ad9e5d84bba80 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/default-visibility.md @@ -0,0 +1,44 @@ +# `default-visibility` + +The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/131090 + +------------------------ + +This flag can be used to override the target's +[`default_visibility`](https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/struct.TargetOptions.html#structfield.default_visibility) +setting. + +This option only affects building of shared objects and should have no effect on executables. + +Visibility an be set to one of three options: + +* protected +* hidden +* interposable + +## Hidden visibility + +Using `-Zdefault-visibility=hidden` is roughly equivalent to Clang's +[`-fvisibility=hidden`](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fvisibility) +cmdline flag. Hidden symbols will not be exported from the created shared object, so cannot be +referenced from other shared objects or from executables. + +## Protected visibility + +Using `-Zdefault-visibility=protected` will cause rust-mangled symbols to be emitted with +"protected" visibility. This signals the compiler, the linker and the runtime linker that these +symbols cannot be overridden by the executable or by other shared objects earlier in the load order. + +This will allow the compiler to emit direct references to symbols, which may improve performance. It +also removes the need for these symbols to be resolved when a shared object built with this option +is loaded. + +Using protected visibility when linking with GNU ld prior to 2.40 will result in linker errors when +building for Linux. Other linkers such as LLD are not affected. + +## Interposable + +Using `-Zdefault-visibility=interposable` will cause symbols to be emitted with "default" +visibility. On platforms that support it, this makes it so that symbols can be interposed, which +means that they can be overridden by symbols with the same name from the executable or by other +shared objects earier in the load order. diff --git a/tests/codegen/default-hidden-visibility.rs b/tests/codegen/default-visibility.rs similarity index 60% rename from tests/codegen/default-hidden-visibility.rs rename to tests/codegen/default-visibility.rs index 2bea8f62a40db..884d386ec20a3 100644 --- a/tests/codegen/default-hidden-visibility.rs +++ b/tests/codegen/default-visibility.rs @@ -1,11 +1,12 @@ -// Verifies that `Session::default_hidden_visibility` is affected when using the related cmdline -// flag. This is a regression test for https://github.com/rust-lang/compiler-team/issues/656. See +// Verifies that `Session::default_visibility` is affected when using the related cmdline +// flag. This is a regression test for https://github.com/rust-lang/compiler-team/issues/782. See // also https://github.com/rust-lang/rust/issues/73295 and // https://github.com/rust-lang/rust/issues/37530. -//@ revisions:DEFAULT YES NO -//@[YES] compile-flags: -Zdefault-hidden-visibility=yes -//@[NO] compile-flags: -Zdefault-hidden-visibility=no +//@ revisions:DEFAULT HIDDEN PROTECTED INTERPOSABLE +//@[HIDDEN] compile-flags: -Zdefault-visibility=hidden +//@[PROTECTED] compile-flags: -Zdefault-visibility=protected +//@[INTERPOSABLE] compile-flags: -Zdefault-visibility=interposable // The test scenario is specifically about visibility of symbols exported out of dynamically linked // libraries. @@ -26,6 +27,7 @@ pub static tested_symbol: [u8; 6] = *b"foobar"; // //@ only-x86_64-unknown-linux-gnu -// DEFAULT: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = constant -// YES: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = hidden constant -// NO: @{{.*}}default_hidden_visibility{{.*}}tested_symbol{{.*}} = constant +// HIDDEN: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = hidden constant +// PROTECTED: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = protected constant +// INTERPOSABLE: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant +// DEFAULT: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant From 13881f5404037e25a88d0b79a836e232dc73b1fc Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 30 Sep 2024 10:18:55 +0200 Subject: [PATCH 1124/1301] add caches to multiple type folders --- .../rustc_infer/src/infer/relate/combine.rs | 7 +- .../src/infer/relate/type_relating.rs | 41 ++++++++- compiler/rustc_infer/src/infer/resolve.rs | 14 ++- compiler/rustc_middle/src/ty/fold.rs | 25 ++++- .../rustc_next_trait_solver/src/resolve.rs | 11 ++- .../src/solve/eval_ctxt/mod.rs | 51 +++++++--- .../src/data_structures/delayed_map.rs | 92 +++++++++++++++++++ .../mod.rs} | 3 + 8 files changed, 222 insertions(+), 22 deletions(-) create mode 100644 compiler/rustc_type_ir/src/data_structures/delayed_map.rs rename compiler/rustc_type_ir/src/{data_structures.rs => data_structures/mod.rs} (92%) diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index e75d7b7db1453..3b2ef3fe981e3 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -36,10 +36,15 @@ use crate::traits::{Obligation, PredicateObligation}; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { pub infcx: &'infcx InferCtxt<'tcx>, + // Immutable fields pub trace: TypeTrace<'tcx>, pub param_env: ty::ParamEnv<'tcx>, - pub goals: Vec>>, pub define_opaque_types: DefineOpaqueTypes, + // Mutable fields + // + // Adding any additional field likely requires + // changes to the cache of `TypeRelating`. + pub goals: Vec>>, } impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index a402a8009ff8d..c8ced4a2cda85 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -4,6 +4,7 @@ use rustc_middle::ty::relate::{ }; use rustc_middle::ty::{self, Ty, TyCtxt, TyVar}; use rustc_span::Span; +use rustc_type_ir::data_structures::DelayedSet; use tracing::{debug, instrument}; use super::combine::CombineFields; @@ -13,9 +14,36 @@ use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; /// Enforce that `a` is equal to or a subtype of `b`. pub struct TypeRelating<'combine, 'a, 'tcx> { + // Partially mutable. fields: &'combine mut CombineFields<'a, 'tcx>, + + // Immutable fields. structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, + + /// The cache has only tracks the `ambient_variance` as its the + /// only field which is mutable and which meaningfully changes + /// the result when relating types. + /// + /// The cache does not track whether the state of the + /// `InferCtxt` has been changed or whether we've added any + /// obligations to `self.fields.goals`. Whether a goal is added + /// once or multiple times is not really meaningful. + /// + /// Changes in the inference state may delay some type inference to + /// the next fulfillment loop. Given that this loop is already + /// necessary, this is also not a meaningful change. Consider + /// the following three relations: + /// ```text + /// Vec sub Vec + /// ?0 eq u32 + /// Vec sub Vec + /// ``` + /// Without a cache, the second `Vec sub Vec` would eagerly + /// constrain `?1` to `u32`. When using the cache entry from the + /// first time we've related these types, this only happens when + /// later proving the `Subtype(?0, ?1)` goal from the first relation. + cache: DelayedSet<(ty::Variance, Ty<'tcx>, Ty<'tcx>)>, } impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { @@ -24,7 +52,12 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, ) -> TypeRelating<'combine, 'infcx, 'tcx> { - TypeRelating { fields: f, structurally_relate_aliases, ambient_variance } + TypeRelating { + fields: f, + structurally_relate_aliases, + ambient_variance, + cache: Default::default(), + } } } @@ -78,6 +111,10 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { let a = infcx.shallow_resolve(a); let b = infcx.shallow_resolve(b); + if self.cache.contains(&(self.ambient_variance, a, b)) { + return Ok(a); + } + match (a.kind(), b.kind()) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { match self.ambient_variance { @@ -160,6 +197,8 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { } } + assert!(self.cache.insert((self.ambient_variance, a, b))); + Ok(a) } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 34625ffb77879..671a66d504f9d 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -2,6 +2,7 @@ use rustc_middle::bug; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; +use rustc_type_ir::data_structures::DelayedMap; use super::{FixupError, FixupResult, InferCtxt}; @@ -15,12 +16,15 @@ use super::{FixupError, FixupResult, InferCtxt}; /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, + /// We're able to use a cache here as the folder does + /// not have any mutable state. + cache: DelayedMap, Ty<'tcx>>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - OpportunisticVarResolver { infcx } + OpportunisticVarResolver { infcx, cache: Default::default() } } } @@ -33,9 +37,13 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { if !t.has_non_region_infer() { t // micro-optimize -- if there is nothing in this type that this fold affects... + } else if let Some(&ty) = self.cache.get(&t) { + return ty; } else { - let t = self.infcx.shallow_resolve(t); - t.super_fold_with(self) + let shallow = self.infcx.shallow_resolve(t); + let res = shallow.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 2ee7497497aa9..e152d3f5fbe9f 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,5 +1,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; +use rustc_type_ir::data_structures::DelayedMap; pub use rustc_type_ir::fold::{ FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region, shift_vars, }; @@ -131,12 +132,20 @@ impl<'a, 'tcx> TypeFolder> for RegionFolder<'a, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Bound vars replacer +/// A delegate used when instantiating bound vars. +/// +/// Any implementation must make sure that each bound variable always +/// gets mapped to the same result. `BoundVarReplacer` caches by using +/// a `DelayedMap` which does not cache the first few types it encounters. pub trait BoundVarReplacerDelegate<'tcx> { fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>; fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>; fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx>; } +/// A simple delegate taking 3 mutable functions. The used functions must +/// always return the same result for each bound variable, no matter how +/// frequently they are called. pub struct FnMutDelegate<'a, 'tcx> { pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), @@ -164,11 +173,15 @@ struct BoundVarReplacer<'tcx, D> { current_index: ty::DebruijnIndex, delegate: D, + + /// This cache only tracks the `DebruijnIndex` and assumes that it does not matter + /// for the delegate how often its methods get used. + cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>, } impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> { fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self { - BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate } + BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate, cache: Default::default() } } } @@ -197,7 +210,15 @@ where debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST)); ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32()) } - _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ if t.has_vars_bound_at_or_above(self.current_index) => { + if let Some(&ty) = self.cache.get(&(self.current_index, t)) { + return ty; + } + + let res = t.super_fold_with(self); + assert!(self.cache.insert((self.current_index, t), res)); + res + } _ => t, } } diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 132b7400300c5..a37066cec669f 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -1,3 +1,4 @@ +use rustc_type_ir::data_structures::DelayedMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; @@ -15,11 +16,12 @@ where I: Interner, { delegate: &'a D, + cache: DelayedMap, } impl<'a, D: SolverDelegate> EagerResolver<'a, D> { pub fn new(delegate: &'a D) -> Self { - EagerResolver { delegate } + EagerResolver { delegate, cache: Default::default() } } } @@ -42,7 +44,12 @@ impl, I: Interner> TypeFolder for EagerResolv ty::Infer(ty::FloatVar(vid)) => self.delegate.opportunistic_resolve_float_var(vid), _ => { if t.has_infer() { - t.super_fold_with(self) + if let Some(&ty) = self.cache.get(&t) { + return ty; + } + let res = t.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res } else { t } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 12ad0724b5cb1..12b4b3cb3a9d8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; -use rustc_type_ir::data_structures::ensure_sufficient_stack; +use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; @@ -579,18 +579,16 @@ where #[instrument(level = "trace", skip(self))] pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + goal.predicate = + goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); self.nested_goals.normalizes_to_goals.push(goal); } #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + goal.predicate = + goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); self.nested_goals.goals.push((source, goal)); } @@ -654,6 +652,7 @@ where term: I::Term, universe_of_term: ty::UniverseIndex, delegate: &'a D, + cache: HashSet, } impl, I: Interner> ContainsTermOrNotNameable<'_, D, I> { @@ -671,6 +670,10 @@ where { type Result = ControlFlow<()>; fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + if self.cache.contains(&t) { + return ControlFlow::Continue(()); + } + match t.kind() { ty::Infer(ty::TyVar(vid)) => { if let ty::TermKind::Ty(term) = self.term.kind() { @@ -683,17 +686,18 @@ where } } - self.check_nameable(self.delegate.universe_of_ty(vid).unwrap()) + self.check_nameable(self.delegate.universe_of_ty(vid).unwrap())?; } - ty::Placeholder(p) => self.check_nameable(p.universe()), + ty::Placeholder(p) => self.check_nameable(p.universe())?, _ => { if t.has_non_region_infer() || t.has_placeholders() { - t.super_visit_with(self) - } else { - ControlFlow::Continue(()) + t.super_visit_with(self)? } } } + + assert!(self.cache.insert(t)); + ControlFlow::Continue(()) } fn visit_const(&mut self, c: I::Const) -> Self::Result { @@ -728,6 +732,7 @@ where delegate: self.delegate, universe_of_term, term: goal.predicate.term, + cache: Default::default(), }; goal.predicate.alias.visit_with(&mut visitor).is_continue() && goal.param_env.visit_with(&mut visitor).is_continue() @@ -1015,6 +1020,17 @@ where { ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv, + cache: HashMap, +} + +impl<'me, 'a, D, I> ReplaceAliasWithInfer<'me, 'a, D, I> +where + D: SolverDelegate, + I: Interner, +{ + fn new(ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv) -> Self { + ReplaceAliasWithInfer { ecx, param_env, cache: Default::default() } + } } impl TypeFolder for ReplaceAliasWithInfer<'_, '_, D, I> @@ -1041,7 +1057,16 @@ where ); infer_ty } - _ => ty.super_fold_with(self), + _ if ty.has_aliases() => { + if let Some(&entry) = self.cache.get(&ty) { + return entry; + } + + let res = ty.super_fold_with(self); + assert!(self.cache.insert(ty, res).is_none()); + res + } + _ => ty, } } diff --git a/compiler/rustc_type_ir/src/data_structures/delayed_map.rs b/compiler/rustc_type_ir/src/data_structures/delayed_map.rs new file mode 100644 index 0000000000000..7e7406e37066e --- /dev/null +++ b/compiler/rustc_type_ir/src/data_structures/delayed_map.rs @@ -0,0 +1,92 @@ +use std::hash::Hash; + +use crate::data_structures::{HashMap, HashSet}; + +const CACHE_CUTOFF: u32 = 32; + +/// A hashmap which only starts hashing after ignoring the first few inputs. +/// +/// This is used in type folders as in nearly all cases caching is not worth it +/// as nearly all folded types are tiny. However, there are very rare incredibly +/// large types for which caching is necessary to avoid hangs. +#[derive(Debug)] +pub struct DelayedMap { + cache: HashMap, + count: u32, +} + +impl Default for DelayedMap { + fn default() -> Self { + DelayedMap { cache: Default::default(), count: 0 } + } +} + +impl DelayedMap { + #[inline(always)] + pub fn insert(&mut self, key: K, value: V) -> bool { + if self.count >= CACHE_CUTOFF { + self.cold_insert(key, value) + } else { + self.count += 1; + true + } + } + + #[cold] + #[inline(never)] + fn cold_insert(&mut self, key: K, value: V) -> bool { + self.cache.insert(key, value).is_none() + } + + #[inline(always)] + pub fn get(&self, key: &K) -> Option<&V> { + if self.cache.is_empty() { None } else { self.cold_get(key) } + } + + #[cold] + #[inline(never)] + fn cold_get(&self, key: &K) -> Option<&V> { + self.cache.get(key) + } +} + +#[derive(Debug)] +pub struct DelayedSet { + cache: HashSet, + count: u32, +} + +impl Default for DelayedSet { + fn default() -> Self { + DelayedSet { cache: Default::default(), count: 0 } + } +} + +impl DelayedSet { + #[inline(always)] + pub fn insert(&mut self, value: T) -> bool { + if self.count >= CACHE_CUTOFF { + self.cold_insert(value) + } else { + self.count += 1; + true + } + } + + #[cold] + #[inline(never)] + fn cold_insert(&mut self, value: T) -> bool { + self.cache.insert(value) + } + + #[inline(always)] + pub fn contains(&self, value: &T) -> bool { + !self.cache.is_empty() && self.cold_contains(value) + } + + #[cold] + #[inline(never)] + fn cold_contains(&self, value: &T) -> bool { + self.cache.contains(value) + } +} diff --git a/compiler/rustc_type_ir/src/data_structures.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs similarity index 92% rename from compiler/rustc_type_ir/src/data_structures.rs rename to compiler/rustc_type_ir/src/data_structures/mod.rs index 96036e53b0abb..d9766d91845cb 100644 --- a/compiler/rustc_type_ir/src/data_structures.rs +++ b/compiler/rustc_type_ir/src/data_structures/mod.rs @@ -6,6 +6,8 @@ pub use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; pub type IndexMap = indexmap::IndexMap>; pub type IndexSet = indexmap::IndexSet>; +mod delayed_map; + #[cfg(feature = "nightly")] mod impl_ { pub use rustc_data_structures::sso::{SsoHashMap, SsoHashSet}; @@ -24,4 +26,5 @@ mod impl_ { } } +pub use delayed_map::{DelayedMap, DelayedSet}; pub use impl_::*; From a7b114420c70a1c0bc082d078d5284af6ff4af23 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 30 Sep 2024 10:19:00 +0200 Subject: [PATCH 1125/1301] add tests --- .../coherence-alias-hang-with-region.rs | 30 ++++++++++++++++ .../overflow}/coherence-alias-hang.rs | 11 ++++-- .../next-solver/overflow/nalgebra-hang.rs | 35 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs rename tests/ui/traits/{ => next-solver/overflow}/coherence-alias-hang.rs (57%) create mode 100644 tests/ui/traits/next-solver/overflow/nalgebra-hang.rs diff --git a/tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs b/tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs new file mode 100644 index 0000000000000..4ade8a13ca914 --- /dev/null +++ b/tests/ui/traits/next-solver/overflow/coherence-alias-hang-with-region.rs @@ -0,0 +1,30 @@ +//@ check-pass +//@ revisions: ai ia ii +//@ compile-flags: -Znext-solver=coherence + +// Regression test for nalgebra hang . + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Id = T; +trait NotImplemented {} + +struct W<'a, T: ?Sized, U: ?Sized>(&'a (), *const T, *const U); +trait Trait { + type Assoc: ?Sized; +} +impl<'a, T: ?Sized + Trait> Trait for W<'a, T, T> { + #[cfg(ai)] + type Assoc = W<'a, T::Assoc, Id>; + #[cfg(ia)] + type Assoc = W<'a, Id, T::Assoc>; + #[cfg(ii)] + type Assoc = W<'a, Id, Id>; +} + +trait Overlap {} +impl<'a, T: ?Sized> Overlap for W<'a, T, T> {} +impl Overlap for T {} + +fn main() {} diff --git a/tests/ui/traits/coherence-alias-hang.rs b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs similarity index 57% rename from tests/ui/traits/coherence-alias-hang.rs rename to tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs index c2b4d2e42d214..0d5f42231e4ad 100644 --- a/tests/ui/traits/coherence-alias-hang.rs +++ b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs @@ -1,6 +1,8 @@ //@ check-pass -//@ revisions: current next -//[next]@ compile-flags: -Znext-solver +//@ revisions: ai_current ai_next ia_current ia_next ii_current ii_next +//@[ai_next] compile-flags: -Znext-solver +//@[ia_next] compile-flags: -Znext-solver +//@[ii_next] compile-flags: -Znext-solver // Regression test for nalgebra hang . @@ -15,7 +17,12 @@ trait Trait { type Assoc: ?Sized; } impl Trait for W { + #[cfg(any(ai_current, ai_next))] type Assoc = W>; + #[cfg(any(ia_current, ia_next))] + type Assoc = W, T::Assoc>; + #[cfg(any(ii_current, ii_next))] + type Assoc = W, Id>; } trait Overlap {} diff --git a/tests/ui/traits/next-solver/overflow/nalgebra-hang.rs b/tests/ui/traits/next-solver/overflow/nalgebra-hang.rs new file mode 100644 index 0000000000000..4bc6039c57dfd --- /dev/null +++ b/tests/ui/traits/next-solver/overflow/nalgebra-hang.rs @@ -0,0 +1,35 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +// Regression test for nalgebra hang from +// https://github.com/rust-lang/rust/pull/130654#issuecomment-2365465354 +trait HasAlias {} + +struct Dummy; +trait DummyTrait { + type DummyType; +} +impl DummyTrait for Dummy { + type DummyType = T; +} +type AliasOf = ::DummyType; + +struct Matrix(T, S); +type OMatrix = Matrix>; + +impl HasAlias for OMatrix {} + +trait SimdValue { + type Element; +} +impl> SimdValue for OMatrix { + type Element = OMatrix; +} + +trait Unimplemented {} +pub trait MyFrom {} +impl MyFrom for T {} +impl> MyFrom for OMatrix {} + +fn main() {} From f06a46ee4dc7587c5f353dc2e396e996068cebac Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:08:22 +0200 Subject: [PATCH 1126/1301] consider `wait()` calls in nested bodies --- clippy_lints/src/zombie_processes.rs | 7 +++++++ tests/ui/zombie_processes.rs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 58d71bee299a4..8d9241cc7d9a2 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -6,6 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; use rustc_session::declare_lint_pass; use rustc_span::sym; use std::ops::ControlFlow; @@ -119,6 +120,7 @@ enum WaitFinder<'a, 'tcx> { } impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; type Result = ControlFlow; fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { @@ -204,6 +206,11 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { walk_expr(self, ex) } + + fn nested_visit_map(&mut self) -> Self::Map { + let (Self::Found(cx, _) | Self::WalkUpTo(cx, _)) = self; + cx.tcx.hir() + } } /// This function has shared logic between the different kinds of nodes that can trigger the lint. diff --git a/tests/ui/zombie_processes.rs b/tests/ui/zombie_processes.rs index a2abc7fc3a179..b41bcce3f7f7c 100644 --- a/tests/ui/zombie_processes.rs +++ b/tests/ui/zombie_processes.rs @@ -131,6 +131,13 @@ fn main() { } x.wait().unwrap(); } + + { + let mut x = Command::new("").spawn().unwrap(); + std::thread::spawn(move || { + x.wait().unwrap(); + }); + } } fn process_child(c: Child) { From 30ff4006e136123645b9fcca511b13652b8a5f98 Mon Sep 17 00:00:00 2001 From: Slanterns Date: Wed, 2 Oct 2024 01:27:11 +0800 Subject: [PATCH 1127/1301] update `Literal`'s intro --- library/proc_macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 5522d556a5975..72b597a8083ba 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1166,7 +1166,7 @@ impl fmt::Debug for Ident { } } -/// A literal string (`"hello"`), byte string (`b"hello"`), +/// A literal string (`"hello"`), byte string (`b"hello"`), C string (`c"hello"`), /// character (`'a'`), byte character (`b'a'`), an integer or floating point number /// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`). /// Boolean literals like `true` and `false` do not belong here, they are `Ident`s. From 1c7e8246d53da3b68589c9fe2aa2c830fae3f164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Tue, 1 Oct 2024 18:35:26 +0000 Subject: [PATCH 1128/1301] Revert "Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags" This reverts commit 5a7058c5a542ec42d1fa9b524f7b4f7d6845d1e9. In [#131059] we found out that `-Zon-broken-pipe=kill` is actually **load-bearing** [1] for (at least) `rustc` and `rustdoc` to have the kill-process-on-broken-pipe behavior, e.g. `rustc --print=sysroot | false` will ICE and `rustdoc --print=sysroot | false` will panic on a broken pipe. [#131059]: https://github.com/rust-lang/rust/issues/131059 [1]: https://github.com/rust-lang/rust/issues/131059#issuecomment-2385822033 --- src/bootstrap/src/core/build_steps/compile.rs | 4 ++++ src/bootstrap/src/core/build_steps/tool.rs | 11 +++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index bb1d8d279288d..eaa982d4e2bbd 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1053,6 +1053,10 @@ pub fn rustc_cargo( cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)"); + // If the rustc output is piped to e.g. `head -n1` we want the process to be + // killed, rather than having an error bubble up and cause a panic. + cargo.rustflag("-Zon-broken-pipe=kill"); + if builder.config.llvm_enzyme { cargo.rustflag("-l").rustflag("Enzyme-19"); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index fa2c1b8360f4c..64dfe054d9c77 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -200,10 +200,6 @@ pub fn prepare_tool_cargo( cargo.arg("--features").arg(features.join(", ")); } - // Warning: be very careful with RUSTFLAGS or command-line options, as conditionally applied - // RUSTFLAGS or cli flags can lead to hard-to-diagnose rebuilds due to flag differences, causing - // previous tool build artifacts to get invalidated. - // Enable internal lints for clippy and rustdoc // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]` // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 @@ -213,6 +209,13 @@ pub fn prepare_tool_cargo( // See https://github.com/rust-lang/rust/issues/116538 cargo.rustflag("-Zunstable-options"); + // `-Zon-broken-pipe=kill` breaks cargo tests + if !path.ends_with("cargo") { + // If the output is piped to e.g. `head -n1` we want the process to be killed, + // rather than having an error bubble up and cause a panic. + cargo.rustflag("-Zon-broken-pipe=kill"); + } + cargo } From 9b52fb555835224e085170b67857a250dd6f176b Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 1 Oct 2024 12:44:10 -0700 Subject: [PATCH 1129/1301] Split out method receivers in feature gate test --- .../feature-gate-pin_ergonomics.rs | 4 ++- .../feature-gate-pin_ergonomics.stderr | 27 +++---------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index d56a046fd62ee..3382504af9d61 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -15,8 +15,10 @@ fn foo(_: Pin<&mut Foo>) { fn bar(mut x: Pin<&mut Foo>) { foo(x); foo(x); //~ ERROR use of moved value: `x` +} - x.foo(); //~ ERROR use of moved value: `x` +fn baz(mut x: Pin<&mut Foo>) { + x.foo(); x.foo(); //~ ERROR use of moved value: `x` } diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index bc49088f3d783..430b78662414a 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -17,31 +17,10 @@ LL | fn foo(_: Pin<&mut Foo>) { | in this function error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:19:5 + --> $DIR/feature-gate-pin_ergonomics.rs:22:5 | -LL | fn bar(mut x: Pin<&mut Foo>) { - | ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait -LL | foo(x); -LL | foo(x); - | - value moved here -LL | -LL | x.foo(); - | ^ value used here after move - | -note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary - --> $DIR/feature-gate-pin_ergonomics.rs:12:11 - | -LL | fn foo(_: Pin<&mut Foo>) { - | --- ^^^^^^^^^^^^^ this parameter takes ownership of the value - | | - | in this function - -error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:20:5 - | -LL | fn bar(mut x: Pin<&mut Foo>) { +LL | fn baz(mut x: Pin<&mut Foo>) { | ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait -... LL | x.foo(); | ----- `x` moved due to this method call LL | x.foo(); @@ -57,6 +36,6 @@ help: consider reborrowing the `Pin` instead of moving it LL | x.as_mut().foo(); | +++++++++ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0382`. From c4ce8c114b06840c3521a189ee44958b713fb33a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 11:53:23 +0200 Subject: [PATCH 1130/1301] make InterpResult a dedicated type to avoid accidentally discarding the error --- .../src/const_eval/dummy_machine.rs | 7 +- .../src/const_eval/eval_queries.rs | 53 ++-- .../src/const_eval/machine.rs | 68 ++--- .../rustc_const_eval/src/const_eval/mod.rs | 20 +- .../src/const_eval/valtrees.rs | 8 +- .../rustc_const_eval/src/interpret/call.rs | 58 ++--- .../rustc_const_eval/src/interpret/cast.rs | 24 +- .../src/interpret/discriminant.rs | 19 +- .../src/interpret/eval_context.rs | 49 ++-- .../rustc_const_eval/src/interpret/intern.rs | 10 +- .../src/interpret/intrinsics.rs | 27 +- .../rustc_const_eval/src/interpret/machine.rs | 49 ++-- .../rustc_const_eval/src/interpret/memory.rs | 148 +++++------ .../rustc_const_eval/src/interpret/operand.rs | 51 ++-- .../src/interpret/operator.rs | 37 +-- .../rustc_const_eval/src/interpret/place.rs | 76 +++--- .../src/interpret/projection.rs | 20 +- .../rustc_const_eval/src/interpret/stack.rs | 26 +- .../rustc_const_eval/src/interpret/step.rs | 28 +- .../rustc_const_eval/src/interpret/traits.rs | 14 +- .../rustc_const_eval/src/interpret/util.rs | 8 +- .../src/interpret/validity.rs | 182 +++++++------ .../rustc_const_eval/src/interpret/visitor.rs | 10 +- .../src/util/check_validity_requirement.rs | 3 +- compiler/rustc_middle/src/lib.rs | 2 + compiler/rustc_middle/src/mir/consts.rs | 4 +- .../src/mir/interpret/allocation.rs | 15 +- .../rustc_middle/src/mir/interpret/error.rs | 239 ++++++++++++++---- .../rustc_middle/src/mir/interpret/mod.rs | 12 +- .../rustc_middle/src/mir/interpret/value.rs | 38 +-- compiler/rustc_middle/src/ty/consts.rs | 2 +- .../src/dataflow_const_prop.rs | 64 +++-- compiler/rustc_mir_transform/src/gvn.rs | 115 ++++----- .../rustc_mir_transform/src/jump_threading.rs | 44 ++-- .../src/known_panics_lint.rs | 56 ++-- src/tools/clippy/clippy_utils/src/consts.rs | 10 +- src/tools/miri/src/alloc_addresses/mod.rs | 24 +- src/tools/miri/src/borrow_tracker/mod.rs | 8 +- .../src/borrow_tracker/stacked_borrows/mod.rs | 52 ++-- .../borrow_tracker/stacked_borrows/stack.rs | 14 +- .../tree_borrows/diagnostics.rs | 4 +- .../src/borrow_tracker/tree_borrows/mod.rs | 34 +-- .../src/borrow_tracker/tree_borrows/tree.rs | 6 +- src/tools/miri/src/concurrency/data_race.rs | 36 +-- src/tools/miri/src/concurrency/init_once.rs | 8 +- src/tools/miri/src/concurrency/sync.rs | 46 ++-- src/tools/miri/src/concurrency/thread.rs | 34 +-- src/tools/miri/src/concurrency/weak_memory.rs | 22 +- src/tools/miri/src/diagnostics.rs | 6 +- src/tools/miri/src/eval.rs | 10 +- src/tools/miri/src/helpers.rs | 92 +++---- src/tools/miri/src/intrinsics/atomic.rs | 22 +- src/tools/miri/src/intrinsics/mod.rs | 18 +- src/tools/miri/src/intrinsics/simd.rs | 7 +- src/tools/miri/src/machine.rs | 48 ++-- src/tools/miri/src/operator.rs | 2 +- src/tools/miri/src/shims/alloc.rs | 20 +- src/tools/miri/src/shims/backtrace.rs | 8 +- src/tools/miri/src/shims/env.rs | 8 +- src/tools/miri/src/shims/extern_static.rs | 8 +- src/tools/miri/src/shims/foreign_items.rs | 35 +-- src/tools/miri/src/shims/native_lib.rs | 10 +- src/tools/miri/src/shims/os_str.rs | 25 +- src/tools/miri/src/shims/panic.rs | 10 +- src/tools/miri/src/shims/time.rs | 32 +-- src/tools/miri/src/shims/tls.rs | 32 +-- .../src/shims/unix/android/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/env.rs | 40 +-- src/tools/miri/src/shims/unix/fd.rs | 46 ++-- .../miri/src/shims/unix/foreign_items.rs | 6 +- .../src/shims/unix/freebsd/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/fs.rs | 225 +++++++++-------- src/tools/miri/src/shims/unix/linux/epoll.rs | 40 +-- .../miri/src/shims/unix/linux/eventfd.rs | 12 +- .../src/shims/unix/linux/foreign_items.rs | 8 +- src/tools/miri/src/shims/unix/linux/mem.rs | 6 +- src/tools/miri/src/shims/unix/linux/sync.rs | 8 +- .../src/shims/unix/macos/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/macos/sync.rs | 12 +- src/tools/miri/src/shims/unix/mem.rs | 20 +- .../src/shims/unix/solarish/foreign_items.rs | 4 +- src/tools/miri/src/shims/unix/sync.rs | 92 +++---- src/tools/miri/src/shims/unix/thread.rs | 16 +- .../miri/src/shims/unix/unnamed_socket.rs | 8 +- .../miri/src/shims/wasi/foreign_items.rs | 4 +- src/tools/miri/src/shims/windows/env.rs | 30 +-- .../miri/src/shims/windows/foreign_items.rs | 14 +- src/tools/miri/src/shims/windows/handle.rs | 6 +- src/tools/miri/src/shims/windows/sync.rs | 18 +- src/tools/miri/src/shims/windows/thread.rs | 2 +- src/tools/miri/src/shims/x86/aesni.rs | 6 +- src/tools/miri/src/shims/x86/avx.rs | 4 +- src/tools/miri/src/shims/x86/avx2.rs | 4 +- src/tools/miri/src/shims/x86/bmi.rs | 6 +- src/tools/miri/src/shims/x86/mod.rs | 88 +++---- src/tools/miri/src/shims/x86/sha.rs | 8 +- src/tools/miri/src/shims/x86/sse.rs | 4 +- src/tools/miri/src/shims/x86/sse2.rs | 4 +- src/tools/miri/src/shims/x86/sse3.rs | 4 +- src/tools/miri/src/shims/x86/sse41.rs | 4 +- src/tools/miri/src/shims/x86/sse42.rs | 16 +- src/tools/miri/src/shims/x86/ssse3.rs | 4 +- 102 files changed, 1590 insertions(+), 1468 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index ed1c912e6cb03..743924faa21d8 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -6,7 +6,8 @@ use rustc_middle::{bug, span_bug, ty}; use rustc_span::def_id::DefId; use crate::interpret::{ - self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, throw_machine_stop, + self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, interp_ok, + throw_machine_stop, }; /// Macro for machine-specific `InterpError` without allocation. @@ -79,7 +80,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { throw_machine_stop_str!("can't access mutable globals in ConstProp"); } - Ok(()) + interp_ok(()) } fn find_mir_or_eval_fn( @@ -127,7 +128,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { right: &interpret::ImmTy<'tcx, Self::Provenance>, ) -> interpret::InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> { use rustc_middle::mir::BinOp::*; - Ok(match bin_op { + interp_ok(match bin_op { Eq | Ne | Lt | Le | Gt | Ge => { // Types can differ, e.g. fn ptrs with different `for`. assert_eq!(left.layout.abi, right.layout.abi); diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index a241edbd77ad9..672353e629d44 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -20,7 +20,7 @@ use crate::const_eval::CheckAlignment; use crate::interpret::{ CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, - eval_nullary_intrinsic, intern_const_alloc_recursive, throw_exhaust, + eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; use crate::{CTRL_C_RECEIVED, errors}; @@ -98,19 +98,19 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( return Err(ecx .tcx .dcx() - .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }) - .into()); + .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })) + .into(); } Err(InternResult::FoundBadMutablePointer) => { return Err(ecx .tcx .dcx() - .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }) - .into()); + .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind })) + .into(); } } - Ok(R::make_result(ret, ecx)) + interp_ok(R::make_result(ret, ecx)) } /// The `InterpCx` is only meant to be used to do field and index projections into constants for @@ -147,7 +147,8 @@ pub fn mk_eval_cx_for_const_val<'tcx>( ty: Ty<'tcx>, ) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> { let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No); - let op = ecx.const_val_to_op(val, ty, None).ok()?; + // FIXME: is it a problem to discard the error here? + let op = ecx.const_val_to_op(val, ty, None).discard_err()?; Some((ecx, op)) } @@ -185,12 +186,16 @@ pub(super) fn op_to_const<'tcx>( _ => false, }; let immediate = if force_as_immediate { - match ecx.read_immediate(op) { + match ecx.read_immediate(op).report_err() { Ok(imm) => Right(imm), - Err(err) if !for_diagnostics => { - panic!("normalization works on validated constants: {err:?}") + Err(err) => { + if for_diagnostics { + // This discard the error, but for diagnostics that's okay. + op.as_mplace_or_imm() + } else { + panic!("normalization works on validated constants: {err:?}") + } } - _ => op.as_mplace_or_imm(), } } else { op.as_mplace_or_imm() @@ -283,17 +288,19 @@ pub fn eval_to_const_value_raw_provider<'tcx>( let ty::FnDef(_, args) = ty.kind() else { bug!("intrinsic with type {:?}", ty); }; - return eval_nullary_intrinsic(tcx, key.param_env, def_id, args).map_err(|error| { - let span = tcx.def_span(def_id); - - super::report( - tcx, - error.into_kind(), - span, - || (span, vec![]), - |span, _| errors::NullaryIntrinsicError { span }, - ) - }); + return eval_nullary_intrinsic(tcx, key.param_env, def_id, args).report_err().map_err( + |error| { + let span = tcx.def_span(def_id); + + super::report( + tcx, + error.into_kind(), + span, + || (span, vec![]), + |span, _| errors::NullaryIntrinsicError { span }, + ) + }, + ); } tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) @@ -376,6 +383,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( ); let res = ecx.load_mir(cid.instance.def, cid.promoted); res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) + .report_err() .map_err(|error| report_eval_error(&ecx, cid, error)) } @@ -400,6 +408,7 @@ fn const_validate_mplace<'tcx>( } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) + .report_err() // Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted // error about the validation failure. .map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 6d74998b7d88e..4aec74595bc86 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -24,8 +24,8 @@ use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, RangeSet, Scalar, - StackPopCleanup, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, - throw_unsup, throw_unsup_format, + StackPopCleanup, compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, + throw_ub_custom, throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -247,7 +247,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { let msg = Symbol::intern(self.read_str(&msg_place)?); let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); - return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); + return Err(ConstEvalErrKind::Panic { msg, file, line, col }).into(); } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { // For panic_fmt, call const_panic_fmt instead. let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None); @@ -259,16 +259,16 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { self.cur_span(), ); - return Ok(Some(new_instance)); + return interp_ok(Some(new_instance)); } else if self.tcx.is_lang_item(def_id, LangItem::AlignOffset) { let args = self.copy_fn_args(args); // For align_offset, we replace the function call if the pointer has no address. match self.align_offset(instance, &args, dest, ret)? { - ControlFlow::Continue(()) => return Ok(Some(instance)), - ControlFlow::Break(()) => return Ok(None), + ControlFlow::Continue(()) => return interp_ok(Some(instance)), + ControlFlow::Break(()) => return interp_ok(None), } } - Ok(Some(instance)) + interp_ok(Some(instance)) } /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer @@ -323,25 +323,25 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { dest, StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable }, )?; - Ok(ControlFlow::Break(())) + interp_ok(ControlFlow::Break(())) } else { // Not alignable in const, return `usize::MAX`. let usize_max = Scalar::from_target_usize(self.target_usize_max(), self); self.write_scalar(usize_max, dest)?; self.return_to_block(ret)?; - Ok(ControlFlow::Break(())) + interp_ok(ControlFlow::Break(())) } } Err(_addr) => { // The pointer has an address, continue with function call. - Ok(ControlFlow::Continue(())) + interp_ok(ControlFlow::Continue(())) } } } /// See documentation on the `ptr_guaranteed_cmp` intrinsic. fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> { - Ok(match (a, b) { + interp_ok(match (a, b) { // Comparisons between integers are always known. (Scalar::Int { .. }, Scalar::Int { .. }) => { if a == b { @@ -403,8 +403,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { instance: ty::InstanceKind<'tcx>, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { match instance { - ty::InstanceKind::Item(def) => Ok(ecx.tcx.mir_for_ctfe(def)), - _ => Ok(ecx.tcx.instance_mir(instance)), + ty::InstanceKind::Item(def) => interp_ok(ecx.tcx.mir_for_ctfe(def)), + _ => interp_ok(ecx.tcx.instance_mir(instance)), } } @@ -422,7 +422,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // Replace some functions. let Some(instance) = ecx.hook_special_const_fn(orig_instance, args, dest, ret)? else { // Call has already been handled. - return Ok(None); + return interp_ok(None); }; // Only check non-glue functions @@ -444,14 +444,14 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // This is a const fn. Call it. // In case of replacement, we return the *original* instance to make backtraces work out // (and we hope this does not confuse the FnAbi checks too much). - Ok(Some((ecx.load_mir(instance.def, None)?, orig_instance))) + interp_ok(Some((ecx.load_mir(instance.def, None)?, orig_instance))) } fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> { let msg = Symbol::intern(msg); let span = ecx.find_closest_untracked_caller_location(); let (file, line, col) = ecx.location_triple_for_span(span); - Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()) + Err(ConstEvalErrKind::Panic { msg, file, line, col }).into() } fn call_intrinsic( @@ -464,7 +464,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ) -> InterpResult<'tcx, Option>> { // Shared intrinsics. if ecx.eval_intrinsic(instance, args, dest, target)? { - return Ok(None); + return interp_ok(None); } let intrinsic_name = ecx.tcx.item_name(instance.def_id()); @@ -541,7 +541,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { "intrinsic `{intrinsic_name}` is not supported at compile-time" ); } - return Ok(Some(ty::Instance { + return interp_ok(Some(ty::Instance { def: ty::InstanceKind::Item(instance.def_id()), args: instance.args, })); @@ -550,7 +550,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // Intrinsic is done, jump to next block. ecx.return_to_block(target)?; - Ok(None) + interp_ok(None) } fn assert_panic( @@ -581,7 +581,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } }; - Err(ConstEvalErrKind::AssertFailure(err).into()) + Err(ConstEvalErrKind::AssertFailure(err)).into() } fn binary_ptr_op( @@ -652,7 +652,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -670,7 +670,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { if !ecx.recursion_limit.value_within_limit(ecx.stack().len() + 1) { throw_exhaust!(StackFrameLimitReached) } else { - Ok(frame) + interp_ok(frame) } } @@ -700,22 +700,22 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { if is_write { // Write access. These are never allowed, but we give a targeted error message. match alloc.mutability { - Mutability::Not => Err(err_ub!(WriteToReadOnly(alloc_id)).into()), - Mutability::Mut => Err(ConstEvalErrKind::ModifiedGlobal.into()), + Mutability::Not => throw_ub!(WriteToReadOnly(alloc_id)), + Mutability::Mut => Err(ConstEvalErrKind::ModifiedGlobal).into(), } } else { // Read access. These are usually allowed, with some exceptions. if machine.can_access_mut_global == CanAccessMutGlobal::Yes { // Machine configuration allows us read from anything (e.g., `static` initializer). - Ok(()) + interp_ok(()) } else if alloc.mutability == Mutability::Mut { // Machine configuration does not allow us to read statics (e.g., `const` // initializer). - Err(ConstEvalErrKind::ConstAccessesMutGlobal.into()) + Err(ConstEvalErrKind::ConstAccessesMutGlobal).into() } else { // Immutable global, this read is fine. assert_eq!(alloc.mutability, Mutability::Not); - Ok(()) + interp_ok(()) } } } @@ -748,9 +748,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // even when there is interior mutability.) place.map_provenance(CtfeProvenance::as_shared_ref) }; - Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout)) + interp_ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout)) } else { - Ok(val.clone()) + interp_ok(val.clone()) } } @@ -763,20 +763,20 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ) -> InterpResult<'tcx> { if range.size == Size::ZERO { // Nothing to check. - return Ok(()); + return interp_ok(()); } // Reject writes through immutable pointers. if immutable { - return Err(ConstEvalErrKind::WriteThroughImmutablePointer.into()); + return Err(ConstEvalErrKind::WriteThroughImmutablePointer).into(); } // Everything else is fine. - Ok(()) + interp_ok(()) } fn before_alloc_read(ecx: &InterpCx<'tcx, Self>, alloc_id: AllocId) -> InterpResult<'tcx> { // Check if this is the currently evaluated static. if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) { - return Err(ConstEvalErrKind::RecursiveStatic.into()); + return Err(ConstEvalErrKind::RecursiveStatic).into(); } // If this is another static, make sure we fire off the query to detect cycles. // But only do that when checks for static recursion are enabled. @@ -788,7 +788,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; } } - Ok(()) + interp_ok(()) } fn cached_union_data_range<'e>( diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 48aeee2e6f05e..ba19f642795a9 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,13 +1,12 @@ // Not in interpret to make sure we do not use private implementation details -use rustc_middle::mir::interpret::InterpErrorInfo; use rustc_middle::query::{Key, TyCtxtAt}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_target::abi::VariantIdx; use tracing::instrument; -use crate::interpret::{InterpCx, format_interp_error}; +use crate::interpret::InterpCx; mod dummy_machine; mod error; @@ -33,17 +32,6 @@ pub(crate) enum ValTreeCreationError<'tcx> { } pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError<'tcx>>; -impl<'tcx> From> for ValTreeCreationError<'tcx> { - fn from(err: InterpErrorInfo<'tcx>) -> Self { - ty::tls::with(|tcx| { - bug!( - "Unexpected Undefined Behavior error during valtree construction: {}", - format_interp_error(tcx.dcx(), err), - ) - }) - } -} - #[instrument(skip(tcx), level = "debug")] pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( tcx: TyCtxtAt<'tcx>, @@ -60,8 +48,8 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( return None; } ty::Adt(def, _) => { - let variant = ecx.read_discriminant(&op).ok()?; - let down = ecx.project_downcast(&op, variant).ok()?; + let variant = ecx.read_discriminant(&op).discard_err()?; + let down = ecx.project_downcast(&op, variant).discard_err()?; (def.variants()[variant].fields.len(), Some(variant), down) } ty::Tuple(args) => (args.len(), None, op), @@ -70,7 +58,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( let fields_iter = (0..field_count) .map(|i| { - let field_op = ecx.project_field(&down, i).ok()?; + let field_op = ecx.project_field(&down, i).discard_err()?; let val = op_to_const(&ecx, &field_op, /* for diagnostics */ true); Some((val, field_op.layout.ty)) }) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index a11d491bdd2c9..9e80e666ba91b 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -92,7 +92,7 @@ fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::zst()) } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { - let val = ecx.read_immediate(place)?; + let val = ecx.read_immediate(place).unwrap(); let val = val.to_scalar_int().unwrap(); *num_nodes += 1; @@ -114,7 +114,7 @@ fn const_to_valtree_inner<'tcx>( // equality at compile-time (see `ptr_guaranteed_cmp`). // However we allow those that are just integers in disguise. // First, get the pointer. Remember it might be wide! - let val = ecx.read_immediate(place)?; + let val = ecx.read_immediate(place).unwrap(); // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. if matches!(val.layout.abi, Abi::ScalarPair(..)) { @@ -135,7 +135,7 @@ fn const_to_valtree_inner<'tcx>( ty::FnPtr(..) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Ref(_, _, _) => { - let derefd_place = ecx.deref_pointer(place)?; + let derefd_place = ecx.deref_pointer(place).unwrap(); const_to_valtree_inner(ecx, &derefd_place, num_nodes) } @@ -159,7 +159,7 @@ fn const_to_valtree_inner<'tcx>( bug!("uninhabited types should have errored and never gotten converted to valtree") } - let variant = ecx.read_discriminant(place)?; + let variant = ecx.read_discriminant(place).unwrap(); branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 19aa76bf1eae1..4945563f4a4ee 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -15,8 +15,8 @@ use tracing::{info, instrument, trace}; use super::{ CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, - Projectable, Provenance, ReturnAction, Scalar, StackPopCleanup, StackPopInfo, throw_ub, - throw_ub_custom, throw_unsup_format, + Projectable, Provenance, ReturnAction, Scalar, StackPopCleanup, StackPopInfo, interp_ok, + throw_ub, throw_ub_custom, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -64,7 +64,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { arg: &FnArg<'tcx, M::Provenance>, field: usize, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { - Ok(match arg { + interp_ok(match arg { FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?), FnArg::InPlace(mplace) => FnArg::InPlace(self.project_field(mplace, field)?), }) @@ -97,7 +97,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // another type. let ty::Adt(def, args) = layout.ty.kind() else { // Not an ADT, so definitely no NPO. - return Ok(layout); + return interp_ok(layout); }; let inner = if self.tcx.is_diagnostic_item(sym::Option, def.did()) { // The wrapped type is the only arg. @@ -111,10 +111,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else if rhs.is_1zst() { lhs } else { - return Ok(layout); // no NPO + return interp_ok(layout); // no NPO } } else { - return Ok(layout); // no NPO + return interp_ok(layout); // no NPO }; // Check if the inner type is one of the NPO-guaranteed ones. @@ -126,7 +126,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Stop at NPO types so that we don't miss that attribute in the check below! def.is_struct() && !is_npo(def) }); - Ok(match inner.ty.kind() { + interp_ok(match inner.ty.kind() { ty::Ref(..) | ty::FnPtr(..) => { // Option<&T> behaves like &T, and same for fn() inner @@ -153,11 +153,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, bool> { // Fast path: equal types are definitely compatible. if caller.ty == callee.ty { - return Ok(true); + return interp_ok(true); } // 1-ZST are compatible with all 1-ZST (and with nothing else). if caller.is_1zst() || callee.is_1zst() { - return Ok(caller.is_1zst() && callee.is_1zst()); + return interp_ok(caller.is_1zst() && callee.is_1zst()); } // Unfold newtypes and NPO optimizations. let unfold = |layout: TyAndLayout<'tcx>| { @@ -180,17 +180,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => None, }; if let (Some(caller), Some(callee)) = (thin_pointer(caller), thin_pointer(callee)) { - return Ok(caller == callee); + return interp_ok(caller == callee); } // For wide pointers we have to get the pointee type. let pointee_ty = |ty: Ty<'tcx>| -> InterpResult<'tcx, Option>> { // We cannot use `builtin_deref` here since we need to reject `Box`. - Ok(Some(match ty.kind() { + interp_ok(Some(match ty.kind() { ty::Ref(_, ty, _) => *ty, ty::RawPtr(ty, _) => *ty, // We only accept `Box` with the default allocator. _ if ty.is_box_global(*self.tcx) => ty.expect_boxed_ty(), - _ => return Ok(None), + _ => return interp_ok(None), })) }; if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) { @@ -202,7 +202,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty); ty.ptr_metadata_ty(*self.tcx, normalize) }; - return Ok(meta_ty(caller) == meta_ty(callee)); + return interp_ok(meta_ty(caller) == meta_ty(callee)); } // Compatible integer types (in particular, usize vs ptr-sized-u32/u64). @@ -217,11 +217,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; if let (Some(caller), Some(callee)) = (int_ty(caller.ty), int_ty(callee.ty)) { // This is okay if they are the same integer type. - return Ok(caller == callee); + return interp_ok(caller == callee); } // Fall back to exact equality. - Ok(caller == callee) + interp_ok(caller == callee) } fn check_argument_compat( @@ -235,13 +235,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Ensure that our checks imply actual ABI compatibility for this concrete call. // (This can fail e.g. if `#[rustc_nonnull_optimization_guaranteed]` is used incorrectly.) assert!(caller_abi.eq_abi(callee_abi)); - Ok(true) + interp_ok(true) } else { trace!( "check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}", caller_abi, callee_abi ); - Ok(false) + interp_ok(false) } } @@ -266,7 +266,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !already_live { self.storage_live(callee_arg.as_local().unwrap())?; } - return Ok(()); + return interp_ok(()); } // Find next caller arg. let Some((caller_arg, caller_abi)) = caller_args.next() else { @@ -308,7 +308,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let FnArg::InPlace(mplace) = caller_arg { M::protect_in_place_function_argument(self, mplace)?; } - Ok(()) + interp_ok(()) } /// The main entry point for creating a new stack frame: performs ABI checks and initializes @@ -536,7 +536,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { unwind, ); } else { - Ok(()) + interp_ok(()) } } ty::InstanceKind::VTableShim(..) @@ -561,7 +561,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { unwind, )? else { - return Ok(()); + return interp_ok(()); }; // Special handling for the closure ABI: untuple the last argument. @@ -572,7 +572,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("init_fn_call: Will pass last argument by untupling"); Cow::from( args.iter() - .map(|a| Ok(a.clone())) + .map(|a| interp_ok(a.clone())) .chain( (0..untuple_arg.layout().fields.count()) .map(|i| self.fn_arg_field(untuple_arg, i)), @@ -886,27 +886,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // this transmute. res } else { - Ok(()) + interp_ok(()) }; // All right, now it is time to actually pop the frame. - let stack_pop_info = self.pop_stack_frame_raw(unwinding)?; - - // Report error from return value copy, if any. - copy_ret_result?; + // An error here takes precedence over the copy error. + let (stack_pop_info, ()) = self.pop_stack_frame_raw(unwinding).and(copy_ret_result)?; match stack_pop_info.return_action { ReturnAction::Normal => {} ReturnAction::NoJump => { // The hook already did everything. - return Ok(()); + return interp_ok(()); } ReturnAction::NoCleanup => { // If we are not doing cleanup, also skip everything else. assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); assert!(!unwinding, "tried to skip cleanup during unwinding"); // Skip machine hook. - return Ok(()); + return interp_ok(()); } } @@ -931,7 +929,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.stack().is_empty(), "only the bottommost frame can have StackPopCleanup::Root" ); - Ok(()) + interp_ok(()) } } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 85e7d91c2ad50..565a7d1624249 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -14,7 +14,8 @@ use tracing::trace; use super::util::ensure_monomorphic_enough; use super::{ - FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, throw_ub, throw_ub_custom, + FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, interp_ok, throw_ub, + throw_ub_custom, }; use crate::fluent_generated as fluent; @@ -157,7 +158,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op_allow_transmute(src, dest)?; } } - Ok(()) + interp_ok(()) } /// Handles 'IntToInt' and 'IntToFloat' casts. @@ -169,7 +170,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert!(src.layout.ty.is_integral() || src.layout.ty.is_char() || src.layout.ty.is_bool()); assert!(cast_to.ty.is_floating_point() || cast_to.ty.is_integral() || cast_to.ty.is_char()); - Ok(ImmTy::from_scalar( + interp_ok(ImmTy::from_scalar( self.cast_from_int_like(src.to_scalar(), src.layout, cast_to.ty)?, cast_to, )) @@ -192,7 +193,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F64 => self.cast_from_float(src.to_scalar().to_f64()?, cast_to.ty), FloatTy::F128 => self.cast_from_float(src.to_scalar().to_f128()?, cast_to.ty), }; - Ok(ImmTy::from_scalar(val, cast_to)) + interp_ok(ImmTy::from_scalar(val, cast_to)) } /// Handles 'FnPtrToPtr' and 'PtrToPtr' casts. @@ -206,14 +207,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Handle casting any ptr to raw ptr (might be a fat ptr). if cast_to.size == src.layout.size { // Thin or fat pointer that just has the ptr kind of target type changed. - return Ok(ImmTy::from_immediate(**src, cast_to)); + return interp_ok(ImmTy::from_immediate(**src, cast_to)); } else { // Casting the metadata away from a fat ptr. assert_eq!(src.layout.size, 2 * self.pointer_size()); assert_eq!(cast_to.size, self.pointer_size()); assert!(src.layout.ty.is_unsafe_ptr()); return match **src { - Immediate::ScalarPair(data, _) => Ok(ImmTy::from_scalar(data, cast_to)), + Immediate::ScalarPair(data, _) => interp_ok(ImmTy::from_scalar(data, cast_to)), Immediate::Scalar(..) => span_bug!( self.cur_span(), "{:?} input to a fat-to-thin cast ({} -> {})", @@ -240,7 +241,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(ptr) => M::expose_ptr(self, ptr)?, Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP. }; - Ok(ImmTy::from_scalar(self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, cast_to)) + interp_ok(ImmTy::from_scalar( + self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, + cast_to, + )) } pub fn pointer_with_exposed_provenance_cast( @@ -258,7 +262,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Then turn address into pointer. let ptr = M::ptr_from_addr_cast(self, addr)?; - Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(ptr, self), cast_to)) + interp_ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(ptr, self), cast_to)) } /// Low-level cast helper function. This works directly on scalars and can take 'int-like' input @@ -280,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty), }; - Ok(match *cast_ty.kind() { + interp_ok(match *cast_ty.kind() { // int -> int Int(_) | Uint(_) => { let size = match *cast_ty.kind() { @@ -505,7 +509,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.unsize_into(&src_field, cast_ty_field, &dst_field)?; } } - Ok(()) + interp_ok(()) } _ => { // Do not ICE if we are not monomorphic enough. diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 3bab2b3261e1e..81e0b1e12caf4 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -7,7 +7,8 @@ use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants}; use tracing::{instrument, trace}; use super::{ - ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable, err_ub, throw_ub, + ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable, err_ub, interp_ok, + throw_ub, }; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { @@ -48,7 +49,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if actual_variant != variant_index { throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); } - Ok(()) + interp_ok(()) } } } @@ -89,7 +90,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(UninhabitedEnumVariantRead(index)) } } - return Ok(index); + return interp_ok(index); } Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { (tag, tag_encoding, tag_field) @@ -205,7 +206,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if op.layout().for_variant(self, index).abi.is_uninhabited() { throw_ub!(UninhabitedEnumVariantRead(index)) } - Ok(index) + interp_ok(index) } pub fn discriminant_for_variant( @@ -226,7 +227,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Scalar::from_uint(variant.as_u32(), discr_layout.size) } }; - Ok(ImmTy::from_scalar(discr_value, discr_layout)) + interp_ok(ImmTy::from_scalar(discr_value, discr_layout)) } /// Computes how to write the tag of a given variant of enum `ty`: @@ -247,7 +248,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // discriminant is encoded implicitly, so any attempt to write // the wrong discriminant for a `Single` enum will reliably // result in UB. - Ok(None) + interp_ok(None) } abi::Variants::Multiple { @@ -265,7 +266,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let tag_size = tag_layout.size(self); let tag_val = tag_size.truncate(discr_val); let tag = ScalarInt::try_from_uint(tag_val, tag_size).unwrap(); - Ok(Some((tag, tag_field))) + interp_ok(Some((tag, tag_field))) } abi::Variants::Multiple { @@ -274,7 +275,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } if untagged_variant == variant_index => { // The untagged variant is implicitly encoded simply by having a // value that is outside the niche variants. - Ok(None) + interp_ok(None) } abi::Variants::Multiple { @@ -299,7 +300,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let tag = self .binary_op(mir::BinOp::Add, &variant_index_relative_val, &niche_start_val)? .to_scalar_int()?; - Ok(Some((tag, tag_field))) + interp_ok(Some((tag, tag_field))) } } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index f4c4a6fb0bf5b..5165f95afd5b7 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -19,9 +19,9 @@ use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument, trace}; use super::{ - Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlaceMeta, - Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, - throw_inval, throw_ub, throw_ub_custom, + Frame, FrameInfo, GlobalId, InterpError, InterpErrorInfo, InterpResult, MPlaceTy, Machine, + MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, + err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, }; use crate::{ReportErrorExt, fluent_generated as fluent, util}; @@ -73,7 +73,7 @@ where } impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { - type LayoutOfResult = InterpResult<'tcx, TyAndLayout<'tcx>>; + type LayoutOfResult = Result, InterpError<'tcx>>; #[inline] fn layout_tcx_at_span(&self) -> Span { @@ -82,29 +82,24 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } #[inline] - fn handle_layout_err( - &self, - err: LayoutError<'tcx>, - _: Span, - _: Ty<'tcx>, - ) -> InterpErrorInfo<'tcx> { - err_inval!(Layout(err)).into() + fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> InterpError<'tcx> { + err_inval!(Layout(err)) } } impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { - type FnAbiOfResult = InterpResult<'tcx, &'tcx FnAbi<'tcx, Ty<'tcx>>>; + type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpError<'tcx>>; fn handle_fn_abi_err( &self, err: FnAbiError<'tcx>, _span: Span, _fn_abi_request: FnAbiRequest<'tcx>, - ) -> InterpErrorInfo<'tcx> { + ) -> InterpError<'tcx> { match err { - FnAbiError::Layout(err) => err_inval!(Layout(err)).into(), + FnAbiError::Layout(err) => err_inval!(Layout(err)), FnAbiError::AdjustForForeignAbi(err) => { - err_inval!(FnAbiAdjustForForeignAbi(err)).into() + err_inval!(FnAbiAdjustForForeignAbi(err)) } } } @@ -160,7 +155,7 @@ pub(super) fn from_known_layout<'tcx>( ); } } - Ok(known_layout) + interp_ok(known_layout) } } } @@ -262,7 +257,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(err) = body.tainted_by_errors { throw_inval!(AlreadyReported(ReportedErrorInfo::tainted_by_errors(err))); } - Ok(body) + interp_ok(body) } /// Call this on things you got out of the MIR (so it is as generic as the current @@ -305,7 +300,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("param_env: {:#?}", self.param_env); trace!("args: {:#?}", args); match ty::Instance::try_resolve(*self.tcx, self.param_env, def, args) { - Ok(Some(instance)) => Ok(instance), + Ok(Some(instance)) => interp_ok(instance), Ok(None) => throw_inval!(TooGeneric), // FIXME(eddyb) this could be a bit more specific than `AlreadyReported`. @@ -401,7 +396,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { layout: &TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { if layout.is_sized() { - return Ok(Some((layout.size, layout.align.abi))); + return interp_ok(Some((layout.size, layout.align.abi))); } match layout.ty.kind() { ty::Adt(..) | ty::Tuple(..) => { @@ -425,7 +420,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { else { // A field with an extern type. We don't know the actual dynamic size // or the alignment. - return Ok(None); + return interp_ok(None); }; // # First compute the dynamic alignment @@ -456,12 +451,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if full_size > self.max_size_of_val() { throw_ub!(InvalidMeta(InvalidMetaKind::TooBig)); } - Ok(Some((full_size, full_align))) + interp_ok(Some((full_size, full_align))) } ty::Dynamic(expected_trait, _, ty::Dyn) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). - Ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) + interp_ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) } ty::Slice(_) | ty::Str => { @@ -474,10 +469,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if size > self.max_size_of_val() { throw_ub!(InvalidMeta(InvalidMetaKind::SliceTooBig)); } - Ok(Some((size, elem.align.abi))) + interp_ok(Some((size, elem.align.abi))) } - ty::Foreign(_) => Ok(None), + ty::Foreign(_) => interp_ok(None), _ => span_bug!(self.cur_span(), "size_and_align_of::<{}> not supported", layout.ty), } @@ -503,7 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn return_to_block(&mut self, target: Option) -> InterpResult<'tcx> { if let Some(target) = target { self.go_to_block(target); - Ok(()) + interp_ok(()) } else { throw_ub!(Unreachable) } @@ -530,10 +525,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::unwind_terminate(self, reason)?; // This might have pushed a new stack frame, or it terminated execution. // Either way, `loc` will not be updated. - return Ok(()); + return interp_ok(()); } }; - Ok(()) + interp_ok(()) } /// Call a query that can return `ErrorHandled`. Should be used for statics and other globals. diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 81659df7c533b..7a1b92601a441 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -26,7 +26,9 @@ use rustc_span::def_id::LocalDefId; use rustc_span::sym; use tracing::{instrument, trace}; -use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub}; +use super::{ + AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok, +}; use crate::const_eval; use crate::errors::NestedStaticInThreadLocal; @@ -307,7 +309,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>> ) -> InterpResult<'tcx, ()> { if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { // The constant is already in global memory. Do nothing. - return Ok(()); + return interp_ok(()); } // Move allocation to `tcx`. if let Some(_) = @@ -318,7 +320,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>> // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance") } - Ok(()) + interp_ok(()) } impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> { @@ -342,6 +344,6 @@ impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> { panic!("`intern_with_temp_alloc` with nested allocations"); } } - Ok(alloc_id) + interp_ok(alloc_id) } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 4c34ffc5d4ec1..52780cc6a3a6f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -18,7 +18,7 @@ use super::util::ensure_monomorphic_enough; use super::{ Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, - err_ub_custom, err_unsup_format, throw_inval, throw_ub_custom, throw_ub_format, + err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; @@ -39,7 +39,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( ) -> InterpResult<'tcx, ConstValue<'tcx>> { let tp_ty = args.type_at(0); let name = tcx.item_name(def_id); - Ok(match name { + interp_ok(match name { sym::type_name => { ensure_monomorphic_enough(tcx, tp_ty)?; let alloc = alloc_type_name(tcx, tp_ty); @@ -329,6 +329,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ) + .into() })?; // Perform division by size to compute return value. @@ -378,7 +379,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::panic_nounwind(self, &msg)?; // Skip the `return_to_block` at the end (we panicked, we do not return). - return Ok(true); + return interp_ok(true); } } sym::simd_insert => { @@ -438,12 +439,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Unsupported intrinsic: skip the return_to_block below. - _ => return Ok(false), + _ => return interp_ok(false), } trace!("{:?}", self.dump_place(&dest.clone().into())); self.return_to_block(ret)?; - Ok(true) + interp_ok(true) } pub(super) fn eval_nondiverging_intrinsic( @@ -457,7 +458,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !cond { throw_ub_custom!(fluent::const_eval_assume_false); } - Ok(()) + interp_ok(()) } NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { count, @@ -499,7 +500,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } _ => bug!("not a numeric intrinsic: {}", name), }; - Ok(Scalar::from_uint(bits_out, ret_layout.size)) + interp_ok(Scalar::from_uint(bits_out, ret_layout.size)) } pub fn exact_div( @@ -540,7 +541,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (val, overflowed) = self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair(); - Ok(if overflowed.to_bool()? { + interp_ok(if overflowed.to_bool()? { let size = l.layout.size; if l.layout.abi.is_signed() { // For signed ints the saturated value depends on the sign of the first @@ -582,7 +583,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // The offset must be in bounds starting from `ptr`. self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?; // This also implies that there is no overflow, so we are done. - Ok(ptr.wrapping_signed_offset(offset_bytes, self)) + interp_ok(ptr.wrapping_signed_offset(offset_bytes, self)) } /// Copy `count*size_of::()` many bytes from `*src` to `*dst`. @@ -628,7 +629,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&right, &left)?; self.copy_op(&temp, &right)?; self.deallocate_ptr(temp.ptr(), None, kind)?; - Ok(()) + interp_ok(()) } pub fn write_bytes_intrinsic( @@ -669,7 +670,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `Ordering`'s discriminants are -1/0/+1, so casting does the right thing. let result = Ord::cmp(left_bytes, right_bytes) as i32; - Ok(Scalar::from_i32(result)) + interp_ok(Scalar::from_i32(result)) } pub(crate) fn raw_eq_intrinsic( @@ -687,13 +688,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { this.check_ptr_align(ptr, layout.align.abi)?; let Some(alloc_ref) = self.get_ptr_alloc(ptr, layout.size)? else { // zero-sized access - return Ok(&[]); + return interp_ok(&[]); }; alloc_ref.get_bytes_strip_provenance() }; let lhs_bytes = get_bytes(self, lhs)?; let rhs_bytes = get_bytes(self, rhs)?; - Ok(Scalar::from_bool(lhs_bytes == rhs_bytes)) + interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes)) } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index a809f74e8eb90..0f796c3122210 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -20,7 +20,8 @@ use rustc_target::spec::abi::Abi as CallAbi; use super::{ AllocBytes, AllocId, AllocKind, AllocRange, Allocation, CTFE_ALLOC_SALT, ConstAllocation, CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, MemoryKind, - Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, throw_unsup, throw_unsup_format, + Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup, + throw_unsup_format, }; /// Data returned by [`Machine::after_stack_pop`], and consumed by @@ -185,7 +186,7 @@ pub trait Machine<'tcx>: Sized { ecx: &InterpCx<'tcx, Self>, instance: ty::InstanceKind<'tcx>, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { - Ok(ecx.tcx.instance_mir(instance)) + interp_ok(ecx.tcx.instance_mir(instance)) } /// Entry point to all function calls. @@ -280,7 +281,7 @@ pub trait Machine<'tcx>: Sized { /// Called before a basic block terminator is executed. #[inline] fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Determines the result of a `NullaryOp::UbChecks` invocation. @@ -290,7 +291,7 @@ pub trait Machine<'tcx>: Sized { /// You can use this to detect long or endlessly running programs. #[inline] fn increment_const_eval_counter(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called before a global allocation is accessed. @@ -304,7 +305,7 @@ pub trait Machine<'tcx>: Sized { _static_def_id: Option, _is_write: bool, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Return the `AllocId` for the given thread-local static in the current thread. @@ -422,7 +423,7 @@ pub trait Machine<'tcx>: Sized { _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Hook for performing extra checks on any memory read access, @@ -433,7 +434,7 @@ pub trait Machine<'tcx>: Sized { /// Used to prevent statics from self-initializing by reading from their own memory /// as it is being initialized. fn before_alloc_read(_ecx: &InterpCx<'tcx, Self>, _alloc_id: AllocId) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Hook for performing extra checks on a memory write access. @@ -446,7 +447,7 @@ pub trait Machine<'tcx>: Sized { _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Hook for performing extra operations on a memory deallocation. @@ -460,7 +461,7 @@ pub trait Machine<'tcx>: Sized { _align: Align, _kind: MemoryKind, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Executes a retagging operation for a single pointer. @@ -471,7 +472,7 @@ pub trait Machine<'tcx>: Sized { _kind: mir::RetagKind, val: &ImmTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> { - Ok(val.clone()) + interp_ok(val.clone()) } /// Executes a retagging operation on a compound value. @@ -482,7 +483,7 @@ pub trait Machine<'tcx>: Sized { _kind: mir::RetagKind, _place: &PlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called on places used for in-place function argument and return value handling. @@ -516,7 +517,7 @@ pub trait Machine<'tcx>: Sized { /// Called immediately after a stack frame got pushed and its locals got initialized. fn after_stack_push(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called just before the return value is copied to the caller-provided return place. @@ -524,7 +525,7 @@ pub trait Machine<'tcx>: Sized { _ecx: &InterpCx<'tcx, Self>, _frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called immediately after a stack frame got popped, but before jumping back to the caller. @@ -537,14 +538,14 @@ pub trait Machine<'tcx>: Sized { ) -> InterpResult<'tcx, ReturnAction> { // By default, we do not support unwinding from panics assert!(!unwinding); - Ok(ReturnAction::Normal) + interp_ok(ReturnAction::Normal) } /// Called immediately after an "immediate" local variable is read /// (i.e., this is called for reads that do not end up accessing addressable memory). #[inline(always)] fn after_local_read(_ecx: &InterpCx<'tcx, Self>, _local: mir::Local) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called immediately after an "immediate" local variable is assigned a new value @@ -556,7 +557,7 @@ pub trait Machine<'tcx>: Sized { _local: mir::Local, _storage_live: bool, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called immediately after actual memory was allocated for a local @@ -567,7 +568,7 @@ pub trait Machine<'tcx>: Sized { _local: mir::Local, _mplace: &MPlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Evaluate the given constant. The `eval` function will do all the required evaluation, @@ -645,7 +646,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { ) -> InterpResult<$tcx> { // For now we don't do any checking here. We can't use `tcx.sess` because that can differ // between crates, and we need to ensure that const-eval always behaves the same. - Ok(()) + interp_ok(()) } #[inline(always)] @@ -665,7 +666,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> { // We can't look at `tcx.sess` here as that can differ across crates, which can lead to // unsound differences in evaluating the same constant at different instantiation sites. - Ok(true) + interp_ok(true) } #[inline(always)] @@ -675,7 +676,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { alloc: &'b Allocation, ) -> InterpResult<$tcx, Cow<'b, Allocation>> { // Overwrite default implementation: no need to adjust anything. - Ok(Cow::Borrowed(alloc)) + interp_ok(Cow::Borrowed(alloc)) } fn init_alloc_extra( @@ -685,7 +686,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { _size: Size, _align: Align, ) -> InterpResult<$tcx, Self::AllocExtra> { - Ok(()) + interp_ok(()) } fn extern_static_pointer( @@ -693,7 +694,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { def_id: DefId, ) -> InterpResult<$tcx, Pointer> { // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail. - Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id).into(), Size::ZERO)) + interp_ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id).into(), Size::ZERO)) } #[inline(always)] @@ -702,7 +703,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { ptr: Pointer, _kind: Option>, ) -> InterpResult<$tcx, Pointer> { - Ok(ptr) + interp_ok(ptr) } #[inline(always)] @@ -713,7 +714,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { // Allow these casts, but make the pointer not dereferenceable. // (I.e., they behave like transmutation.) // This is correct because no pointers can ever be exposed in compile-time evaluation. - Ok(Pointer::from_addr_invalid(addr)) + interp_ok(Pointer::from_addr_invalid(addr)) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index c3b506d848cdb..e6ab8ca12a886 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -23,7 +23,7 @@ use tracing::{debug, instrument, trace}; use super::{ AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer, - PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, throw_ub, + PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -82,7 +82,7 @@ pub enum FnVal<'tcx, Other> { impl<'tcx, Other> FnVal<'tcx, Other> { pub fn as_instance(self) -> InterpResult<'tcx, Instance<'tcx>> { match self { - FnVal::Instance(instance) => Ok(instance), + FnVal::Instance(instance) => interp_ok(instance), FnVal::Other(_) => { throw_unsup_format!("'foreign' function pointers are not supported in this context") } @@ -284,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.mem_copy(ptr, new_ptr.into(), old_size.min(new_size), /*nonoverlapping*/ true)?; self.deallocate_ptr(ptr, old_size_and_align, kind)?; - Ok(new_ptr) + interp_ok(new_ptr) } #[instrument(skip(self), level = "debug")] @@ -330,8 +330,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)), - } - .into()); + }) + .into(); }; if alloc.mutability.is_not() { @@ -376,7 +376,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { bug!("Nothing can be deallocated twice"); } - Ok(()) + interp_ok(()) } /// Internal helper function to determine the allocation and offset of a pointer (if any). @@ -395,7 +395,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { |this, alloc_id, offset, prov| { let (size, align) = this .get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?; - Ok((size, align, (alloc_id, offset, prov))) + interp_ok((size, align, (alloc_id, offset, prov))) }, ) } @@ -412,9 +412,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| { let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?; - Ok((size, align, ())) + interp_ok((size, align, ())) })?; - Ok(()) + interp_ok(()) } /// Check whether the given pointer points to live memory for a signed amount of bytes. @@ -428,9 +428,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| { let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?; - Ok((size, align, ())) + interp_ok((size, align, ())) })?; - Ok(()) + interp_ok(()) } /// Low-level helper function to check if a ptr is in-bounds and potentially return a reference @@ -455,10 +455,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, Option> { // Everything is okay with size 0. if size == 0 { - return Ok(None); + return interp_ok(None); } - Ok(match this.borrow().ptr_try_get_alloc_id(ptr, size) { + interp_ok(match this.borrow().ptr_try_get_alloc_id(ptr, size) { Err(addr) => { // We couldn't get a proper allocation. throw_ub!(DanglingIntPointer { addr, inbounds_size: size, msg }); @@ -498,7 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(misaligned) = misaligned { throw_ub!(AlignmentCheckFailed(misaligned, msg)) } - Ok(()) + interp_ok(()) } pub(super) fn is_ptr_misaligned( @@ -634,7 +634,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `get_global_alloc` that we can actually use directly without inserting anything anywhere. // So the error type is `InterpResult<'tcx, &Allocation>`. let a = self.memory.alloc_map.get_or(id, || { - let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?; + // We have to funnel the `InterpErrorInfo` through a `Result` to match the `get_or` API, + // so we use `report_err` for that. + let alloc = self.get_global_alloc(id, /*is_write*/ false).report_err().map_err(Err)?; match alloc { Cow::Borrowed(alloc) => { // We got a ref, cheaply return that as an "error" so that the @@ -653,8 +655,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }); // Now unpack that funny error type match a { - Ok(a) => Ok(&a.1), - Err(a) => a, + Ok(a) => interp_ok(&a.1), + Err(a) => a.into(), } } @@ -662,7 +664,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// The caller is responsible for calling the access hooks! pub fn get_alloc_bytes_unchecked_raw(&self, id: AllocId) -> InterpResult<'tcx, *const u8> { let alloc = self.get_alloc_raw(id)?; - Ok(alloc.get_bytes_unchecked_raw()) + interp_ok(alloc.get_bytes_unchecked_raw()) } /// Bounds-checked *but not align-checked* allocation access. @@ -680,7 +682,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CheckInAllocMsg::MemoryAccessTest, |this, alloc_id, offset, prov| { let alloc = this.get_alloc_raw(alloc_id)?; - Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) + interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) }, )?; // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized @@ -703,20 +705,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { range, )?; } - Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id })) + interp_ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id })) } else { - Ok(None) + interp_ok(None) } } /// Return the `extra` field of the given allocation. pub fn get_alloc_extra<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, &'a M::AllocExtra> { - Ok(&self.get_alloc_raw(id)?.extra) + interp_ok(&self.get_alloc_raw(id)?.extra) } /// Return the `mutability` field of the given allocation. pub fn get_alloc_mutability<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, Mutability> { - Ok(self.get_alloc_raw(id)?.mutability) + interp_ok(self.get_alloc_raw(id)?.mutability) } /// Gives raw mutable access to the `Allocation`, without bounds or alignment checks. @@ -750,7 +752,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if alloc.mutability.is_not() { throw_ub!(WriteToReadOnly(id)) } - Ok((alloc, &mut self.machine)) + interp_ok((alloc, &mut self.machine)) } /// Gives raw, mutable access to the `Allocation` address, without bounds or alignment checks. @@ -760,7 +762,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { id: AllocId, ) -> InterpResult<'tcx, *mut u8> { let alloc = self.get_alloc_raw_mut(id)?.0; - Ok(alloc.get_bytes_unchecked_raw_mut()) + interp_ok(alloc.get_bytes_unchecked_raw_mut()) } /// Bounds-checked *but not align-checked* allocation access. @@ -781,7 +783,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CheckInAllocMsg::MemoryAccessTest, |this, alloc_id, offset, prov| { let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?; - Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine))) + interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine))) }, )?; @@ -790,9 +792,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !validation_in_progress { M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; } - Ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id })) + interp_ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id })) } else { - Ok(None) + interp_ok(None) } } @@ -802,7 +804,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { id: AllocId, ) -> InterpResult<'tcx, (&'a mut M::AllocExtra, &'a mut M)> { let (alloc, machine) = self.get_alloc_raw_mut(id)?; - Ok((&mut alloc.extra, machine)) + interp_ok((&mut alloc.extra, machine)) } /// Check whether an allocation is live. This is faster than calling @@ -904,7 +906,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if matches!(kind, AllocKind::Dead) { throw_ub!(PointerUseAfterFree(id, msg)) } - Ok((size, align)) + interp_ok((size, align)) } fn get_fn_alloc(&self, id: AllocId) -> Option> { @@ -928,7 +930,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))) } self.get_fn_alloc(alloc_id) - .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into()) + .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))) + .into() } /// Get the dynamic type of the given vtable pointer. @@ -951,12 +954,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(expected_dyn_type) = expected_trait { self.check_vtable_for_type(vtable_dyn_type, expected_dyn_type)?; } - Ok(ty) + interp_ok(ty) } pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not; - Ok(()) + interp_ok(()) } /// Create a lazy debug printer that prints the given allocation and all allocations it points @@ -1144,10 +1147,11 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> pub fn write_scalar(&mut self, range: AllocRange, val: Scalar) -> InterpResult<'tcx> { let range = self.range.subrange(range); debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id); - Ok(self - .alloc + + self.alloc .write_scalar(&self.tcx, range, val) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// `offset` is relative to this allocation reference, not the base of the allocation. @@ -1158,26 +1162,27 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> /// Mark the given sub-range (relative to this allocation reference) as uninitialized. pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> { let range = self.range.subrange(range); - Ok(self - .alloc + + self.alloc .write_uninit(&self.tcx, range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// Mark the entire referenced range as uninitialized pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> { - Ok(self - .alloc + self.alloc .write_uninit(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// Remove all provenance in the reference range. pub fn clear_provenance(&mut self) -> InterpResult<'tcx> { - Ok(self - .alloc + self.alloc .clear_provenance(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } } @@ -1189,12 +1194,10 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr read_provenance: bool, ) -> InterpResult<'tcx, Scalar> { let range = self.range.subrange(range); - let res = self - .alloc + self.alloc .read_scalar(&self.tcx, range, read_provenance) - .map_err(|e| e.to_interp_error(self.alloc_id))?; - debug!("read_scalar at {:?}{range:?}: {res:?}", self.alloc_id); - Ok(res) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// `range` is relative to this allocation reference, not the base of the allocation. @@ -1212,10 +1215,10 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr /// `range` is relative to this allocation reference, not the base of the allocation. pub fn get_bytes_strip_provenance<'b>(&'b self) -> InterpResult<'tcx, &'a [u8]> { - Ok(self - .alloc + self.alloc .get_bytes_strip_provenance(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id))?) + .map_err(|e| e.to_interp_error(self.alloc_id)) + .into() } /// Returns whether the allocation has provenance anywhere in the range of the `AllocRef`. @@ -1236,14 +1239,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, &[u8]> { let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else { // zero-sized access - return Ok(&[]); + return interp_ok(&[]); }; // Side-step AllocRef and directly access the underlying bytes more efficiently. // (We are staying inside the bounds here so all is good.) - Ok(alloc_ref - .alloc - .get_bytes_strip_provenance(&alloc_ref.tcx, alloc_ref.range) - .map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?) + interp_ok( + alloc_ref + .alloc + .get_bytes_strip_provenance(&alloc_ref.tcx, alloc_ref.range) + .map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?, + ) } /// Writes the given stream of bytes into memory. @@ -1263,7 +1268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size)? else { // zero-sized access assert_matches!(src.next(), None, "iterator said it was empty but returned an element"); - return Ok(()); + return interp_ok(()); }; // Side-step AllocRef and directly access the underlying bytes more efficiently. @@ -1279,7 +1284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { *dest = src.next().expect("iterator was shorter than it said it would be"); } assert_matches!(src.next(), None, "iterator was longer than it said it would be"); - Ok(()) + interp_ok(()) } pub fn mem_copy( @@ -1316,7 +1321,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Source alloc preparations and access hooks. let Some((src_alloc_id, src_offset, src_prov)) = src_parts else { // Zero-sized *source*, that means dest is also zero-sized and we have nothing to do. - return Ok(()); + return interp_ok(()); }; let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_range = alloc_range(src_offset, size); @@ -1332,7 +1337,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We already did the source checks and called the hooks so we are good to return early. let Some((dest_alloc_id, dest_offset, dest_prov)) = dest_parts else { // Zero-sized *destination*. - return Ok(()); + return interp_ok(()); }; // Prepare getting source provenance. @@ -1375,7 +1380,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .write_uninit(&tcx, dest_range) .map_err(|e| e.to_interp_error(dest_alloc_id))?; // We can forget about the provenance, this is all not initialized anyway. - return Ok(()); + return interp_ok(()); } // SAFE: The above indexing would have panicked if there weren't at least `size` bytes @@ -1432,7 +1437,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // copy the provenance to the destination dest_alloc.provenance_apply_copy(provenance); - Ok(()) + interp_ok(()) } } @@ -1441,7 +1446,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Test if this value might be null. /// If the machine does not support ptr-to-int casts, this is conservative. pub fn scalar_may_be_null(&self, scalar: Scalar) -> InterpResult<'tcx, bool> { - Ok(match scalar.try_to_scalar_int() { + interp_ok(match scalar.try_to_scalar_int() { Ok(int) => int.is_null(), Err(_) => { // Can only happen during CTFE. @@ -1508,13 +1513,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ptr: Pointer>, size: i64, ) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> { - self.ptr_try_get_alloc_id(ptr, size).map_err(|offset| { - err_ub!(DanglingIntPointer { - addr: offset, - inbounds_size: size, - msg: CheckInAllocMsg::InboundsTest + self.ptr_try_get_alloc_id(ptr, size) + .map_err(|offset| { + err_ub!(DanglingIntPointer { + addr: offset, + inbounds_size: size, + msg: CheckInAllocMsg::InboundsTest + }) }) .into() - }) } } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 31ee3e6519afc..7e6600060b40e 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -16,7 +16,7 @@ use tracing::trace; use super::{ CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub, from_known_layout, - mir_assign_valid_types, throw_ub, + interp_ok, mir_assign_valid_types, throw_ub, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -149,7 +149,7 @@ impl Immediate { } Immediate::Uninit => {} } - Ok(()) + interp_ok(()) } } @@ -307,7 +307,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { data_size: s.size().bytes(), })); } - Ok(s) + interp_ok(s) } #[inline] @@ -430,7 +430,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> { ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway - Ok(self.offset_(offset, layout, ecx)) + interp_ok(self.offset_(offset, layout, ecx)) } #[inline(always)] @@ -438,7 +438,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> { &self, _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone().into()) + interp_ok(self.clone().into()) } } @@ -514,11 +514,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { match self.as_mplace_or_imm() { - Left(mplace) => Ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()), + Left(mplace) => { + interp_ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()) + } Right(imm) => { assert_matches!(meta, MemPlaceMeta::None); // no place to store metadata here // Every part of an uninit is uninit. - Ok(imm.offset_(offset, layout, ecx).into()) + interp_ok(imm.offset_(offset, layout, ecx).into()) } } } @@ -528,7 +530,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { &self, _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) + interp_ok(self.clone()) } } @@ -543,12 +545,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, Option>> { if mplace.layout.is_unsized() { // Don't touch unsized - return Ok(None); + return interp_ok(None); } let Some(alloc) = self.get_place_alloc(mplace)? else { // zero-sized type can be left uninit - return Ok(Some(ImmTy::uninit(mplace.layout))); + return interp_ok(Some(ImmTy::uninit(mplace.layout))); }; // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. @@ -557,7 +559,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // case where some of the bytes are initialized and others are not. So, we need an extra // check that walks over the type of `mplace` to make sure it is truly correct to treat this // like a `Scalar` (or `ScalarPair`). - Ok(match mplace.layout.abi { + interp_ok(match mplace.layout.abi { Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => { let size = s.size(self); assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); @@ -606,7 +608,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &self, src: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Either, ImmTy<'tcx, M::Provenance>>> { - Ok(match src.to_op(self)?.as_mplace_or_imm() { + interp_ok(match src.to_op(self)?.as_mplace_or_imm() { Left(ref mplace) => { if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? { Right(val) @@ -637,7 +639,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if matches!(*imm, Immediate::Uninit) { throw_ub!(InvalidUninitBytes(None)); } - Ok(imm) + interp_ok(imm) } /// Read a scalar from a place @@ -645,7 +647,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &self, op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Scalar> { - Ok(self.read_immediate(op)?.to_scalar()) + interp_ok(self.read_immediate(op)?.to_scalar()) } // Pointer-sized reads are fairly common and need target layout access, so we wrap them in @@ -678,7 +680,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?; let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; - Ok(str) + interp_ok(str) } /// Read from a local of the current frame. @@ -698,7 +700,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert!(!layout.is_unsized()); } M::after_local_read(self, local)?; - Ok(OpTy { op, layout }) + interp_ok(OpTy { op, layout }) } /// Every place can be read from, so we can turn them into an operand. @@ -709,12 +711,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match place.as_mplace_or_local() { - Left(mplace) => Ok(mplace.into()), + Left(mplace) => interp_ok(mplace.into()), Right((local, offset, locals_addr, _)) => { debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`. debug_assert_eq!(locals_addr, self.frame().locals_addr()); let base = self.local_to_op(local, None)?; - Ok(match offset { + interp_ok(match offset { Some(offset) => base.offset(offset, place.layout, self)?, None => { // In the common case this hasn't been projected. @@ -764,7 +766,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } } - Ok(op) + interp_ok(op) } /// Evaluate the operand, returning a place where you can then find the data. @@ -794,7 +796,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } }; trace!("{:?}: {:?}", mir_op, op); - Ok(op) + interp_ok(op) } pub(crate) fn const_val_to_op( @@ -805,12 +807,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Other cases need layout. let adjust_scalar = |scalar| -> InterpResult<'tcx, _> { - Ok(match scalar { + interp_ok(match scalar { Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_root_pointer(ptr)?, size), Scalar::Int(int) => Scalar::Int(int), }) }; - let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?; + let layout = + from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty).into())?; let imm = match val_val { mir::ConstValue::Indirect { alloc_id, offset } => { // This is const data, no mutation allowed. @@ -818,7 +821,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CtfeProvenance::from(alloc_id).as_immutable(), offset, ))?; - return Ok(self.ptr_to_mplace(ptr.into(), layout).into()); + return interp_ok(self.ptr_to_mplace(ptr.into(), layout).into()); } mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(), mir::ConstValue::ZeroSized => Immediate::Uninit, @@ -829,7 +832,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self) } }; - Ok(OpTy { op: Operand::Immediate(imm), layout }) + interp_ok(OpTy { op: Operand::Immediate(imm), layout }) } } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 863b330a74cd5..52cd9b898bb72 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::sym; use rustc_target::abi::Size; use tracing::trace; -use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, throw_ub}; +use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, interp_ok, throw_ub}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn three_way_compare(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> { @@ -156,7 +156,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }); } - return Ok(ImmTy::from_scalar_int(result, left.layout)); + return interp_ok(ImmTy::from_scalar_int(result, left.layout)); } // For the remaining ops, the types must be the same on both sides @@ -181,10 +181,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => None, }; if let Some(op) = op { - return Ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx)); + return interp_ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx)); } if bin_op == Cmp { - return Ok(self.three_way_compare(l_signed(), r_signed())); + return interp_ok(self.three_way_compare(l_signed(), r_signed())); } let op: Option (i128, bool)> = match bin_op { Div if r.is_null() => throw_ub!(DivisionByZero), @@ -221,7 +221,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(ArithOverflow { intrinsic }); } let res = ImmTy::from_scalar_int(result, left.layout); - return Ok(if with_overflow { + return interp_ok(if with_overflow { let overflow = ImmTy::from_bool(overflow, *self.tcx); ImmTy::from_pair(res, overflow, *self.tcx) } else { @@ -234,10 +234,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let r = r_unsigned(); if bin_op == Cmp { - return Ok(self.three_way_compare(l, r)); + return interp_ok(self.three_way_compare(l, r)); } - Ok(match bin_op { + interp_ok(match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), Ne => ImmTy::from_bool(l != r, *self.tcx), @@ -339,7 +339,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(PointerArithOverflow) } let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?; - Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout)) + interp_ok(ImmTy::from_scalar( + Scalar::from_maybe_pointer(offset_ptr, self), + left.layout, + )) } // Fall back to machine hook so Miri can support more pointer ops. @@ -366,20 +369,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert_eq!(left.layout.ty, right.layout.ty); let left = left.to_scalar(); let right = right.to_scalar(); - Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) + interp_ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) } ty::Bool => { assert_eq!(left.layout.ty, right.layout.ty); let left = left.to_scalar(); let right = right.to_scalar(); - Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) + interp_ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) } ty::Float(fty) => { assert_eq!(left.layout.ty, right.layout.ty); let layout = left.layout; let left = left.to_scalar(); let right = right.to_scalar(); - Ok(match fty { + interp_ok(match fty { FloatTy::F16 => { self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?) } @@ -447,7 +450,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Not => !val, _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op), }; - Ok(ImmTy::from_bool(res, *self.tcx)) + interp_ok(ImmTy::from_bool(res, *self.tcx)) } ty::Float(fty) => { let val = val.to_scalar(); @@ -462,7 +465,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F64 => Scalar::from_f64(-val.to_f64()?), FloatTy::F128 => Scalar::from_f128(-val.to_f128()?), }; - Ok(ImmTy::from_scalar(res, layout)) + interp_ok(ImmTy::from_scalar(res, layout)) } ty::Int(..) => { let val = val.to_scalar().to_int(layout.size)?; @@ -472,7 +475,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op), }; let res = ScalarInt::truncate_from_int(res, layout.size).0; - Ok(ImmTy::from_scalar(res.into(), layout)) + interp_ok(ImmTy::from_scalar(res.into(), layout)) } ty::Uint(..) => { let val = val.to_scalar().to_uint(layout.size)?; @@ -481,12 +484,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op), }; let res = ScalarInt::truncate_from_uint(res, layout.size).0; - Ok(ImmTy::from_scalar(res.into(), layout)) + interp_ok(ImmTy::from_scalar(res.into(), layout)) } ty::RawPtr(..) | ty::Ref(..) => { assert_eq!(un_op, PtrMetadata); let (_, meta) = val.to_scalar_and_meta(); - Ok(match meta { + interp_ok(match meta { MemPlaceMeta::Meta(scalar) => { let ty = un_op.ty(*self.tcx, val.layout.ty); let layout = self.layout_of(ty)?; @@ -514,7 +517,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let layout = self.layout_of(arg_ty)?; let usize_layout = || self.layout_of(self.tcx.types.usize).unwrap(); - Ok(match null_op { + interp_ok(match null_op { SizeOf => { if !layout.abi.is_sized() { span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`"); diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 05264d32c6ba8..449d4c6bd7dea 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size}; use tracing::{instrument, trace}; use super::{ - AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, DiscardInterpError, ImmTy, Immediate, - InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, - Projectable, Provenance, Scalar, alloc_range, mir_assign_valid_types, + AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, + Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance, + Scalar, alloc_range, interp_ok, mir_assign_valid_types, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -90,7 +90,7 @@ impl MemPlace { } OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx), }; - Ok(MemPlace { ptr, meta, misaligned: self.misaligned }) + interp_ok(MemPlace { ptr, meta, misaligned: self.misaligned }) } } @@ -163,7 +163,10 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { layout: TyAndLayout<'tcx>, ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { - Ok(MPlaceTy { mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, layout }) + interp_ok(MPlaceTy { + mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, + layout, + }) } #[inline(always)] @@ -171,7 +174,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { &self, _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone().into()) + interp_ok(self.clone().into()) } } @@ -279,7 +282,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { layout: TyAndLayout<'tcx>, ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { - Ok(match self.as_mplace_or_local() { + interp_ok(match self.as_mplace_or_local() { Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(), Right((local, old_offset, locals_addr, _)) => { debug_assert!(layout.is_sized(), "unsized locals should live in memory"); @@ -367,7 +370,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { &self, _ecx: &mut InterpCx<'tcx, M>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> { - Ok(self.clone()) + interp_ok(self.clone()) } } @@ -425,7 +428,7 @@ where // `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced; // we hence can't call `size_and_align_of` since that asserts more validity than we want. let ptr = ptr.to_pointer(self)?; - Ok(self.ptr_with_meta_to_mplace(ptr, meta, layout, /*unaligned*/ false)) + interp_ok(self.ptr_with_meta_to_mplace(ptr, meta, layout, /*unaligned*/ false)) } /// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space. @@ -437,7 +440,7 @@ where ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { let imm = mplace.mplace.to_ref(self); let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?; - Ok(ImmTy::from_immediate(imm, layout)) + interp_ok(ImmTy::from_immediate(imm, layout)) } /// Take an operand, representing a pointer, and dereference it to a place. @@ -458,7 +461,7 @@ where trace!("deref to {} on {:?}", val.layout.ty, *val); let mplace = self.ref_to_mplace(&val)?; - Ok(mplace) + interp_ok(mplace) } #[inline] @@ -474,7 +477,7 @@ where // If an access is both OOB and misaligned, we want to see the bounds error. let a = self.get_ptr_alloc(mplace.ptr(), size)?; self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn)?; - Ok(a) + interp_ok(a) } #[inline] @@ -489,17 +492,10 @@ where // We check alignment separately, and raise that error *after* checking everything else. // If an access is both OOB and misaligned, we want to see the bounds error. // However we have to call `check_misalign` first to make the borrow checker happy. - let misalign_err = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn); - match self.get_ptr_alloc_mut(mplace.ptr(), size) { - Ok(a) => { - misalign_err?; - Ok(a) - } - Err(e) => { - misalign_err.discard_interp_err(); - Err(e) - } - } + let misalign_res = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn); + // An error from get_ptr_alloc_mut takes precedence. + let (a, ()) = self.get_ptr_alloc_mut(mplace.ptr(), size).and(misalign_res)?; + interp_ok(a) } /// Turn a local in the current frame into a place. @@ -519,7 +515,7 @@ where Operand::Indirect(mplace) => Place::Ptr(*mplace), } }; - Ok(PlaceTy { place, layout }) + interp_ok(PlaceTy { place, layout }) } /// Computes a place. You should only use this if you intend to write into this @@ -556,7 +552,7 @@ where ) } } - Ok(place) + interp_ok(place) } /// Given a place, returns either the underlying mplace or a reference to where the value of @@ -572,7 +568,7 @@ where (&mut Immediate, TyAndLayout<'tcx>, mir::Local), >, > { - Ok(match place.to_place().as_mplace_or_local() { + interp_ok(match place.to_place().as_mplace_or_local() { Left(mplace) => Left(mplace), Right((local, offset, locals_addr, layout)) => { if offset.is_some() { @@ -617,7 +613,7 @@ where )?; } - Ok(()) + interp_ok(()) } /// Write a scalar to a place @@ -667,7 +663,7 @@ where self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)?; } } - Ok(()) + interp_ok(()) } /// Write an immediate to memory. @@ -690,7 +686,7 @@ where let tcx = *self.tcx; let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else { // zero-sized access - return Ok(()); + return interp_ok(()); }; match value { @@ -715,7 +711,7 @@ where alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?; alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; // We don't have to reset padding here, `write_immediate` will anyway do a validation run. - Ok(()) + interp_ok(()) } Immediate::Uninit => alloc.write_uninit_full(), } @@ -736,12 +732,12 @@ where Left(mplace) => { let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else { // Zero-sized access - return Ok(()); + return interp_ok(()); }; alloc.write_uninit_full()?; } } - Ok(()) + interp_ok(()) } /// Remove all provenance in the given place. @@ -760,12 +756,12 @@ where Left(mplace) => { let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else { // Zero-sized access - return Ok(()); + return interp_ok(()); }; alloc.clear_provenance()?; } } - Ok(()) + interp_ok(()) } /// Copies the data from an operand to a place. @@ -848,7 +844,7 @@ where )?; } - Ok(()) + interp_ok(()) } /// Copies the data from an operand to a place. @@ -925,7 +921,7 @@ where self.mem_copy(src.ptr(), dest.ptr(), dest_size, /*nonoverlapping*/ true)?; self.check_misalign(src.mplace.misaligned, CheckAlignMsg::BasedOn)?; self.check_misalign(dest.mplace.misaligned, CheckAlignMsg::BasedOn)?; - Ok(()) + interp_ok(()) } /// Ensures that a place is in memory, and returns where it is. @@ -987,7 +983,7 @@ where Place::Ptr(mplace) => mplace, }; // Return with the original layout and align, so that the caller can go on - Ok(MPlaceTy { mplace, layout: place.layout }) + interp_ok(MPlaceTy { mplace, layout: place.layout }) } pub fn allocate_dyn( @@ -1000,7 +996,7 @@ where span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known") }; let ptr = self.allocate_ptr(size, align, kind)?; - Ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) + interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) } pub fn allocate( @@ -1035,7 +1031,7 @@ where }; let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self); let layout = self.layout_of(self.tcx.types.str_).unwrap(); - Ok(self.ptr_with_meta_to_mplace( + interp_ok(self.ptr_with_meta_to_mplace( ptr.into(), MemPlaceMeta::Meta(meta), layout, @@ -1051,7 +1047,7 @@ where let _ = self.tcx.global_alloc(raw.alloc_id); let ptr = self.global_root_pointer(Pointer::from(raw.alloc_id))?; let layout = self.layout_of(raw.ty)?; - Ok(self.ptr_to_mplace(ptr.into(), layout)) + interp_ok(self.ptr_to_mplace(ptr.into(), layout)) } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 621afdb1050a2..cc49d7cebf450 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument}; use super::{ InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar, err_ub, - throw_ub, throw_unsup, + interp_ok, throw_ub, throw_unsup, }; /// Describes the constraints placed on offset-projections. @@ -54,7 +54,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { // Go through the layout. There are lots of types that support a length, // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) match layout.fields { - abi::FieldsShape::Array { count, .. } => Ok(count), + abi::FieldsShape::Array { count, .. } => interp_ok(count), _ => bug!("len not supported on sized type {:?}", layout.ty), } } @@ -115,9 +115,9 @@ impl<'a, 'tcx, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'a, ' &mut self, ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Option<(u64, P)>> { - let Some(idx) = self.range.next() else { return Ok(None) }; + let Some(idx) = self.range.next() else { return interp_ok(None) }; // We use `Wrapping` here since the offset has already been checked when the iterator was created. - Ok(Some(( + interp_ok(Some(( idx, self.base.offset_with_meta( self.stride * idx, @@ -258,7 +258,7 @@ where // SIMD types must be newtypes around arrays, so all we have to do is project to their only field. let array = self.project_field(base, 0)?; let len = array.len(self)?; - Ok((array, len)) + interp_ok((array, len)) } fn project_constant_index>( @@ -300,7 +300,13 @@ where debug!("project_array_fields: {base:?} {len}"); base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?; // Create the iterator. - Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData }) + interp_ok(ArrayIterator { + base, + range: 0..len, + stride, + field_layout, + _phantom: PhantomData, + }) } /// Subslicing @@ -367,7 +373,7 @@ where P: Projectable<'tcx, M::Provenance> + From> + std::fmt::Debug, { use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { + interp_ok(match proj_elem { OpaqueCast(ty) => { span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck") } diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 15868f1b02d92..3bc9f46aea066 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -17,7 +17,7 @@ use tracing::{info_span, instrument, trace}; use super::{ AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, MemoryKind, Operand, Pointer, Provenance, ReturnAction, Scalar, - from_known_layout, throw_ub, throw_unsup, + from_known_layout, interp_ok, throw_ub, throw_unsup, }; use crate::errors; @@ -189,7 +189,7 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { pub(super) fn access(&self) -> InterpResult<'tcx, &Operand> { match &self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), + LocalValue::Live(val) => interp_ok(val), } } @@ -199,7 +199,7 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand> { match &mut self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), + LocalValue::Live(val) => interp_ok(val), } } } @@ -391,7 +391,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let span = info_span!("frame", "{}", instance); self.frame_mut().tracing_span.enter(span); - Ok(()) + interp_ok(()) } /// Low-level helper that pops a stack frame from the stack and returns some information about @@ -426,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return_action = ReturnAction::NoCleanup; }; - Ok(StackPopInfo { return_action, return_to_block, return_place }) + interp_ok(StackPopInfo { return_action, return_to_block, return_place }) } /// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw). @@ -449,7 +449,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Ok(cleanup) + interp_ok(cleanup) } /// In the current stack frame, mark all locals as live that are not arguments and don't have @@ -464,7 +464,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.storage_live(local)?; } } - Ok(()) + interp_ok(()) } pub fn storage_live_dyn( @@ -550,7 +550,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // If the local is already live, deallocate its old memory. let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); self.deallocate_local(old)?; - Ok(()) + interp_ok(()) } /// Mark a storage as live, killing the previous content. @@ -566,7 +566,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // If the local is already dead, this is a NOP. let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); self.deallocate_local(old)?; - Ok(()) + interp_ok(()) } fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { @@ -581,7 +581,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; }; - Ok(()) + interp_ok(()) } #[inline(always)] @@ -593,19 +593,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { let state = &frame.locals[local]; if let Some(layout) = state.layout.get() { - return Ok(layout); + return interp_ok(layout); } let layout = from_known_layout(self.tcx, self.param_env, layout, || { let local_ty = frame.body.local_decls[local].ty; let local_ty = self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; - self.layout_of(local_ty) + self.layout_of(local_ty).into() })?; // Layouts of locals are requested a lot, so we cache them. state.layout.set(Some(layout)); - Ok(layout) + interp_ok(layout) } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 8e2367e0d214c..aa7529556754b 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -14,7 +14,7 @@ use tracing::{info, instrument, trace}; use super::{ FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, - Projectable, Scalar, throw_ub, + Projectable, Scalar, interp_ok, throw_ub, }; use crate::util; @@ -36,7 +36,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { #[inline(always)] pub fn step(&mut self) -> InterpResult<'tcx, bool> { if self.stack().is_empty() { - return Ok(false); + return interp_ok(false); } let Either::Left(loc) = self.frame().loc else { @@ -44,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Just go on unwinding. trace!("unwinding: skipping frame"); self.return_from_current_stack_frame(/* unwinding */ true)?; - return Ok(true); + return interp_ok(true); }; let basic_block = &self.body().basic_blocks[loc.block]; @@ -55,7 +55,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert_eq!(old_frames, self.frame_idx()); // Advance the program counter. self.frame_mut().loc.as_mut().left().unwrap().statement_index += 1; - return Ok(true); + return interp_ok(true); } M::before_terminator(self)?; @@ -67,7 +67,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { info!("// executing {:?}", loc.block); } } - Ok(true) + interp_ok(true) } /// Runs the interpretation logic for the given `mir::Statement` at the current frame and @@ -145,7 +145,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Nop => {} } - Ok(()) + interp_ok(()) } /// Evaluate an assignment statement. @@ -277,7 +277,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("{:?}", self.dump_place(&dest)); - Ok(()) + interp_ok(()) } /// Writes the aggregate to the destination. @@ -313,7 +313,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self); let ptr = ImmTy::from_immediate(ptr_imm, dest.layout); self.copy_op(&ptr, dest)?; - return Ok(()); + return interp_ok(()); } _ => (FIRST_VARIANT, dest.clone(), None), }; @@ -365,7 +365,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { )?; } - Ok(()) + interp_ok(()) } /// Evaluate the arguments of a function call @@ -373,7 +373,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &self, op: &mir::Operand<'tcx>, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { - Ok(match op { + interp_ok(match op { mir::Operand::Copy(_) | mir::Operand::Constant(_) => { // Make a regular copy. let op = self.eval_operand(op, None)?; @@ -442,7 +442,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } }; - Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + interp_ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { @@ -537,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // generic. In order to make sure that generic and non-generic code behaves // roughly the same (and in keeping with Mir semantics) we do nothing here. self.go_to_block(target); - return Ok(()); + return interp_ok(()); } trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty); self.init_drop_in_place_call(&place, instance, target, unwind)?; @@ -566,7 +566,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // By definition, a Resume terminator means // that we're unwinding self.return_from_current_stack_frame(/* unwinding */ true)?; - return Ok(()); + return interp_ok(()); } // It is UB to ever encounter this. @@ -584,6 +584,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Ok(()) + interp_ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 8eead6018ac4a..da7d6853c0ea7 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -5,7 +5,9 @@ use rustc_target::abi::{Align, Size}; use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable, throw_ub}; +use super::{ + InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable, interp_ok, throw_ub, +}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -31,7 +33,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let salt = M::get_global_alloc_salt(self, None); let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, dyn_ty, salt); let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?; - Ok(vtable_ptr.into()) + interp_ok(vtable_ptr.into()) } pub fn get_vtable_size_and_align( @@ -42,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); - Ok((layout.size, layout.align.abi)) + interp_ok((layout.size, layout.align.abi)) } pub(super) fn vtable_entries( @@ -102,7 +104,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Ok(()) + interp_ok(()) } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. @@ -127,7 +129,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { layout, self, )?; - Ok(mplace) + interp_ok(mplace) } /// Turn a `dyn* Trait` type into an value with the actual dynamic type. @@ -147,6 +149,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `data` is already the right thing but has the wrong type. So we transmute it. let layout = self.layout_of(ty)?; let data = data.transmute(layout, self)?; - Ok(data) + interp_ok(data) } } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 2d53badf0f967..8bb5f173a5645 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{ }; use tracing::debug; -use super::{InterpCx, MPlaceTy, MemoryKind, throw_inval}; +use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval}; use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult}; /// Checks whether a type contains generic parameters which must be instantiated. @@ -23,7 +23,7 @@ where { debug!("ensure_monomorphic_enough: ty={:?}", ty); if !ty.has_param() { - return Ok(()); + return interp_ok(()); } struct FoundParam; @@ -78,7 +78,7 @@ where if matches!(ty.visit_with(&mut vis), ControlFlow::Break(FoundParam)) { throw_inval!(TooGeneric); } else { - Ok(()) + interp_ok(()) } } @@ -103,5 +103,5 @@ pub(crate) fn create_static_alloc<'tcx>( assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none()); - Ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) + interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 3c9fdd73100c6..13641ef2bd3f6 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -18,7 +18,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind, - Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, + Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -32,7 +32,7 @@ use super::machine::AllocMap; use super::{ AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub, - format_interp_error, throw_ub, + format_interp_error, }; // for the validation errors @@ -42,7 +42,7 @@ use super::InterpError::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; use super::UnsupportedOpInfo::*; -macro_rules! throw_validation_failure { +macro_rules! err_validation_failure { ($where:expr, $kind: expr) => {{ let where_ = &$where; let path = if !where_.is_empty() { @@ -53,10 +53,16 @@ macro_rules! throw_validation_failure { None }; - throw_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind })) + err_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind })) }}; } +macro_rules! throw_validation_failure { + ($where:expr, $kind: expr) => { + do yeet err_validation_failure!($where, $kind) + }; +} + /// If $e throws an error matching the pattern, throw a validation failure. /// Other errors are passed back to the caller, unchanged -- and if they reach the root of /// the visitor, we make sure only validation errors and `InvalidProgram` errors are left. @@ -91,25 +97,22 @@ macro_rules! try_validation { ($e:expr, $where:expr, $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)? ) => {{ - match $e { - Ok(x) => x, + $e.map_err(|e| { // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - Err(e) => { - let (kind, backtrace) = e.into_parts(); - match kind { - $( - $($p)|+ => { - throw_validation_failure!( - $where, - $kind - ) - } - ),+, - _ => Err::(InterpErrorInfo::from_parts(kind, backtrace))?, - } + let (kind, backtrace) = e.into_parts(); + match kind { + $( + $($p)|+ => { + err_validation_failure!( + $where, + $kind + ).into() + } + ),+, + _ => InterpErrorInfo::from_parts(kind, backtrace), } - } + })? }}; } @@ -381,7 +384,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // Undo changes self.path.truncate(path_len); // Done - Ok(r) + interp_ok(r) } fn read_immediate( @@ -389,7 +392,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { val: &PlaceTy<'tcx, M::Provenance>, expected: ExpectedKind, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - Ok(try_validation!( + interp_ok(try_validation!( self.ecx.read_immediate(val), self.path, Ub(InvalidUninitBytes(None)) => @@ -407,7 +410,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { val: &PlaceTy<'tcx, M::Provenance>, expected: ExpectedKind, ) -> InterpResult<'tcx, Scalar> { - Ok(self.read_immediate(val, expected)?.to_scalar()) + interp_ok(self.read_immediate(val, expected)?.to_scalar()) } fn deref_pointer( @@ -472,7 +475,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { _ => bug!("Unexpected unsized type tail: {:?}", tail), } - Ok(()) + interp_ok(()) } /// Check a reference or `Box`. @@ -635,7 +638,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } // Potentially skip recursive check. if skip_recursive_check { - return Ok(()); + return interp_ok(()); } } else { // This is not CTFE, so it's Miri with recursive checking. @@ -644,7 +647,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // FIXME: should we also skip `UnsafeCell` behind shared references? Currently that is not // needed since validation reads bypass Stacked Borrows and data race checks. if matches!(ptr_kind, PointerKind::Box) { - return Ok(()); + return interp_ok(()); } } let path = &self.path; @@ -657,7 +660,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { new_path }); } - Ok(()) + interp_ok(()) } /// Check if this is a value of primitive type, and if yes check the validity of the value @@ -684,7 +687,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.clear_provenance(value)?; self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::Char => { let scalar = self.read_scalar(value, ExpectedKind::Char)?; @@ -699,7 +702,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.clear_provenance(value)?; self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { // NOTE: Keep this in sync with the array optimization for int/float @@ -716,18 +719,18 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.clear_provenance(value)?; self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::RawPtr(..) => { let place = self.deref_pointer(value, ExpectedKind::RawPtr)?; if place.layout.is_unsized() { self.check_wide_ptr_meta(place.meta(), place.layout)?; } - Ok(true) + interp_ok(true) } ty::Ref(_, _ty, mutbl) => { self.check_safe_pointer(value, PointerKind::Ref(*mutbl))?; - Ok(true) + interp_ok(true) } ty::FnPtr(..) => { let scalar = self.read_scalar(value, ExpectedKind::FnPtr)?; @@ -756,12 +759,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } self.add_data_range_place(value); } - Ok(true) + interp_ok(true) } ty::Never => throw_validation_failure!(self.path, NeverVal), ty::Foreign(..) | ty::FnDef(..) => { // Nothing to check. - Ok(true) + interp_ok(true) } // The above should be all the primitive types. The rest is compound, we // check them by visiting their fields/variants. @@ -774,7 +777,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { | ty::Closure(..) | ty::Pat(..) | ty::CoroutineClosure(..) - | ty::Coroutine(..) => Ok(false), + | ty::Coroutine(..) => interp_ok(false), // Some types only occur during typechecking, they have no layout. // We should not see them here and we could not check them anyway. ty::Error(_) @@ -811,11 +814,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { max_value }) } else { - return Ok(()); + return interp_ok(()); } } else if scalar_layout.is_always_valid(self.ecx) { // Easy. (This is reachable if `enforce_number_validity` is set.) - return Ok(()); + return interp_ok(()); } else { // Conservatively, we reject, because the pointer *could* have a bad // value. @@ -828,7 +831,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { }; // Now compare. if valid_range.contains(bits) { - Ok(()) + interp_ok(()) } else { throw_validation_failure!(self.path, OutOfRange { value: format!("{bits}"), @@ -887,7 +890,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } fn reset_padding(&mut self, place: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { - let Some(data_bytes) = self.data_bytes.as_mut() else { return Ok(()) }; + let Some(data_bytes) = self.data_bytes.as_mut() else { return interp_ok(()) }; // Our value must be in memory, otherwise we would not have set up `data_bytes`. let mplace = self.ecx.force_allocation(place)?; // Determine starting offset and size. @@ -899,14 +902,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // If there is no padding at all, we can skip the rest: check for // a single data range covering the entire value. if data_bytes.0 == &[(start_offset, size)] { - return Ok(()); + return interp_ok(()); } // Get a handle for the allocation. Do this only once, to avoid looking up the same // allocation over and over again. (Though to be fair, iterating the value already does // exactly that.) let Some(mut alloc) = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)? else { // A ZST, no padding to clear. - return Ok(()); + return interp_ok(()); }; // Add a "finalizer" data range at the end, so that the iteration below finds all gaps // between ranges. @@ -933,7 +936,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { padding_cleared_until = offset + size; } assert!(padding_cleared_until == start_offset + size); - Ok(()) + interp_ok(()) } /// Computes the data range of this union type: @@ -1073,7 +1076,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { self.with_elem(PathElem::EnumTag, move |this| { - Ok(try_validation!( + interp_ok(try_validation!( this.ecx.read_discriminant(val), this.path, Ub(InvalidTag(val)) => InvalidEnumTag { @@ -1137,7 +1140,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, data_bytes.add_range(base_offset + offset, size); } } - Ok(()) + interp_ok(()) } #[inline] @@ -1147,7 +1150,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.check_safe_pointer(val, PointerKind::Box)?; - Ok(()) + interp_ok(()) } #[inline] @@ -1160,7 +1163,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // We assume that the Scalar validity range does not restrict these values // any further than `try_visit_primitive` does! if self.try_visit_primitive(val)? { - return Ok(()); + return interp_ok(()); } // Special check preventing `UnsafeCell` in the inner part of constants @@ -1207,7 +1210,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // If the size is 0, there is nothing to check. // (`size` can only be 0 if `len` is 0, and empty arrays are always valid.) if size == Size::ZERO { - return Ok(()); + return interp_ok(()); } // Now that we definitely have a non-ZST array, we know it lives in memory -- except it may // be an uninitialized local variable, those are also "immediate". @@ -1227,37 +1230,33 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // No need for an alignment check here, this is not an actual memory access. let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0"); - match alloc.get_bytes_strip_provenance() { - // In the happy case, we needn't check anything else. - Ok(_) => {} + alloc.get_bytes_strip_provenance().map_err(|err| { // Some error happened, try to provide a more detailed description. - Err(err) => { - // For some errors we might be able to provide extra information. - // (This custom logic does not fit the `try_validation!` macro.) - let (kind, backtrace) = err.into_parts(); - match kind { - Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { - // Some byte was uninitialized, determine which - // element that byte belongs to so we can - // provide an index. - let i = usize::try_from( - access.bad.start.bytes() / layout.size.bytes(), - ) - .unwrap(); - self.path.push(PathElem::ArrayElem(i)); - - if matches!(kind, Ub(InvalidUninitBytes(_))) { - throw_validation_failure!(self.path, Uninit { expected }) - } else { - throw_validation_failure!(self.path, PointerAsInt { expected }) - } - } + // For some errors we might be able to provide extra information. + // (This custom logic does not fit the `try_validation!` macro.) + let (kind, backtrace) = err.into_parts(); + match kind { + Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { + // Some byte was uninitialized, determine which + // element that byte belongs to so we can + // provide an index. + let i = usize::try_from( + access.bad.start.bytes() / layout.size.bytes(), + ) + .unwrap(); + self.path.push(PathElem::ArrayElem(i)); - // Propagate upwards (that will also check for unexpected errors). - _ => return Err(InterpErrorInfo::from_parts(kind, backtrace)), + if matches!(kind, Ub(InvalidUninitBytes(_))) { + err_validation_failure!(self.path, Uninit { expected }).into() + } else { + err_validation_failure!(self.path, PointerAsInt { expected }).into() + } } + + // Propagate upwards (that will also check for unexpected errors). + _ => return InterpErrorInfo::from_parts(kind, backtrace), } - } + })?; // Don't forget that these are all non-pointer types, and thus do not preserve // provenance. @@ -1335,7 +1334,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, } } - Ok(()) + interp_ok(()) } } @@ -1351,7 +1350,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty); // Run the visitor. - match self.run_for_validation(|ecx| { + self.run_for_validation(|ecx| { let reset_padding = reset_provenance_and_padding && { // Check if `val` is actually stored in memory. If not, padding is not even // represented and we need not reset it. @@ -1367,29 +1366,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; v.visit_value(val)?; v.reset_padding(val)?; - InterpResult::Ok(()) - }) { - Ok(()) => Ok(()), - // Pass through validation failures and "invalid program" issues. - Err(err) - if matches!( - err.kind(), - err_ub!(ValidationError { .. }) - | InterpError::InvalidProgram(_) - | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) - ) => - { - Err(err) - } - // Complain about any other kind of error -- those are bad because we'd like to - // report them in a way that shows *where* in the value the issue lies. - Err(err) => { + interp_ok(()) + }) + .map_err(|err| { + if !matches!( + err.kind(), + err_ub!(ValidationError { .. }) + | InterpError::InvalidProgram(_) + | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) + ) { bug!( "Unexpected error during validation: {}", format_interp_error(self.tcx.dcx(), err) ); } - } + err + }) } /// This function checks the data at `op` to be const-valid. @@ -1460,6 +1452,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /*reset_provenance_and_padding*/ false, )?; } - Ok(()) + interp_ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index d004a3f08929f..647917dbb67ed 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{FieldIdx, FieldsShape, VariantIdx, Variants}; use tracing::trace; -use super::{InterpCx, MPlaceTy, Machine, Projectable, throw_inval}; +use super::{InterpCx, MPlaceTy, Machine, Projectable, interp_ok, throw_inval}; /// How to traverse a value and what to do when we are at the leaves. pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { @@ -46,14 +46,14 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { /// Visits the given value as a union. No automatic recursion can happen here. #[inline(always)] fn visit_union(&mut self, _v: &Self::V, _fields: NonZero) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. /// The type of `v` will be a raw pointer to `T`, but this is a field of `Box` and the /// pointee type is the actual `T`. `box_ty` provides the full type of the `Box` itself. #[inline(always)] fn visit_box(&mut self, _box_ty: Ty<'tcx>, _v: &Self::V) -> InterpResult<'tcx> { - Ok(()) + interp_ok(()) } /// Called each time we recurse down to a field of a "product-like" aggregate @@ -165,7 +165,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { self.visit_field(v, 1, &alloc)?; // We visited all parts of this one. - return Ok(()); + return interp_ok(()); } // Non-normalized types should never show up here. @@ -222,6 +222,6 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { Variants::Single { .. } => {} } - Ok(()) + interp_ok(()) } } diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 421648d9e7b2a..649179d5b1e08 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,5 +1,4 @@ use rustc_middle::bug; -use rustc_middle::mir::interpret::DiscardInterpError; use rustc_middle::ty::layout::{ HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement, }; @@ -76,7 +75,7 @@ fn check_validity_requirement_strict<'tcx>( /*recursive*/ false, /*reset_provenance_and_padding*/ false, ) - .discard_interp_err() + .discard_err() .is_some()) } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index a32b19b067a55..70e61df1ab4d1 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -61,6 +61,8 @@ #![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(try_blocks)] +#![feature(try_trait_v2)] +#![feature(try_trait_v2_yeet)] #![feature(type_alias_impl_trait)] #![feature(yeet_expr)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index b34f5b48b782a..4262460d928b6 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -148,7 +148,7 @@ impl<'tcx> ConstValue<'tcx> { /* read_provenance */ true, ) .ok()?; - let ptr = ptr.to_pointer(&tcx).ok()?; + let ptr = ptr.to_pointer(&tcx).discard_err()?; let len = a .read_scalar( &tcx, @@ -156,7 +156,7 @@ impl<'tcx> ConstValue<'tcx> { /* read_provenance */ false, ) .ok()?; - let len = len.to_target_usize(&tcx).ok()?; + let len = len.to_target_usize(&tcx).discard_err()?; if len == 0 { return Some(&[]); } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index b8ecfa3c3f946..04d035e27ba3e 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -20,7 +20,7 @@ use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ AllocId, BadBytesAccess, CtfeProvenance, InterpError, InterpResult, Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, - UnsupportedOpInfo, read_target_uint, write_target_uint, + UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, }; use crate::ty; @@ -318,8 +318,9 @@ impl Allocation { pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { Self::uninit_inner(size, align, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); - InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted).into() + InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) + .into() } /// Try to create an Allocation of `size` bytes, panics if there is not enough memory @@ -355,12 +356,12 @@ impl Allocation { impl Allocation { /// Adjust allocation from the ones in `tcx` to a custom Machine instance /// with a different `Provenance` and `Byte` type. - pub fn adjust_from_tcx( + pub fn adjust_from_tcx<'tcx, Prov: Provenance, Bytes: AllocBytes>( &self, cx: &impl HasDataLayout, - mut alloc_bytes: impl FnMut(&[u8], Align) -> Result, - mut adjust_ptr: impl FnMut(Pointer) -> Result, Err>, - ) -> Result, Err> { + mut alloc_bytes: impl FnMut(&[u8], Align) -> InterpResult<'tcx, Bytes>, + mut adjust_ptr: impl FnMut(Pointer) -> InterpResult<'tcx, Pointer>, + ) -> InterpResult<'tcx, Allocation> { // Copy the data. let mut bytes = alloc_bytes(&*self.bytes, self.align)?; // Adjust provenance of pointers stored in this allocation. @@ -377,7 +378,7 @@ impl Allocation { new_provenance.push((offset, ptr_prov)); } // Create allocation. - Ok(Allocation { + interp_ok(Allocation { bytes, provenance: ProvenanceMap::from_presorted_ptrs(new_provenance), init_mask: self.init_mask.clone(), diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 06fdb9e1ecdee..431043b043192 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::backtrace::Backtrace; use std::borrow::Cow; -use std::{fmt, mem}; +use std::{convert, fmt, mem, ops}; use either::Either; use rustc_ast_ir::Mutability; @@ -106,55 +106,15 @@ rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); /// macros for this. /// /// Interpreter errors must *not* be silently discarded (that will lead to a panic). Instead, -/// explicitly call `discard_interp_err` if this is really the right thing to do. Note that if +/// explicitly call `discard_err` if this is really the right thing to do. Note that if /// this happens during const-eval or in Miri, it could lead to a UB error being lost! #[derive(Debug)] pub struct InterpErrorInfo<'tcx>(Box>); -/// Calling `.ok()` on an `InterpResult` leads to a panic because of the guard. -/// To still let people opt-in to discarding interpreter errors, we have this extension trait. -pub trait DiscardInterpError { - type Output; - - fn discard_interp_err(self) -> Option; -} - -impl<'tcx, T> DiscardInterpError for InterpResult<'tcx, T> { - type Output = T; - - fn discard_interp_err(self) -> Option { - match self { - Ok(v) => Some(v), - Err(e) => { - // Disarm the guard. - mem::forget(e.0.guard); - None - } - } - } -} - -#[derive(Debug)] -struct Guard; - -impl Drop for Guard { - fn drop(&mut self) { - // We silence the guard if we are already panicking, to avoid double-panics. - if !std::thread::panicking() { - panic!( - "an interpreter error got improperly discarded; use `discard_interp_err()` instead of `ok()` if this is intentional" - ); - } - } -} - #[derive(Debug)] struct InterpErrorInfoInner<'tcx> { kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace, - /// This makes us panic on drop. This is used to catch - /// accidentally discarding an interpreter error. - guard: Guard, } #[derive(Debug)] @@ -195,23 +155,16 @@ impl InterpErrorBacktrace { impl<'tcx> InterpErrorInfo<'tcx> { pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { - let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace, guard }) = self; - mem::forget(guard); // The error got explicitly discarded right here. + let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; (kind, backtrace) } pub fn into_kind(self) -> InterpError<'tcx> { - let InterpErrorInfo(box InterpErrorInfoInner { kind, guard, .. }) = self; - mem::forget(guard); // The error got explicitly discarded right here. - kind - } - - pub fn discard_interp_err(self) { - mem::forget(self.0.guard); // The error got explicitly discarded right here. + self.0.kind } pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { - Self(Box::new(InterpErrorInfoInner { kind, backtrace, guard: Guard })) + Self(Box::new(InterpErrorInfoInner { kind, backtrace })) } #[inline] @@ -245,7 +198,6 @@ impl<'tcx> From> for InterpErrorInfo<'tcx> { InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace: InterpErrorBacktrace::new(), - guard: Guard, })) } } @@ -654,8 +606,6 @@ pub enum InterpError<'tcx> { MachineStop(Box), } -pub type InterpResult<'tcx, T = ()> = Result>; - impl InterpError<'_> { /// Some errors do string formatting even if the error is never printed. /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, @@ -783,3 +733,182 @@ macro_rules! throw_exhaust { macro_rules! throw_machine_stop { ($($tt:tt)*) => { do yeet $crate::err_machine_stop!($($tt)*) }; } + +/// Guard type that panics on drop. +#[derive(Debug)] +struct Guard; + +impl Drop for Guard { + fn drop(&mut self) { + // We silence the guard if we are already panicking, to avoid double-panics. + if !std::thread::panicking() { + panic!( + "an interpreter error got improperly discarded; use `discard_err()` if this is intentional" + ); + } + } +} + +/// The result type used by the interpreter. This is a newtype around `Result` +/// to block access to operations like `ok()` that discard UB errors. +/// +/// We also make things panic if this type is ever implicitly dropped. +#[derive(Debug)] +pub struct InterpResult_<'tcx, T> { + res: Result>, + guard: Guard, +} + +// Type alias to be able to set a default type argument. +pub type InterpResult<'tcx, T = ()> = InterpResult_<'tcx, T>; + +impl<'tcx, T> ops::Try for InterpResult_<'tcx, T> { + type Output = T; + type Residual = InterpResult_<'tcx, convert::Infallible>; + + #[inline] + fn from_output(output: Self::Output) -> Self { + InterpResult_::new(Ok(output)) + } + + #[inline] + fn branch(self) -> ops::ControlFlow { + match self.disarm() { + Ok(v) => ops::ControlFlow::Continue(v), + Err(e) => ops::ControlFlow::Break(InterpResult_::new(Err(e))), + } + } +} + +impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { + #[inline] + #[track_caller] + fn from_residual(residual: InterpResult_<'tcx, convert::Infallible>) -> Self { + match residual.disarm() { + Err(e) => Self::new(Err(e)), + } + } +} + +// Allow `yeet`ing `InterpError` in functions returning `InterpResult_`. +impl<'tcx, T> ops::FromResidual>> for InterpResult_<'tcx, T> { + #[inline] + fn from_residual(ops::Yeet(e): ops::Yeet>) -> Self { + Self::new(Err(e.into())) + } +} + +// Allow `?` on `Result<_, InterpError>` in functions returning `InterpResult_`. +// This is useful e.g. for `option.ok_or_else(|| err_ub!(...))`. +impl<'tcx, T, E: Into>> ops::FromResidual> + for InterpResult_<'tcx, T> +{ + #[inline] + fn from_residual(residual: Result) -> Self { + match residual { + Err(e) => Self::new(Err(e.into())), + } + } +} + +impl<'tcx, T, E: Into>> From> for InterpResult<'tcx, T> { + #[inline] + fn from(value: Result) -> Self { + Self::new(value.map_err(|e| e.into())) + } +} + +impl<'tcx, T, V: FromIterator> FromIterator> for InterpResult<'tcx, V> { + fn from_iter>>(iter: I) -> Self { + Self::new(iter.into_iter().map(|x| x.disarm()).collect()) + } +} + +impl<'tcx, T> InterpResult_<'tcx, T> { + #[inline(always)] + fn new(res: Result>) -> Self { + Self { res, guard: Guard } + } + + #[inline(always)] + fn disarm(self) -> Result> { + mem::forget(self.guard); + self.res + } + + /// Discard the error information in this result. Only use this if ignoring Undefined Behavior is okay! + #[inline] + pub fn discard_err(self) -> Option { + self.disarm().ok() + } + + /// Look at the `Result` wrapped inside of this. + /// Must only be used to report the error! + #[inline] + pub fn report_err(self) -> Result> { + self.disarm() + } + + #[inline] + pub fn map(self, f: impl FnOnce(T) -> U) -> InterpResult<'tcx, U> { + InterpResult_::new(self.disarm().map(f)) + } + + #[inline] + pub fn map_err( + self, + f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, + ) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().map_err(f)) + } + + #[inline] + pub fn inspect_err(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().inspect_err(f)) + } + + #[inline] + #[track_caller] + pub fn unwrap(self) -> T { + self.disarm().unwrap() + } + + #[inline] + #[track_caller] + pub fn unwrap_or_else(self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> T) -> T { + self.disarm().unwrap_or_else(f) + } + + #[inline] + #[track_caller] + pub fn expect(self, msg: &str) -> T { + self.disarm().expect(msg) + } + + #[inline] + pub fn and_then(self, f: impl FnOnce(T) -> InterpResult<'tcx, U>) -> InterpResult<'tcx, U> { + InterpResult_::new(self.disarm().and_then(|t| f(t).disarm())) + } + + /// Returns success if both `self` and `other` succeed, while ensuring we don't + /// accidentally drop an error. + /// + /// If both are an error, `self` will be reported. + #[inline] + pub fn and(self, other: InterpResult<'tcx, U>) -> InterpResult<'tcx, (T, U)> { + match self.disarm() { + Ok(t) => interp_ok((t, other?)), + Err(e) => { + // Discard the other error. + drop(other.disarm()); + // Return `self`. + InterpResult_::new(Err(e)) + } + } + } +} + +#[inline(always)] +pub fn interp_ok<'tcx, T>(x: T) -> InterpResult<'tcx, T> { + InterpResult_::new(Ok(x)) +} diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 8ce6f9d230052..115bcdbc58987 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -34,12 +34,12 @@ pub use self::allocation::{ InitChunkIter, alloc_range, }; pub use self::error::{ - BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, DiscardInterpError, ErrorHandled, - EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, - EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, - InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, - ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, - ValidationErrorInfo, ValidationErrorKind, + BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, + EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, + InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo, + MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, + ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, + ValidationErrorKind, interp_ok, }; pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance}; pub use self::value::Scalar; diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index c6c2ab414ed41..061a55bfda8d6 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -8,7 +8,7 @@ use rustc_target::abi::{HasDataLayout, Size}; use super::{ AllocId, CtfeProvenance, InterpResult, Pointer, PointerArithmetic, Provenance, - ScalarSizeMismatch, + ScalarSizeMismatch, interp_ok, }; use crate::ty::ScalarInt; @@ -273,10 +273,10 @@ impl<'tcx, Prov: Provenance> Scalar { .to_bits_or_ptr_internal(cx.pointer_size()) .map_err(|s| err_ub!(ScalarSizeMismatch(s)))? { - Right(ptr) => Ok(ptr.into()), + Right(ptr) => interp_ok(ptr.into()), Left(bits) => { let addr = u64::try_from(bits).unwrap(); - Ok(Pointer::from_addr_invalid(addr)) + interp_ok(Pointer::from_addr_invalid(addr)) } } } @@ -311,12 +311,12 @@ impl<'tcx, Prov: Provenance> Scalar { if matches!(self, Scalar::Ptr(..)) { *self = self.to_scalar_int()?.into(); } - Ok(()) + interp_ok(()) } #[inline(always)] pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> { - self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into()) + self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None))).into() } #[inline(always)] @@ -330,20 +330,22 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); - self.to_scalar_int()?.try_to_bits(target_size).map_err(|size| { - err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { - target_size: target_size.bytes(), - data_size: size.bytes(), - })) + self.to_scalar_int()? + .try_to_bits(target_size) + .map_err(|size| { + err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { + target_size: target_size.bytes(), + data_size: size.bytes(), + })) + }) .into() - }) } pub fn to_bool(self) -> InterpResult<'tcx, bool> { let val = self.to_u8()?; match val { - 0 => Ok(false), - 1 => Ok(true), + 0 => interp_ok(false), + 1 => interp_ok(true), _ => throw_ub!(InvalidBool(val)), } } @@ -351,7 +353,7 @@ impl<'tcx, Prov: Provenance> Scalar { pub fn to_char(self) -> InterpResult<'tcx, char> { let val = self.to_u32()?; match std::char::from_u32(val) { - Some(c) => Ok(c), + Some(c) => interp_ok(c), None => throw_ub!(InvalidChar(val)), } } @@ -392,7 +394,7 @@ impl<'tcx, Prov: Provenance> Scalar { /// Fails if the scalar is a pointer. pub fn to_target_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { let b = self.to_uint(cx.data_layout().pointer_size)?; - Ok(u64::try_from(b).unwrap()) + interp_ok(u64::try_from(b).unwrap()) } /// Converts the scalar to produce a signed integer of the given size. @@ -400,7 +402,7 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_int(self, size: Size) -> InterpResult<'tcx, i128> { let b = self.to_bits(size)?; - Ok(size.sign_extend(b)) + interp_ok(size.sign_extend(b)) } /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer. @@ -432,13 +434,13 @@ impl<'tcx, Prov: Provenance> Scalar { /// Fails if the scalar is a pointer. pub fn to_target_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> { let b = self.to_int(cx.data_layout().pointer_size)?; - Ok(i64::try_from(b).unwrap()) + interp_ok(i64::try_from(b).unwrap()) } #[inline] pub fn to_float(self) -> InterpResult<'tcx, F> { // Going through `to_bits` to check size and truncation. - Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?)) + interp_ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?)) } #[inline] diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 73d0acf95f4f5..389d20f315ff0 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -519,7 +519,7 @@ impl<'tcx> Const<'tcx> { } pub fn try_to_bool(self) -> Option { - self.try_to_scalar()?.to_bool().ok() + self.try_to_valtree()?.try_to_scalar_int()?.try_to_bool().ok() } #[inline] diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 905143b2efbc2..002216f50f2d1 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -4,7 +4,7 @@ use rustc_const_eval::const_eval::{DummyMachine, throw_machine_stop_str}; use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable, + ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable, interp_ok, }; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; @@ -238,6 +238,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Elem(op) => self .ecx .int_to_int_or_float(&op, layout) + .discard_err() .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, @@ -251,6 +252,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Elem(op) => self .ecx .float_to_float_or_int(&op, layout) + .discard_err() .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, @@ -273,6 +275,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Elem(value) => self .ecx .unary_op(*op, &value) + .discard_err() .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, @@ -366,10 +369,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } Operand::Constant(box constant) => { - if let Some(constant) = self - .ecx - .eval_mir_constant(&constant.const_, constant.span, None) - .discard_interp_err() + if let Some(constant) = + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err() { self.assign_constant(state, place, constant, &[]); } @@ -391,7 +392,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { for &(mut proj_elem) in projection { if let PlaceElem::Index(index) = proj_elem { if let FlatSet::Elem(index) = state.get(index.into(), &self.map) - && let Some(offset) = index.to_target_usize(&self.tcx).discard_interp_err() + && let Some(offset) = index.to_target_usize(&self.tcx).discard_err() && let Some(min_length) = offset.checked_add(1) { proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false }; @@ -399,40 +400,35 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return; } } - operand = - if let Some(operand) = self.ecx.project(&operand, proj_elem).discard_interp_err() { - operand - } else { - return; - } + operand = if let Some(operand) = self.ecx.project(&operand, proj_elem).discard_err() { + operand + } else { + return; + } } self.map.for_each_projection_value( place, operand, &mut |elem, op| match elem { - TrackElem::Field(idx) => { - self.ecx.project_field(op, idx.as_usize()).discard_interp_err() - } - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_interp_err(), + TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).discard_err(), + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(), TrackElem::Discriminant => { - let variant = self.ecx.read_discriminant(op).discard_interp_err()?; - let discr_value = self - .ecx - .discriminant_for_variant(op.layout.ty, variant) - .discard_interp_err()?; + let variant = self.ecx.read_discriminant(op).discard_err()?; + let discr_value = + self.ecx.discriminant_for_variant(op.layout.ty, variant).discard_err()?; Some(discr_value.into()) } TrackElem::DerefLen => { - let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_interp_err()?.into(); - let len_usize = op.len(&self.ecx).discard_interp_err()?; + let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_err()?.into(); + let len_usize = op.len(&self.ecx).discard_err()?; let layout = self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).unwrap(); Some(ImmTy::from_uint(len_usize, layout).into()) } }, &mut |place, op| { - if let Some(imm) = self.ecx.read_immediate_raw(op).discard_interp_err() + if let Some(imm) = self.ecx.read_immediate_raw(op).discard_err() && let Some(imm) = imm.right() { let elem = self.wrap_immediate(*imm); @@ -456,7 +452,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom), // Both sides are known, do the actual computation. (FlatSet::Elem(left), FlatSet::Elem(right)) => { - match self.ecx.binary_op(op, &left, &right).discard_interp_err() { + match self.ecx.binary_op(op, &left, &right).discard_err() { // Ideally this would return an Immediate, since it's sometimes // a pair and sometimes not. But as a hack we always return a pair // and just make the 2nd component `Bottom` when it does not exist. @@ -479,7 +475,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } let arg_scalar = const_arg.to_scalar(); - let Some(arg_value) = arg_scalar.to_bits(layout.size).discard_interp_err() else { + let Some(arg_value) = arg_scalar.to_bits(layout.size).discard_err() else { return (FlatSet::Top, FlatSet::Top); }; @@ -527,10 +523,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return None; } let enum_ty_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?; - let discr_value = self - .ecx - .discriminant_for_variant(enum_ty_layout.ty, variant_index) - .discard_interp_err()?; + let discr_value = + self.ecx.discriminant_for_variant(enum_ty_layout.ty, variant_index).discard_err()?; Some(discr_value.to_scalar()) } @@ -584,7 +578,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { map: &Map<'tcx>, ) -> Option> { let ty = place.ty(self.local_decls, self.patch.tcx).ty; - let layout = ecx.layout_of(ty).discard_interp_err()?; + let layout = ecx.layout_of(ty).ok()?; if layout.is_zst() { return Some(Const::zero_sized(ty)); @@ -606,7 +600,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { .intern_with_temp_alloc(layout, |ecx, dest| { try_write_constant(ecx, dest, place, ty, state, map) }) - .discard_interp_err()?; + .discard_err()?; return Some(Const::Val(ConstValue::Indirect { alloc_id, offset: Size::ZERO }, ty)); } @@ -643,7 +637,7 @@ fn try_write_constant<'tcx>( // Fast path for ZSTs. if layout.is_zst() { - return Ok(()); + return interp_ok(()); } // Fast path for scalars. @@ -728,7 +722,7 @@ fn try_write_constant<'tcx>( ty::Error(_) | ty::Infer(..) | ty::CoroutineWitness(..) => bug!(), } - Ok(()) + interp_ok(()) } impl<'mir, 'tcx> @@ -841,7 +835,7 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> { if let PlaceElem::Index(local) = elem { let offset = self.before_effect.get(&(location, local.into()))?; let offset = offset.try_to_scalar()?; - let offset = offset.to_target_usize(&self.tcx).discard_interp_err()?; + let offset = offset.to_target_usize(&self.tcx).discard_err()?; let min_length = offset.checked_add(1)?; Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false }) } else { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 4dbcc6af9bde4..50c9702cb9b02 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -87,8 +87,8 @@ use std::borrow::Cow; use either::Either; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, - Scalar, intern_const_alloc_for_constprop, + ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, + intern_const_alloc_for_constprop, }; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; @@ -393,7 +393,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Repeat(..) => return None, Constant { ref value, disambiguator: _ } => { - self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_interp_err()? + self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()? } Aggregate(kind, variant, ref fields) => { let fields = fields @@ -414,39 +414,37 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty, }; let variant = if ty.is_enum() { Some(variant) } else { None }; - let ty = self.ecx.layout_of(ty).discard_interp_err()?; + let ty = self.ecx.layout_of(ty).ok()?; if ty.is_zst() { ImmTy::uninit(ty).into() } else if matches!(kind, AggregateTy::RawPtr { .. }) { // Pointers don't have fields, so don't `project_field` them. - let data = self.ecx.read_pointer(fields[0]).discard_interp_err()?; + let data = self.ecx.read_pointer(fields[0]).discard_err()?; let meta = if fields[1].layout.is_zst() { MemPlaceMeta::None } else { - MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_interp_err()?) + MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_err()?) }; let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); ImmTy::from_immediate(ptr_imm, ty).into() } else if matches!(ty.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) { - let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_interp_err()?; + let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; let variant_dest = if let Some(variant) = variant { - self.ecx.project_downcast(&dest, variant).discard_interp_err()? + self.ecx.project_downcast(&dest, variant).discard_err()? } else { dest.clone() }; for (field_index, op) in fields.into_iter().enumerate() { - let field_dest = self - .ecx - .project_field(&variant_dest, field_index) - .discard_interp_err()?; - self.ecx.copy_op(op, &field_dest).discard_interp_err()?; + let field_dest = + self.ecx.project_field(&variant_dest, field_index).discard_err()?; + self.ecx.copy_op(op, &field_dest).discard_err()?; } self.ecx .write_discriminant(variant.unwrap_or(FIRST_VARIANT), &dest) - .discard_interp_err()?; + .discard_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) - .discard_interp_err()?; + .discard_err()?; dest.into() } else { return None; @@ -472,7 +470,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // This should have been replaced by a `ConstantIndex` earlier. ProjectionElem::Index(_) => return None, }; - self.ecx.project(value, elem).discard_interp_err()? + self.ecx.project(value, elem).discard_err()? } Address { place, kind, provenance: _ } => { if !place.is_indirect_first_projection() { @@ -480,14 +478,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let local = self.locals[place.local]?; let pointer = self.evaluated[local].as_ref()?; - let mut mplace = self.ecx.deref_pointer(pointer).discard_interp_err()?; + let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?; for proj in place.projection.iter().skip(1) { // We have no call stack to associate a local with a value, so we cannot // interpret indexing. if matches!(proj, ProjectionElem::Index(_)) { return None; } - mplace = self.ecx.project(&mplace, proj).discard_interp_err()?; + mplace = self.ecx.project(&mplace, proj).discard_err()?; } let pointer = mplace.to_ref(&self.ecx); let ty = match kind { @@ -499,28 +497,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ), AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), }; - let layout = self.ecx.layout_of(ty).discard_interp_err()?; + let layout = self.ecx.layout_of(ty).ok()?; ImmTy::from_immediate(pointer, layout).into() } Discriminant(base) => { let base = self.evaluated[base].as_ref()?; - let variant = self.ecx.read_discriminant(base).discard_interp_err()?; - let discr_value = self - .ecx - .discriminant_for_variant(base.layout.ty, variant) - .discard_interp_err()?; + let variant = self.ecx.read_discriminant(base).discard_err()?; + let discr_value = + self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?; discr_value.into() } Len(slice) => { let slice = self.evaluated[slice].as_ref()?; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); - let len = slice.len(&self.ecx).discard_interp_err()?; + let len = slice.len(&self.ecx).discard_err()?; let imm = ImmTy::from_uint(len, usize_layout); imm.into() } NullaryOp(null_op, ty) => { - let layout = self.ecx.layout_of(ty).discard_interp_err()?; + let layout = self.ecx.layout_of(ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op && layout.is_unsized() { @@ -542,36 +538,36 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } UnaryOp(un_op, operand) => { let operand = self.evaluated[operand].as_ref()?; - let operand = self.ecx.read_immediate(operand).discard_interp_err()?; - let val = self.ecx.unary_op(un_op, &operand).discard_interp_err()?; + let operand = self.ecx.read_immediate(operand).discard_err()?; + let val = self.ecx.unary_op(un_op, &operand).discard_err()?; val.into() } BinaryOp(bin_op, lhs, rhs) => { let lhs = self.evaluated[lhs].as_ref()?; - let lhs = self.ecx.read_immediate(lhs).discard_interp_err()?; + let lhs = self.ecx.read_immediate(lhs).discard_err()?; let rhs = self.evaluated[rhs].as_ref()?; - let rhs = self.ecx.read_immediate(rhs).discard_interp_err()?; - let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_interp_err()?; + let rhs = self.ecx.read_immediate(rhs).discard_err()?; + let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?; val.into() } Cast { kind, value, from: _, to } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.evaluated[value].as_ref()?; - let value = self.ecx.read_immediate(value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.int_to_int_or_float(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.evaluated[value].as_ref()?; - let value = self.ecx.read_immediate(value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.float_to_float_or_int(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?; res.into() } CastKind::Transmute => { let value = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; + let to = self.ecx.layout_of(to).ok()?; // `offset` for immediates only supports scalar/scalar-pair ABIs, // so bail out if the target is not one. if value.as_mplace_or_imm().is_right() { @@ -581,29 +577,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { _ => return None, } } - value.offset(Size::ZERO, to, &self.ecx).discard_interp_err()? + value.offset(Size::ZERO, to, &self.ecx).discard_err()? } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => { let src = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_interp_err()?; - self.ecx.unsize_into(src, to, &dest.clone().into()).discard_interp_err()?; + let to = self.ecx.layout_of(to).ok()?; + let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?; + self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) - .discard_interp_err()?; + .discard_err()?; dest.into() } CastKind::FnPtrToPtr | CastKind::PtrToPtr => { let src = self.evaluated[value].as_ref()?; - let src = self.ecx.read_immediate(src).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let ret = self.ecx.ptr_to_ptr(&src, to).discard_interp_err()?; + let src = self.ecx.read_immediate(src).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?; ret.into() } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => { let src = self.evaluated[value].as_ref()?; - let src = self.ecx.read_immediate(src).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; + let src = self.ecx.read_immediate(src).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; ImmTy::from_immediate(*src, to).into() } _ => return None, @@ -715,7 +711,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let Some(idx) = self.locals[idx_local] { if let Some(offset) = self.evaluated[idx].as_ref() - && let Some(offset) = self.ecx.read_target_usize(offset).discard_interp_err() + && let Some(offset) = self.ecx.read_target_usize(offset).discard_err() && let Some(min_length) = offset.checked_add(1) { projection.to_mut()[i] = @@ -875,7 +871,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let DefKind::Enum = self.tcx.def_kind(enum_did) { let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args); - let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_interp_err()?; + let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?; return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty)); } @@ -1225,13 +1221,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } - let layout = self.ecx.layout_of(lhs_ty).discard_interp_err()?; + let layout = self.ecx.layout_of(lhs_ty).ok()?; let as_bits = |value| { let constant = self.evaluated[value].as_ref()?; if layout.abi.is_scalar() { - let scalar = self.ecx.read_scalar(constant).discard_interp_err()?; - scalar.to_bits(constant.layout.size).discard_interp_err() + let scalar = self.ecx.read_scalar(constant).discard_err()?; + scalar.to_bits(constant.layout.size).discard_err() } else { // `constant` is a wide pointer. Do not evaluate to bits. None @@ -1491,7 +1487,7 @@ fn op_to_prop_const<'tcx>( // If this constant has scalar ABI, return it as a `ConstValue::Scalar`. if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi - && let Some(scalar) = ecx.read_scalar(op).discard_interp_err() + && let Some(scalar) = ecx.read_scalar(op).discard_err() { if !scalar.try_to_scalar_int().is_ok() { // Check that we do not leak a pointer. @@ -1505,12 +1501,12 @@ fn op_to_prop_const<'tcx>( // If this constant is already represented as an `Allocation`, // try putting it into global memory to return it. if let Either::Left(mplace) = op.as_mplace_or_imm() { - let (size, _align) = ecx.size_and_align_of_mplace(&mplace).discard_interp_err()??; + let (size, _align) = ecx.size_and_align_of_mplace(&mplace).discard_err()??; // Do not try interning a value that contains provenance. // Due to https://github.com/rust-lang/rust/issues/79738, doing so could lead to bugs. // FIXME: remove this hack once that issue is fixed. - let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_interp_err()??; + let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_err()??; if alloc_ref.has_provenance() { return None; } @@ -1518,7 +1514,7 @@ fn op_to_prop_const<'tcx>( let pointer = mplace.ptr().into_pointer_or_addr().ok()?; let (prov, offset) = pointer.into_parts(); let alloc_id = prov.alloc_id(); - intern_const_alloc_for_constprop(ecx, alloc_id).discard_interp_err()?; + intern_const_alloc_for_constprop(ecx, alloc_id).discard_err()?; // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything // by `GlobalAlloc::Memory`, so do fall through to copying if needed. @@ -1533,9 +1529,8 @@ fn op_to_prop_const<'tcx>( } // Everything failed: create a new allocation to hold the data. - let alloc_id = ecx - .intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)) - .discard_interp_err()?; + let alloc_id = + ecx.intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)).discard_err()?; let value = ConstValue::Indirect { alloc_id, offset: Size::ZERO }; // Check that we do not leak a pointer. diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index e69a7e552f606..1844b97887ab9 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -37,9 +37,7 @@ use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; -use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, Immediate, InterpCx, OpTy, Projectable, -}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; @@ -202,7 +200,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { debug!(?discr, ?bb); let discr_ty = discr.ty(self.body, self.tcx).ty; - let Some(discr_layout) = self.ecx.layout_of(discr_ty).discard_interp_err() else { + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return; }; @@ -392,28 +390,24 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { lhs, constant, &mut |elem, op| match elem { - TrackElem::Field(idx) => { - self.ecx.project_field(op, idx.as_usize()).discard_interp_err() - } - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_interp_err(), + TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).discard_err(), + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(), TrackElem::Discriminant => { - let variant = self.ecx.read_discriminant(op).discard_interp_err()?; - let discr_value = self - .ecx - .discriminant_for_variant(op.layout.ty, variant) - .discard_interp_err()?; + let variant = self.ecx.read_discriminant(op).discard_err()?; + let discr_value = + self.ecx.discriminant_for_variant(op.layout.ty, variant).discard_err()?; Some(discr_value.into()) } TrackElem::DerefLen => { - let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_interp_err()?.into(); - let len_usize = op.len(&self.ecx).discard_interp_err()?; + let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_err()?.into(); + let len_usize = op.len(&self.ecx).discard_err()?; let layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); Some(ImmTy::from_uint(len_usize, layout).into()) } }, &mut |place, op| { if let Some(conditions) = state.try_get_idx(place, &self.map) - && let Some(imm) = self.ecx.read_immediate_raw(op).discard_interp_err() + && let Some(imm) = self.ecx.read_immediate_raw(op).discard_err() && let Some(imm) = imm.right() && let Immediate::Scalar(Scalar::Int(int)) = *imm { @@ -437,10 +431,8 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { match rhs { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { - let Some(constant) = self - .ecx - .eval_mir_constant(&constant.const_, constant.span, None) - .discard_interp_err() + let Some(constant) = + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err() else { return; }; @@ -482,7 +474,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { && let Some(discr_value) = self .ecx .discriminant_for_variant(agg_ty, *variant_index) - .discard_interp_err() + .discard_err() { self.process_immediate(bb, discr_target, discr_value, state); } @@ -567,7 +559,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant // of a niche encoding. If we cannot ensure that we write to the discriminant, do // nothing. - let Some(enum_layout) = self.ecx.layout_of(enum_ty).discard_interp_err() else { + let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else { return; }; let writes_discriminant = match enum_layout.variants { @@ -582,10 +574,8 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } => *variant_index != untagged_variant, }; if writes_discriminant { - let Some(discr) = self - .ecx - .discriminant_for_variant(enum_ty, *variant_index) - .discard_interp_err() + let Some(discr) = + self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() else { return; }; @@ -662,7 +652,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let Some(discr) = discr.place() else { return }; let discr_ty = discr.ty(self.body, self.tcx).ty; - let Some(discr_layout) = self.ecx.layout_of(discr_ty).discard_interp_err() else { + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return; }; let Some(conditions) = state.try_get(discr.as_ref(), &self.map) else { return }; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index b165ea53440e0..ccc029b1e281e 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ - DiscardInterpError, ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, + ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, interp_ok, }; use rustc_data_structures::fx::FxHashSet; use rustc_hir::HirId; @@ -101,8 +101,7 @@ impl<'tcx> Value<'tcx> { } (PlaceElem::Index(idx), Value::Aggregate { fields, .. }) => { let idx = prop.get_const(idx.into())?.immediate()?; - let idx = - prop.ecx.read_target_usize(idx).discard_interp_err()?.try_into().ok()?; + let idx = prop.ecx.read_target_usize(idx).discard_err()?.try_into().ok()?; if idx <= FieldIdx::MAX_AS_U32 { fields.get(FieldIdx::from_u32(idx)).unwrap_or(&Value::Uninit) } else { @@ -232,22 +231,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { where F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { - match f(self) { - Ok(val) => Some(val), - Err(error) => { - trace!("InterpCx operation failed: {:?}", error); + f(self) + .map_err(|err| { + trace!("InterpCx operation failed: {:?}", err); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, // dedicated error variants should be introduced instead. assert!( - !error.kind().formatted_string(), + !err.kind().formatted_string(), "known panics lint encountered formatting error: {}", - format_interp_error(self.ecx.tcx.dcx(), error), + format_interp_error(self.ecx.tcx.dcx(), err), ); - error.discard_interp_err(); - None - } - } + err + }) + .discard_err() } /// Returns the value, if any, of evaluating `c`. @@ -317,7 +314,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .ecx .binary_op(BinOp::SubWithOverflow, &ImmTy::from_int(0, arg.layout), &arg)? .to_scalar_pair(); - Ok((arg, overflow.to_bool()?)) + interp_ok((arg, overflow.to_bool()?)) })?; if overflow { self.report_assert_as_lint( @@ -349,9 +346,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // We need the type of the LHS. We cannot use `place_layout` as that is the type // of the result, which for checked binops is not the same! let left_ty = left.ty(self.local_decls(), self.tcx); - let left_size = self.ecx.layout_of(left_ty).discard_interp_err()?.size; + let left_size = self.ecx.layout_of(left_ty).ok()?.size; let right_size = r.layout.size; - let r_bits = r.to_scalar().to_bits(right_size).discard_interp_err(); + let r_bits = r.to_scalar().to_bits(right_size).discard_err(); if r_bits.is_some_and(|b| b >= left_size.bits() as u128) { debug!("check_binary_op: reporting assert for {:?}", location); let panic = AssertKind::Overflow( @@ -498,7 +495,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // This can be `None` if the lhs wasn't const propagated and we just // triggered the assert on the value of the rhs. self.eval_operand(op) - .and_then(|op| self.ecx.read_immediate(&op).discard_interp_err()) + .and_then(|op| self.ecx.read_immediate(&op).discard_err()) .map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int())) }; let msg = match msg { @@ -542,7 +539,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } use rustc_middle::mir::Rvalue::*; - let layout = self.ecx.layout_of(dest.ty(self.body, self.tcx).ty).discard_interp_err()?; + let layout = self.ecx.layout_of(dest.ty(self.body, self.tcx).ty).ok()?; trace!(?layout); let val: Value<'_> = match *rvalue { @@ -604,7 +601,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Len(place) => { let len = match self.get_const(place)? { - Value::Immediate(src) => src.len(&self.ecx).discard_interp_err()?, + Value::Immediate(src) => src.len(&self.ecx).discard_err()?, Value::Aggregate { fields, .. } => fields.len() as u64, Value::Uninit => match place.ty(self.local_decls(), self.tcx).ty.kind() { ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, @@ -617,7 +614,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Ref(..) | RawPtr(..) => return None, NullaryOp(ref null_op, ty) => { - let op_layout = self.use_ecx(|this| this.ecx.layout_of(ty))?; + let op_layout = self.ecx.layout_of(ty).ok()?; let val = match null_op { NullOp::SizeOf => op_layout.size.bytes(), NullOp::AlignOf => op_layout.align.abi.bytes(), @@ -635,21 +632,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Cast(ref kind, ref value, to) => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.eval_operand(value)?; - let value = self.ecx.read_immediate(&value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.int_to_int_or_float(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(&value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.eval_operand(value)?; - let value = self.ecx.read_immediate(&value).discard_interp_err()?; - let to = self.ecx.layout_of(to).discard_interp_err()?; - let res = self.ecx.float_to_float_or_int(&value, to).discard_interp_err()?; + let value = self.ecx.read_immediate(&value).discard_err()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?; res.into() } CastKind::Transmute => { let value = self.eval_operand(value)?; - let to = self.ecx.layout_of(to).discard_interp_err()?; + let to = self.ecx.layout_of(to).ok()?; // `offset` for immediates only supports scalar/scalar-pair ABIs, // so bail out if the target is not one. match (value.layout.abi, to.abi) { @@ -658,7 +655,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { _ => return None, } - value.offset(Size::ZERO, to, &self.ecx).discard_interp_err()?.into() + value.offset(Size::ZERO, to, &self.ecx).discard_err()?.into() } _ => return None, }, @@ -783,8 +780,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { TerminatorKind::SwitchInt { ref discr, ref targets } => { if let Some(ref value) = self.eval_operand(discr) && let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) - && let Some(constant) = - value_const.to_bits(value_const.size()).discard_interp_err() + && let Some(constant) = value_const.to_bits(value_const.size()).discard_err() { // We managed to evaluate the discriminant, so we know we only need to visit // one target. diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index bf47cf6d372d2..53a1170d6a618 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -870,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), - FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), - FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)), - FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().ok()?)), + FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)), + FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), + FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), + FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)), }); } Some(Constant::Vec(res)) @@ -903,7 +903,7 @@ fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option: crate::MiriInterpCxExt<'tcx> { AllocKind::Dead => unreachable!(), }; // Ensure this pointer's provenance is exposed, so that it can be used by FFI code. - return Ok(base_ptr.expose_provenance().try_into().unwrap()); + return interp_ok(base_ptr.expose_provenance().try_into().unwrap()); } // We are not in native lib mode, so we control the addresses ourselves. if let Some((reuse_addr, clock)) = @@ -209,7 +209,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(clock) = clock { ecx.acquire_clock(&clock); } - Ok(reuse_addr) + interp_ok(reuse_addr) } else { // We have to pick a fresh address. // Leave some space to the previous allocation, to give it some chance to be less aligned. @@ -234,7 +234,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_exhaust!(AddressSpaceFull); } - Ok(base_addr) + interp_ok(base_addr) } } @@ -248,7 +248,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let global_state = &mut *global_state; match global_state.base_addr.get(&alloc_id) { - Some(&addr) => Ok(addr), + Some(&addr) => interp_ok(addr), None => { // First time we're looking for the absolute address of this allocation. let base_addr = @@ -274,7 +274,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { }; global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id)); - Ok(base_addr) + interp_ok(base_addr) } } } @@ -287,12 +287,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let global_state = ecx.machine.alloc_addresses.get_mut(); // In strict mode, we don't need this, so we can save some cycles by not tracking it. if global_state.provenance_mode == ProvenanceMode::Strict { - return Ok(()); + return interp_ok(()); } // Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation // via int2ptr. if !ecx.is_alloc_live(alloc_id) { - return Ok(()); + return interp_ok(()); } trace!("Exposing allocation id {alloc_id:?}"); let global_state = ecx.machine.alloc_addresses.get_mut(); @@ -300,7 +300,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if ecx.machine.borrow_tracker.is_some() { ecx.expose_tag(alloc_id, tag)?; } - Ok(()) + interp_ok(()) } fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> { @@ -337,7 +337,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // cast is fairly irrelevant. Instead we generate this as a "wildcard" pointer, such that // *every time the pointer is used*, we do an `AllocId` lookup to find the (exposed) // allocation it might be referencing. - Ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr))) + interp_ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr))) } /// Convert a relative (tcx) pointer to a Miri pointer. @@ -359,7 +359,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(base_addr), ); // Add offset with the right kind of pointer-overflowing arithmetic. - Ok(base_ptr.wrapping_offset(offset, ecx)) + interp_ok(base_ptr.wrapping_offset(offset, ecx)) } // This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved @@ -390,9 +390,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(prepared_alloc_bytes.len(), bytes.len()); // Copy allocation contents into prepared memory. prepared_alloc_bytes.copy_from_slice(bytes); - Ok(prepared_alloc_bytes) + interp_ok(prepared_alloc_bytes) } else { - Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) + interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) } } diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 89fd8c28af2ae..5204558f98c1f 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -322,7 +322,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match method { BorrowTrackerMethod::StackedBorrows => { this.tcx.tcx.dcx().warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op"); - Ok(()) + interp_ok(()) } BorrowTrackerMethod::TreeBorrows => this.tb_give_pointer_debug_name(ptr, nth_parent, name), @@ -333,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let Some(borrow_tracker) = &this.machine.borrow_tracker else { eprintln!("attempted to print borrow state, but no borrow state is being tracked"); - return Ok(()); + return interp_ok(()); }; let method = borrow_tracker.borrow().borrow_tracker_method; match method { @@ -376,7 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } borrow_tracker.borrow_mut().end_call(&frame.extra); - Ok(()) + interp_ok(()) } } @@ -489,7 +489,7 @@ impl AllocState { alloc_id: AllocId, // diagnostics ) -> InterpResult<'tcx> { match self { - AllocState::StackedBorrows(_sb) => Ok(()), + AllocState::StackedBorrows(_sb) => interp_ok(()), AllocState::TreeBorrows(tb) => tb.borrow_mut().release_protector(machine, global, tag, alloc_id), } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index b270e484bdba4..fdc7a675fb713 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -230,7 +230,7 @@ impl<'tcx> Stack { } if !item.protected() { - return Ok(()); + return interp_ok(()); } // We store tags twice, once in global.protected_tags and once in each call frame. @@ -252,10 +252,10 @@ impl<'tcx> Stack { let allowed = matches!(cause, ItemInvalidationCause::Dealloc) && matches!(protector_kind, ProtectorKind::WeakProtector); if !allowed { - return Err(dcx.protector_error(item, protector_kind).into()); + return Err(dcx.protector_error(item, protector_kind)).into(); } } - Ok(()) + interp_ok(()) } /// Test if a memory `access` using pointer tagged `tag` is granted. @@ -295,7 +295,7 @@ impl<'tcx> Stack { self.pop_items_after(first_incompatible_idx, |item| { Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?; dcx.log_invalidation(item.tag()); - Ok(()) + interp_ok(()) })?; } else { // On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses. @@ -316,7 +316,7 @@ impl<'tcx> Stack { self.disable_uniques_starting_at(first_incompatible_idx, |item| { Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?; dcx.log_invalidation(item.tag()); - Ok(()) + interp_ok(()) })?; } @@ -345,7 +345,7 @@ impl<'tcx> Stack { } // Done. - Ok(()) + interp_ok(()) } /// Deallocate a location: Like a write access, but also there must be no @@ -367,7 +367,7 @@ impl<'tcx> Stack { Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?; } - Ok(()) + interp_ok(()) } /// Derive a new pointer from one with the given tag. @@ -418,7 +418,7 @@ impl<'tcx> Stack { "reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown" ); self.set_unknown_bottom(global.next_ptr_tag); - return Ok(()); + return interp_ok(()); }; // SharedReadWrite can coexist with "existing loans", meaning they don't act like a write @@ -431,7 +431,7 @@ impl<'tcx> Stack { // Put the new item there. trace!("reborrow: adding item {:?}", new); self.insert(new_idx, new); - Ok(()) + interp_ok(()) } } // # Stacked Borrows Core End @@ -491,7 +491,7 @@ impl<'tcx> Stacks { f(stack, &mut dcx, &mut self.exposed_tags)?; dcx_builder = dcx.unbuild(); } - Ok(()) + interp_ok(()) } } @@ -576,7 +576,7 @@ impl Stacks { self.for_each(alloc_range(Size::ZERO, size), dcx, |stack, dcx, exposed_tags| { stack.dealloc(tag, &state, dcx, exposed_tags) })?; - Ok(()) + interp_ok(()) } } @@ -623,7 +623,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { drop(global); // don't hold that reference any longer than we have to let Some((alloc_id, base_offset, orig_tag)) = loc else { - return Ok(()) + return interp_ok(()) }; let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id); @@ -655,7 +655,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { // No stacked borrows on these allocations. } } - Ok(()) + interp_ok(()) }; if size == Size::ZERO { @@ -676,12 +676,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { { log_creation(this, Some((alloc_id, base_offset, orig_tag)))?; // Still give it the new provenance, it got retagged after all. - return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); + return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); } else { // This pointer doesn't come with an AllocId. :shrug: log_creation(this, None)?; // Provenance unchanged. - return Ok(place.ptr().provenance); + return interp_ok(place.ptr().provenance); } } @@ -800,12 +800,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { )?; } } - Ok(()) + interp_ok(()) })?; } } - Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) + interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) } fn sb_retag_place( @@ -832,7 +832,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { *shown = true; this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); }); - return Ok(place.clone()); + return interp_ok(place.clone()); } }; @@ -845,7 +845,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { // Adjust place. // (If the closure gets called, that means the old provenance was `Some`, and hence the new // one must also be `Some`.) - Ok(place.clone().map_provenance(|_| new_prov.unwrap())) + interp_ok(place.clone().map_provenance(|_| new_prov.unwrap())) } /// Retags an individual pointer, returning the retagged version. @@ -859,7 +859,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let place = this.ref_to_mplace(val)?; let new_place = this.sb_retag_place(&place, new_perm, info)?; - Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) + interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) } } @@ -917,7 +917,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { in_field: self.in_field, })?; self.ecx.write_immediate(*val, place)?; - Ok(()) + interp_ok(()) } } impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> { @@ -935,7 +935,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx); self.retag_ptr_inplace(place, new_perm)?; } - Ok(()) + interp_ok(()) } fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { @@ -944,7 +944,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This optimization is crucial for ZSTs, because they can contain way more fields // than we can ever visit. if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() { - return Ok(()); + return interp_ok(()); } // Check the type of this value to see what to do with it (retag, or recurse). @@ -983,7 +983,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(()) + interp_ok(()) } } } @@ -1028,7 +1028,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // No stacked borrows on these allocations. } } - Ok(()) + interp_ok(()) } fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> { @@ -1046,6 +1046,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } println!(" ]"); } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index 6b5ef7d59c554..f024796c0a722 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -4,9 +4,9 @@ use std::ops::Range; use rustc_data_structures::fx::FxHashSet; use tracing::trace; -use crate::ProvenanceExtra; use crate::borrow_tracker::stacked_borrows::{Item, Permission}; use crate::borrow_tracker::{AccessKind, BorTag}; +use crate::{InterpResult, ProvenanceExtra, interp_ok}; /// Exactly what cache size we should use is a difficult trade-off. There will always be some /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up @@ -380,8 +380,8 @@ impl<'tcx> Stack { pub fn disable_uniques_starting_at( &mut self, disable_start: usize, - mut visitor: impl FnMut(Item) -> crate::InterpResult<'tcx>, - ) -> crate::InterpResult<'tcx> { + mut visitor: impl FnMut(Item) -> InterpResult<'tcx>, + ) -> InterpResult<'tcx> { #[cfg(feature = "stack-cache")] let unique_range = self.unique_range.clone(); #[cfg(not(feature = "stack-cache"))] @@ -420,16 +420,16 @@ impl<'tcx> Stack { #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); - Ok(()) + interp_ok(()) } /// Produces an iterator which iterates over `range` in reverse, and when dropped removes that /// range of `Item`s from this `Stack`. - pub fn pop_items_after crate::InterpResult<'tcx>>( + pub fn pop_items_after InterpResult<'tcx>>( &mut self, start: usize, mut visitor: V, - ) -> crate::InterpResult<'tcx> { + ) -> InterpResult<'tcx> { while self.borrows.len() > start { let item = self.borrows.pop().unwrap(); visitor(item)?; @@ -474,6 +474,6 @@ impl<'tcx> Stack { #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); - Ok(()) + interp_ok(()) } } 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 af9ec129c69f7..cb840f19e3b61 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -226,7 +226,7 @@ impl<'tcx> Tree { } else { eprintln!("Tag {tag:?} (to be named '{name}') not found!"); } - Ok(()) + interp_ok(()) } /// Debug helper: determines if the tree contains a tag. @@ -798,6 +798,6 @@ impl<'tcx> Tree { /* print warning message about tags not shown */ !show_unnamed, ); } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 30e940b4439a4..acfb76030f5ff 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -56,7 +56,7 @@ impl<'tcx> Tree { // handle them as much as we can. let tag = match prov { ProvenanceExtra::Concrete(tag) => tag, - ProvenanceExtra::Wildcard => return Ok(()), + ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); let span = machine.current_span(); @@ -81,7 +81,7 @@ impl<'tcx> Tree { // handle them as much as we can. let tag = match prov { ProvenanceExtra::Concrete(tag) => tag, - ProvenanceExtra::Wildcard => return Ok(()), + ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); let span = machine.current_span(); @@ -213,7 +213,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } drop(global); // don't hold that reference any longer than we have to - Ok(()) + interp_ok(()) }; trace!("Reborrow of size {:?}", ptr_size); @@ -235,13 +235,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); log_creation(this, None)?; // Keep original provenance. - return Ok(place.ptr().provenance); + return interp_ok(place.ptr().provenance); } }; log_creation(this, Some((alloc_id, base_offset, parent_prov)))?; let orig_tag = match parent_prov { - ProvenanceExtra::Wildcard => return Ok(place.ptr().provenance), // TODO: handle wildcard pointers + ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle wildcard pointers ProvenanceExtra::Concrete(tag) => tag, }; @@ -279,7 +279,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here // There's not actually any bytes here where accesses could even be tracked. // Just produce the new provenance, nothing else to do. - return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); + return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); } let span = this.machine.current_span(); @@ -312,7 +312,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) + interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) } fn tb_retag_place( @@ -350,7 +350,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Adjust place. // (If the closure gets called, that means the old provenance was `Some`, and hence the new // one must also be `Some`.) - Ok(place.clone().map_provenance(|_| new_prov.unwrap())) + interp_ok(place.clone().map_provenance(|_| new_prov.unwrap())) } /// Retags an individual pointer, returning the retagged version. @@ -362,7 +362,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let place = this.ref_to_mplace(val)?; let new_place = this.tb_retag_place(&place, new_perm)?; - Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) + interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) } } @@ -384,7 +384,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(new_perm) = new_perm { this.tb_retag_reference(val, new_perm) } else { - Ok(val.clone()) + interp_ok(val.clone()) } } @@ -421,7 +421,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = self.ecx.tb_retag_reference(&val, new_perm)?; self.ecx.write_immediate(*val, place)?; } - Ok(()) + interp_ok(()) } } impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> { @@ -446,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); self.retag_ptr_inplace(place, new_perm)?; } - Ok(()) + interp_ok(()) } fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { @@ -455,7 +455,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This optimization is crucial for ZSTs, because they can contain way more fields // than we can ever visit. if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() { - return Ok(()); + return interp_ok(()); } // Check the type of this value to see what to do with it (retag, or recurse). @@ -503,7 +503,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } - Ok(()) + interp_ok(()) } } } @@ -549,7 +549,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // No tree borrows on these allocations. } } - Ok(()) + interp_ok(()) } /// Display the tree. @@ -575,7 +575,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id), _ => { eprintln!("Can't give the name {name} to Wildcard pointer"); - return Ok(()); + return interp_ok(()); } }; let alloc_extra = this.get_alloc_extra(alloc_id)?; @@ -605,5 +605,5 @@ fn inner_ptr_of_unique<'tcx>( assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field"); let ptr = ecx.project_field(&nonnull, 0)?; // Finally a plain `*mut` - Ok(ptr) + interp_ok(ptr) } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 9fe134ed34bc9..410f4a58ac530 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -637,7 +637,7 @@ impl<'tcx> Tree { { perms.insert(idx, perm); } - Ok(()) + interp_ok(()) } /// Deallocation requires @@ -688,7 +688,7 @@ impl<'tcx> Tree { }, )?; } - Ok(()) + interp_ok(()) } /// Map the per-node and per-location `LocationState::perform_access` @@ -827,7 +827,7 @@ impl<'tcx> Tree { } } } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 5910be22a690a..82c4f6d3007e6 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -624,7 +624,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, || { this.validate_atomic_load(place, atomic) })?; - Ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?) + interp_ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?) } /// Perform an atomic write operation at the memory location. @@ -641,7 +641,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // The program didn't actually do a read, so suppress the memory access hooks. // This is also a very special exception where we just ignore an error -- if this read // was UB e.g. because the memory is uninitialized, we don't want to know! - let old_val = this.run_for_validation(|this| this.read_scalar(dest)).discard_interp_err(); + let old_val = this.run_for_validation(|this| this.read_scalar(dest)).discard_err(); this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; this.validate_atomic_store(dest, atomic)?; this.buffered_atomic_write(val, dest, atomic, old_val) @@ -668,7 +668,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.validate_atomic_rmw(place, atomic)?; this.buffered_atomic_rmw(val.to_scalar(), place, atomic, old.to_scalar())?; - Ok(old) + interp_ok(old) } /// Perform an atomic exchange with a memory place and a new @@ -688,7 +688,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.validate_atomic_rmw(place, atomic)?; this.buffered_atomic_rmw(new, place, atomic, old)?; - Ok(old) + interp_ok(old) } /// Perform an conditional atomic exchange with a memory place and a new @@ -720,7 +720,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.buffered_atomic_rmw(new_val.to_scalar(), place, atomic, old.to_scalar())?; // Return the old value. - Ok(old) + interp_ok(old) } /// Perform an atomic compare and exchange at a given memory location. @@ -777,7 +777,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } // Return the old value. - Ok(res) + interp_ok(res) } /// Update the data-race detector for an atomic fence on the current thread. @@ -809,11 +809,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } // Increment timestamp in case of release semantics. - Ok(atomic != AtomicFenceOrd::Acquire) + interp_ok(atomic != AtomicFenceOrd::Acquire) }, ) } else { - Ok(()) + interp_ok(()) } } @@ -1047,7 +1047,7 @@ impl VClockAlloc { let current_span = machine.current_span(); let global = machine.data_race.as_ref().unwrap(); if !global.race_detecting() { - return Ok(()); + return interp_ok(()); } let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads); let mut alloc_ranges = self.alloc_ranges.borrow_mut(); @@ -1070,7 +1070,7 @@ impl VClockAlloc { ); } } - Ok(()) + interp_ok(()) } /// Detect data-races for an unsynchronized write operation. It will not perform @@ -1089,7 +1089,7 @@ impl VClockAlloc { let current_span = machine.current_span(); let global = machine.data_race.as_mut().unwrap(); if !global.race_detecting() { - return Ok(()); + return interp_ok(()); } let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads); for (mem_clocks_range, mem_clocks) in @@ -1111,7 +1111,7 @@ impl VClockAlloc { ); } } - Ok(()) + interp_ok(()) } } @@ -1307,7 +1307,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { } } } - Ok(()) + interp_ok(()) } /// Update the data-race detector for an atomic read occurring at the @@ -1399,9 +1399,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); assert!(access.is_atomic()); - let Some(data_race) = &this.machine.data_race else { return Ok(()) }; + let Some(data_race) = &this.machine.data_race else { return interp_ok(()) }; if !data_race.race_detecting() { - return Ok(()); + return interp_ok(()); } let size = place.layout.size; let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0)?; @@ -1444,7 +1444,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { } // This conservatively assumes all operations have release semantics - Ok(true) + interp_ok(true) }, )?; @@ -1460,7 +1460,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { } } - Ok(()) + interp_ok(()) } } @@ -1757,7 +1757,7 @@ impl GlobalState { clocks.increment_clock(index, current_span); } } - Ok(()) + interp_ok(()) } /// Internal utility to identify a thread stored internally diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 9c2c6ae133023..8985135f4e892 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -37,9 +37,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { lock, offset, |ecx| &mut ecx.machine.sync.init_onces, - |_| Ok(Default::default()), + |_| interp_ok(Default::default()), )? - .ok_or_else(|| err_ub_format!("init_once has invalid ID").into()) + .ok_or_else(|| err_ub_format!("init_once has invalid ID")).into() } #[inline] @@ -101,7 +101,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(waiter, BlockReason::InitOnce(id))?; } - Ok(()) + interp_ok(()) } #[inline] @@ -126,7 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(waiter, BlockReason::InitOnce(id))?; } - Ok(()) + interp_ok(()) } /// Synchronize with the previous completion of an InitOnce. diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 7fe67e5c6727e..3b57af641b589 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -206,7 +206,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )? .to_scalar_pair(); - Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { + interp_ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { // We set the in-memory ID to `next_index`, now also create this object in the machine // state. let obj = create_obj(this)?; @@ -247,7 +247,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_index = get_objs(this).push(obj); this.write_scalar(Scalar::from_u32(new_index.to_u32()), &id_place)?; - Ok(new_index) + interp_ok(new_index) } fn condvar_reacquire_mutex( @@ -266,7 +266,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Don't forget to write the return value. this.write_scalar(retval, &dest)?; } - Ok(()) + interp_ok(()) } } @@ -307,7 +307,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.mutexes, |ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("mutex has invalid ID").into()) + .ok_or_else(|| err_ub_format!("mutex has invalid ID")).into() } /// Retrieve the additional data stored for a mutex. @@ -334,7 +334,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.rwlocks, |ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("rwlock has invalid ID").into()) + .ok_or_else(|| err_ub_format!("rwlock has invalid ID")).into() } /// Retrieve the additional data stored for a rwlock. @@ -375,7 +375,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.condvars, |ecx| initialize_data(ecx).map(|data| Condvar { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("condvar has invalid ID").into()) + .ok_or_else(|| err_ub_format!("condvar has invalid ID")).into() } /// Retrieve the additional data stored for a condvar. @@ -428,11 +428,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); let mutex = &mut this.machine.sync.mutexes[id]; - Ok(if let Some(current_owner) = mutex.owner { + interp_ok(if let Some(current_owner) = mutex.owner { // Mutex is locked. if current_owner != this.machine.threads.active_thread() { // Only the owner can unlock the mutex. - return Ok(None); + return interp_ok(None); } let old_lock_count = mutex.lock_count; mutex.lock_count = old_lock_count.strict_sub(1); @@ -484,7 +484,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(retval, &dest)?; } - Ok(()) + interp_ok(()) } ), ); @@ -546,7 +546,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("rwlock_reader_unlock: {:?} held one less time by {:?}", id, thread); } } - Entry::Vacant(_) => return Ok(false), // we did not even own this lock + Entry::Vacant(_) => return interp_ok(false), // we did not even own this lock } if let Some(data_race) = &this.machine.data_race { // Add this to the shared-release clock of all concurrent readers. @@ -565,7 +565,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(writer, BlockReason::RwLock(id))?; } } - Ok(true) + interp_ok(true) } /// Put the reader in the queue waiting for the lock and block it. @@ -593,7 +593,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { @unblock = |this| { this.rwlock_reader_lock(id); this.write_scalar(retval, &dest)?; - Ok(()) + interp_ok(()) } ), ); @@ -620,10 +620,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); let rwlock = &mut this.machine.sync.rwlocks[id]; - Ok(if let Some(current_writer) = rwlock.writer { + interp_ok(if let Some(current_writer) = rwlock.writer { if current_writer != thread { // Only the owner can unlock the rwlock. - return Ok(false); + return interp_ok(false); } rwlock.writer = None; trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, thread); @@ -676,7 +676,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { @unblock = |this| { this.rwlock_writer_lock(id); this.write_scalar(retval, &dest)?; - Ok(()) + interp_ok(()) } ), ); @@ -749,7 +749,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } ), ); - Ok(()) + interp_ok(()) } /// Wake up some thread (if there is any) sleeping on the conditional @@ -764,10 +764,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condvar.clock.clone_from(&*data_race.release_clock(&this.machine.threads)); } let Some(waiter) = condvar.waiters.pop_front() else { - return Ok(false); + return interp_ok(false); }; this.unblock_thread(waiter, BlockReason::Condvar(id))?; - Ok(true) + interp_ok(true) } /// Wait for the futex to be signaled, or a timeout. @@ -808,7 +808,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Write the return value. this.write_scalar(retval_succ, &dest)?; - Ok(()) + interp_ok(()) } @timeout = |this| { // Remove the waiter from the futex. @@ -818,7 +818,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Set errno and write return value. this.set_last_error(errno_timeout)?; this.write_scalar(retval_timeout, &dest)?; - Ok(()) + interp_ok(()) } ), ); @@ -828,7 +828,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn futex_wake(&mut self, addr: u64, bitset: u32) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); let Some(futex) = this.machine.sync.futexes.get_mut(&addr) else { - return Ok(false); + return interp_ok(false); }; let data_race = &this.machine.data_race; @@ -839,10 +839,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Wake up the first thread in the queue that matches any of the bits in the bitset. let Some(i) = futex.waiters.iter().position(|w| w.bitset & bitset != 0) else { - return Ok(false); + return interp_ok(false); }; let waiter = futex.waiters.remove(i).unwrap(); this.unblock_thread(waiter.thread, BlockReason::Futex { addr })?; - Ok(true) + interp_ok(true) } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index f6b453efcbeb7..dcae85109a5bf 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -622,7 +622,7 @@ impl<'tcx> ThreadManager<'tcx> { } self.threads[id].join_status = ThreadJoinStatus::Detached; - Ok(()) + interp_ok(()) } /// Mark that the active thread tries to join the thread with `joined_thread_id`. @@ -657,7 +657,7 @@ impl<'tcx> ThreadManager<'tcx> { if let Some(data_race) = &mut this.machine.data_race { data_race.thread_joined(&this.machine.threads, joined_thread_id); } - Ok(()) + interp_ok(()) } ), ); @@ -667,7 +667,7 @@ impl<'tcx> ThreadManager<'tcx> { data_race.thread_joined(self, joined_thread_id); } } - Ok(()) + interp_ok(()) } /// Mark that the active thread tries to exclusively join the thread with `joined_thread_id`. @@ -754,7 +754,7 @@ impl<'tcx> ThreadManager<'tcx> { // This thread and the program can keep going. if self.threads[self.active_thread].state.is_enabled() && !self.yield_active_thread { // The currently active thread is still enabled, just continue with it. - return Ok(SchedulingAction::ExecuteStep); + return interp_ok(SchedulingAction::ExecuteStep); } // The active thread yielded or got terminated. Let's see if there are any timeouts to take // care of. We do this *before* running any other thread, to ensure that timeouts "in the @@ -764,7 +764,7 @@ impl<'tcx> ThreadManager<'tcx> { // let potential_sleep_time = self.next_callback_wait_time(clock); if potential_sleep_time == Some(Duration::ZERO) { - return Ok(SchedulingAction::ExecuteTimeoutCallback); + return interp_ok(SchedulingAction::ExecuteTimeoutCallback); } // No callbacks immediately scheduled, pick a regular thread to execute. // The active thread blocked or yielded. So we go search for another enabled thread. @@ -793,7 +793,7 @@ impl<'tcx> ThreadManager<'tcx> { } self.yield_active_thread = false; if self.threads[self.active_thread].state.is_enabled() { - return Ok(SchedulingAction::ExecuteStep); + return interp_ok(SchedulingAction::ExecuteStep); } // We have not found a thread to execute. if self.threads.iter().all(|thread| thread.state.is_terminated()) { @@ -802,7 +802,7 @@ impl<'tcx> ThreadManager<'tcx> { // All threads are currently blocked, but we have unexecuted // timeout_callbacks, which may unblock some of the threads. Hence, // sleep until the first callback. - Ok(SchedulingAction::Sleep(sleep_time)) + interp_ok(SchedulingAction::Sleep(sleep_time)) } else { throw_machine_stop!(TerminationInfo::Deadlock); } @@ -848,7 +848,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // https://github.com/rust-lang/miri/issues/1763). In this case, // just do nothing, which effectively just returns to the // scheduler. - Ok(()) + interp_ok(()) } #[inline] @@ -861,7 +861,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { .expect("`on_stack_empty` not set up, or already running"); let res = callback(this)?; this.active_thread_mut().on_stack_empty = Some(callback); - Ok(res) + interp_ok(res) } } @@ -879,7 +879,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) { // We already have a thread-specific allocation id for this // thread-local static. - Ok(old_alloc) + interp_ok(old_alloc) } else { // We need to allocate a thread-specific allocation id for this // thread-local static. @@ -892,7 +892,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut alloc = alloc.inner().adjust_from_tcx( &this.tcx, |bytes, align| { - Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) + interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) }, |ptr| this.global_root_pointer(ptr), )?; @@ -901,7 +901,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Create a fresh allocation with this content. let ptr = this.insert_allocation(alloc, MiriMemoryKind::Tls.into())?; this.machine.threads.set_thread_local_alloc(def_id, ptr); - Ok(ptr) + interp_ok(ptr) } } @@ -964,7 +964,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Restore the old active thread frame. this.machine.threads.set_active_thread_id(old_thread_id); - Ok(new_thread_id) + interp_ok(new_thread_id) } /// Handles thread termination of the active thread: wakes up threads joining on this one, @@ -1022,7 +1022,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.unblock_thread(thread, unblock_reason)?; } - Ok(()) + interp_ok(()) } /// Block the current thread, with an optional timeout. @@ -1078,7 +1078,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let old_thread = this.machine.threads.set_active_thread_id(thread); callback.unblock(this)?; this.machine.threads.set_active_thread_id(old_thread); - Ok(()) + interp_ok(()) } #[inline] @@ -1095,7 +1095,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> { let this = self.eval_context_mut(); this.machine.threads.join_thread(joined_thread_id, this.machine.data_race.as_mut())?; - Ok(()) + interp_ok(()) } #[inline] @@ -1104,7 +1104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine .threads .join_thread_exclusive(joined_thread_id, this.machine.data_race.as_mut())?; - Ok(()) + interp_ok(()) } #[inline] diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 307695436c61e..800c301a82175 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -195,10 +195,10 @@ impl StoreBufferAlloc { AccessType::PerfectlyOverlapping(pos) => pos, // If there is nothing here yet, that means there wasn't an atomic write yet so // we can't return anything outdated. - _ => return Ok(None), + _ => return interp_ok(None), }; let store_buffer = Ref::map(self.store_buffers.borrow(), |buffer| &buffer[pos]); - Ok(Some(store_buffer)) + interp_ok(Some(store_buffer)) } /// Gets a mutable store buffer associated with an atomic object in this allocation, @@ -223,7 +223,7 @@ impl StoreBufferAlloc { pos_range.start } }; - Ok(&mut buffers[pos]) + interp_ok(&mut buffers[pos]) } } @@ -284,7 +284,7 @@ impl<'tcx> StoreBuffer { let (index, clocks) = global.active_thread_state(thread_mgr); let loaded = store_elem.load_impl(index, &clocks, is_seqcst); - Ok((loaded, recency)) + interp_ok((loaded, recency)) } fn buffered_write( @@ -297,7 +297,7 @@ impl<'tcx> StoreBuffer { let (index, clocks) = global.active_thread_state(thread_mgr); self.store_impl(val, index, &clocks.clock, is_seqcst); - Ok(()) + interp_ok(()) } #[allow(clippy::if_same_then_else, clippy::needless_bool)] @@ -470,7 +470,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst); buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?; } - Ok(()) + interp_ok(()) } fn buffered_atomic_read( @@ -508,14 +508,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }); } - return Ok(loaded); + return interp_ok(loaded); } } } // Race detector or weak memory disabled, simply read the latest value validate()?; - Ok(Some(latest_in_mo)) + interp_ok(Some(latest_in_mo)) } /// Add the given write to the store buffer. (Does not change machine memory.) @@ -546,7 +546,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Caller should've written to dest with the vanilla scalar write, we do nothing here - Ok(()) + interp_ok(()) } /// Caller should never need to consult the store buffer for the latest value. @@ -570,7 +570,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { alloc_buffers.get_store_buffer(alloc_range(base_offset, size))? else { // No store buffer, nothing to do. - return Ok(()); + return interp_ok(()); }; buffer.read_from_last_store( global, @@ -579,6 +579,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index ee087ece9d833..5b1bad28c07c7 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -223,10 +223,7 @@ pub fn report_error<'tcx>( let info = info.downcast_ref::().expect("invalid MachineStop payload"); use TerminationInfo::*; let title = match info { - &Exit { code, leak_check } => { - e.discard_interp_err(); - return Some((code, leak_check)); - } + &Exit { code, leak_check } => return Some((code, leak_check)), Abort(_) => Some("abnormal termination"), UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) => Some("unsupported operation"), @@ -378,7 +375,6 @@ pub fn report_error<'tcx>( InvalidProgramInfo::AlreadyReported(_) ) => { // This got already reported. No point in reporting it again. - e.discard_interp_err(); return None; } _ => diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 3739d46bd60f0..ece76e581f248 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -259,7 +259,7 @@ impl<'tcx> MainThreadState<'tcx> { throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true }); } } - Ok(Poll::Pending) + interp_ok(Poll::Pending) } } @@ -420,7 +420,7 @@ pub fn create_ecx<'tcx>( } } - Ok(ecx) + interp_ok(ecx) } /// Evaluates the entry function specified by `entry_id`. @@ -436,7 +436,7 @@ pub fn eval_entry<'tcx>( // Copy setting before we move `config`. let ignore_leaks = config.ignore_leaks; - let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config) { + let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config).report_err() { Ok(v) => v, Err(err) => { let (kind, backtrace) = err.into_parts(); @@ -453,7 +453,7 @@ pub fn eval_entry<'tcx>( panic::resume_unwind(panic_payload) }); // `Ok` can never happen. - let Err(res) = res; + let Err(err) = res.report_err(); // Machine cleanup. Only do this if all threads have terminated; threads that are still running // might cause Stacked Borrows errors (https://github.com/rust-lang/miri/issues/2396). @@ -466,7 +466,7 @@ pub fn eval_entry<'tcx>( } // Process the result. - let (return_code, leak_check) = report_error(&ecx, res)?; + let (return_code, leak_check) = report_error(&ecx, err)?; if leak_check && !ignore_leaks { // Check for thread leaks. if !ecx.have_all_terminated() { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 0cd6ba69aac9d..70ebca0f32961 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -228,7 +228,7 @@ pub fn iter_exported_symbols<'tcx>( } } } - Ok(()) + interp_ok(()) } /// Convert a softfloat type to its corresponding hostfloat type. @@ -431,7 +431,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let field = this.project_field(dest, idx)?; this.write_int(val, &field)?; } - Ok(()) + interp_ok(()) } /// Write the given fields of the given place. @@ -445,7 +445,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let field = this.project_field_named(dest, name)?; this.write_int(val, &field)?; } - Ok(()) + interp_ok(()) } /// Write a 0 of the appropriate size to `dest`. @@ -455,7 +455,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Test if this pointer equals 0. fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> { - Ok(ptr.addr().bytes() == 0) + interp_ok(ptr.addr().bytes() == 0) } /// Generate some random bytes, and write them to `dest`. @@ -466,7 +466,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // any additional checks - it's okay if the pointer is invalid, // since we wouldn't actually be writing to it. if len == 0 { - return Ok(()); + return interp_ok(()); } let this = self.eval_context_mut(); @@ -571,7 +571,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } cur_addr += unsafe_cell_size; // Done - Ok(()) + interp_ok(()) }; // Run a visitor { @@ -589,7 +589,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if unsafe_cell_size != Size::ZERO { unsafe_cell_action(&place.ptr(), unsafe_cell_size) } else { - Ok(()) + interp_ok(()) } }, }; @@ -599,7 +599,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // So pretend there is a 0-sized `UnsafeCell` at the end. unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?; // Done! - return Ok(()); + return interp_ok(()); /// Visiting the memory covered by a `MemPlace`, being aware of /// whether we are inside an `UnsafeCell` or not. @@ -642,7 +642,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { (self.unsafe_cell_action)(v) } else if self.ecx.type_is_freeze(v.layout.ty) { // This is `Freeze`, there cannot be an `UnsafeCell` - Ok(()) + interp_ok(()) } else if matches!(v.layout.fields, FieldsShape::Union(..)) { // A (non-frozen) union. We fall back to whatever the type says. (self.unsafe_cell_action)(v) @@ -691,7 +691,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !self.eval_context_ref().machine.communicate() { self.reject_in_isolation(name, RejectOpWith::Abort)?; } - Ok(()) + interp_ok(()) } /// Helper function used inside the shims of foreign functions which reject the op @@ -713,13 +713,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .dcx() .warn(format!("{op_name} was made to return an error due to isolation")); } - Ok(()) + interp_ok(()) } RejectOpWith::Warning => { this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string())); - Ok(()) + interp_ok(()) } - RejectOpWith::NoWarning => Ok(()), // no warning + RejectOpWith::NoWarning => interp_ok(()), // no warning } } @@ -750,14 +750,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { - Ok(errno_place.clone()) + interp_ok(errno_place.clone()) } else { // Allocate new place, set initial value to 0. let errno_layout = this.machine.layouts.u32; let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?; this.write_scalar(Scalar::from_u32(0), &errno_place)?; this.active_thread_mut().last_error = Some(errno_place.clone()); - Ok(errno_place) + interp_ok(errno_place) } } @@ -783,14 +783,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if target.families.iter().any(|f| f == "unix") { for &(name, kind) in UNIX_IO_ERROR_TABLE { if err.kind() == kind { - return Ok(this.eval_libc(name)); + return interp_ok(this.eval_libc(name)); } } throw_unsup_format!("unsupported io error: {err}") } else if target.families.iter().any(|f| f == "windows") { for &(name, kind) in WINDOWS_IO_ERROR_TABLE { if err.kind() == kind { - return Ok(this.eval_windows("c", name)); + return interp_ok(this.eval_windows("c", name)); } } throw_unsup_format!("unsupported io error: {err}"); @@ -814,18 +814,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let errnum = errnum.to_i32()?; for &(name, kind) in UNIX_IO_ERROR_TABLE { if errnum == this.eval_libc_i32(name) { - return Ok(Some(kind)); + return interp_ok(Some(kind)); } } - return Ok(None); + return interp_ok(None); } else if target.families.iter().any(|f| f == "windows") { let errnum = errnum.to_u32()?; for &(name, kind) in WINDOWS_IO_ERROR_TABLE { if errnum == this.eval_windows("c", name).to_u32()? { - return Ok(Some(kind)); + return interp_ok(Some(kind)); } } - return Ok(None); + return interp_ok(None); } else { throw_unsup_format!( "converting errnum into io::Error is unsupported for OS {}", @@ -839,8 +839,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.set_last_error(self.io_error_to_errnum(err)?) } - /// Helper function that consumes an `std::io::Result` and returns an - /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns + /// Helper function that consumes a `std::io::Result` and returns a + /// `InterpResult<'tcx, T>` instead. In case the result is an error, this function returns /// `Ok(-1)` and sets the last OS error accordingly. /// /// This function uses `T: From` instead of `i32` directly because some IO related @@ -850,10 +850,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { result: std::io::Result, ) -> InterpResult<'tcx, T> { match result { - Ok(ok) => Ok(ok), + Ok(ok) => interp_ok(ok), Err(e) => { self.eval_context_mut().set_last_error_from_io_error(e)?; - Ok((-1).into()) + interp_ok((-1).into()) } } } @@ -866,7 +866,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_ref(); let ptr = this.read_pointer(op)?; - Ok(this.ptr_to_mplace(ptr, layout)) + interp_ok(this.ptr_to_mplace(ptr, layout)) } /// Calculates the MPlaceTy given the offset and layout of an access on an operand @@ -884,7 +884,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Ensure that the access is within bounds. assert!(base_layout.size >= offset + value_layout.size); let value_place = op_place.offset(offset, value_layout, this)?; - Ok(value_place) + interp_ok(value_place) } fn deref_pointer_and_read( @@ -924,7 +924,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?; let nanoseconds = nanoseconds_scalar.to_target_isize(this)?; - Ok(try { + interp_ok(try { // tv_sec must be non-negative. let seconds: u64 = seconds.try_into().ok()?; // tv_nsec must be non-negative. @@ -947,7 +947,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ptr = ptr.to_pointer(this)?; let len = len.to_target_usize(this)?; let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; - Ok(bytes) + interp_ok(bytes) } /// Read a sequence of bytes until the first null terminator. @@ -992,11 +992,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let string_length = u64::try_from(c_str.len()).unwrap(); let string_length = string_length.strict_add(1); if size < string_length { - return Ok((false, string_length)); + return interp_ok((false, string_length)); } self.eval_context_mut() .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?; - Ok((true, string_length)) + interp_ok((true, string_length)) } /// Helper function to read a sequence of unsigned integers of the given size and alignment @@ -1031,7 +1031,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(wchars) + interp_ok(wchars) } /// Read a sequence of u16 until the first null terminator. @@ -1056,7 +1056,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let string_length = u64::try_from(wide_str.len()).unwrap(); let string_length = string_length.strict_add(1); if size < string_length { - return Ok((false, string_length)); + return interp_ok((false, string_length)); } // Store the UTF-16 string. @@ -1068,7 +1068,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let offset = u64::try_from(offset).unwrap(); alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?; } - Ok((true, string_length)) + interp_ok((true, string_length)) } /// Read a sequence of wchar_t until the first null terminator. @@ -1093,7 +1093,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { abi.name() ) } - Ok(()) + interp_ok(()) } fn frame_in_std(&self) -> bool { @@ -1128,7 +1128,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // message is slightly different here to make automated analysis easier let error_msg = format!("unsupported Miri functionality: {error_msg}"); this.start_panic(error_msg.as_ref(), mir::UnwindAction::Continue)?; - Ok(()) + interp_ok(()) } else { throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(error_msg)); } @@ -1148,7 +1148,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // compiler-builtins when running other crates, but Miri can still be run on // compiler-builtins itself (or any crate that uses it as a normal dependency) if self.eval_context_ref().tcx.is_compiler_builtins(instance.def_id().krate) { - return Ok(()); + return interp_ok(()); } throw_machine_stop!(TerminationInfo::SymbolShimClashing { @@ -1156,7 +1156,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { span: body.span.data(), }) } - Ok(()) + interp_ok(()) } fn check_shim<'a, const N: usize>( @@ -1242,11 +1242,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) { // Floating point value is NaN (flagged with INVALID_OP) or outside the range // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). - Ok(None) + interp_ok(None) } else { // Floating point value can be represented by the integer type after rounding. // The INEXACT flag is ignored on purpose to allow rounding. - Ok(Some(ImmTy::from_scalar(val, cast_to))) + interp_ok(Some(ImmTy::from_scalar(val, cast_to))) } } @@ -1283,7 +1283,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}" ); } - Ok(()) + interp_ok(()) } /// Lookup an array of immediates stored as a linker section of name `name`. @@ -1296,7 +1296,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { iter_exported_symbols(tcx, |_cnum, def_id| { let attrs = tcx.codegen_fn_attrs(def_id); let Some(link_section) = attrs.link_section else { - return Ok(()); + return interp_ok(()); }; if link_section.as_str() == name { let instance = ty::Instance::mono(tcx, def_id); @@ -1308,10 +1308,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = this.read_immediate(&const_val)?; array.push(val); } - Ok(()) + interp_ok(()) })?; - Ok(array) + interp_ok(array) } } @@ -1361,7 +1361,7 @@ where &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, { if let Ok(ops) = args.try_into() { - return Ok(ops); + return interp_ok(ops); } throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) } @@ -1401,7 +1401,7 @@ pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar { pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> { let val = elem.to_scalar().to_int(elem.layout.size)?; - Ok(match val { + interp_ok(match val { 0 => false, -1 => true, _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index b6fd9ba1e825c..8507b0f49ded8 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -115,9 +115,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } @@ -138,7 +138,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let val = this.read_scalar_atomic(&place, atomic)?; // Perform regular store. this.write_scalar(val, dest)?; - Ok(()) + interp_ok(()) } fn atomic_store(&mut self, args: &[OpTy<'tcx>], atomic: AtomicWriteOrd) -> InterpResult<'tcx> { @@ -151,7 +151,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let val = this.read_scalar(val)?; // Perform atomic store this.write_scalar_atomic(val, &place, atomic)?; - Ok(()) + interp_ok(()) } fn compiler_fence_intrinsic( @@ -162,7 +162,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let [] = check_arg_count(args)?; let _ = atomic; //FIXME: compiler fences are currently ignored - Ok(()) + interp_ok(()) } fn atomic_fence_intrinsic( @@ -173,7 +173,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let [] = check_arg_count(args)?; this.atomic_fence(atomic)?; - Ok(()) + interp_ok(()) } fn atomic_rmw_op( @@ -203,17 +203,17 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { AtomicOp::Min => { let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?; this.write_immediate(*old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } AtomicOp::Max => { let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?; this.write_immediate(*old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } AtomicOp::MirOp(op, not) => { let old = this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?; this.write_immediate(*old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } } } @@ -232,7 +232,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let old = this.atomic_exchange_scalar(&place, new, atomic)?; this.write_scalar(old, dest)?; // old value is returned - Ok(()) + interp_ok(()) } fn atomic_compare_exchange_impl( @@ -261,7 +261,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // Return old value. this.write_immediate(old, dest)?; - Ok(()) + interp_ok(()) } fn atomic_compare_exchange( diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 23ce2f8ed1f91..665dd7c441a55 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -29,7 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // See if the core engine can handle this intrinsic. if this.eval_intrinsic(instance, args, dest, ret)? { - return Ok(None); + return interp_ok(None); } let intrinsic_name = this.tcx.item_name(instance.def_id()); let intrinsic_name = intrinsic_name.as_str(); @@ -51,7 +51,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that" ); } - Ok(Some(ty::Instance { + interp_ok(Some(ty::Instance { def: ty::InstanceKind::Item(instance.def_id()), args: instance.args, })) @@ -59,14 +59,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; - Ok(None) + interp_ok(None) } EmulateItemResult::NeedsUnwind => { // Jump to the unwind block to begin unwinding. this.unwind_to_block(unwind)?; - Ok(None) + interp_ok(None) } - EmulateItemResult::AlreadyJumped => Ok(None), + EmulateItemResult::AlreadyJumped => interp_ok(None), } } @@ -99,7 +99,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "catch_unwind" => { this.handle_catch_unwind(args, dest, ret)?; // This pushed a stack frame, don't jump to `ret`. - return Ok(EmulateItemResult::AlreadyJumped); + return interp_ok(EmulateItemResult::AlreadyJumped); } // Raw memory accesses @@ -378,7 +378,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ty::Float(fty) = x.layout.ty.kind() else { bug!("float_finite: non-float input type {}", x.layout.ty) }; - Ok(match fty { + interp_ok(match fty { FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(), FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), @@ -429,9 +429,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap"))) } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 7f43c61cd66d8..de293495e8618 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -248,7 +248,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = this.binary_op(mir_op, &left, &right).map_err(|err| { match err.kind() { &InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { - err.discard_interp_err(); // This resets the interpreter backtrace, but it's not worth avoiding that. let shift_amount = match shift_amount { Either::Left(v) => v.to_string(), @@ -787,9 +786,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } fn fminmax_op( @@ -805,7 +804,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let left = left.to_scalar(); let right = right.to_scalar(); - Ok(match float_ty { + interp_ok(match float_ty { FloatTy::F16 => unimplemented!("f16_f128"), FloatTy::F32 => { let left = left.to_f32()?; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index fb5ccbcd4fe22..b9cebcfe9cd82 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -730,7 +730,7 @@ impl<'tcx> MiriMachine<'tcx> { EnvVars::init(this, config)?; MiriMachine::init_extern_statics(this)?; ThreadManager::init(this, on_main_stack_empty); - Ok(()) + interp_ok(()) } pub(crate) fn add_extern_static(this: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) { @@ -992,7 +992,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { throw_ub_format!("{msg}"); } } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1019,7 +1019,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } // Otherwise, load the MIR. - Ok(Some((ecx.load_mir(instance.def, None)?, instance))) + interp_ok(Some((ecx.load_mir(instance.def, None)?, instance))) } #[inline(always)] @@ -1072,7 +1072,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ret: None, unwind: mir::UnwindAction::Unreachable, })?; - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1097,7 +1097,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> { - Ok(ecx.tcx.sess.ub_checks()) + interp_ok(ecx.tcx.sess.ub_checks()) } fn thread_local_static_pointer( @@ -1136,7 +1136,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { shim_align = shim_align.bytes(), ) } - Ok(ptr) + interp_ok(ptr) } else { throw_unsup_format!("extern static `{link_name}` is not supported by Miri",) } @@ -1186,7 +1186,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { .insert(id, (ecx.machine.current_span(), None)); } - Ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace }) + interp_ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace }) } fn adjust_alloc_root_pointer( @@ -1233,7 +1233,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { Provenance::Wildcard => { // No need to do anything for wildcard pointers as // their provenances have already been previously exposed. - Ok(()) + interp_ok(()) } } } @@ -1286,7 +1286,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { |ptr| ecx.global_root_pointer(ptr), )?; let extra = Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?; - Ok(Cow::Owned(alloc.with_extra(extra))) + interp_ok(Cow::Owned(alloc.with_extra(extra))) } #[inline(always)] @@ -1310,7 +1310,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(weak_memory) = &alloc_extra.weak_memory { weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1334,7 +1334,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(weak_memory) = &alloc_extra.weak_memory { weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); } - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1367,7 +1367,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { *deallocated_at = Some(machine.current_span()); } machine.free_alloc_id(alloc_id, size, align, kind); - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1379,7 +1379,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if ecx.machine.borrow_tracker.is_some() { ecx.retag_ptr_value(kind, val) } else { - Ok(val.clone()) + interp_ok(val.clone()) } } @@ -1392,7 +1392,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if ecx.machine.borrow_tracker.is_some() { ecx.retag_place_contents(kind, place)?; } - Ok(()) + interp_ok(()) } fn protect_in_place_function_argument( @@ -1413,7 +1413,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Conveniently this also ensures that the place actually points to suitable memory. ecx.write_uninit(&protected_place)?; // Now we throw away the protected place, ensuring its tag is never used again. - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1447,7 +1447,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { data_race: ecx.machine.data_race.as_ref().map(|_| data_race::FrameState::default()), }; - Ok(frame.with_extra(extra)) + interp_ok(frame.with_extra(extra)) } fn stack<'a>( @@ -1489,7 +1489,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Make sure some time passes. ecx.machine.clock.tick(); - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1500,7 +1500,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { let stack_len = ecx.active_thread_stack().len(); ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1); } - Ok(()) + interp_ok(()) } fn before_stack_pop( @@ -1516,7 +1516,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // concurrency and what it prints is just plain wrong. So we print our own information // instead. (Cc https://github.com/rust-lang/miri/issues/2266) info!("Leaving {}", ecx.frame().instance()); - Ok(()) + interp_ok(()) } #[inline(always)] @@ -1554,7 +1554,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(data_race) = &ecx.frame().extra.data_race { data_race.local_read(local, &ecx.machine); } - Ok(()) + interp_ok(()) } fn after_local_write( @@ -1565,7 +1565,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if let Some(data_race) = &ecx.frame().extra.data_race { data_race.local_write(local, storage_live, &ecx.machine); } - Ok(()) + interp_ok(()) } fn after_local_moved_to_memory( @@ -1587,7 +1587,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { { data_race.local_moved_to_memory(local, alloc_info.data_race.as_mut().unwrap(), machine); } - Ok(()) + interp_ok(()) } fn eval_mir_constant( @@ -1611,9 +1611,9 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { Entry::Vacant(ve) => { let op = eval(ecx, val, span, layout)?; ve.insert(op.clone()); - Ok(op) + interp_ok(op) } - Entry::Occupied(oe) => Ok(oe.get().clone()), + Entry::Occupied(oe) => interp_ok(oe.get().clone()), } } diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index 6b8c0fe87adb3..c0911fa717f50 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -21,7 +21,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); trace!("ptr_op: {:?} {:?} {:?}", *left, bin_op, *right); - Ok(match bin_op { + interp_ok(match bin_op { Eq | Ne | Lt | Le | Gt | Ge => { assert_eq!(left.layout.abi, right.layout.abi); // types can differ, e.g. fn ptrs with different `for` let size = this.pointer_size(); diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 057d7ef5e67b9..e73344367ec3f 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -61,7 +61,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(allocator_kind) = this.tcx.allocator_kind(()) else { // in real code, this symbol does not exist without an allocator - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); }; match allocator_kind { @@ -71,11 +71,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and not execute any Miri shim. Somewhat unintuitively doing so is done // by returning `NotSupported`, which triggers the `lookup_exported_symbol` // fallback case in `emulate_foreign_item`. - Ok(EmulateItemResult::NotSupported) + interp_ok(EmulateItemResult::NotSupported) } AllocatorKind::Default => { default(this)?; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } } @@ -92,7 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) .unwrap(); } - Ok(ptr.into()) + interp_ok(ptr.into()) } fn posix_memalign( @@ -109,7 +109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Align must be power of 2, and also at least ptr-sized (POSIX rules). // But failure to adhere to this is not UB, it's an error condition. if !align.is_power_of_two() || align < this.pointer_size().bytes() { - Ok(this.eval_libc("EINVAL")) + interp_ok(this.eval_libc("EINVAL")) } else { let ptr = this.allocate_ptr( Size::from_bytes(size), @@ -117,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MiriMemoryKind::C.into(), )?; this.write_pointer(ptr, &memptr)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } @@ -126,7 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !this.ptr_is_null(ptr)? { this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?; } - Ok(()) + interp_ok(()) } fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> { @@ -148,7 +148,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { new_align, MiriMemoryKind::C.into(), )?; - Ok(new_ptr.into()) + interp_ok(new_ptr.into()) } } } @@ -188,9 +188,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), )?; - Ok(ptr.into()) + interp_ok(ptr.into()) } - _ => Ok(Pointer::null()), + _ => interp_ok(Pointer::null()), } } } diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 1d20189b6fd8f..25afda4edc853 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -107,7 +107,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags), }; - Ok(()) + interp_ok(()) } fn resolve_frame_pointer( @@ -135,7 +135,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let name = fn_instance.to_string(); let filename = lo.file.name.prefer_remapped_unconditionaly().to_string(); - Ok((fn_instance, lo, name, filename)) + interp_ok((fn_instance, lo, name, filename)) } fn handle_miri_resolve_frame( @@ -213,7 +213,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(fn_ptr, &this.project_field(dest, 4)?)?; } - Ok(()) + interp_ok(()) } fn handle_miri_resolve_frame_names( @@ -237,6 +237,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_bytes_ptr(this.read_pointer(name_ptr)?, name.bytes())?; this.write_bytes_ptr(this.read_pointer(filename_ptr)?, filename.bytes())?; - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 557d648682c7c..e99a8fd6e8c09 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -56,15 +56,15 @@ impl<'tcx> EnvVars<'tcx> { }; ecx.machine.env_vars = env_vars; - Ok(()) + interp_ok(()) } pub(crate) fn cleanup(ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx> { let this = ecx.eval_context_mut(); match this.machine.env_vars { EnvVars::Unix(_) => UnixEnvVars::cleanup(this), - EnvVars::Windows(_) => Ok(()), // no cleanup needed - EnvVars::Uninit => Ok(()), + EnvVars::Windows(_) => interp_ok(()), // no cleanup needed + EnvVars::Uninit => interp_ok(()), } } @@ -104,7 +104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn get_env_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); match &this.machine.env_vars { - EnvVars::Uninit => Ok(None), + EnvVars::Uninit => interp_ok(None), EnvVars::Unix(vars) => vars.get(this, name), EnvVars::Windows(vars) => vars.get(name), } diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs index 788de8162cb70..5559ea2750b3b 100644 --- a/src/tools/miri/src/shims/extern_static.rs +++ b/src/tools/miri/src/shims/extern_static.rs @@ -11,7 +11,7 @@ impl<'tcx> MiriMachine<'tcx> { let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?; this.write_immediate(*val, &place)?; Self::add_extern_static(this, name, place.ptr()); - Ok(()) + interp_ok(()) } /// Zero-initialized pointer-sized extern statics are pretty common. @@ -26,7 +26,7 @@ impl<'tcx> MiriMachine<'tcx> { let val = ImmTy::from_int(0, this.machine.layouts.usize); Self::alloc_extern_static(this, name, val)?; } - Ok(()) + interp_ok(()) } /// Extern statics that are initialized with function pointers to the symbols of the same name. @@ -41,7 +41,7 @@ impl<'tcx> MiriMachine<'tcx> { let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout); Self::alloc_extern_static(this, name, val)?; } - Ok(()) + interp_ok(()) } /// Sets up the "extern statics" for this machine. @@ -87,6 +87,6 @@ impl<'tcx> MiriMachine<'tcx> { } _ => {} // No "extern statics" supported on this target } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 6a835098e6048..78b07f68b44f6 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -62,7 +62,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let handler = this .lookup_exported_symbol(Symbol::intern(name))? .expect("missing alloc error handler symbol"); - return Ok(Some(handler)); + return interp_ok(Some(handler)); } _ => {} } @@ -80,18 +80,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { EmulateItemResult::AlreadyJumped => (), EmulateItemResult::NotSupported => { if let Some(body) = this.lookup_exported_symbol(link_name)? { - return Ok(Some(body)); + return interp_ok(Some(body)); } this.handle_unsupported_foreign_item(format!( "can't call foreign function `{link_name}` on OS `{os}`", os = this.tcx.sess.target.os, ))?; - return Ok(None); + return interp_ok(None); } } - Ok(None) + interp_ok(None) } fn is_dyn_sym(&self, name: &str) -> bool { @@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx> { let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?; assert!(res.is_none(), "DynSyms that delegate are not supported"); - Ok(()) + interp_ok(()) } /// Lookup the body of a function that has `link_name` as the symbol name. @@ -143,7 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { tcx.item_name(def_id) } else { // Skip over items without an explicitly defined symbol name. - return Ok(()); + return interp_ok(()); }; if symbol_name == link_name { if let Some((original_instance, original_cnum)) = instance_and_crate { @@ -175,15 +175,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum)); } - Ok(()) + interp_ok(()) })?; e.insert(instance_and_crate.map(|ic| ic.0)) } }; match instance { - None => Ok(None), // no symbol with this name - Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))), + None => interp_ok(None), // no symbol with this name + Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))), } } } @@ -214,7 +214,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - Ok(()) + interp_ok(()) } fn emulate_foreign_item_inner( @@ -234,7 +234,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. if this.call_native_fn(link_name, dest, args)? { - return Ok(EmulateItemResult::NeedsReturn); + return interp_ok(EmulateItemResult::NeedsReturn); } } @@ -268,7 +268,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // // // ... // - // Ok(Scalar::from_u32(42)) + // interp_ok(Scalar::from_u32(42)) // } // ``` // You might find existing shims not following this pattern, most @@ -281,7 +281,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_start_unwind" => { let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?; this.handle_miri_start_unwind(payload)?; - return Ok(EmulateItemResult::NeedsUnwind); + return interp_ok(EmulateItemResult::NeedsUnwind); } "miri_run_provenance_gc" => { let [] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -294,6 +294,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { err_machine_stop!(TerminationInfo::Abort(format!( "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}" ))) + .into() })?; this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; } @@ -524,7 +525,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "__rust_alloc" => return this.emulate_allocator(default), "miri_alloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsReturn); + return interp_ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -584,7 +585,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } "miri_dealloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsReturn); + return interp_ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -1000,11 +1001,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner( this, link_name, abi, args, dest, ), - _ => Ok(EmulateItemResult::NotSupported), + _ => interp_ok(EmulateItemResult::NotSupported), }, }; // We only fall through to here if we did *not* hit the `_` arm above, // i.e., if we actually emulated the function with one of the shims. - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index 2b5acee244cba..3f282017bb79d 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -73,11 +73,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // have the output_type `Tuple([])`. ty::Tuple(t_list) if t_list.len() == 0 => { unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; - return Ok(ImmTy::uninit(dest.layout)); + return interp_ok(ImmTy::uninit(dest.layout)); } _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name), }; - Ok(ImmTy::from_scalar(scalar, dest.layout)) + interp_ok(ImmTy::from_scalar(scalar, dest.layout)) } /// Get the pointer to the function of the specified name in the shared object file, @@ -142,7 +142,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(ptr) => ptr, None => { // Shared object file does not export this function -- try the shims next. - return Ok(false); + return interp_ok(false); } }; @@ -164,7 +164,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Call the function and store output, depending on return type in the function signature. let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; this.write_immediate(*ret, dest)?; - Ok(true) + interp_ok(true) } } @@ -221,7 +221,7 @@ impl<'a> CArg { /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> { - Ok(match v.layout.ty.kind() { + interp_ok(match v.layout.ty.kind() { // If the primitive provided can be converted to a type matching the type pattern // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. // the ints diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 17c451059208f..7080edb26a5ff 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -19,14 +19,14 @@ pub enum PathConversion { #[cfg(unix)] pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> { - Ok(OsStr::from_bytes(bytes)) + interp_ok(OsStr::from_bytes(bytes)) } #[cfg(not(unix))] pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> { // We cannot use `from_encoded_bytes_unchecked` here since we can't trust `bytes`. let s = std::str::from_utf8(bytes) .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?; - Ok(OsStr::new(s)) + interp_ok(OsStr::new(s)) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -50,13 +50,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { { #[cfg(windows)] pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec) -> InterpResult<'tcx, OsString> { - Ok(OsString::from_wide(&u16_vec[..])) + interp_ok(OsString::from_wide(&u16_vec[..])) } #[cfg(not(windows))] pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec) -> InterpResult<'tcx, OsString> { let s = String::from_utf16(&u16_vec[..]) .map_err(|_| err_unsup_format!("{:?} is not a valid utf-16 string", u16_vec))?; - Ok(s.into()) + interp_ok(s.into()) } let u16_vec = self.eval_context_ref().read_wide_str(ptr)?; @@ -87,7 +87,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, (bool, u64)> { #[cfg(windows)] fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec> { - Ok(os_str.encode_wide().collect()) + interp_ok(os_str.encode_wide().collect()) } #[cfg(not(windows))] fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec> { @@ -97,7 +97,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { os_str .to_str() .map(|s| s.encode_utf16().collect()) - .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into()) + .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str)) + .into() } let u16_vec = os_str_to_u16vec(os_str)?; @@ -109,7 +110,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.eval_context_mut().write_wide_str(truncated_data, ptr, size)?; assert!(written && written_len == size); } - Ok((written, size_needed)) + interp_ok((written, size_needed)) } /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the @@ -148,7 +149,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr(), size).unwrap(); assert!(written); - Ok(arg_place.ptr()) + interp_ok(arg_place.ptr()) } /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`. @@ -164,7 +165,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr(), size).unwrap(); assert!(written); - Ok(arg_place.ptr()) + interp_ok(arg_place.ptr()) } /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed. @@ -175,7 +176,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let os_str = this.read_os_str_from_c_str(ptr)?; - Ok(match this.convert_path(Cow::Borrowed(os_str), PathConversion::TargetToHost) { + interp_ok(match this.convert_path(Cow::Borrowed(os_str), PathConversion::TargetToHost) { Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)), Cow::Owned(y) => Cow::Owned(PathBuf::from(y)), }) @@ -186,7 +187,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let os_str = this.read_os_str_from_wide_str(ptr)?; - Ok(this.convert_path(Cow::Owned(os_str), PathConversion::TargetToHost).into_owned().into()) + interp_ok( + this.convert_path(Cow::Owned(os_str), PathConversion::TargetToHost).into_owned().into(), + ) } /// Write a Path to the machine memory (as a null-terminated sequence of bytes), diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 55b814a09fa01..9bb0d7d0ce6f6 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -54,7 +54,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread = this.active_thread_mut(); thread.panic_payloads.push(payload); - Ok(()) + interp_ok(()) } /// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`. @@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(CatchUnwindData { catch_fn, data, dest: dest.clone(), ret }); } - Ok(()) + interp_ok(()) } fn handle_stack_pop_unwind( @@ -150,9 +150,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // We pushed a new stack frame, the engine should not do any jumping now! - Ok(ReturnAction::NoJump) + interp_ok(ReturnAction::NoJump) } else { - Ok(ReturnAction::Normal) + interp_ok(ReturnAction::Normal) } } @@ -254,6 +254,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; } } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 694c52b54657a..21c5421f10941 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -11,7 +11,7 @@ use crate::*; /// Returns the time elapsed between the provided time and the unix epoch as a `Duration`. pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> { time.duration_since(SystemTime::UNIX_EPOCH) - .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported").into()) + .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported")).into() } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -82,7 +82,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); }; let tv_sec = duration.as_secs(); @@ -90,7 +90,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &tp)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn gettimeofday( @@ -110,7 +110,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !this.ptr_is_null(tz)? { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let duration = system_time_to_duration(&SystemTime::now())?; @@ -119,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &tv)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } // The localtime() function shall convert the time in seconds since the Epoch pointed to by @@ -206,7 +206,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?; this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?; } - Ok(result.ptr()) + interp_ok(result.ptr()) } #[allow(non_snake_case, clippy::arithmetic_side_effects)] fn GetSystemTimeAsFileTime( @@ -236,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap(); this.write_int_fields(&[dwLowDateTime.into(), dwHighDateTime.into()], &filetime)?; - Ok(()) + interp_ok(()) } #[allow(non_snake_case)] @@ -255,7 +255,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported") })?; this.write_scalar(Scalar::from_i64(qpc), &this.deref_pointer(lpPerformanceCount_op)?)?; - Ok(Scalar::from_i32(-1)) // return non-zero on success + interp_ok(Scalar::from_i32(-1)) // return non-zero on success } #[allow(non_snake_case)] @@ -276,7 +276,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Scalar::from_i64(1_000_000_000), &this.deref_pointer_as(lpFrequency_op, this.machine.layouts.u64)?, )?; - Ok(Scalar::from_i32(-1)) // Return non-zero on success + interp_ok(Scalar::from_i32(-1)) // Return non-zero on success } fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> { @@ -290,7 +290,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = u64::try_from(duration.as_nanos()).map_err(|_| { err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported") })?; - Ok(Scalar::from_u64(res)) + interp_ok(Scalar::from_u64(res)) } fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -305,7 +305,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (numer, denom) = (1, 1); this.write_int_fields(&[numer.into(), denom.into()], &info)?; - Ok(Scalar::from_i32(0)) // KERN_SUCCESS + interp_ok(Scalar::from_i32(0)) // KERN_SUCCESS } fn nanosleep( @@ -324,7 +324,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } }; @@ -334,10 +334,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { callback!( @capture<'tcx> {} @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } - @timeout = |_this| { Ok(()) } + @timeout = |_this| { interp_ok(()) } ), ); - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } #[allow(non_snake_case)] @@ -356,9 +356,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { callback!( @capture<'tcx> {} @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } - @timeout = |_this| { Ok(()) } + @timeout = |_this| { interp_ok(()) } ), ); - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index b3ea7098dfe81..94b2d1bc782aa 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -68,14 +68,14 @@ impl<'tcx> TlsData<'tcx> { if max_size.bits() < 128 && new_key >= (1u128 << max_size.bits()) { throw_unsup_format!("we ran out of TLS key space"); } - Ok(new_key) + interp_ok(new_key) } pub fn delete_tls_key(&mut self, key: TlsKey) -> InterpResult<'tcx> { match self.keys.remove(&key) { Some(_) => { trace!("TLS key {} removed", key); - Ok(()) + interp_ok(()) } None => throw_ub_format!("removing a nonexistent TLS key: {}", key), } @@ -91,7 +91,7 @@ impl<'tcx> TlsData<'tcx> { Some(TlsEntry { data, .. }) => { let value = data.get(&thread_id).copied(); trace!("TLS key {} for thread {:?} loaded: {:?}", key, thread_id, value); - Ok(value.unwrap_or_else(|| Scalar::null_ptr(cx))) + interp_ok(value.unwrap_or_else(|| Scalar::null_ptr(cx))) } None => throw_ub_format!("loading from a non-existing TLS key: {}", key), } @@ -113,7 +113,7 @@ impl<'tcx> TlsData<'tcx> { trace!("TLS key {} for thread {:?} removed", key, thread_id); data.remove(&thread_id); } - Ok(()) + interp_ok(()) } None => throw_ub_format!("storing to a non-existing TLS key: {}", key), } @@ -128,7 +128,7 @@ impl<'tcx> TlsData<'tcx> { data: Scalar, ) -> InterpResult<'tcx> { self.macos_thread_dtors.entry(thread).or_default().push((dtor, data)); - Ok(()) + interp_ok(()) } /// Returns a dtor, its argument and its index, if one is supposed to run. @@ -261,7 +261,7 @@ impl<'tcx> TlsDtorsState<'tcx> { } MacOsDtors => { match this.schedule_macos_tls_dtor()? { - Poll::Pending => return Ok(Poll::Pending), + Poll::Pending => return interp_ok(Poll::Pending), // After all macOS destructors are run, the system switches // to destroying the pthread destructors. Poll::Ready(()) => break 'new_state PthreadDtors(Default::default()), @@ -269,14 +269,14 @@ impl<'tcx> TlsDtorsState<'tcx> { } PthreadDtors(state) => { match this.schedule_next_pthread_tls_dtor(state)? { - Poll::Pending => return Ok(Poll::Pending), // just keep going + Poll::Pending => return interp_ok(Poll::Pending), // just keep going Poll::Ready(()) => break 'new_state Done, } } WindowsDtors(dtors) => { if let Some(dtor) = dtors.pop() { this.schedule_windows_tls_dtor(dtor)?; - return Ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) + return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) } else { // No more destructors to run. break 'new_state Done; @@ -284,13 +284,13 @@ impl<'tcx> TlsDtorsState<'tcx> { } Done => { this.machine.tls.delete_all_thread_tls(this.active_thread()); - return Ok(Poll::Ready(())); + return interp_ok(Poll::Ready(())); } } }; self.0 = new_state; - Ok(Poll::Pending) + interp_ok(Poll::Pending) } } @@ -303,7 +303,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Windows has a special magic linker section that is run on certain events. // We don't support most of that, but just enough to make thread-local dtors in `std` work. - Ok(this.lookup_link_section(".CRT$XLB")?) + interp_ok(this.lookup_link_section(".CRT$XLB")?) } fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> { @@ -328,7 +328,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None, StackPopCleanup::Root { cleanup: true }, )?; - Ok(()) + interp_ok(()) } /// Schedule the macOS thread local storage destructors to be executed. @@ -350,10 +350,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { StackPopCleanup::Root { cleanup: true }, )?; - return Ok(Poll::Pending); + return interp_ok(Poll::Pending); } - Ok(Poll::Ready(())) + interp_ok(Poll::Ready(())) } /// Schedule a pthread TLS destructor. Returns `true` if found @@ -387,9 +387,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { StackPopCleanup::Root { cleanup: true }, )?; - return Ok(Poll::Pending); + return interp_ok(Poll::Pending); } - Ok(Poll::Ready(())) + interp_ok(Poll::Ready(())) } } diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index 42552a51edae3..583a1f6500963 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -25,8 +25,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 184a6c238b392..75721814c9948 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -47,7 +47,7 @@ impl<'tcx> UnixEnvVars<'tcx> { let environ_block = alloc_environ_block(ecx, env_vars_machine.values().copied().collect())?; ecx.write_pointer(environ_block, &environ)?; - Ok(UnixEnvVars { map: env_vars_machine, environ }) + interp_ok(UnixEnvVars { map: env_vars_machine, environ }) } pub(crate) fn cleanup(ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx> { @@ -61,7 +61,7 @@ impl<'tcx> UnixEnvVars<'tcx> { let old_vars_ptr = ecx.read_pointer(environ)?; ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; - Ok(()) + interp_ok(()) } pub(crate) fn environ(&self) -> Pointer { @@ -77,14 +77,14 @@ impl<'tcx> UnixEnvVars<'tcx> { // but we do want to do this read so it shows up as a data race. let _vars_ptr = ecx.read_pointer(&self.environ)?; let Some(var_ptr) = self.map.get(name) else { - return Ok(None); + return interp_ok(None); }; // The offset is used to strip the "{name}=" part of the string. let var_ptr = var_ptr.wrapping_offset( Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)), ecx, ); - Ok(Some(var_ptr)) + interp_ok(Some(var_ptr)) } /// Implementation detail for [`InterpCx::get_env_var`]. This basically does `getenv`, complete @@ -97,9 +97,9 @@ impl<'tcx> UnixEnvVars<'tcx> { let var_ptr = self.get_ptr(ecx, name)?; if let Some(ptr) = var_ptr { let var = ecx.read_os_str_from_c_str(ptr)?; - Ok(Some(var.to_owned())) + interp_ok(Some(var.to_owned())) } else { - Ok(None) + interp_ok(None) } } } @@ -133,7 +133,7 @@ fn alloc_environ_block<'tcx>( let place = ecx.project_field(&vars_place, idx)?; ecx.write_pointer(var, &place)?; } - Ok(vars_place.ptr()) + interp_ok(vars_place.ptr()) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -146,7 +146,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let name = this.read_os_str_from_c_str(name_ptr)?; let var_ptr = this.machine.env_vars.unix().get_ptr(this, name)?; - Ok(var_ptr.unwrap_or_else(Pointer::null)) + interp_ok(var_ptr.unwrap_or_else(Pointer::null)) } fn setenv( @@ -174,12 +174,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } this.update_environ()?; - Ok(Scalar::from_i32(0)) // return zero on success + interp_ok(Scalar::from_i32(0)) // return zero on success } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -200,12 +200,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } this.update_environ()?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -219,14 +219,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`getcwd`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Pointer::null()); + return interp_ok(Pointer::null()); } // If we cannot get the current directory, we return null match env::current_dir() { Ok(cwd) => { if this.write_path_to_c_str(&cwd, buf, size)?.0 { - return Ok(buf); + return interp_ok(buf); } let erange = this.eval_libc("ERANGE"); this.set_last_error(erange)?; @@ -234,7 +234,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Err(e) => this.set_last_error_from_io_error(e)?, } - Ok(Pointer::null()) + interp_ok(Pointer::null()) } fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -247,11 +247,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`chdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = env::set_current_dir(path).map(|()| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } /// Updates the `environ` static. @@ -267,7 +267,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let environ_block = alloc_environ_block(this, vals)?; this.write_pointer(environ_block, &environ)?; - Ok(()) + interp_ok(()) } fn getpid(&mut self) -> InterpResult<'tcx, Scalar> { @@ -278,7 +278,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `libc::getpid` returns an i32, however, `std::process::id()` return an u32. // So we un-do the conversion that stdlib does and turn it back into an i32. // In `Scalar` representation, these are the same, so we don't need to anything else. - Ok(Scalar::from_u32(this.get_pid())) + interp_ok(Scalar::from_u32(this.get_pid())) } fn linux_gettid(&mut self) -> InterpResult<'tcx, Scalar> { @@ -290,6 +290,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Compute a TID for this thread, ensuring that the main thread has PID == TID. let tid = this.get_pid().strict_add(index); - Ok(Scalar::from_u32(tid)) + interp_ok(Scalar::from_u32(tid)) } } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 6bd753d0d6bee..24e5d5579c3f1 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -277,7 +277,7 @@ impl FileDescriptionRef { fd.file_description.close(communicate_allowed, ecx) } - None => Ok(Ok(())), + None => interp_ok(Ok(())), } } @@ -414,16 +414,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(old_fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; - Ok(Scalar::from_i32(this.machine.fds.insert(fd))) + interp_ok(Scalar::from_i32(this.machine.fds.insert(fd))) } fn dup2(&mut self, old_fd_num: i32, new_fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(old_fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; if new_fd_num != old_fd_num { // Close new_fd if it is previously opened. @@ -433,13 +433,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { old_new_fd.close(this.machine.communicate(), this)?.ok(); } } - Ok(Scalar::from_i32(new_fd_num)) + interp_ok(Scalar::from_i32(new_fd_num)) } fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // We need to check that there aren't unsupported options in `op`. @@ -467,7 +467,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { drop(fd); // return `0` if flock is successful let result = result.map(|()| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { @@ -488,7 +488,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` // always sets this flag when opening a file. However we still need to check that the // file itself is open. - Ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { + interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { this.eval_libc_i32("FD_CLOEXEC") } else { this.fd_not_found()? @@ -509,15 +509,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let start = this.read_scalar(&args[2])?.to_i32()?; match this.machine.fds.get(fd_num) { - Some(fd) => Ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), - None => Ok(Scalar::from_i32(this.fd_not_found()?)), + Some(fd) => interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), + None => interp_ok(Scalar::from_i32(this.fd_not_found()?)), } } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fcntl`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } this.ffullsync_fd(fd_num) @@ -532,12 +532,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fd_num = this.read_scalar(fd_op)?.to_i32()?; let Some(fd) = this.machine.fds.remove(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; let result = fd.close(this.machine.communicate(), this)?; // return `0` if close is successful let result = result.map(|()| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } /// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets @@ -548,7 +548,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let ebadf = this.eval_libc("EBADF"); this.set_last_error(ebadf)?; - Ok((-1).into()) + interp_ok((-1).into()) } /// Read data from `fd` into buffer specified by `buf` and `count`. @@ -586,7 +586,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("read: FD not found"); let res: i32 = this.fd_not_found()?; this.write_int(res, dest)?; - return Ok(()); + return interp_ok(()); }; trace!("read: FD mapped to {fd:?}"); @@ -601,12 +601,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); }; fd.pread(communicate, offset, buf, count, dest, this)? } }; - Ok(()) + interp_ok(()) } fn write( @@ -636,7 +636,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(fd) = this.machine.fds.get(fd_num) else { let res: i32 = this.fd_not_found()?; this.write_int(res, dest)?; - return Ok(()); + return interp_ok(()); }; match offset { @@ -646,12 +646,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); }; fd.pwrite(communicate, buf, count, offset, dest, this)? } }; - Ok(()) + interp_ok(()) } /// Helper to implement `FileDescription::read`: @@ -675,12 +675,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_bytes_ptr(buf, bytes[..read_bytes].iter().copied())?; // The actual read size is always less than what got originally requested so this cannot fail. this.write_int(u64::try_from(read_bytes).unwrap(), dest)?; - Ok(()) + interp_ok(()) } Err(e) => { this.set_last_error_from_io_error(e)?; this.write_int(-1, dest)?; - Ok(()) + interp_ok(()) } } } @@ -695,6 +695,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let result = this.try_unwrap_io_result(result.map(|c| i64::try_from(c).unwrap()))?; this.write_int(result, dest)?; - Ok(()) + 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 528d068ea92c0..b35fe4487b27d 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -828,7 +828,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This function looks and behaves excatly like miri_start_unwind. let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?; this.handle_miri_start_unwind(payload)?; - return Ok(EmulateItemResult::NeedsUnwind); + return interp_ok(EmulateItemResult::NeedsUnwind); } "getuid" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -944,11 +944,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "solaris" | "illumos" => solarish::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), - _ => Ok(EmulateItemResult::NotSupported), + _ => interp_ok(EmulateItemResult::NotSupported), }; } }; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 36f25767a8ea0..e89dd488a2fec 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -84,8 +84,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 92514b67beab1..56280982e6ece 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -121,7 +121,7 @@ impl FileDescription for FileHandle { offset: SeekFrom, ) -> InterpResult<'tcx, io::Result> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); - Ok((&mut &self.file).seek(offset)) + interp_ok((&mut &self.file).seek(offset)) } fn close<'tcx>( @@ -136,7 +136,7 @@ impl FileDescription for FileHandle { // to handle possible errors correctly. let result = self.file.sync_all(); // Now we actually close the file and return the result. - Ok(result) + interp_ok(result) } else { // We drop the file, this closes it but ignores any errors // produced when closing it. This is done because @@ -144,7 +144,7 @@ impl FileDescription for FileHandle { // `/dev/urandom` which are read-only. Check // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 // for a deeper discussion. - Ok(Ok(())) + interp_ok(Ok(())) } } @@ -179,7 +179,7 @@ impl FileDescription for FileHandle { } ret => panic!("Unexpected return value from flock: {ret}"), }; - Ok(res) + interp_ok(res) } #[cfg(target_family = "windows")] @@ -231,7 +231,7 @@ impl FileDescription for FileHandle { } _ => panic!("Unexpected return value: {ret}"), }; - Ok(res) + interp_ok(res) } #[cfg(not(any(target_family = "unix", target_family = "windows")))] @@ -289,7 +289,7 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { &buf, )?; - Ok(0) + interp_ok(0) } fn file_type_to_d_type( @@ -303,26 +303,30 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { match file_type { Ok(file_type) => { match () { - _ if file_type.is_dir() => Ok(this.eval_libc("DT_DIR").to_u8()?.into()), - _ if file_type.is_file() => Ok(this.eval_libc("DT_REG").to_u8()?.into()), - _ if file_type.is_symlink() => Ok(this.eval_libc("DT_LNK").to_u8()?.into()), + _ if file_type.is_dir() => interp_ok(this.eval_libc("DT_DIR").to_u8()?.into()), + _ if file_type.is_file() => interp_ok(this.eval_libc("DT_REG").to_u8()?.into()), + _ if file_type.is_symlink() => + interp_ok(this.eval_libc("DT_LNK").to_u8()?.into()), // Certain file types are only supported when the host is a Unix system. #[cfg(unix)] _ if file_type.is_block_device() => - Ok(this.eval_libc("DT_BLK").to_u8()?.into()), + interp_ok(this.eval_libc("DT_BLK").to_u8()?.into()), #[cfg(unix)] - _ if file_type.is_char_device() => Ok(this.eval_libc("DT_CHR").to_u8()?.into()), + _ if file_type.is_char_device() => + interp_ok(this.eval_libc("DT_CHR").to_u8()?.into()), #[cfg(unix)] - _ if file_type.is_fifo() => Ok(this.eval_libc("DT_FIFO").to_u8()?.into()), + _ if file_type.is_fifo() => + interp_ok(this.eval_libc("DT_FIFO").to_u8()?.into()), #[cfg(unix)] - _ if file_type.is_socket() => Ok(this.eval_libc("DT_SOCK").to_u8()?.into()), + _ if file_type.is_socket() => + interp_ok(this.eval_libc("DT_SOCK").to_u8()?.into()), // Fallback - _ => Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), + _ => interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), } } Err(e) => match e.raw_os_error() { - Some(error) => Ok(error), + Some(error) => interp_ok(error), None => throw_unsup_format!( "the error {} couldn't be converted to a return value", @@ -524,7 +528,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // if the flag contains `O_TMPFILE` then we return a graceful error let eopnotsupp = this.eval_libc("EOPNOTSUPP"); this.set_last_error(eopnotsupp)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } @@ -545,7 +549,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if path.is_symlink() { let eloop = this.eval_libc("ELOOP"); this.set_last_error(eloop)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } mirror |= o_nofollow; @@ -561,14 +565,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`open`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let fd = options .open(path) .map(|file| this.machine.fds.insert_new(FileHandle { file, writable })); - Ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) } fn lseek64(&mut self, fd_num: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { @@ -581,7 +585,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Negative offsets return `EINVAL`. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i64(-1)); + return interp_ok(Scalar::from_i64(-1)); } else { SeekFrom::Start(u64::try_from(offset).unwrap()) } @@ -592,19 +596,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i64(-1)); + return interp_ok(Scalar::from_i64(-1)); }; let communicate = this.machine.communicate(); let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i64(this.fd_not_found()?)); + return interp_ok(Scalar::from_i64(this.fd_not_found()?)); }; let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap()); drop(fd); let result = this.try_unwrap_io_result(result)?; - Ok(Scalar::from_i64(result)) + interp_ok(Scalar::from_i64(result)) } fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -616,11 +620,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = remove_file(path).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn symlink( @@ -647,11 +651,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = create_link(&target, &linkpath).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn macos_fbsd_stat( @@ -673,16 +677,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`stat`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // `stat` always follows symlinks. let metadata = match FileMetadata::from_path(this, &path, true)? { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno + None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno }; - Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) } // `lstat` is used to get symlink metadata. @@ -705,15 +709,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`lstat`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let metadata = match FileMetadata::from_path(this, &path, false)? { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno + None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno }; - Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) } fn macos_fbsd_fstat( @@ -733,14 +737,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fstat`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let metadata = match FileMetadata::from_fd_num(this, fd)? { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), + None => return interp_ok(Scalar::from_i32(-1)), }; - Ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) } fn linux_statx( @@ -765,7 +769,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? { let efault = this.eval_libc("EFAULT"); this.set_last_error(efault)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let statxbuf = this.deref_pointer_as(statxbuf_op, this.libc_ty_layout("statx"))?; @@ -807,7 +811,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.eval_libc("EBADF") }; this.set_last_error(ecode)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // the `_mask_op` parameter specifies the file information that the caller requested. @@ -829,7 +833,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let metadata = match metadata { Some(metadata) => metadata, - None => return Ok(Scalar::from_i32(-1)), + None => return interp_ok(Scalar::from_i32(-1)), }; // The `mode` field specifies the type of the file and the permissions over the file for @@ -848,25 +852,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .accessed .map(|tup| { mask |= this.eval_libc_u32("STATX_ATIME"); - InterpResult::Ok(tup) + interp_ok(tup) }) - .unwrap_or_else(|| Ok((0, 0)))?; + .unwrap_or_else(|| interp_ok((0, 0)))?; let (created_sec, created_nsec) = metadata .created .map(|tup| { mask |= this.eval_libc_u32("STATX_BTIME"); - InterpResult::Ok(tup) + interp_ok(tup) }) - .unwrap_or_else(|| Ok((0, 0)))?; + .unwrap_or_else(|| interp_ok((0, 0)))?; let (modified_sec, modified_nsec) = metadata .modified .map(|tup| { mask |= this.eval_libc_u32("STATX_MTIME"); - InterpResult::Ok(tup) + interp_ok(tup) }) - .unwrap_or_else(|| Ok((0, 0)))?; + .unwrap_or_else(|| interp_ok((0, 0)))?; // Now we write everything to `statxbuf`. We write a zero for the unavailable fields. this.write_int_fields_named( @@ -922,7 +926,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.project_field_named(&statxbuf, "stx_mtime")?, )?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn rename( @@ -938,7 +942,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? { let efault = this.eval_libc("EFAULT"); this.set_last_error(efault)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let oldpath = this.read_path_from_c_str(oldpath_ptr)?; @@ -948,12 +952,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rename`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = rename(oldpath, newpath).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -972,7 +976,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } #[cfg_attr(not(unix), allow(unused_mut))] @@ -988,7 +992,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = builder.create(path).map(|_| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1000,12 +1004,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let result = remove_dir(path).map(|_| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1018,7 +1022,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`opendir`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::null_ptr(this)); + return interp_ok(Scalar::null_ptr(this)); } let result = read_dir(name); @@ -1030,11 +1034,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The libc API for opendir says that this method returns a pointer to an opaque // structure, but we are returning an ID number. Thus, pass it as a scalar of // pointer width. - Ok(Scalar::from_target_usize(id, this)) + interp_ok(Scalar::from_target_usize(id, this)) } Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(Scalar::null_ptr(this)) + interp_ok(Scalar::null_ptr(this)) } } } @@ -1051,7 +1055,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`readdir`", reject_with)?; let eacc = this.eval_libc("EBADF"); this.set_last_error(eacc)?; - return Ok(Scalar::null_ptr(this)); + return interp_ok(Scalar::null_ptr(this)); } let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| { @@ -1129,7 +1133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(old_entry, None, MiriMemoryKind::Runtime.into())?; } - Ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) + interp_ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) } fn macos_fbsd_readdir_r( @@ -1150,13 +1154,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readdir_r`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| { err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir") })?; - Ok(Scalar::from_i32(match open_dir.read_dir.next() { + interp_ok(Scalar::from_i32(match open_dir.read_dir.next() { Some(Ok(dir_entry)) => { // Write into entry, write pointer to result, return 0 on success. // The name is written with write_os_str_to_c_str, while the rest of the @@ -1261,18 +1265,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dirp = this.read_target_usize(dirp_op)?; // Reject if isolation is enabled. - Ok(Scalar::from_i32(if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`closedir`", reject_with)?; - this.fd_not_found()? - } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { - if let Some(entry) = open_dir.entry { - this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; - } - drop(open_dir); - 0 - } else { - this.fd_not_found()? - })) + interp_ok(Scalar::from_i32( + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`closedir`", reject_with)?; + this.fd_not_found()? + } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { + if let Some(entry) = open_dir.entry { + this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; + } + drop(open_dir); + 0 + } else { + this.fd_not_found()? + }, + )) } fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> { @@ -1282,11 +1288,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`ftruncate64`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // FIXME: Support ftruncate64 for all FDs @@ -1299,19 +1305,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = file.set_len(length); drop(fd); let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; - Ok(Scalar::from_i32(result)) + interp_ok(Scalar::from_i32(result)) } else { drop(fd); let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } else { drop(fd); // The file is not writable let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -1329,7 +1335,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fsync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } self.ffullsync_fd(fd) @@ -1338,7 +1344,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(fd_num) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1346,7 +1352,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_all); drop(fd); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1358,11 +1364,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fdatasync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let Some(fd) = this.machine.fds.get(fd) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1370,7 +1376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); drop(fd); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } fn sync_file_range( @@ -1390,7 +1396,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if offset < 0 || nbytes < 0 { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE") | this.eval_libc_i32("SYNC_FILE_RANGE_WRITE") @@ -1398,18 +1404,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if flags & allowed_flags != flags { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`sync_file_range`", reject_with)?; // Set error code as "EBADF" (bad fd) - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); } let Some(fd) = this.machine.fds.get(fd) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1417,7 +1423,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); drop(fd); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + interp_ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } fn readlink( @@ -1437,7 +1443,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`readlink`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(-1); + return interp_ok(-1); } let result = std::fs::read_link(pathname); @@ -1456,11 +1462,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { path_bytes = &path_bytes[..bufsize] } this.write_bytes_ptr(buf, path_bytes.iter().copied())?; - Ok(path_bytes.len().try_into().unwrap()) + interp_ok(path_bytes.len().try_into().unwrap()) } Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(-1) + interp_ok(-1) } } } @@ -1472,7 +1478,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fd = this.read_scalar(miri_fd)?.to_i32()?; let error = if let Some(fd) = this.machine.fds.get(fd) { if fd.is_tty(this.machine.communicate()) { - return Ok(Scalar::from_i32(1)); + return interp_ok(Scalar::from_i32(1)); } else { this.eval_libc("ENOTTY") } @@ -1481,7 +1487,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.eval_libc("EBADF") }; this.set_last_error(error)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn realpath( @@ -1500,7 +1506,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`realpath`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_target_usize(0, this)); + return interp_ok(Scalar::from_target_usize(0, this)); } let result = std::fs::canonicalize(pathname); @@ -1531,16 +1537,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // seems like a bit of a mess anyway: . let enametoolong = this.eval_libc("ENAMETOOLONG"); this.set_last_error(enametoolong)?; - return Ok(Scalar::from_target_usize(0, this)); + return interp_ok(Scalar::from_target_usize(0, this)); } processed_ptr }; - Ok(Scalar::from_maybe_pointer(dest, this)) + interp_ok(Scalar::from_maybe_pointer(dest, this)) } Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(Scalar::from_target_usize(0, this)) + interp_ok(Scalar::from_target_usize(0, this)) } } } @@ -1575,7 +1581,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`mkstemp`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // Get the bytes of the suffix we expect in _target_ encoding. @@ -1593,7 +1599,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if last_six_char_bytes != suffix_bytes { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // At this point we know we have 6 ASCII 'X' characters as a suffix. @@ -1648,7 +1654,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match file { Ok(f) => { let fd = this.machine.fds.insert_new(FileHandle { file: f, writable: true }); - return Ok(Scalar::from_i32(fd)); + return interp_ok(Scalar::from_i32(fd)); } Err(e) => match e.kind() { @@ -1659,7 +1665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // "On error, -1 is returned, and errno is set to // indicate the error" this.set_last_error_from_io_error(e)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } }, } @@ -1668,7 +1674,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We ran out of attempts to create the file, return an error. let eexist = this.eval_libc("EEXIST"); this.set_last_error(eexist)?; - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } } @@ -1678,12 +1684,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn extract_sec_and_nsec<'tcx>( time: std::io::Result, ) -> InterpResult<'tcx, Option<(u64, u32)>> { - time.ok() - .map(|time| { + match time.ok() { + Some(time) => { let duration = system_time_to_duration(&time)?; - Ok((duration.as_secs(), duration.subsec_nanos())) - }) - .transpose() + interp_ok(Some((duration.as_secs(), duration.subsec_nanos()))) + } + None => interp_ok(None), + } } /// Stores a file's metadata in order to avoid code duplication in the different metadata related @@ -1738,7 +1745,7 @@ impl FileMetadata { Ok(metadata) => metadata, Err(e) => { ecx.set_last_error_from_io_error(e)?; - return Ok(None); + return interp_ok(None); } }; @@ -1761,6 +1768,6 @@ impl FileMetadata { let modified = extract_sec_and_nsec(metadata.modified())?; // FIXME: Provide more fields using platform specific methods. - Ok(Some(FileMetadata { mode, size, created, accessed, modified })) + interp_ok(Some(FileMetadata { mode, size, created, accessed, modified })) } } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 675a404ae117c..08f1381caef72 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -142,7 +142,7 @@ impl FileDescription for Epoll { _communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { - Ok(Ok(())) + interp_ok(Ok(())) } } @@ -211,7 +211,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { epoll_instance.ready_list = Rc::new(RefCell::new(BTreeMap::new())); let fd = this.machine.fds.insert_new(Epoll::default()); - Ok(Scalar::from_i32(fd)) + interp_ok(Scalar::from_i32(fd)) } /// This function performs control operations on the `Epoll` instance referred to by the file @@ -263,12 +263,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if epfd_value == fd { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } // Check if epfd is a valid epoll file descriptor. let Some(epfd) = this.machine.fds.get(epfd_value) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; let epoll_file_description = epfd .downcast::() @@ -278,7 +278,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ready_list = &epoll_file_description.ready_list; let Some(fd_ref) = this.machine.fds.get(fd) else { - return Ok(Scalar::from_i32(this.fd_not_found()?)); + return interp_ok(Scalar::from_i32(this.fd_not_found()?)); }; let id = fd_ref.get_id(); @@ -330,13 +330,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if interest_list.contains_key(&epoll_key) { let eexist = this.eval_libc("EEXIST"); this.set_last_error(eexist)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } else { if !interest_list.contains_key(&epoll_key) { let enoent = this.eval_libc("ENOENT"); this.set_last_error(enoent)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } } @@ -364,7 +364,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Notification will be returned for current epfd if there is event in the file // descriptor we registered. check_and_update_one_event_interest(&fd_ref, interest, id, this)?; - return Ok(Scalar::from_i32(0)); + return interp_ok(Scalar::from_i32(0)); } else if op == epoll_ctl_del { let epoll_key = (id, fd); @@ -372,7 +372,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(epoll_interest) = interest_list.remove(&epoll_key) else { let enoent = this.eval_libc("ENOENT"); this.set_last_error(enoent)?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); }; // All related Weak will fail to upgrade after the drop. drop(epoll_interest); @@ -390,9 +390,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .unwrap() .retain(|event| event.upgrade().is_some()); - return Ok(Scalar::from_i32(0)); + return interp_ok(Scalar::from_i32(0)); } - Ok(Scalar::from_i32(-1)) + interp_ok(Scalar::from_i32(-1)) } /// The `epoll_wait()` system call waits for events on the `Epoll` @@ -446,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); } // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap() @@ -459,7 +459,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(epfd) = this.machine.fds.get(epfd_value) else { let result_value: i32 = this.fd_not_found()?; this.write_int(result_value, dest)?; - return Ok(()); + return interp_ok(()); }; // Create a weak ref of epfd and pass it to callback so we will make sure that epfd // is not close after the thread unblocks. @@ -508,7 +508,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } @unblock = |this| { blocking_epoll_callback(epfd_value, weak_epfd, &dest, &event, this)?; - Ok(()) + interp_ok(()) } @timeout = |this| { // No notification after blocking timeout. @@ -521,12 +521,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .thread_id.borrow_mut() .retain(|&id| id != this.active_thread()); this.write_int(0, &dest)?; - Ok(()) + interp_ok(()) } ), ); } - Ok(()) + interp_ok(()) } /// For a specific file description, get its ready events and update the corresponding ready @@ -577,7 +577,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { for thread_id in waiter { this.unblock_thread(thread_id, BlockReason::Epoll)?; } - Ok(()) + interp_ok(()) } } @@ -623,9 +623,9 @@ fn check_and_update_one_event_interest<'tcx>( let event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); // Triggers the notification by inserting it to the ready list. ready_list.insert(epoll_key, event_instance); - Ok(true) + interp_ok(true) } else { - Ok(false) + interp_ok(false) } } @@ -665,5 +665,5 @@ fn blocking_epoll_callback<'tcx>( } } ecx.write_int(num_of_events, dest)?; - Ok(()) + interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index ab7652ca721ca..12e99644357a6 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -37,7 +37,7 @@ impl FileDescription for Event { // We only check the status of EPOLLIN and EPOLLOUT flags for eventfd. If other event flags // need to be supported in the future, the check should be added here. - Ok(EpollReadyEvents { + interp_ok(EpollReadyEvents { epollin: self.counter.get() != 0, epollout: self.counter.get() != MAX_COUNTER, ..EpollReadyEvents::new() @@ -49,7 +49,7 @@ impl FileDescription for Event { _communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result<()>> { - Ok(Ok(())) + interp_ok(Ok(())) } /// Read the counter in the buffer and return the counter if succeeded. @@ -68,7 +68,7 @@ impl FileDescription for Event { if len < ty.size.bytes_usize() { ecx.set_last_error_from_io_error(Error::from(ErrorKind::InvalidInput))?; ecx.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); } // eventfd read at the size of u64. @@ -80,7 +80,7 @@ impl FileDescription for Event { if self.is_nonblock { ecx.set_last_error_from_io_error(Error::from(ErrorKind::WouldBlock))?; ecx.write_int(-1, dest)?; - return Ok(()); + return interp_ok(()); } throw_unsup_format!("eventfd: blocking is unsupported"); @@ -100,7 +100,7 @@ impl FileDescription for Event { ecx.write_int(buf_place.layout.size.bytes(), dest)?; } - Ok(()) + interp_ok(()) } /// A write call adds the 8-byte integer value supplied in @@ -226,6 +226,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { clock: RefCell::new(VClock::default()), }); - Ok(Scalar::from_i32(fd_value)) + interp_ok(Scalar::from_i32(fd_value)) } } diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index e2e76aad7cd6e..9726dac7e516b 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -134,7 +134,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, // neither of which have any effect on our current PRNG. // See for a discussion of argument sizes. - let _flags = this.read_scalar(&args[3])?.to_i32(); + let _flags = this.read_scalar(&args[3])?.to_i32()?; this.gen_random(ptr, len)?; this.write_scalar(Scalar::from_target_usize(len, this), dest)?; @@ -147,7 +147,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.handle_unsupported_foreign_item(format!( "can't execute syscall with ID {id}" ))?; - return Ok(EmulateItemResult::AlreadyJumped); + return interp_ok(EmulateItemResult::AlreadyJumped); } } } @@ -190,9 +190,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index b11f60048a070..4f2e17d50c8b4 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -25,7 +25,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } if flags & this.eval_libc_i32("MREMAP_FIXED") != 0 { @@ -39,7 +39,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } let align = this.machine.page_align(); @@ -60,6 +60,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .unwrap(); } - Ok(Scalar::from_pointer(ptr, this)) + interp_ok(Scalar::from_pointer(ptr, this)) } } diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index bd21203907419..5108bc8299cbb 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -78,7 +78,7 @@ pub fn futex<'tcx>( let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return Ok(()); + return interp_ok(()); } let timeout = this.deref_pointer_as(&args[3], this.libc_ty_layout("timespec"))?; @@ -91,7 +91,7 @@ pub fn futex<'tcx>( let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return Ok(()); + return interp_ok(()); } }; let timeout_clock = if op & futex_realtime == futex_realtime { @@ -201,7 +201,7 @@ pub fn futex<'tcx>( let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return Ok(()); + return interp_ok(()); } // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait // will see the latest value on addr which could be changed by our caller @@ -221,5 +221,5 @@ pub fn futex<'tcx>( op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op), } - Ok(()) + interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index ce4ea0816f016..2751d379dc006 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -207,9 +207,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.os_unfair_lock_assert_not_owner(lock_op)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 8f9237a1d46ea..2f96849d0d2d9 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -20,7 +20,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // os_unfair_lock holds a 32-bit value, is initialized with zero and // must be assumed to be opaque. Therefore, we can just store our // internal mutex ID in the structure without anyone noticing. - this.mutex_get_or_create_id(&lock, 0, |_| Ok(None)) + this.mutex_get_or_create_id(&lock, 0, |_| interp_ok(None)) } } @@ -43,7 +43,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_lock(id); } - Ok(()) + interp_ok(()) } fn os_unfair_lock_trylock( @@ -63,7 +63,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_bool(true), dest)?; } - Ok(()) + interp_ok(()) } fn os_unfair_lock_unlock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -77,7 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } - Ok(()) + interp_ok(()) } fn os_unfair_lock_assert_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -90,7 +90,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } - Ok(()) + interp_ok(()) } fn os_unfair_lock_assert_not_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -103,6 +103,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 0397ba3b91ad9..9273748ef3bfd 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -49,7 +49,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { && matches!(&*this.tcx.sess.target.os, "macos" | "solaris" | "illumos") && (flags & map_fixed) != 0 { - return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); + return interp_ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); } let prot_read = this.eval_libc_i32("PROT_READ"); @@ -58,11 +58,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } if length == 0 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } // If a user tries to map a file, we want to loudly inform them that this is not going @@ -104,11 +104,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let align = this.machine.page_align(); let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); }; if map_length > this.target_usize_max() { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } let ptr = @@ -121,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) .unwrap(); - Ok(Scalar::from_pointer(ptr, this)) + interp_ok(Scalar::from_pointer(ptr, this)) } fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -135,16 +135,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if addr.addr().bytes() % this.machine.page_size != 0 { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); } let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(Scalar::from_i32(-1)); + return interp_ok(Scalar::from_i32(-1)); }; if length > this.target_usize_max() { this.set_last_error(this.eval_libc("EINVAL"))?; - return Ok(this.eval_libc("MAP_FAILED")); + return interp_ok(this.eval_libc("MAP_FAILED")); } let length = Size::from_bytes(length); @@ -154,6 +154,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MemoryKind::Machine(MiriMemoryKind::Mmap), )?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index a0cc4a62bfd2b..c10098f2733ac 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -99,8 +99,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index fea994663c076..017291f81a22e 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -10,7 +10,7 @@ use crate::*; #[inline] fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { - Ok(match &*ecx.tcx.sess.target.os { + interp_ok(match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "macos" | "freebsd" | "android" => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) @@ -112,7 +112,7 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { } } - Ok(offset) + interp_ok(offset) } /// Eagerly create and initialize a new mutex. @@ -125,7 +125,7 @@ fn mutex_create<'tcx>( let address = mutex.ptr().addr().bytes(); let data = Box::new(AdditionalMutexData { address, kind }); ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, Some(data))?; - Ok(()) + interp_ok(()) } /// Returns the `MutexId` of the mutex stored at `mutex_op`. @@ -144,7 +144,7 @@ fn mutex_get_id<'tcx>( // an ID yet. We have to determine the mutex kind from the static initializer. let kind = mutex_kind_from_static_initializer(ecx, &mutex)?; - Ok(Some(Box::new(AdditionalMutexData { kind, address }))) + interp_ok(Some(Box::new(AdditionalMutexData { kind, address }))) })?; // Check that the mutex has not been moved since last use. @@ -155,7 +155,7 @@ fn mutex_get_id<'tcx>( throw_ub_format!("pthread_mutex_t can't be moved after first use") } - Ok(id) + interp_ok(id) } /// Returns the kind of a static initializer. @@ -163,7 +163,7 @@ fn mutex_kind_from_static_initializer<'tcx>( ecx: &MiriInterpCx<'tcx>, mutex: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, MutexKind> { - Ok(match &*ecx.tcx.sess.target.os { + interp_ok(match &*ecx.tcx.sess.target.os { // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT. "linux" => { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; @@ -186,7 +186,7 @@ fn mutex_translate_kind<'tcx>( ecx: &MiriInterpCx<'tcx>, kind: i32, ) -> InterpResult<'tcx, MutexKind> { - Ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) { + interp_ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) { MutexKind::Normal } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { MutexKind::ErrorCheck @@ -237,7 +237,7 @@ fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { ); } - Ok(offset) + interp_ok(offset) } fn rwlock_get_id<'tcx>( @@ -248,7 +248,7 @@ fn rwlock_get_id<'tcx>( let address = rwlock.ptr().addr().bytes(); let id = ecx.rwlock_get_or_create_id(&rwlock, rwlock_id_offset(ecx)?, |_| { - Ok(Some(Box::new(AdditionalRwLockData { address }))) + interp_ok(Some(Box::new(AdditionalRwLockData { address }))) })?; // Check that the rwlock has not been moved since last use. @@ -259,7 +259,7 @@ fn rwlock_get_id<'tcx>( throw_ub_format!("pthread_rwlock_t can't be moved after first use") } - Ok(id) + interp_ok(id) } // pthread_condattr_t. @@ -268,7 +268,7 @@ fn rwlock_get_id<'tcx>( #[inline] fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { - Ok(match &*ecx.tcx.sess.target.os { + interp_ok(match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "freebsd" | "android" => 0, // macOS does not have a clock attribute. os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"), @@ -292,7 +292,7 @@ fn cond_translate_clock_id<'tcx>( ecx: &MiriInterpCx<'tcx>, raw_id: i32, ) -> InterpResult<'tcx, ClockId> { - Ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { + interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { ClockId::Realtime } else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") { ClockId::Monotonic @@ -342,7 +342,7 @@ fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { ); } - Ok(offset) + interp_ok(offset) } #[derive(Debug, Clone, Copy)] @@ -369,7 +369,7 @@ fn cond_get_id<'tcx>( let address = cond.ptr().addr().bytes(); let id = ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?, |_ecx| { // This used the static initializer. The clock there is always CLOCK_REALTIME. - Ok(Some(Box::new(AdditionalCondData { address, clock_id: ClockId::Realtime }))) + interp_ok(Some(Box::new(AdditionalCondData { address, clock_id: ClockId::Realtime }))) })?; // Check that the mutex has not been moved since last use. @@ -380,7 +380,7 @@ fn cond_get_id<'tcx>( throw_ub_format!("pthread_cond_t can't be moved after first use") } - Ok(id) + interp_ok(id) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -390,7 +390,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutexattr_set_kind(this, attr_op, PTHREAD_MUTEX_KIND_UNCHANGED)?; - Ok(()) + interp_ok(()) } fn pthread_mutexattr_settype( @@ -411,10 +411,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutexattr_set_kind(this, attr_op, kind)?; } else { let einval = this.eval_libc_i32("EINVAL"); - return Ok(Scalar::from_i32(einval)); + return interp_ok(Scalar::from_i32(einval)); } - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -439,7 +439,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?, )?; - Ok(()) + interp_ok(()) } fn pthread_mutex_init( @@ -458,7 +458,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutex_create(this, mutex_op, kind)?; - Ok(()) + interp_ok(()) } fn pthread_mutex_lock( @@ -478,7 +478,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let owner_thread = this.mutex_get_owner(id); if owner_thread != this.active_thread() { this.mutex_enqueue_and_block(id, Some((Scalar::from_i32(0), dest.clone()))); - return Ok(()); + return interp_ok(()); } else { // Trying to acquire the same mutex again. match kind { @@ -498,7 +498,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { 0 }; this.write_scalar(Scalar::from_i32(ret), dest)?; - Ok(()) + interp_ok(()) } fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -510,7 +510,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .expect("data should always exist for pthread mutexes") .kind; - Ok(Scalar::from_i32(if this.mutex_is_locked(id) { + interp_ok(Scalar::from_i32(if this.mutex_is_locked(id) { let owner_thread = this.mutex_get_owner(id); if owner_thread != this.active_thread() { this.eval_libc_i32("EBUSY") @@ -542,7 +542,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(_old_locked_count) = this.mutex_unlock(id)? { // The mutex was locked by the current thread. - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } else { // The mutex was locked by another thread or not locked at all. See // the “Unlock When Not Owner” column in @@ -557,7 +557,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread" ), MutexKind::ErrorCheck | MutexKind::Recursive => - Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))), + interp_ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))), } } } @@ -579,7 +579,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // FIXME: delete interpreter state associated with this mutex. - Ok(()) + interp_ok(()) } fn pthread_rwlock_rdlock( @@ -598,7 +598,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - Ok(()) + interp_ok(()) } fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -607,10 +607,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_write_locked(id) { - Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) + interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { this.rwlock_reader_lock(id); - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } @@ -642,7 +642,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - Ok(()) + interp_ok(()) } fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -651,10 +651,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_locked(id) { - Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) + interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { this.rwlock_writer_lock(id); - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } @@ -665,7 +665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { #[allow(clippy::if_same_then_else)] if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? { - Ok(()) + interp_ok(()) } else { throw_ub_format!("unlocked an rwlock that was not locked by the active thread"); } @@ -688,7 +688,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // FIXME: delete interpreter state associated with this rwlock. - Ok(()) + interp_ok(()) } fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -703,7 +703,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condattr_set_clock_id(this, attr_op, default_clock_id)?; } - Ok(()) + interp_ok(()) } fn pthread_condattr_setclock( @@ -720,10 +720,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condattr_set_clock_id(this, attr_op, clock_id)?; } else { let einval = this.eval_libc_i32("EINVAL"); - return Ok(Scalar::from_i32(einval)); + return interp_ok(Scalar::from_i32(einval)); } - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn pthread_condattr_getclock( @@ -736,7 +736,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let clock_id = condattr_get_clock_id(this, attr_op)?; this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?; - Ok(()) + interp_ok(()) } fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -754,7 +754,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?, )?; - Ok(()) + interp_ok(()) } fn pthread_cond_init( @@ -781,21 +781,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(Box::new(AdditionalCondData { address, clock_id })), )?; - Ok(()) + interp_ok(()) } fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; this.condvar_signal(id)?; - Ok(()) + interp_ok(()) } fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; while this.condvar_signal(id)? {} - Ok(()) + interp_ok(()) } fn pthread_cond_wait( @@ -818,7 +818,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), )?; - Ok(()) + interp_ok(()) } fn pthread_cond_timedwait( @@ -845,7 +845,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => { let einval = this.eval_libc("EINVAL"); this.write_scalar(einval, dest)?; - return Ok(()); + return interp_ok(()); } }; let timeout_clock = match clock_id { @@ -865,7 +865,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), )?; - Ok(()) + interp_ok(()) } fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -882,6 +882,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_uninit(&this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?)?; // FIXME: delete interpreter state associated with this condvar. - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 96af4ae51efaa..5515524f2f187 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -27,7 +27,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.layouts.mut_raw_ptr, )?; - Ok(()) + interp_ok(()) } fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -41,7 +41,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?; - Ok(()) + interp_ok(()) } fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { @@ -53,14 +53,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /*allow_terminated_joined*/ false, )?; - Ok(()) + interp_ok(()) } fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let thread_id = this.active_thread(); - Ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size)) + interp_ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size)) } /// Set the name of the current thread. `max_name_len` is the maximal length of the name @@ -81,12 +81,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Comparing with `>=` to account for null terminator. if name.len() >= max_name_len { - return Ok(this.eval_libc("ERANGE")); + return interp_ok(this.eval_libc("ERANGE")); } this.set_thread_name(thread, name); - Ok(Scalar::from_u32(0)) + interp_ok(Scalar::from_u32(0)) } fn pthread_getname_np( @@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let name = this.get_thread_name(thread).unwrap_or(b"").to_owned(); let (success, _written) = this.write_c_str(&name, name_out, len)?; - Ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }) + interp_ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }) } fn sched_yield(&mut self) -> InterpResult<'tcx, ()> { @@ -114,6 +114,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.yield_active_thread(); - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index d1bfa563387dd..763f9f2402768 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -103,7 +103,7 @@ impl FileDescription for AnonSocket { epoll_ready_events.epollerr = true; } } - Ok(epoll_ready_events) + interp_ok(epoll_ready_events) } fn close<'tcx>( @@ -122,7 +122,7 @@ impl FileDescription for AnonSocket { // Notify peer fd that close has happened, since that can unblock reads and writes. ecx.check_and_update_readiness(&peer_fd)?; } - Ok(Ok(())) + interp_ok(Ok(())) } fn read<'tcx>( @@ -344,7 +344,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(sv0, &sv)?; this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } fn pipe2( @@ -396,6 +396,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(pipefd0, &pipefd)?; this.write_scalar(pipefd1, &pipefd.offset(pipefd.layout.size, pipefd.layout, this)?)?; - Ok(Scalar::from_i32(0)) + interp_ok(Scalar::from_i32(0)) } } diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index e9fe90081d09c..b5de950de9adc 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -33,8 +33,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(res, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 9707482b1e2c2..a6ace6f9bdc4c 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -24,12 +24,12 @@ impl WindowsEnvVars { _ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, env_vars: FxHashMap, ) -> InterpResult<'tcx, Self> { - Ok(Self { map: env_vars }) + interp_ok(Self { map: env_vars }) } /// Implementation detail for [`InterpCx::get_env_var`]. pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option> { - Ok(self.map.get(name).cloned()) + interp_ok(self.map.get(name).cloned()) } } @@ -52,7 +52,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let buf_size = this.read_scalar(size_op)?.to_u32()?; // in characters let name = this.read_os_str_from_wide_str(name_ptr)?; - Ok(match this.machine.env_vars.windows().map.get(&name).cloned() { + interp_ok(match this.machine.env_vars.windows().map.get(&name).cloned() { Some(val) => { Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str( &val, @@ -89,7 +89,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let envblock_ptr = this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Runtime.into())?; // If the function succeeds, the return value is a pointer to the environment block of the current process. - Ok(envblock_ptr) + interp_ok(envblock_ptr) } #[allow(non_snake_case)] @@ -100,7 +100,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let env_block_ptr = this.read_pointer(env_block_op)?; this.deallocate_ptr(env_block_ptr, None, MiriMemoryKind::Runtime.into())?; // If the function succeeds, the return value is nonzero. - Ok(Scalar::from_i32(1)) + interp_ok(Scalar::from_i32(1)) } #[allow(non_snake_case)] @@ -128,11 +128,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if this.ptr_is_null(value_ptr)? { // Delete environment variable `{name}` if it exists. this.machine.env_vars.windows_mut().map.remove(&name); - Ok(this.eval_windows("c", "TRUE")) + interp_ok(this.eval_windows("c", "TRUE")) } else { let value = this.read_os_str_from_wide_str(value_ptr)?; this.machine.env_vars.windows_mut().map.insert(name, value); - Ok(this.eval_windows("c", "TRUE")) + interp_ok(this.eval_windows("c", "TRUE")) } } @@ -151,7 +151,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_u32(0)); + return interp_ok(Scalar::from_u32(0)); } // If we cannot get the current directory, we return 0 @@ -159,13 +159,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(cwd) => { // This can in fact return 0. It is up to the caller to set last_error to 0 // beforehand and check it afterwards to exclude that case. - return Ok(Scalar::from_u32(windows_check_buffer_size( + return interp_ok(Scalar::from_u32(windows_check_buffer_size( this.write_path_to_wide_str(&cwd, buf, size)?, ))); } Err(e) => this.set_last_error_from_io_error(e)?, } - Ok(Scalar::from_u32(0)) + interp_ok(Scalar::from_u32(0)) } #[allow(non_snake_case)] @@ -184,14 +184,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(this.eval_windows("c", "FALSE")); + return interp_ok(this.eval_windows("c", "FALSE")); } match env::set_current_dir(path) { - Ok(()) => Ok(this.eval_windows("c", "TRUE")), + Ok(()) => interp_ok(this.eval_windows("c", "TRUE")), Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(this.eval_windows("c", "FALSE")) + interp_ok(this.eval_windows("c", "FALSE")) } } } @@ -201,7 +201,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetCurrentProcessId"); - Ok(Scalar::from_u32(this.get_pid())) + interp_ok(Scalar::from_u32(this.get_pid())) } #[allow(non_snake_case)] @@ -227,7 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // See for docs. - Ok(match directories::UserDirs::new() { + interp_ok(match directories::UserDirs::new() { Some(dirs) => { let home = dirs.home_dir(); let size_avail = if this.ptr_is_null(size.ptr())? { diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 22634c509bed0..a4f40f9447e79 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -22,7 +22,7 @@ pub fn is_dyn_sym(name: &str) -> bool { #[cfg(windows)] fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { // We are on Windows so we can simply let the host do this. - Ok(path::absolute(path)) + interp_ok(path::absolute(path)) } #[cfg(unix)] @@ -33,7 +33,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { // If it starts with `//` (these were backslashes but are already converted) // then this is a magic special path, we just leave it unchanged. if bytes.get(0).copied() == Some(b'/') && bytes.get(1).copied() == Some(b'/') { - return Ok(Ok(path.into())); + return interp_ok(Ok(path.into())); }; // Special treatment for Windows' magic filenames: they are treated as being relative to `\\.\`. let magic_filenames = &[ @@ -43,7 +43,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { if magic_filenames.iter().any(|m| m.as_bytes() == bytes) { let mut result: Vec = br"//./".into(); result.extend(bytes); - return Ok(Ok(bytes_to_os_str(&result)?.into())); + return interp_ok(Ok(bytes_to_os_str(&result)?.into())); } // Otherwise we try to do something kind of close to what Windows does, but this is probably not // right in all cases. We iterate over the components between `/`, and remove trailing `.`, @@ -71,7 +71,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { } } // Let the host `absolute` function do working-dir handling - Ok(path::absolute(bytes_to_os_str(&result)?)) + interp_ok(path::absolute(bytes_to_os_str(&result)?)) } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -769,12 +769,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This function looks and behaves excatly like miri_start_unwind. let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?; this.handle_miri_start_unwind(payload)?; - return Ok(EmulateItemResult::NeedsUnwind); + return interp_ok(EmulateItemResult::NeedsUnwind); } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index 0a8e0e8fdfb75..69d78f58bed25 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -139,10 +139,10 @@ impl Handle { signed_handle as u32 } else { // if a handle doesn't fit in an i32, it isn't valid. - return Ok(None); + return interp_ok(None); }; - Ok(Self::from_packed(handle)) + interp_ok(Self::from_packed(handle)) } } @@ -167,6 +167,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => this.invalid_handle("CloseHandle")?, } - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 5786b17e50c92..6755c23039eeb 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -24,7 +24,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); - Ok(match this.init_once_status(id) { + interp_ok(match this.init_once_status(id) { InitOnceStatus::Uninitialized => { this.init_once_begin(id); this.write_scalar(this.eval_windows("c", "TRUE"), pending_place)?; @@ -70,7 +70,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.init_once_try_begin(id, &pending_place, dest)? { // Done! - return Ok(()); + return interp_ok(()); } // We have to block, and then try again when we are woken up. @@ -86,11 +86,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { @unblock = |this| { let ret = this.init_once_try_begin(id, &pending_place, &dest)?; assert!(ret, "we were woken up but init_once_try_begin still failed"); - Ok(()) + interp_ok(()) } ), ); - Ok(()) + interp_ok(()) } fn InitOnceComplete( @@ -130,7 +130,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.init_once_fail(id)?; } - Ok(this.eval_windows("c", "TRUE")) + interp_ok(this.eval_windows("c", "TRUE")) } fn WaitOnAddress( @@ -154,7 +154,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let invalid_param = this.eval_windows("c", "ERROR_INVALID_PARAMETER"); this.set_last_error(invalid_param)?; this.write_scalar(Scalar::from_i32(0), dest)?; - return Ok(()); + return interp_ok(()); }; let size = Size::from_bytes(size); @@ -188,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(1), dest)?; - Ok(()) + interp_ok(()) } fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> { @@ -202,7 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let addr = ptr.addr().bytes(); this.futex_wake(addr, u32::MAX)?; - Ok(()) + interp_ok(()) } fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -215,6 +215,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let addr = ptr.addr().bytes(); while this.futex_wake(addr, u32::MAX)? {} - Ok(()) + interp_ok(()) } } diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index a4c177311d46d..a920abea3a8a0 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -79,6 +79,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.join_thread(thread)?; - Ok(0) + interp_ok(0) } } diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index dbbae0731e0b7..3a8138654fd98 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -123,9 +123,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // TODO: Implement the `llvm.x86.aesni.aeskeygenassist` when possible // with an external crate. - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } @@ -162,5 +162,5 @@ fn aes_round<'tcx>( this.write_scalar(Scalar::from_u128(res), &dest)?; } - Ok(()) + interp_ok(()) } diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 169f8f8111adb..080e7c8ce0bcf 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -348,8 +348,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The only thing that needs to be ensured is the correct calling convention. let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 1dda1a10a0503..331b693a3e45b 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -435,8 +435,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { shift_simd_by_simd(this, left, right, which, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/bmi.rs b/src/tools/miri/src/shims/x86/bmi.rs index e70757f4397ee..59e76abf179c2 100644 --- a/src/tools/miri/src/shims/x86/bmi.rs +++ b/src/tools/miri/src/shims/x86/bmi.rs @@ -30,7 +30,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.expect_target_feature_for_intrinsic(link_name, target_feature)?; if is_64_bit && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -93,7 +93,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } result } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), }; let result = if is_64_bit { @@ -103,6 +103,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; this.write_scalar(result, dest)?; - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 8132de7d64697..19c678cb7faca 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -43,7 +43,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/subborrow-u32-subborrow-u64.html "addcarry.32" | "addcarry.64" | "subborrow.32" | "subborrow.64" => { if unprefixed_name.ends_with("64") && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [cb_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; @@ -67,7 +67,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let is_u64 = unprefixed_name.ends_with("64"); if is_u64 && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [c_in, a, b, out] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; @@ -157,9 +157,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } @@ -244,7 +244,7 @@ impl FloatBinOp { this.expect_target_feature_for_intrinsic(intrinsic, "avx")?; unord = !unord; } - Ok(Self::Cmp { gt, lt, eq, unord }) + interp_ok(Self::Cmp { gt, lt, eq, unord }) } } @@ -266,7 +266,7 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( Some(std::cmp::Ordering::Equal) => eq, Some(std::cmp::Ordering::Greater) => gt, }; - Ok(bool_to_simd_element(res, Size::from_bits(F::BITS))) + interp_ok(bool_to_simd_element(res, Size::from_bits(F::BITS))) } FloatBinOp::Min => { let left_scalar = left.to_scalar(); @@ -280,9 +280,9 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( || right.is_nan() || left >= right { - Ok(right_scalar) + interp_ok(right_scalar) } else { - Ok(left_scalar) + interp_ok(left_scalar) } } FloatBinOp::Max => { @@ -297,9 +297,9 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( || right.is_nan() || left <= right { - Ok(right_scalar) + interp_ok(right_scalar) } else { - Ok(left_scalar) + interp_ok(left_scalar) } } } @@ -332,7 +332,7 @@ fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>( this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } - Ok(()) + interp_ok(()) } /// Performs `which` operation on each component of `left` and @@ -360,7 +360,7 @@ fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } #[derive(Copy, Clone)] @@ -391,7 +391,7 @@ fn unary_op_f32<'tcx>( // Apply a relative error with a magnitude on the order of 2^-12 to simulate the // inaccuracy of RCP. let res = apply_random_float_error(this, div, -12); - Ok(Scalar::from_f32(res)) + interp_ok(Scalar::from_f32(res)) } FloatUnaryOp::Rsqrt => { let op = op.to_scalar().to_u32()?; @@ -401,7 +401,7 @@ fn unary_op_f32<'tcx>( // Apply a relative error with a magnitude on the order of 2^-12 to simulate the // inaccuracy of RSQRT. let res = apply_random_float_error(this, rsqrt, -12); - Ok(Scalar::from_f32(res)) + interp_ok(Scalar::from_f32(res)) } } } @@ -442,7 +442,7 @@ fn unary_op_ss<'tcx>( this.copy_op(&this.project_index(&op, i)?, &this.project_index(&dest, i)?)?; } - Ok(()) + interp_ok(()) } /// Performs `which` operation on each component of `op`, storing the @@ -466,7 +466,7 @@ fn unary_op_ps<'tcx>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } enum ShiftOp { @@ -532,7 +532,7 @@ fn shift_simd_by_scalar<'tcx>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } /// Shifts each element of `left` by the corresponding element of `right`. @@ -587,7 +587,7 @@ fn shift_simd_by_simd<'tcx>( this.write_scalar(res, &dest)?; } - Ok(()) + interp_ok(()) } /// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts @@ -633,7 +633,7 @@ fn round_first<'tcx, F: rustc_apfloat::Float>( this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } - Ok(()) + interp_ok(()) } // Rounds all elements of `op` according to `rounding`. @@ -659,7 +659,7 @@ fn round_all<'tcx, F: rustc_apfloat::Float>( )?; } - Ok(()) + interp_ok(()) } /// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of @@ -671,14 +671,14 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R match rounding & !0b1000 { // When the third bit is 0, the rounding mode is determined by the // first two bits. - 0b000 => Ok(rustc_apfloat::Round::NearestTiesToEven), - 0b001 => Ok(rustc_apfloat::Round::TowardNegative), - 0b010 => Ok(rustc_apfloat::Round::TowardPositive), - 0b011 => Ok(rustc_apfloat::Round::TowardZero), + 0b000 => interp_ok(rustc_apfloat::Round::NearestTiesToEven), + 0b001 => interp_ok(rustc_apfloat::Round::TowardNegative), + 0b010 => interp_ok(rustc_apfloat::Round::TowardPositive), + 0b011 => interp_ok(rustc_apfloat::Round::TowardZero), // When the third bit is 1, the rounding mode is determined by the // SSE status register. Since we do not support modifying it from // Miri (or Rust), we assume it to be at its default mode (round-to-nearest). - 0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven), + 0b100..=0b111 => interp_ok(rustc_apfloat::Round::NearestTiesToEven), rounding => panic!("invalid rounding mode 0x{rounding:02x}"), } } @@ -717,7 +717,7 @@ fn convert_float_to_int<'tcx>( this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?; } - Ok(()) + interp_ok(()) } /// Calculates absolute value of integers in `op` and stores the result in `dest`. @@ -747,7 +747,7 @@ fn int_abs<'tcx>( this.write_immediate(*res, &dest)?; } - Ok(()) + interp_ok(()) } /// Splits `op` (which must be a SIMD vector) into 128-bit chunks. @@ -778,7 +778,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( .unwrap(); let chunked_op = op.transmute(chunked_layout, this)?; - Ok((num_chunks, items_per_chunk, chunked_op)) + interp_ok((num_chunks, items_per_chunk, chunked_op)) } /// Horizontally performs `which` operation on adjacent values of @@ -830,7 +830,7 @@ fn horizontal_bin_op<'tcx>( } } - Ok(()) + interp_ok(()) } /// Conditionally multiplies the packed floating-point elements in @@ -892,7 +892,7 @@ fn conditional_dot_product<'tcx>( } } - Ok(()) + interp_ok(()) } /// Calculates two booleans. @@ -923,7 +923,7 @@ fn test_bits_masked<'tcx>( masked_set &= (op & mask) == mask; } - Ok((all_zero, masked_set)) + interp_ok((all_zero, masked_set)) } /// Calculates two booleans. @@ -956,7 +956,7 @@ fn test_high_bits_masked<'tcx>( negated &= (!op & mask) >> high_bit_offset == 0; } - Ok((direct, negated)) + interp_ok((direct, negated)) } /// Conditionally loads from `ptr` according the high bit of each @@ -989,7 +989,7 @@ fn mask_load<'tcx>( } } - Ok(()) + interp_ok(()) } /// Conditionally stores into `ptr` according the high bit of each @@ -1023,7 +1023,7 @@ fn mask_store<'tcx>( } } - Ok(()) + interp_ok(()) } /// Compute the sum of absolute differences of quadruplets of unsigned @@ -1082,7 +1082,7 @@ fn mpsadbw<'tcx>( } } - Ok(()) + interp_ok(()) } /// Multiplies packed 16-bit signed integer values, truncates the 32-bit @@ -1120,7 +1120,7 @@ fn pmulhrsw<'tcx>( this.write_scalar(Scalar::from_i16(res), &dest)?; } - Ok(()) + interp_ok(()) } /// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`, @@ -1182,7 +1182,7 @@ fn pclmulqdq<'tcx>( let dest_high = this.project_index(&dest, 1)?; this.write_scalar(Scalar::from_u64(result_high), &dest_high)?; - Ok(()) + interp_ok(()) } /// Packs two N-bit integer vectors to a single N/2-bit integers. @@ -1227,7 +1227,7 @@ fn pack_generic<'tcx>( } } - Ok(()) + interp_ok(()) } /// Converts two 16-bit integer vectors to a single 8-bit integer @@ -1245,7 +1245,7 @@ fn packsswb<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i16()?; let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX }); - Ok(Scalar::from_i8(res)) + interp_ok(Scalar::from_i8(res)) }) } @@ -1264,7 +1264,7 @@ fn packuswb<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i16()?; let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX }); - Ok(Scalar::from_u8(res)) + interp_ok(Scalar::from_u8(res)) }) } @@ -1283,7 +1283,7 @@ fn packssdw<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i32()?; let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX }); - Ok(Scalar::from_i16(res)) + interp_ok(Scalar::from_i16(res)) }) } @@ -1302,7 +1302,7 @@ fn packusdw<'tcx>( pack_generic(this, left, right, dest, |op| { let op = op.to_i32()?; let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX }); - Ok(Scalar::from_u16(res)) + interp_ok(Scalar::from_u16(res)) }) } @@ -1334,7 +1334,7 @@ fn psign<'tcx>( this.write_immediate(*res, &dest)?; } - Ok(()) + interp_ok(()) } /// Calcultates either `a + b + cb_in` or `a - b - cb_in` depending on the value @@ -1358,5 +1358,5 @@ fn carrying_add<'tcx>( this.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(this); let cb_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?; - Ok((sum, Scalar::from_u8(cb_out.into()))) + interp_ok((sum, Scalar::from_u8(cb_out.into()))) } diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs index d9cd34377e683..8553771b694e6 100644 --- a/src/tools/miri/src/shims/x86/sha.rs +++ b/src/tools/miri/src/shims/x86/sha.rs @@ -31,7 +31,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let projected = &this.project_index(reg, i.try_into().unwrap())?; *dst = this.read_scalar(projected)?.to_u32()? } - Ok(res) + interp_ok(res) } fn write<'c>( @@ -45,7 +45,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let projected = &this.project_index(dest, i.try_into().unwrap())?; this.write_scalar(Scalar::from_u32(part), projected)?; } - Ok(()) + interp_ok(()) } match unprefixed_name { @@ -106,9 +106,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = sha256msg2(a, b); write(this, &dest, result)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index 254d25bb5293a..013900e95d362 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -201,8 +201,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 7c23e37266430..de72e63f8aff8 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -344,8 +344,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs index ef5a55d6eb28e..9aa4dd5995059 100644 --- a/src/tools/miri/src/shims/x86/sse3.rs +++ b/src/tools/miri/src/shims/x86/sse3.rs @@ -47,8 +47,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mem_copy(src_ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index 8fcdc491c0ca3..7bf8ddea2fec8 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -172,8 +172,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(res.into()), dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index 0b6e0d9e0d7e7..f31cd40634972 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -115,7 +115,7 @@ fn compare_strings<'tcx>( (false, true) => i32::from(ch.to_i8()?), (false, false) => i32::from(ch.to_u8()?), }; - Ok(result) + interp_ok(result) }; for i in 0..len2 { @@ -183,7 +183,7 @@ fn compare_strings<'tcx>( _ => (), } - Ok(result) + interp_ok(result) } /// Obtain the arguments of the intrinsic based on its name. @@ -235,7 +235,7 @@ fn deconstruct_args<'tcx>( let str1 = str1.transmute(array_layout, this)?; let str2 = str2.transmute(array_layout, this)?; - Ok((str1, str2, Some((len1, len2)), imm)) + interp_ok((str1, str2, Some((len1, len2)), imm)) } else { let [str1, str2, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let imm = this.read_scalar(imm)?.to_u8()?; @@ -244,7 +244,7 @@ fn deconstruct_args<'tcx>( let str1 = str1.transmute(array_layout, this)?; let str2 = str2.transmute(array_layout, this)?; - Ok((str1, str2, None, imm)) + interp_ok((str1, str2, None, imm)) } } @@ -266,7 +266,7 @@ fn implicit_len<'tcx>( break; } } - Ok(result) + interp_ok(result) } #[inline] @@ -433,7 +433,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; if bit_size == 64 && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateItemResult::NotSupported); + return interp_ok(EmulateItemResult::NotSupported); } let [left, right] = @@ -493,8 +493,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index 096b0fb9e4b17..76a2451dea415 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -133,8 +133,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { psign(this, left, right, dest)?; } - _ => return Ok(EmulateItemResult::NotSupported), + _ => return interp_ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsReturn) + interp_ok(EmulateItemResult::NeedsReturn) } } From 4891dd4627be38fa6133fcf3cfb0c1a65867bf87 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Sep 2024 11:53:23 +0200 Subject: [PATCH 1131/1301] make InterpResult a dedicated type to avoid accidentally discarding the error --- clippy_utils/src/consts.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index bf47cf6d372d2..53a1170d6a618 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -870,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), - FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), - FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)), - FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().ok()?)), + FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)), + FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), + FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), + FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)), }); } Some(Constant::Vec(res)) @@ -903,7 +903,7 @@ fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option Date: Sun, 15 Sep 2024 18:31:58 +0200 Subject: [PATCH 1132/1301] Stabilize `const_slice_from_raw_parts_mut` --- library/alloc/tests/lib.rs | 1 - library/core/src/lib.rs | 1 - library/core/src/ptr/mod.rs | 2 +- library/core/src/ptr/non_null.rs | 5 ++++- library/core/src/slice/raw.rs | 3 ++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 1d07a7690da43..58d39416d9577 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -7,7 +7,6 @@ #![feature(const_cow_is_borrowed)] #![feature(const_heap)] #![cfg_attr(bootstrap, feature(const_mut_refs))] -#![feature(const_slice_from_raw_parts_mut)] #![feature(const_ptr_write)] #![feature(const_try)] #![feature(core_intrinsics)] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 817d9e3b962e3..b5bc985519187 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -147,7 +147,6 @@ #![feature(const_replace)] #![feature(const_size_of_val)] #![feature(const_size_of_val_raw)] -#![feature(const_slice_from_raw_parts_mut)] #![feature(const_slice_from_ref)] #![feature(const_slice_split_at_mut)] #![feature(const_str_as_mut)] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index b6df780fe2f4a..205b25f2d1a5d 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -992,7 +992,7 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { /// ``` #[inline] #[stable(feature = "slice_from_raw_parts", since = "1.42.0")] -#[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] +#[rustc_const_stable(feature = "const_slice_from_raw_parts_mut", since = "CURRENT_RUSTC_VERSION")] #[rustc_diagnostic_item = "ptr_slice_from_raw_parts_mut"] pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { from_raw_parts_mut(data, len) diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index e7a265f7e2b7d..da2404e25f389 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1433,7 +1433,10 @@ impl NonNull<[T]> { /// (Note that this example artificially demonstrates a use of this method, /// but `let slice = NonNull::from(&x[..]);` would be a better way to write code like this.) #[stable(feature = "nonnull_slice_from_raw_parts", since = "1.70.0")] - #[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] + #[rustc_const_stable( + feature = "const_slice_from_raw_parts_mut", + since = "CURRENT_RUSTC_VERSION" + )] #[must_use] #[inline] pub const fn slice_from_raw_parts(data: NonNull, len: usize) -> Self { diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 2cf3fecb47542..84e916b9a84e7 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -171,7 +171,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] /// [`NonNull::dangling()`]: ptr::NonNull::dangling #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] +#[rustc_const_stable(feature = "const_slice_from_raw_parts_mut", since = "CURRENT_RUSTC_VERSION")] +#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] #[must_use] #[rustc_diagnostic_item = "slice_from_raw_parts_mut"] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { From c1acccdf1744561d4dda9b943fa91d873cea3b40 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 1 Oct 2024 14:57:38 +0200 Subject: [PATCH 1133/1301] std: replace `LazyBox` with `OnceBox` This PR replaces the `LazyBox` wrapper used to allocate the pthread primitives with `OnceBox`, which has a more familiar API mirroring that of `OnceLock`. This cleans up the code in preparation for larger changes like #128184 (from which this PR was split) and allows some neat optimizations, like avoid an acquire-load of the allocation pointer in `Mutex::unlock`, where the initialization of the allocation must have already been observed. Additionally, I've gotten rid of the TEEOS `Condvar` code, it's just a duplicate of the pthread one anyway and I didn't want to repeat myself. --- library/std/src/sys/sync/condvar/mod.rs | 8 +- library/std/src/sys/sync/condvar/pthread.rs | 38 ++++---- library/std/src/sys/sync/condvar/sgx.rs | 29 +++--- library/std/src/sys/sync/condvar/teeos.rs | 101 -------------------- library/std/src/sys/sync/mod.rs | 3 + library/std/src/sys/sync/mutex/mod.rs | 2 +- library/std/src/sys/sync/mutex/pthread.rs | 81 +++++++++------- library/std/src/sys/sync/mutex/sgx.rs | 28 +++--- library/std/src/sys/sync/once_box.rs | 82 ++++++++++++++++ library/std/src/sys/sync/rwlock/teeos.rs | 8 +- library/std/src/sys_common/lazy_box.rs | 88 ----------------- library/std/src/sys_common/mod.rs | 1 - 12 files changed, 185 insertions(+), 284 deletions(-) delete mode 100644 library/std/src/sys/sync/condvar/teeos.rs create mode 100644 library/std/src/sys/sync/once_box.rs delete mode 100644 library/std/src/sys_common/lazy_box.rs diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs index 6849cacf88e76..d0c998a559737 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -12,7 +12,10 @@ cfg_if::cfg_if! { ))] { mod futex; pub use futex::Condvar; - } else if #[cfg(target_family = "unix")] { + } else if #[cfg(any( + target_family = "unix", + target_os = "teeos", + ))] { mod pthread; pub use pthread::Condvar; } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { @@ -24,9 +27,6 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "solid_asp3")] { mod itron; pub use itron::Condvar; - } else if #[cfg(target_os = "teeos")] { - mod teeos; - pub use teeos::Condvar; } else if #[cfg(target_os = "xous")] { mod xous; pub use xous::Condvar; diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index 5b5e7770b0627..986cd0cb7d188 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -2,31 +2,25 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::sync::{Mutex, mutex}; +use crate::sys::sync::{Mutex, OnceBox}; #[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; #[cfg(target_os = "nto")] use crate::sys::time::TIMESPEC_MAX_CAPPED; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; use crate::time::Duration; struct AllocatedCondvar(UnsafeCell); pub struct Condvar { - inner: LazyBox, + inner: OnceBox, mutex: AtomicPtr, } -#[inline] -fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { - c.inner.0.get() -} - unsafe impl Send for AllocatedCondvar {} unsafe impl Sync for AllocatedCondvar {} -impl LazyInit for AllocatedCondvar { - fn init() -> Box { +impl AllocatedCondvar { + fn new() -> Box { let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); cfg_if::cfg_if! { @@ -37,7 +31,7 @@ impl LazyInit for AllocatedCondvar { target_vendor = "apple", ))] { // `pthread_condattr_setclock` is unfortunately not supported on these platforms. - } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { + } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "teeos"))] { // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet // So on that platform, init() should always be called // Moreover, that platform does not have pthread_condattr_setclock support, @@ -82,7 +76,11 @@ impl Drop for AllocatedCondvar { impl Condvar { pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + Condvar { inner: OnceBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + } + + fn get(&self) -> *mut libc::pthread_cond_t { + self.inner.get_or_init(AllocatedCondvar::new).0.get() } #[inline] @@ -98,21 +96,21 @@ impl Condvar { #[inline] pub fn notify_one(&self) { - let r = unsafe { libc::pthread_cond_signal(raw(self)) }; + let r = unsafe { libc::pthread_cond_signal(self.get()) }; debug_assert_eq!(r, 0); } #[inline] pub fn notify_all(&self) { - let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; + let r = unsafe { libc::pthread_cond_broadcast(self.get()) }; debug_assert_eq!(r, 0); } #[inline] pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = mutex::raw(mutex); + let mutex = mutex.get_assert_locked(); self.verify(mutex); - let r = libc::pthread_cond_wait(raw(self), mutex); + let r = libc::pthread_cond_wait(self.get(), mutex); debug_assert_eq!(r, 0); } @@ -129,7 +127,7 @@ impl Condvar { pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { use crate::sys::time::Timespec; - let mutex = mutex::raw(mutex); + let mutex = mutex.get_assert_locked(); self.verify(mutex); #[cfg(not(target_os = "nto"))] @@ -144,7 +142,7 @@ impl Condvar { .and_then(|t| t.to_timespec_capped()) .unwrap_or(TIMESPEC_MAX_CAPPED); - let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); + let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); assert!(r == libc::ETIMEDOUT || r == 0); r == 0 } @@ -162,7 +160,7 @@ impl Condvar { use crate::sys::time::SystemTime; use crate::time::Instant; - let mutex = mutex::raw(mutex); + let mutex = mutex.get_assert_locked(); self.verify(mutex); // OSX implementation of `pthread_cond_timedwait` is buggy @@ -188,7 +186,7 @@ impl Condvar { .and_then(|t| t.to_timespec()) .unwrap_or(TIMESPEC_MAX); - let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); + let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); // ETIMEDOUT is not a totally reliable method of determining timeout due diff --git a/library/std/src/sys/sync/condvar/sgx.rs b/library/std/src/sys/sync/condvar/sgx.rs index ecb5872f60d90..e60715e4b592e 100644 --- a/library/std/src/sys/sync/condvar/sgx.rs +++ b/library/std/src/sys/sync/condvar/sgx.rs @@ -1,44 +1,39 @@ use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable}; -use crate::sys::sync::Mutex; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::sys::sync::{Mutex, OnceBox}; use crate::time::Duration; -/// FIXME: `UnsafeList` is not movable. -struct AllocatedCondvar(SpinMutex>); - pub struct Condvar { - inner: LazyBox, -} - -impl LazyInit for AllocatedCondvar { - fn init() -> Box { - Box::new(AllocatedCondvar(SpinMutex::new(WaitVariable::new(())))) - } + // FIXME: `UnsafeList` is not movable. + inner: OnceBox>>, } impl Condvar { pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new() } + Condvar { inner: OnceBox::new() } + } + + fn get(&self) -> &SpinMutex> { + self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(())))) } #[inline] pub fn notify_one(&self) { - let _ = WaitQueue::notify_one(self.inner.0.lock()); + let _ = WaitQueue::notify_one(self.get().lock()); } #[inline] pub fn notify_all(&self) { - let _ = WaitQueue::notify_all(self.inner.0.lock()); + let _ = WaitQueue::notify_all(self.get().lock()); } pub unsafe fn wait(&self, mutex: &Mutex) { - let guard = self.inner.0.lock(); + let guard = self.get().lock(); WaitQueue::wait(guard, || unsafe { mutex.unlock() }); mutex.lock() } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let success = WaitQueue::wait_timeout(&self.inner.0, dur, || unsafe { mutex.unlock() }); + let success = WaitQueue::wait_timeout(self.get(), dur, || unsafe { mutex.unlock() }); mutex.lock(); success } diff --git a/library/std/src/sys/sync/condvar/teeos.rs b/library/std/src/sys/sync/condvar/teeos.rs deleted file mode 100644 index 943867cd76169..0000000000000 --- a/library/std/src/sys/sync/condvar/teeos.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::sync::mutex::{self, Mutex}; -use crate::sys::time::TIMESPEC_MAX; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::time::Duration; - -extern "C" { - pub fn pthread_cond_timedwait( - cond: *mut libc::pthread_cond_t, - lock: *mut libc::pthread_mutex_t, - adstime: *const libc::timespec, - ) -> libc::c_int; -} - -struct AllocatedCondvar(UnsafeCell); - -pub struct Condvar { - inner: LazyBox, - mutex: AtomicPtr, -} - -#[inline] -fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { - c.inner.0.get() -} - -unsafe impl Send for AllocatedCondvar {} -unsafe impl Sync for AllocatedCondvar {} - -impl LazyInit for AllocatedCondvar { - fn init() -> Box { - let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); - - let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; - assert_eq!(r, 0); - - condvar - } -} - -impl Drop for AllocatedCondvar { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; - debug_assert_eq!(r, 0); - } -} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } - } - - #[inline] - fn verify(&self, mutex: *mut libc::pthread_mutex_t) { - match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { - Ok(_) => {} // Stored the address - Err(n) if n == mutex => {} // Lost a race to store the same address - _ => panic!("attempted to use a condition variable with two mutexes"), - } - } - - #[inline] - pub fn notify_one(&self) { - let r = unsafe { libc::pthread_cond_signal(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub fn notify_all(&self) { - let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = unsafe { mutex::raw(mutex) }; - self.verify(mutex); - let r = unsafe { libc::pthread_cond_wait(raw(self), mutex) }; - debug_assert_eq!(r, 0); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - use crate::sys::time::Timespec; - - let mutex = unsafe { mutex::raw(mutex) }; - self.verify(mutex); - - let timeout = Timespec::now(libc::CLOCK_MONOTONIC) - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec()) - .unwrap_or(TIMESPEC_MAX); - - let r = unsafe { pthread_cond_timedwait(raw(self), mutex, &timeout) }; - assert!(r == libc::ETIMEDOUT || r == 0); - r == 0 - } -} diff --git a/library/std/src/sys/sync/mod.rs b/library/std/src/sys/sync/mod.rs index 52fac5902a296..0691e96785198 100644 --- a/library/std/src/sys/sync/mod.rs +++ b/library/std/src/sys/sync/mod.rs @@ -1,11 +1,14 @@ mod condvar; mod mutex; mod once; +mod once_box; mod rwlock; mod thread_parking; pub use condvar::Condvar; pub use mutex::Mutex; pub use once::{Once, OnceState}; +#[allow(unused)] // Only used on some platforms. +use once_box::OnceBox; pub use rwlock::RwLock; pub use thread_parking::Parker; diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs index 73d9bd273de17..360df3fc4b55d 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -19,7 +19,7 @@ cfg_if::cfg_if! { target_os = "teeos", ))] { mod pthread; - pub use pthread::{Mutex, raw}; + pub use pthread::Mutex; } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { mod windows7; pub use windows7::{Mutex, raw}; diff --git a/library/std/src/sys/sync/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs index 1c407bc253776..87c95f45f964e 100644 --- a/library/std/src/sys/sync/mutex/pthread.rs +++ b/library/std/src/sys/sync/mutex/pthread.rs @@ -2,24 +2,19 @@ use crate::cell::UnsafeCell; use crate::io::Error; use crate::mem::{MaybeUninit, forget}; use crate::sys::cvt_nz; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::sys::sync::OnceBox; struct AllocatedMutex(UnsafeCell); pub struct Mutex { - inner: LazyBox, -} - -#[inline] -pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { - m.inner.0.get() + inner: OnceBox, } unsafe impl Send for AllocatedMutex {} unsafe impl Sync for AllocatedMutex {} -impl LazyInit for AllocatedMutex { - fn init() -> Box { +impl AllocatedMutex { + fn new() -> Box { let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))); // Issue #33770 @@ -60,24 +55,6 @@ impl LazyInit for AllocatedMutex { mutex } - - fn destroy(mutex: Box) { - // We're not allowed to pthread_mutex_destroy a locked mutex, - // so check first if it's unlocked. - if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { - unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; - drop(mutex); - } else { - // The mutex is locked. This happens if a MutexGuard is leaked. - // In this case, we just leak the Mutex too. - forget(mutex); - } - } - - fn cancel_init(_: Box) { - // In this case, we can just drop it without any checks, - // since it cannot have been locked yet. - } } impl Drop for AllocatedMutex { @@ -99,11 +76,33 @@ impl Drop for AllocatedMutex { impl Mutex { #[inline] pub const fn new() -> Mutex { - Mutex { inner: LazyBox::new() } + Mutex { inner: OnceBox::new() } + } + + /// Gets access to the pthread mutex under the assumption that the mutex is + /// locked. + /// + /// This allows skipping the initialization check, as the mutex can only be + /// locked if it is already initialized, and allows relaxing the ordering + /// on the pointer load, since the allocation cannot have been modified + /// since the `lock` and the lock must have occurred on the current thread. + /// + /// # Safety + /// Causes undefined behaviour if the mutex is not locked. + #[inline] + pub(crate) unsafe fn get_assert_locked(&self) -> *mut libc::pthread_mutex_t { + unsafe { self.inner.get_unchecked().0.get() } } #[inline] - pub unsafe fn lock(&self) { + fn get(&self) -> *mut libc::pthread_mutex_t { + // If initialization fails, the mutex is destroyed. This is always sound, + // however, as the mutex cannot have been locked yet. + self.inner.get_or_init(AllocatedMutex::new).0.get() + } + + #[inline] + pub fn lock(&self) { #[cold] #[inline(never)] fn fail(r: i32) -> ! { @@ -111,7 +110,7 @@ impl Mutex { panic!("failed to lock mutex: {error}"); } - let r = libc::pthread_mutex_lock(raw(self)); + let r = unsafe { libc::pthread_mutex_lock(self.get()) }; // As we set the mutex type to `PTHREAD_MUTEX_NORMAL` above, we expect // the lock call to never fail. Unfortunately however, some platforms // (Solaris) do not conform to the standard, and instead always provide @@ -126,13 +125,29 @@ impl Mutex { #[inline] pub unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(raw(self)); + let r = libc::pthread_mutex_unlock(self.get_assert_locked()); debug_assert_eq!(r, 0); } #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(raw(self)) == 0 + pub fn try_lock(&self) -> bool { + unsafe { libc::pthread_mutex_trylock(self.get()) == 0 } + } +} + +impl Drop for Mutex { + fn drop(&mut self) { + let Some(mutex) = self.inner.take() else { return }; + // We're not allowed to pthread_mutex_destroy a locked mutex, + // so check first if it's unlocked. + if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { + unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; + drop(mutex); + } else { + // The mutex is locked. This happens if a MutexGuard is leaked. + // In this case, we just leak the Mutex too. + forget(mutex); + } } } diff --git a/library/std/src/sys/sync/mutex/sgx.rs b/library/std/src/sys/sync/mutex/sgx.rs index 65d1e880f7baf..8529e85797043 100644 --- a/library/std/src/sys/sync/mutex/sgx.rs +++ b/library/std/src/sys/sync/mutex/sgx.rs @@ -1,28 +1,24 @@ use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable, try_lock_or_false}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -/// FIXME: `UnsafeList` is not movable. -struct AllocatedMutex(SpinMutex>); +use crate::sys::sync::OnceBox; pub struct Mutex { - inner: LazyBox, -} - -impl LazyInit for AllocatedMutex { - fn init() -> Box { - Box::new(AllocatedMutex(SpinMutex::new(WaitVariable::new(false)))) - } + // FIXME: `UnsafeList` is not movable. + inner: OnceBox>>, } // Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 impl Mutex { pub const fn new() -> Mutex { - Mutex { inner: LazyBox::new() } + Mutex { inner: OnceBox::new() } + } + + fn get(&self) -> &SpinMutex> { + self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(false)))) } #[inline] pub fn lock(&self) { - let mut guard = self.inner.0.lock(); + let mut guard = self.get().lock(); if *guard.lock_var() { // Another thread has the lock, wait WaitQueue::wait(guard, || {}) @@ -35,7 +31,9 @@ impl Mutex { #[inline] pub unsafe fn unlock(&self) { - let guard = self.inner.0.lock(); + // SAFETY: the mutex was locked by the current thread, so it has been + // initialized already. + let guard = unsafe { self.inner.get_unchecked().lock() }; if let Err(mut guard) = WaitQueue::notify_one(guard) { // No other waiters, unlock *guard.lock_var_mut() = false; @@ -46,7 +44,7 @@ impl Mutex { #[inline] pub fn try_lock(&self) -> bool { - let mut guard = try_lock_or_false!(self.inner.0); + let mut guard = try_lock_or_false!(self.get()); if *guard.lock_var() { // Another thread has the lock false diff --git a/library/std/src/sys/sync/once_box.rs b/library/std/src/sys/sync/once_box.rs new file mode 100644 index 0000000000000..1422b5a172162 --- /dev/null +++ b/library/std/src/sys/sync/once_box.rs @@ -0,0 +1,82 @@ +//! A racily-initialized alternative to `OnceLock>`. +//! +//! This is used to implement synchronization primitives that need allocation, +//! like the pthread versions. + +#![allow(dead_code)] // Only used on some platforms. + +use crate::mem::replace; +use crate::ptr::null_mut; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed}; + +pub(crate) struct OnceBox { + ptr: AtomicPtr, +} + +impl OnceBox { + #[inline] + pub const fn new() -> Self { + Self { ptr: AtomicPtr::new(null_mut()) } + } + + /// Gets access to the value, assuming it is already initialized and this + /// initialization has been observed by the current thread. + /// + /// Since all modifications to the pointer have already been observed, the + /// pointer load in this function can be performed with relaxed ordering, + /// potentially allowing the optimizer to turn code like this: + /// ```rust, ignore + /// once_box.get_or_init(|| Box::new(42)); + /// unsafe { once_box.get_unchecked() } + /// ``` + /// into + /// ```rust, ignore + /// once_box.get_or_init(|| Box::new(42)) + /// ``` + /// + /// # Safety + /// This causes undefined behaviour if the assumption above is violated. + #[inline] + pub unsafe fn get_unchecked(&self) -> &T { + unsafe { &*self.ptr.load(Relaxed) } + } + + #[inline] + pub fn get_or_init(&self, f: impl FnOnce() -> Box) -> &T { + let ptr = self.ptr.load(Acquire); + match unsafe { ptr.as_ref() } { + Some(val) => val, + None => self.initialize(f), + } + } + + #[inline] + pub fn take(&mut self) -> Option> { + let ptr = replace(self.ptr.get_mut(), null_mut()); + if !ptr.is_null() { Some(unsafe { Box::from_raw(ptr) }) } else { None } + } + + #[cold] + fn initialize(&self, f: impl FnOnce() -> Box) -> &T { + let new_ptr = Box::into_raw(f()); + match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) { + Ok(_) => unsafe { &*new_ptr }, + Err(ptr) => { + // Lost the race to another thread. + // Drop the value we created, and use the one from the other thread instead. + drop(unsafe { Box::from_raw(new_ptr) }); + unsafe { &*ptr } + } + } + } +} + +unsafe impl Send for OnceBox {} +unsafe impl Sync for OnceBox {} + +impl Drop for OnceBox { + fn drop(&mut self) { + self.take(); + } +} diff --git a/library/std/src/sys/sync/rwlock/teeos.rs b/library/std/src/sys/sync/rwlock/teeos.rs index ef9b1ab51546c..763430223834b 100644 --- a/library/std/src/sys/sync/rwlock/teeos.rs +++ b/library/std/src/sys/sync/rwlock/teeos.rs @@ -14,22 +14,22 @@ impl RwLock { #[inline] pub fn read(&self) { - unsafe { self.inner.lock() }; + self.inner.lock() } #[inline] pub fn try_read(&self) -> bool { - unsafe { self.inner.try_lock() } + self.inner.try_lock() } #[inline] pub fn write(&self) { - unsafe { self.inner.lock() }; + self.inner.lock() } #[inline] pub unsafe fn try_write(&self) -> bool { - unsafe { self.inner.try_lock() } + self.inner.try_lock() } #[inline] diff --git a/library/std/src/sys_common/lazy_box.rs b/library/std/src/sys_common/lazy_box.rs deleted file mode 100644 index b45b05f63baaa..0000000000000 --- a/library/std/src/sys_common/lazy_box.rs +++ /dev/null @@ -1,88 +0,0 @@ -#![allow(dead_code)] // Only used on some platforms. - -// This is used to wrap pthread {Mutex, Condvar, RwLock} in. - -use crate::marker::PhantomData; -use crate::ops::{Deref, DerefMut}; -use crate::ptr::null_mut; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::Ordering::{AcqRel, Acquire}; - -pub(crate) struct LazyBox { - ptr: AtomicPtr, - _phantom: PhantomData, -} - -pub(crate) trait LazyInit { - /// This is called before the box is allocated, to provide the value to - /// move into the new box. - /// - /// It might be called more than once per LazyBox, as multiple threads - /// might race to initialize it concurrently, each constructing and initializing - /// their own box. All but one of them will be passed to `cancel_init` right after. - fn init() -> Box; - - /// Any surplus boxes from `init()` that lost the initialization race - /// are passed to this function for disposal. - /// - /// The default implementation calls destroy(). - fn cancel_init(x: Box) { - Self::destroy(x); - } - - /// This is called to destroy a used box. - /// - /// The default implementation just drops it. - fn destroy(_: Box) {} -} - -impl LazyBox { - #[inline] - pub const fn new() -> Self { - Self { ptr: AtomicPtr::new(null_mut()), _phantom: PhantomData } - } - - #[inline] - fn get_pointer(&self) -> *mut T { - let ptr = self.ptr.load(Acquire); - if ptr.is_null() { self.initialize() } else { ptr } - } - - #[cold] - fn initialize(&self) -> *mut T { - let new_ptr = Box::into_raw(T::init()); - match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) { - Ok(_) => new_ptr, - Err(ptr) => { - // Lost the race to another thread. - // Drop the box we created, and use the one from the other thread instead. - T::cancel_init(unsafe { Box::from_raw(new_ptr) }); - ptr - } - } - } -} - -impl Deref for LazyBox { - type Target = T; - #[inline] - fn deref(&self) -> &T { - unsafe { &*self.get_pointer() } - } -} - -impl DerefMut for LazyBox { - #[inline] - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.get_pointer() } - } -} - -impl Drop for LazyBox { - fn drop(&mut self) { - let ptr = *self.ptr.get_mut(); - if !ptr.is_null() { - T::destroy(unsafe { Box::from_raw(ptr) }); - } - } -} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index aa27886ff6f9c..4f7a131f6bb90 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -22,7 +22,6 @@ mod tests; pub mod fs; pub mod io; -pub mod lazy_box; pub mod process; pub mod wstr; pub mod wtf8; From 9be9141730ce8f78d5ffa9f72541f2eca13ab1d1 Mon Sep 17 00:00:00 2001 From: Henry Jiang Date: Tue, 1 Oct 2024 16:06:24 -0400 Subject: [PATCH 1134/1301] increase stack size for aix --- compiler/rustc_data_structures/src/stack.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_data_structures/src/stack.rs b/compiler/rustc_data_structures/src/stack.rs index 7ff1339c5ab34..72add175091fa 100644 --- a/compiler/rustc_data_structures/src/stack.rs +++ b/compiler/rustc_data_structures/src/stack.rs @@ -5,7 +5,11 @@ const RED_ZONE: usize = 100 * 1024; // 100k // Only the first stack that is pushed, grows exponentially (2^n * STACK_PER_RECURSION) from then // on. This flag has performance relevant characteristics. Don't set it too high. +#[cfg(not(target_os="aix"))] const STACK_PER_RECURSION: usize = 1024 * 1024; // 1MB +// LLVM for AIX doesn't feature TCO, increase recursion size for workaround. +#[cfg(target_os="aix")] +const STACK_PER_RECURSION: usize = 16 * 1024 * 1024; // 16MB /// Grows the stack on demand to prevent stack overflow. Call this in strategic locations /// to "break up" recursive calls. E.g. almost any call to `visit_expr` or equivalent can benefit From 9de4b62faf0d0156106a6614589bc78da6531e13 Mon Sep 17 00:00:00 2001 From: Anushrut <32019180+AvatarSenju@users.noreply.github.com> Date: Wed, 2 Oct 2024 01:47:18 +0530 Subject: [PATCH 1135/1301] Update helper docs display disable option Updated helper docs via configure.py to make it clearer that users can control options with enable and disable --- src/bootstrap/configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 49d564642bd65..a555a26367d51 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -193,7 +193,7 @@ def is_value_list(key): if option.value: print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc)) else: - print('\t{:30} {}'.format('--enable-{}'.format(option.name), option.desc)) + print('\t{:30} {}'.format('--enable-{} OR --disable-{}'.format(option.name, option.name), option.desc)) print('') print('This configure script is a thin configuration shim over the true') print('configuration system, `config.toml`. You can explore the comments') From c88cb08afc100a400cf8f0095bac5b83175b5791 Mon Sep 17 00:00:00 2001 From: Rob Shearman Date: Thu, 18 Jul 2024 13:38:05 +0100 Subject: [PATCH 1136/1301] Fix `mut_mutex_lock` when reference not ultimately mutable When there is are multiple references where one of the references isn't mutable then this results in a false-positive for `mut_mutex_lock` as it only checks the mutability of the first reference level. Fix this by using `peel_mid_ty_refs_is_mutable` which correctly determines whether the reference is ultimately mutable and thus whether `Mutex::get_lock()` can actually be used. Fixes #9854 --- clippy_lints/src/methods/mut_mutex_lock.rs | 6 +++--- tests/ui/mut_mutex_lock.fixed | 12 ++++++++++++ tests/ui/mut_mutex_lock.rs | 12 ++++++++++++ tests/ui/mut_mutex_lock.stderr | 8 +++++++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 83e8370f939bc..320523aceb675 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::{Span, sym}; use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) - && let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind() + && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) + && ref_depth >= 1 && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) diff --git a/tests/ui/mut_mutex_lock.fixed b/tests/ui/mut_mutex_lock.fixed index bbedbb2bed23f..29c5a27cb6247 100644 --- a/tests/ui/mut_mutex_lock.fixed +++ b/tests/ui/mut_mutex_lock.fixed @@ -9,6 +9,11 @@ fn mut_mutex_lock() { let mut value = value_mutex.get_mut().unwrap(); *value += 1; + + let mut value_mutex = Mutex::new(42_u8); + let mut_ref_mut_ref_mutex = &mut &mut value_mutex; + let mut value = mut_ref_mut_ref_mutex.get_mut().unwrap(); + *value += 1; } fn no_owned_mutex_lock() { @@ -24,4 +29,11 @@ fn issue9415() { *guard += 1; } +fn mut_ref_ref_mutex_lock() { + let mutex = Mutex::new(42_u8); + let mut_ref_ref_mutex = &mut &mutex; + let mut guard = mut_ref_ref_mutex.lock().unwrap(); + *guard += 1; +} + fn main() {} diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 74116100e82a7..fcdb3ff97dbc5 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -9,6 +9,11 @@ fn mut_mutex_lock() { let mut value = value_mutex.lock().unwrap(); *value += 1; + + let mut value_mutex = Mutex::new(42_u8); + let mut_ref_mut_ref_mutex = &mut &mut value_mutex; + let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); + *value += 1; } fn no_owned_mutex_lock() { @@ -24,4 +29,11 @@ fn issue9415() { *guard += 1; } +fn mut_ref_ref_mutex_lock() { + let mutex = Mutex::new(42_u8); + let mut_ref_ref_mutex = &mut &mutex; + let mut guard = mut_ref_ref_mutex.lock().unwrap(); + *guard += 1; +} + fn main() {} diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index a3d4905c04cb7..92601c4c61215 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -7,5 +7,11 @@ LL | let mut value = value_mutex.lock().unwrap(); = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mutex_lock)]` -error: aborting due to 1 previous error +error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference + --> tests/ui/mut_mutex_lock.rs:15:43 + | +LL | let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); + | ^^^^ help: change this to: `get_mut` + +error: aborting due to 2 previous errors From 5b1a2b8712887fff7e0295d4e87aac24ea73797d Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 1 Oct 2024 20:52:17 +0000 Subject: [PATCH 1137/1301] TransmuteFrom: Gracefully handle unnormalized types and normalization errors Fixes #130413 --- .../traits/fulfillment_errors.rs | 8 +++++ .../src/error_reporting/traits/mod.rs | 1 + compiler/rustc_transmute/src/layout/tree.rs | 5 +-- tests/ui/transmutability/assoc-bound.rs | 25 +++++++++++++++ tests/ui/transmutability/assoc-bound.stderr | 31 +++++++++++++++++++ 5 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/ui/transmutability/assoc-bound.rs create mode 100644 tests/ui/transmutability/assoc-bound.stderr 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 34a0f182ab45f..f494d9637f324 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 @@ -245,6 +245,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, "silent safe transmute error" ); } + GetSafeTransmuteErrorAndReason::Default => { + (err_msg, None) + } GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation, @@ -2221,6 +2224,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> GetSafeTransmuteErrorAndReason { use rustc_transmute::Answer; + if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { + return GetSafeTransmuteErrorAndReason::Default; + } + // Erase regions because layout code doesn't particularly care about regions. let trait_ref = self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); @@ -2243,6 +2250,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let dst = trait_ref.args.type_at(0); let src = trait_ref.args.type_at(1); + let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 109bae10b54cc..becc1acfb6641 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -43,6 +43,7 @@ pub struct ImplCandidate<'tcx> { enum GetSafeTransmuteErrorAndReason { Silent, + Default, Error { err_msg: String, safe_transmute_explanation: Option }, } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 6d5859a5a6563..17eddbfcd7f5e 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -195,10 +195,11 @@ pub(crate) mod rustc { impl<'tcx> From<&LayoutError<'tcx>> for Err { fn from(err: &LayoutError<'tcx>) -> Self { match err { - LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout, + LayoutError::Unknown(..) + | LayoutError::ReferencesError(..) + | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, LayoutError::Cycle(err) => Self::TypeError(*err), - err => unimplemented!("{:?}", err), } } } diff --git a/tests/ui/transmutability/assoc-bound.rs b/tests/ui/transmutability/assoc-bound.rs new file mode 100644 index 0000000000000..e8a20b45cde34 --- /dev/null +++ b/tests/ui/transmutability/assoc-bound.rs @@ -0,0 +1,25 @@ +#![crate_type = "lib"] +#![feature(transmutability)] + +trait A { + type AssocA; +} + +trait B { + type AssocB: std::mem::TransmuteFrom<()>; +} + +impl B for (T, u8) +where + T: A, +{ + type AssocB = T::AssocA; //~ERROR: the trait bound `::AssocA: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not satisfied [E0277] +} + + +impl B for (T, u16) +where + for<'a> &'a i32: A, +{ + type AssocB = <&'static i32 as A>::AssocA; //~ERROR: `()` cannot be safely transmuted into `<&i32 as A>::AssocA` +} diff --git a/tests/ui/transmutability/assoc-bound.stderr b/tests/ui/transmutability/assoc-bound.stderr new file mode 100644 index 0000000000000..08d9089439645 --- /dev/null +++ b/tests/ui/transmutability/assoc-bound.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `::AssocA: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not satisfied + --> $DIR/assoc-bound.rs:16:19 + | +LL | type AssocB = T::AssocA; + | ^^^^^^^^^ the trait `TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not implemented for `::AssocA` + | +note: required by a bound in `B::AssocB` + --> $DIR/assoc-bound.rs:9:18 + | +LL | type AssocB: std::mem::TransmuteFrom<()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `B::AssocB` +help: consider further restricting the associated type + | +LL | T: A, ::AssocA: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }> + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0277]: `()` cannot be safely transmuted into `<&i32 as A>::AssocA` + --> $DIR/assoc-bound.rs:24:19 + | +LL | type AssocB = <&'static i32 as A>::AssocA; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<&i32 as A>::AssocA` has an unknown layout + | +note: required by a bound in `B::AssocB` + --> $DIR/assoc-bound.rs:9:18 + | +LL | type AssocB: std::mem::TransmuteFrom<()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `B::AssocB` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 162ee75e436875afc0ff877e4fca90a13f879a1c Mon Sep 17 00:00:00 2001 From: Henry Jiang Date: Tue, 1 Oct 2024 17:21:56 -0400 Subject: [PATCH 1138/1301] format --- compiler/rustc_data_structures/src/stack.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_data_structures/src/stack.rs b/compiler/rustc_data_structures/src/stack.rs index 72add175091fa..3d6d000348324 100644 --- a/compiler/rustc_data_structures/src/stack.rs +++ b/compiler/rustc_data_structures/src/stack.rs @@ -5,10 +5,10 @@ const RED_ZONE: usize = 100 * 1024; // 100k // Only the first stack that is pushed, grows exponentially (2^n * STACK_PER_RECURSION) from then // on. This flag has performance relevant characteristics. Don't set it too high. -#[cfg(not(target_os="aix"))] +#[cfg(not(target_os = "aix"))] const STACK_PER_RECURSION: usize = 1024 * 1024; // 1MB // LLVM for AIX doesn't feature TCO, increase recursion size for workaround. -#[cfg(target_os="aix")] +#[cfg(target_os = "aix")] const STACK_PER_RECURSION: usize = 16 * 1024 * 1024; // 16MB /// Grows the stack on demand to prevent stack overflow. Call this in strategic locations From 38ea690363e24acf4970b2a7e942af6c05893691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 1 Oct 2024 23:16:35 +0000 Subject: [PATCH 1139/1301] fix extension for `-Zdump-mir-dataflow` graphviz files --- compiler/rustc_mir_dataflow/src/framework/engine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index da01a9740943d..faf5c610a0c47 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -285,7 +285,7 @@ where } None if dump_enabled(tcx, A::NAME, def_id) => { - create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? + create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? } _ => return (Ok(()), results), From 98d6fdb24206f632e0f151719bbdcc73ff67ff2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 1 Oct 2024 23:55:55 +0000 Subject: [PATCH 1140/1301] make `Borrows` dataflow dumps about its loan domain --- compiler/rustc_borrowck/src/dataflow.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 4af5a10f5812d..5ea48d3fc7e94 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -1,5 +1,3 @@ -use std::fmt; - use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph; use rustc_index::bit_set::BitSet; @@ -425,10 +423,6 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location } } - pub fn location(&self, idx: BorrowIndex) -> &Location { - &self.borrow_set[idx].reserve_location - } - /// Add all borrows to the kill set, if those borrows are out of scope at `location`. /// That means they went out of a nonlexical scope fn kill_loans_out_of_scope_at_location( @@ -615,8 +609,4 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } } -impl DebugWithContext> for BorrowIndex { - fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", ctxt.location(*self)) - } -} +impl DebugWithContext for BorrowIndex {} From bb5a8276be2d3dbc97d0f52e90db15455d542edf Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 22 Jun 2024 01:27:59 -0600 Subject: [PATCH 1141/1301] add unstable support for outputting file checksums for use in cargo --- Cargo.lock | 26 ++++ .../src/debuginfo/metadata.rs | 1 + compiler/rustc_interface/src/interface.rs | 3 +- compiler/rustc_interface/src/lib.rs | 1 + compiler/rustc_interface/src/passes.rs | 105 +++++++++++-- compiler/rustc_interface/src/tests.rs | 2 + compiler/rustc_metadata/src/rmeta/decoder.rs | 2 + .../src/ich/impls_syntax.rs | 2 + compiler/rustc_session/src/config.rs | 4 + compiler/rustc_session/src/options.rs | 22 ++- compiler/rustc_span/Cargo.toml | 1 + compiler/rustc_span/src/lib.rs | 146 +++++++++++++++++- compiler/rustc_span/src/source_map.rs | 16 +- compiler/rustc_span/src/source_map/tests.rs | 2 + compiler/rustc_span/src/tests.rs | 10 +- src/tools/tidy/src/deps.rs | 6 + 16 files changed, 321 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c1071ccbc226..6348046751a8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,6 +199,12 @@ dependencies = [ "object 0.36.4", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.6" @@ -262,6 +268,19 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "blake3" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -719,6 +738,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -4374,6 +4399,7 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ + "blake3", "derive-where", "indexmap", "itoa", diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 964b83c0fa07a..b7a6f80956dd3 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -630,6 +630,7 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5, rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1, rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256, + rustc_span::SourceFileHashAlgorithm::Blake3 => llvm::ChecksumKind::None, }; let hash_value = hex_encode(source_file.src_hash.hash_bytes()); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index bd38b3c109a41..3920d3077d361 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -389,12 +389,13 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let path_mapping = config.opts.file_path_mapping(); let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); + let checksum_hash_kind = config.opts.unstable_opts.checksum_hash_algorithm(); util::run_in_thread_pool_with_globals( &early_dcx, config.opts.edition, config.opts.unstable_opts.threads, - SourceMapInputs { file_loader, path_mapping, hash_kind }, + SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }, |current_gcx| { // The previous `early_dcx` can't be reused here because it doesn't // impl `Send`. Creating a new one is fine. diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index b81a740270163..1c4dda2a4367e 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,6 +1,7 @@ // tidy-alphabetical-start #![feature(decl_macro)] #![feature(file_buffered)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 84b320975bbab..b9b65755db5d2 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -32,8 +32,8 @@ use rustc_session::cstore::Untracked; use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; -use rustc_span::FileName; use rustc_span::symbol::{Symbol, sym}; +use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; @@ -417,15 +417,23 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let result: io::Result<()> = try { // Build a list of files used to compile the output and // write Makefile-compatible dependency rules - let mut files: Vec = sess + let mut files: Vec<(String, u64, Option)> = sess .source_map() .files() .iter() .filter(|fmap| fmap.is_real_file()) .filter(|fmap| !fmap.is_imported()) - .map(|fmap| escape_dep_filename(&fmap.name.prefer_local().to_string())) + .map(|fmap| { + ( + escape_dep_filename(&fmap.name.prefer_local().to_string()), + fmap.source_len.0 as u64, + fmap.checksum_hash, + ) + }) .collect(); + let checksum_hash_algo = sess.opts.unstable_opts.checksum_hash_algorithm; + // Account for explicitly marked-to-track files // (e.g. accessed in proc macros). let file_depinfo = sess.psess.file_depinfo.borrow(); @@ -437,22 +445,58 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P // The entries will be used to declare dependencies between files in a // Makefile-like output, so the iteration order does not matter. + fn hash_iter_files>( + it: impl Iterator, + checksum_hash_algo: Option, + ) -> impl Iterator)> { + it.map(move |path| { + match checksum_hash_algo.and_then(|algo| { + fs::File::open(path.as_ref()) + .and_then(|mut file| { + SourceFileHash::new(algo, &mut file).map(|h| (file, h)) + }) + .and_then(|(file, h)| file.metadata().map(|m| (m.len(), h))) + .map_err(|e| { + tracing::error!( + "failed to compute checksum, omitting it from dep-info {} {e}", + path.as_ref().display() + ) + }) + .ok() + }) { + Some((file_len, checksum)) => (path, file_len, Some(checksum)), + None => (path, 0, None), + } + }) + } + #[allow(rustc::potential_query_instability)] - let extra_tracked_files = - file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str()))); + let extra_tracked_files = hash_iter_files( + file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str()))), + checksum_hash_algo, + ); files.extend(extra_tracked_files); // We also need to track used PGO profile files if let Some(ref profile_instr) = sess.opts.cg.profile_use { - files.push(normalize_path(profile_instr.as_path().to_path_buf())); + files.extend(hash_iter_files( + iter::once(normalize_path(profile_instr.as_path().to_path_buf())), + checksum_hash_algo, + )); } if let Some(ref profile_sample) = sess.opts.unstable_opts.profile_sample_use { - files.push(normalize_path(profile_sample.as_path().to_path_buf())); + files.extend(hash_iter_files( + iter::once(normalize_path(profile_sample.as_path().to_path_buf())), + checksum_hash_algo, + )); } // Debugger visualizer files for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) { - files.push(normalize_path(debugger_visualizer.path.clone().unwrap())); + files.extend(hash_iter_files( + iter::once(normalize_path(debugger_visualizer.path.clone().unwrap())), + checksum_hash_algo, + )); } if sess.binary_dep_depinfo() { @@ -460,33 +504,54 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P if backend.contains('.') { // If the backend name contain a `.`, it is the path to an external dynamic // library. If not, it is not a path. - files.push(backend.to_string()); + files.extend(hash_iter_files( + iter::once(backend.to_string()), + checksum_hash_algo, + )); } } for &cnum in tcx.crates(()) { let source = tcx.used_crate_source(cnum); if let Some((path, _)) = &source.dylib { - files.push(escape_dep_filename(&path.display().to_string())); + files.extend(hash_iter_files( + iter::once(escape_dep_filename(&path.display().to_string())), + checksum_hash_algo, + )); } if let Some((path, _)) = &source.rlib { - files.push(escape_dep_filename(&path.display().to_string())); + files.extend(hash_iter_files( + iter::once(escape_dep_filename(&path.display().to_string())), + checksum_hash_algo, + )); } if let Some((path, _)) = &source.rmeta { - files.push(escape_dep_filename(&path.display().to_string())); + files.extend(hash_iter_files( + iter::once(escape_dep_filename(&path.display().to_string())), + checksum_hash_algo, + )); } } } let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + writeln!( + file, + "{}: {}\n", + path.display(), + files + .iter() + .map(|(path, _file_len, _checksum_hash_algo)| path.as_str()) + .intersperse(" ") + .collect::() + )?; } // Emit a fake target for each input file to the compilation. This // prevents `make` from spitting out an error if a file is later // deleted. For more info see #28735 - for path in files { + for (path, _file_len, _checksum_hash_algo) in &files { writeln!(file, "{path}:")?; } @@ -510,6 +575,18 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } + // If caller requested this information, add special comments about source file checksums. + // These are not necessarily the same checksums as was used in the debug files. + if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { + for (path, file_len, checksum_hash) in + files.iter().filter_map(|(path, file_len, hash_algo)| { + hash_algo.map(|hash_algo| (path, file_len, hash_algo)) + }) + { + writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; + } + } + Ok(()) }; diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 895897eca6b8b..536ce154cd071 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -44,10 +44,12 @@ where let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target); + let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm(); let sm_inputs = Some(SourceMapInputs { file_loader: Box::new(RealFileLoader) as _, path_mapping: sessopts.file_path_mapping(), hash_kind, + checksum_hash_kind, }); rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 2157324d5cc3c..f02fd2ab6fe3d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1702,6 +1702,7 @@ impl<'a> CrateMetadataRef<'a> { let rustc_span::SourceFile { mut name, src_hash, + checksum_hash, start_pos: original_start_pos, source_len, lines, @@ -1752,6 +1753,7 @@ impl<'a> CrateMetadataRef<'a> { let local_version = sess.source_map().new_imported_source_file( name, src_hash, + checksum_hash, stable_id, source_len.to_u32(), self.cnum, diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 8d7a6e4fa9b0c..5e450979273ab 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -68,6 +68,8 @@ impl<'a> HashStable> for SourceFile { // Do not hash the source as it is not encoded src: _, ref src_hash, + // Already includes src_hash, this is redundant + checksum_hash: _, external_src: _, start_pos: _, source_len: _, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 0d293415aa9c7..d2c03e588ff36 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1242,6 +1242,10 @@ impl UnstableOptions { } }) } + + pub fn checksum_hash_algorithm(&self) -> Option { + self.checksum_hash_algorithm + } } // The type of entry function, so users can have their own entry functions diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 1de09b8be4d63..a1d85dfad32af 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -418,7 +418,8 @@ mod desc { "one of: `legacy`, `v0` (RFC 2603), or `hashed`"; pub(crate) const parse_opt_symbol_visibility: &str = "one of: `hidden`, `protected`, or `interposable`"; - pub(crate) const parse_src_file_hash: &str = "either `md5` or `sha1`"; + pub(crate) const parse_cargo_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; + pub(crate) const parse_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; pub(crate) const parse_relocation_model: &str = "one of supported relocation models (`rustc --print relocation-models`)"; pub(crate) const parse_code_model: &str = @@ -1288,6 +1289,23 @@ mod parse { true } + pub(crate) fn parse_cargo_src_file_hash( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) { + Some(hash_kind) => { + if hash_kind.supported_in_cargo() { + *slot = Some(hash_kind); + } else { + return false; + } + } + _ => return false, + } + true + } + pub(crate) fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool { match v { Some(s) => { @@ -1688,6 +1706,8 @@ options! { "instrument control-flow architecture protection"), check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], "show all expected values in check-cfg diagnostics (default: no)"), + checksum_hash_algorithm: Option = (None, parse_cargo_src_file_hash, [TRACKED], + "hash algorithm of source files used to check freshness in cargo (`sha256`)"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 3fdfe77ead972..c52d1fcc07fe2 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +blake3 = "1.5.2" derive-where = "1.2.7" indexmap = { version = "2.0.0" } itoa = "1.0" diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 9dbdab84a81e1..5ab1caaa220a7 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -75,7 +75,9 @@ pub mod profiling; use std::borrow::Cow; use std::cmp::{self, Ordering}; +use std::fmt::Display; use std::hash::Hash; +use std::io::{self, Read}; use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -1395,6 +1397,27 @@ pub enum SourceFileHashAlgorithm { Md5, Sha1, Sha256, + Blake3, +} + +impl SourceFileHashAlgorithm { + pub fn supported_in_cargo(&self) -> bool { + match self { + Self::Md5 | Self::Sha1 => false, + Self::Sha256 | Self::Blake3 => true, + } + } +} + +impl Display for SourceFileHashAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::Md5 => "md5", + Self::Sha1 => "sha1", + Self::Sha256 => "sha256", + Self::Blake3 => "blake3", + }) + } } impl FromStr for SourceFileHashAlgorithm { @@ -1405,12 +1428,13 @@ impl FromStr for SourceFileHashAlgorithm { "md5" => Ok(SourceFileHashAlgorithm::Md5), "sha1" => Ok(SourceFileHashAlgorithm::Sha1), "sha256" => Ok(SourceFileHashAlgorithm::Sha256), + "blake3" => Ok(SourceFileHashAlgorithm::Blake3), _ => Err(()), } } } -/// The hash of the on-disk source file used for debug info. +/// The hash of the on-disk source file used for debug info and cargo freshness checks. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[derive(HashStable_Generic, Encodable, Decodable)] pub struct SourceFileHash { @@ -1418,12 +1442,22 @@ pub struct SourceFileHash { value: [u8; 32], } +impl Display for SourceFileHash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}=", self.kind)?; + for byte in self.value[0..self.hash_len()].into_iter() { + write!(f, "{byte:02x}")?; + } + Ok(()) + } +} + impl SourceFileHash { - pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash { + pub fn new_in_memory(kind: SourceFileHashAlgorithm, src: impl AsRef<[u8]>) -> SourceFileHash { let mut hash = SourceFileHash { kind, value: Default::default() }; let len = hash.hash_len(); let value = &mut hash.value[..len]; - let data = src.as_bytes(); + let data = src.as_ref(); match kind { SourceFileHashAlgorithm::Md5 => { value.copy_from_slice(&Md5::digest(data)); @@ -1434,13 +1468,94 @@ impl SourceFileHash { SourceFileHashAlgorithm::Sha256 => { value.copy_from_slice(&Sha256::digest(data)); } - } + SourceFileHashAlgorithm::Blake3 => value.copy_from_slice(blake3::hash(data).as_bytes()), + }; hash } + pub fn new(kind: SourceFileHashAlgorithm, src: impl Read) -> Result { + let mut hash = SourceFileHash { kind, value: Default::default() }; + let len = hash.hash_len(); + let value = &mut hash.value[..len]; + // Buffer size is the recommended amount to fully leverage SIMD instructions on AVX-512 as per + // blake3 documentation. + let mut buf = vec![0; 16 * 1024]; + + fn digest( + mut hasher: T, + mut update: impl FnMut(&mut T, &[u8]), + finish: impl FnOnce(T, &mut [u8]), + mut src: impl Read, + buf: &mut [u8], + value: &mut [u8], + ) -> Result<(), io::Error> { + loop { + let bytes_read = src.read(buf)?; + if bytes_read == 0 { + break; + } + update(&mut hasher, &buf[0..bytes_read]); + } + finish(hasher, value); + Ok(()) + } + + match kind { + SourceFileHashAlgorithm::Sha256 => { + digest( + Sha256::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(&h.finalize()), + src, + &mut buf, + value, + )?; + } + SourceFileHashAlgorithm::Sha1 => { + digest( + Sha1::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(&h.finalize()), + src, + &mut buf, + value, + )?; + } + SourceFileHashAlgorithm::Md5 => { + digest( + Md5::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(&h.finalize()), + src, + &mut buf, + value, + )?; + } + SourceFileHashAlgorithm::Blake3 => { + digest( + blake3::Hasher::new(), + |h, b| { + h.update(b); + }, + |h, out| out.copy_from_slice(h.finalize().as_bytes()), + src, + &mut buf, + value, + )?; + } + } + Ok(hash) + } + /// Check if the stored hash matches the hash of the string. pub fn matches(&self, src: &str) -> bool { - Self::new(self.kind, src) == *self + Self::new_in_memory(self.kind, src.as_bytes()) == *self } /// The bytes of the hash. @@ -1453,7 +1568,7 @@ impl SourceFileHash { match self.kind { SourceFileHashAlgorithm::Md5 => 16, SourceFileHashAlgorithm::Sha1 => 20, - SourceFileHashAlgorithm::Sha256 => 32, + SourceFileHashAlgorithm::Sha256 | SourceFileHashAlgorithm::Blake3 => 32, } } } @@ -1509,6 +1624,10 @@ pub struct SourceFile { pub src: Option>, /// The source code's hash. pub src_hash: SourceFileHash, + /// Used to enable cargo to use checksums to check if a crate is fresh rather + /// than mtimes. This might be the same as `src_hash`, and if the requested algorithm + /// is identical we won't compute it twice. + pub checksum_hash: Option, /// The external source code (used for external crates, which will have a `None` /// value as `self.src`. pub external_src: FreezeLock, @@ -1536,6 +1655,7 @@ impl Clone for SourceFile { name: self.name.clone(), src: self.src.clone(), src_hash: self.src_hash, + checksum_hash: self.checksum_hash, external_src: self.external_src.clone(), start_pos: self.start_pos, source_len: self.source_len, @@ -1552,6 +1672,7 @@ impl Encodable for SourceFile { fn encode(&self, s: &mut S) { self.name.encode(s); self.src_hash.encode(s); + self.checksum_hash.encode(s); // Do not encode `start_pos` as it's global state for this session. self.source_len.encode(s); @@ -1625,6 +1746,7 @@ impl Decodable for SourceFile { fn decode(d: &mut D) -> SourceFile { let name: FileName = Decodable::decode(d); let src_hash: SourceFileHash = Decodable::decode(d); + let checksum_hash: Option = Decodable::decode(d); let source_len: RelativeBytePos = Decodable::decode(d); let lines = { let num_lines: u32 = Decodable::decode(d); @@ -1650,6 +1772,7 @@ impl Decodable for SourceFile { source_len, src: None, src_hash, + checksum_hash, // Unused - the metadata decoder will construct // a new SourceFile, filling in `external_src` properly external_src: FreezeLock::frozen(ExternalSource::Unneeded), @@ -1733,9 +1856,17 @@ impl SourceFile { name: FileName, mut src: String, hash_kind: SourceFileHashAlgorithm, + checksum_hash_kind: Option, ) -> Result { // Compute the file hash before any normalization. - let src_hash = SourceFileHash::new(hash_kind, &src); + let src_hash = SourceFileHash::new_in_memory(hash_kind, src.as_bytes()); + let checksum_hash = checksum_hash_kind.map(|checksum_hash_kind| { + if checksum_hash_kind == hash_kind { + src_hash + } else { + SourceFileHash::new_in_memory(checksum_hash_kind, src.as_bytes()) + } + }); let normalized_pos = normalize_src(&mut src); let stable_id = StableSourceFileId::from_filename_in_current_crate(&name); @@ -1748,6 +1879,7 @@ impl SourceFile { name, src: Some(Lrc::new(src)), src_hash, + checksum_hash, external_src: FreezeLock::frozen(ExternalSource::Unneeded), start_pos: BytePos::from_u32(0), source_len: RelativeBytePos::from_u32(source_len), diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 98447147d3e13..8a02330593711 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -175,6 +175,7 @@ pub struct SourceMapInputs { pub file_loader: Box, pub path_mapping: FilePathMapping, pub hash_kind: SourceFileHashAlgorithm, + pub checksum_hash_kind: Option, } pub struct SourceMap { @@ -187,6 +188,12 @@ pub struct SourceMap { /// The algorithm used for hashing the contents of each source file. hash_kind: SourceFileHashAlgorithm, + + /// Similar to `hash_kind`, however this algorithm is used for checksums to determine if a crate is fresh. + /// `cargo` is the primary user of these. + /// + /// If this is equal to `hash_kind` then the checksum won't be computed twice. + checksum_hash_kind: Option, } impl SourceMap { @@ -195,17 +202,19 @@ impl SourceMap { file_loader: Box::new(RealFileLoader), path_mapping, hash_kind: SourceFileHashAlgorithm::Md5, + checksum_hash_kind: None, }) } pub fn with_inputs( - SourceMapInputs { file_loader, path_mapping, hash_kind }: SourceMapInputs, + SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }: SourceMapInputs, ) -> SourceMap { SourceMap { files: Default::default(), file_loader: IntoDynSyncSend(file_loader), path_mapping, hash_kind, + checksum_hash_kind, } } @@ -307,7 +316,8 @@ impl SourceMap { match self.source_file_by_stable_id(stable_id) { Some(lrc_sf) => Ok(lrc_sf), None => { - let source_file = SourceFile::new(filename, src, self.hash_kind)?; + let source_file = + SourceFile::new(filename, src, self.hash_kind, self.checksum_hash_kind)?; // Let's make sure the file_id we generated above actually matches // the ID we generate for the SourceFile we just created. @@ -326,6 +336,7 @@ impl SourceMap { &self, filename: FileName, src_hash: SourceFileHash, + checksum_hash: Option, stable_id: StableSourceFileId, source_len: u32, cnum: CrateNum, @@ -340,6 +351,7 @@ impl SourceMap { name: filename, src: None, src_hash, + checksum_hash, external_src: FreezeLock::new(ExternalSource::Foreign { kind: ExternalSourceKind::AbsentOk, metadata_index, diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 0c818b94b85a3..360baec273d9a 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -229,6 +229,7 @@ fn t10() { let SourceFile { name, src_hash, + checksum_hash, source_len, lines, multibyte_chars, @@ -240,6 +241,7 @@ fn t10() { let imported_src_file = sm.new_imported_source_file( name, src_hash, + checksum_hash, stable_id, source_len.to_u32(), CrateNum::ZERO, diff --git a/compiler/rustc_span/src/tests.rs b/compiler/rustc_span/src/tests.rs index 48fa786fb1c80..ed1db34463429 100644 --- a/compiler/rustc_span/src/tests.rs +++ b/compiler/rustc_span/src/tests.rs @@ -3,9 +3,13 @@ use super::*; #[test] fn test_lookup_line() { let source = "abcdefghijklm\nabcdefghij\n...".to_owned(); - let mut sf = - SourceFile::new(FileName::Anon(Hash64::ZERO), source, SourceFileHashAlgorithm::Sha256) - .unwrap(); + let mut sf = SourceFile::new( + FileName::Anon(Hash64::ZERO), + source, + SourceFileHashAlgorithm::Sha256, + Some(SourceFileHashAlgorithm::Sha256), + ) + .unwrap(); sf.start_pos = BytePos(3); assert_eq!(sf.lines(), &[RelativeBytePos(0), RelativeBytePos(14), RelativeBytePos(25)]); diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 49d5b71ff2ddf..1ffad06457f9b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -88,7 +88,10 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>, const EXCEPTIONS: ExceptionList = &[ // tidy-alphabetical-start ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc + ("arrayref", "BSD-2-Clause"), // rustc + ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), // rustc ("colored", "MPL-2.0"), // rustfmt + ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), // rustc ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps) ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot @@ -249,14 +252,17 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "annotate-snippets", "anstyle", "ar_archive_writer", + "arrayref", "arrayvec", "autocfg", "bitflags", + "blake3", "block-buffer", "byteorder", // via ruzstd in object in thorin-dwp "cc", "cfg-if", "cfg_aliases", + "constant_time_eq", "cpufeatures", "crc32fast", "crossbeam-channel", From 6ff7a3e2aaa383062aef31f239a0b3939e544a86 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 27 Jul 2024 12:27:49 -0600 Subject: [PATCH 1142/1301] Fix options help text --- compiler/rustc_session/src/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a1d85dfad32af..c32cf3d015f0c 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1707,7 +1707,7 @@ options! { check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], "show all expected values in check-cfg diagnostics (default: no)"), checksum_hash_algorithm: Option = (None, parse_cargo_src_file_hash, [TRACKED], - "hash algorithm of source files used to check freshness in cargo (`sha256`)"), + "hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], From 081661b78d44bd41f8a1fd46b9ed11823cd7811e Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 17:38:28 -0600 Subject: [PATCH 1143/1301] disregard what we believe is supported in cargo for hash type --- compiler/rustc_session/src/options.rs | 6 +----- compiler/rustc_span/src/lib.rs | 9 --------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c32cf3d015f0c..b78d370bce0a8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1295,11 +1295,7 @@ mod parse { ) -> bool { match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) { Some(hash_kind) => { - if hash_kind.supported_in_cargo() { - *slot = Some(hash_kind); - } else { - return false; - } + *slot = Some(hash_kind); } _ => return false, } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5ab1caaa220a7..b55465ddef7f1 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1400,15 +1400,6 @@ pub enum SourceFileHashAlgorithm { Blake3, } -impl SourceFileHashAlgorithm { - pub fn supported_in_cargo(&self) -> bool { - match self { - Self::Md5 | Self::Sha1 => false, - Self::Sha256 | Self::Blake3 => true, - } - } -} - impl Display for SourceFileHashAlgorithm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { From dba814a9221e815a589a3ba48d2ef5287cd951ab Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 18:09:44 -0600 Subject: [PATCH 1144/1301] Pile all the checksum info into a comment that goes in the same order as the file list for the makefile --- compiler/rustc_interface/src/passes.rs | 33 ++++++++++++++++---------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index b9b65755db5d2..cd5624e38bd23 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -536,7 +536,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { for path in out_filenames { - writeln!( + write!( file, "{}: {}\n", path.display(), @@ -546,6 +546,25 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P .intersperse(" ") .collect::() )?; + + // If caller requested this information, add special comments about source file checksums. + // These are not necessarily the same checksums as was used in the debug files. + if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { + assert!( + files.iter().all(|(_path, _file_len, hash_algo)| hash_algo.is_some()), + "all files must have a checksum hash computed to output checksum hashes" + ); + write!(file, " # ")?; + files + .iter() + .filter_map(|(_path, file_len, hash_algo)| { + hash_algo.map(|hash_algo| (path, file_len, hash_algo)) + }) + .try_for_each(|(_path, file_len, checksum_hash)| { + write!(file, "checksum:{checksum_hash} file_len:{file_len}, ") + })?; + } + writeln!(file)?; } // Emit a fake target for each input file to the compilation. This @@ -575,18 +594,6 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } - // If caller requested this information, add special comments about source file checksums. - // These are not necessarily the same checksums as was used in the debug files. - if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { - for (path, file_len, checksum_hash) in - files.iter().filter_map(|(path, file_len, hash_algo)| { - hash_algo.map(|hash_algo| (path, file_len, hash_algo)) - }) - { - writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; - } - } - Ok(()) }; From e3089c787ce11ea45e0bb59a7c43c2803b326290 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 18:26:18 -0600 Subject: [PATCH 1145/1301] improve shell help text --- compiler/rustc_session/src/options.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b78d370bce0a8..d63276db4938b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -418,7 +418,8 @@ mod desc { "one of: `legacy`, `v0` (RFC 2603), or `hashed`"; pub(crate) const parse_opt_symbol_visibility: &str = "one of: `hidden`, `protected`, or `interposable`"; - pub(crate) const parse_cargo_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; + pub(crate) const parse_cargo_src_file_hash: &str = + "one of `blake3`, `md5`, `sha1`, or `sha256`"; pub(crate) const parse_src_file_hash: &str = "one of `md5`, `sha1`, or `sha256`"; pub(crate) const parse_relocation_model: &str = "one of supported relocation models (`rustc --print relocation-models`)"; From 6708d56fd2d5f937bc4b137757f18cd7c8dbd06b Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sat, 28 Sep 2024 23:13:18 -0600 Subject: [PATCH 1146/1301] Fix bug in depinfo output --- compiler/rustc_interface/src/passes.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index cd5624e38bd23..5a8ea410a63f0 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -538,7 +538,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P for path in out_filenames { write!( file, - "{}: {}\n", + "{}: {}", path.display(), files .iter() @@ -565,6 +565,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P })?; } writeln!(file)?; + writeln!(file)?; } // Emit a fake target for each input file to the compilation. This From 6fd9ef606f61f3162590ce66e1aa79319b44f2e9 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sun, 29 Sep 2024 00:06:40 -0600 Subject: [PATCH 1147/1301] no need to comma delimit this, it's already space delimited --- compiler/rustc_interface/src/passes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 5a8ea410a63f0..7429893b51fc6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -554,14 +554,14 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P files.iter().all(|(_path, _file_len, hash_algo)| hash_algo.is_some()), "all files must have a checksum hash computed to output checksum hashes" ); - write!(file, " # ")?; + write!(file, " #")?; files .iter() .filter_map(|(_path, file_len, hash_algo)| { hash_algo.map(|hash_algo| (path, file_len, hash_algo)) }) .try_for_each(|(_path, file_len, checksum_hash)| { - write!(file, "checksum:{checksum_hash} file_len:{file_len}, ") + write!(file, " checksum:{checksum_hash} file_len:{file_len}") })?; } writeln!(file)?; From 0069649c3d23282942e44ee9d5d60546ae920070 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sun, 29 Sep 2024 01:22:30 -0600 Subject: [PATCH 1148/1301] Add basic integration test for checksum-hash-algorithm feature --- tests/run-make/checksum-freshness/expected.d | 3 +++ tests/run-make/checksum-freshness/lib.rs | 5 +++++ tests/run-make/checksum-freshness/rmake.rs | 9 +++++++++ 3 files changed, 17 insertions(+) create mode 100644 tests/run-make/checksum-freshness/expected.d create mode 100644 tests/run-make/checksum-freshness/lib.rs create mode 100644 tests/run-make/checksum-freshness/rmake.rs diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d new file mode 100644 index 0000000000000..c79bc8ff7dec7 --- /dev/null +++ b/tests/run-make/checksum-freshness/expected.d @@ -0,0 +1,3 @@ +lib.d: lib.rs # checksum:blake3=5fd6328c93a3360bc580567aec10731ab3e5efc4a0f957b1bba0db0d27af3767 file_len:110 + +lib.rs: diff --git a/tests/run-make/checksum-freshness/lib.rs b/tests/run-make/checksum-freshness/lib.rs new file mode 100644 index 0000000000000..c2895dc2613c3 --- /dev/null +++ b/tests/run-make/checksum-freshness/lib.rs @@ -0,0 +1,5 @@ +// A basic library to be used in tests with no real purpose. + +pub fn sum(a: i32, b: i32) -> i32 { + a + b +} diff --git a/tests/run-make/checksum-freshness/rmake.rs b/tests/run-make/checksum-freshness/rmake.rs new file mode 100644 index 0000000000000..071db6b145b51 --- /dev/null +++ b/tests/run-make/checksum-freshness/rmake.rs @@ -0,0 +1,9 @@ +use run_make_support::{rfs, rustc}; + +fn main() { + rustc().input("lib.rs").arg("-Zchecksum-hash-algorithm=blake3").emit("dep-info").run(); + let make_file_contents = rfs::read_to_string("lib.d"); + let expected_contents = rfs::read_to_string("expected.d"); + assert_eq!(make_file_contents, expected_contents); + assert!(!expected_contents.is_empty()); +} From 4d9fa6fa775fc70cdaf1996ebe2c5b034d743819 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Sun, 29 Sep 2024 01:27:47 -0600 Subject: [PATCH 1149/1301] add another file to the integration test --- tests/run-make/checksum-freshness/expected.d | 3 ++- tests/run-make/checksum-freshness/foo.rs | 5 +++++ tests/run-make/checksum-freshness/lib.rs | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/checksum-freshness/foo.rs diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d index c79bc8ff7dec7..4b10e293229ce 100644 --- a/tests/run-make/checksum-freshness/expected.d +++ b/tests/run-make/checksum-freshness/expected.d @@ -1,3 +1,4 @@ -lib.d: lib.rs # checksum:blake3=5fd6328c93a3360bc580567aec10731ab3e5efc4a0f957b1bba0db0d27af3767 file_len:110 +lib.d: lib.rs foo.rs # checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 lib.rs: +foo.rs: diff --git a/tests/run-make/checksum-freshness/foo.rs b/tests/run-make/checksum-freshness/foo.rs new file mode 100644 index 0000000000000..d3ef768f18759 --- /dev/null +++ b/tests/run-make/checksum-freshness/foo.rs @@ -0,0 +1,5 @@ +// This is another file, just to prove we can handle two of them + +pub fn subtract(a: i32, b: i32) -> i32 { + a - b +} diff --git a/tests/run-make/checksum-freshness/lib.rs b/tests/run-make/checksum-freshness/lib.rs index c2895dc2613c3..7bc6757959b1d 100644 --- a/tests/run-make/checksum-freshness/lib.rs +++ b/tests/run-make/checksum-freshness/lib.rs @@ -1,5 +1,7 @@ // A basic library to be used in tests with no real purpose. +mod foo; + pub fn sum(a: i32, b: i32) -> i32 { a + b } From 15efbc6e8d78b257f594ddfb64d215c9ac3bee6d Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 1 Oct 2024 21:26:03 -0600 Subject: [PATCH 1150/1301] Write two newlines intentionally --- compiler/rustc_interface/src/passes.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7429893b51fc6..a8afb8c2900ee 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -564,8 +564,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P write!(file, " checksum:{checksum_hash} file_len:{file_len}") })?; } - writeln!(file)?; - writeln!(file)?; + write!(file, "\n\n")?; } // Emit a fake target for each input file to the compilation. This From b48c5f19e07c46d43d03328c86d3f7af6f9c8187 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 1 Oct 2024 23:05:24 -0600 Subject: [PATCH 1151/1301] Restore prior behavior with less duplicate info in dep file --- compiler/rustc_interface/src/passes.rs | 35 ++++++++------------ tests/run-make/checksum-freshness/expected.d | 4 ++- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a8afb8c2900ee..b9b65755db5d2 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -536,9 +536,9 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { for path in out_filenames { - write!( + writeln!( file, - "{}: {}", + "{}: {}\n", path.display(), files .iter() @@ -546,25 +546,6 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P .intersperse(" ") .collect::() )?; - - // If caller requested this information, add special comments about source file checksums. - // These are not necessarily the same checksums as was used in the debug files. - if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { - assert!( - files.iter().all(|(_path, _file_len, hash_algo)| hash_algo.is_some()), - "all files must have a checksum hash computed to output checksum hashes" - ); - write!(file, " #")?; - files - .iter() - .filter_map(|(_path, file_len, hash_algo)| { - hash_algo.map(|hash_algo| (path, file_len, hash_algo)) - }) - .try_for_each(|(_path, file_len, checksum_hash)| { - write!(file, " checksum:{checksum_hash} file_len:{file_len}") - })?; - } - write!(file, "\n\n")?; } // Emit a fake target for each input file to the compilation. This @@ -594,6 +575,18 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } + // If caller requested this information, add special comments about source file checksums. + // These are not necessarily the same checksums as was used in the debug files. + if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { + for (path, file_len, checksum_hash) in + files.iter().filter_map(|(path, file_len, hash_algo)| { + hash_algo.map(|hash_algo| (path, file_len, hash_algo)) + }) + { + writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; + } + } + Ok(()) }; diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d index 4b10e293229ce..51467af53a20e 100644 --- a/tests/run-make/checksum-freshness/expected.d +++ b/tests/run-make/checksum-freshness/expected.d @@ -1,4 +1,6 @@ -lib.d: lib.rs foo.rs # checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 +lib.d: lib.rs foo.rs lib.rs: foo.rs: +# checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 lib.rs +# checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 foo.rs From 807e812077ff9d70840f14822ae6e0e9753bd1fb Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Wed, 2 Oct 2024 08:25:55 +0300 Subject: [PATCH 1152/1301] Handle `rustc-hir-analysis` cases of `rustc::potential_query_instability` lint --- .../rustc_hir_analysis/src/check/wfcheck.rs | 20 ++++++++--------- .../src/coherence/inherent_impls_overlap.rs | 4 ++-- .../src/hir_ty_lowering/mod.rs | 6 ++--- compiler/rustc_hir_analysis/src/lib.rs | 1 - compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/visit.rs | 10 ++++----- compiler/rustc_resolve/src/lib.rs | 2 +- .../self-outlives-lint.stderr | 22 +++++++++---------- 8 files changed, 33 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 70b0b3f5788ed..02d23b95d46e6 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2,7 +2,7 @@ use std::cell::LazyCell; use std::ops::{ControlFlow, Deref}; use hir::intravisit::{self, Visitor}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::ItemKind; @@ -404,7 +404,7 @@ fn check_trait_item<'tcx>( /// ``` fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { // Associates every GAT's def_id to a list of possibly missing bounds detected by this lint. - let mut required_bounds_by_item = FxHashMap::default(); + let mut required_bounds_by_item = FxIndexMap::default(); let associated_items = tcx.associated_items(trait_def_id); // Loop over all GATs together, because if this lint suggests adding a where-clause bound @@ -430,7 +430,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { // Gather the bounds with which all other items inside of this trait constrain the GAT. // This is calculated by taking the intersection of the bounds that each item // constrains the GAT with individually. - let mut new_required_bounds: Option>> = None; + let mut new_required_bounds: Option>> = None; for item in associated_items.in_definition_order() { let item_def_id = item.def_id.expect_local(); // Skip our own GAT, since it does not constrain itself at all. @@ -589,7 +589,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { fn augment_param_env<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - new_predicates: Option<&FxHashSet>>, + new_predicates: Option<&FxIndexSet>>, ) -> ty::ParamEnv<'tcx> { let Some(new_predicates) = new_predicates else { return param_env; @@ -625,9 +625,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( wf_tys: &FxIndexSet>, gat_def_id: LocalDefId, gat_generics: &'tcx ty::Generics, -) -> Option>> { +) -> Option>> { // The bounds we that we would require from `to_check` - let mut bounds = FxHashSet::default(); + let mut bounds = FxIndexSet::default(); let (regions, types) = GATArgsCollector::visit(gat_def_id.to_def_id(), to_check); @@ -789,18 +789,18 @@ fn test_region_obligations<'tcx>( struct GATArgsCollector<'tcx> { gat: DefId, // Which region appears and which parameter index its instantiated with - regions: FxHashSet<(ty::Region<'tcx>, usize)>, + regions: FxIndexSet<(ty::Region<'tcx>, usize)>, // Which params appears and which parameter index its instantiated with - types: FxHashSet<(Ty<'tcx>, usize)>, + types: FxIndexSet<(Ty<'tcx>, usize)>, } impl<'tcx> GATArgsCollector<'tcx> { fn visit>>( gat: DefId, t: T, - ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) { + ) -> (FxIndexSet<(ty::Region<'tcx>, usize)>, FxIndexSet<(Ty<'tcx>, usize)>) { let mut visitor = - GATArgsCollector { gat, regions: FxHashSet::default(), types: FxHashSet::default() }; + GATArgsCollector { gat, regions: FxIndexSet::default(), types: FxIndexSet::default() }; t.visit_with(&mut visitor); (visitor.regions, visitor.types) } diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index b8066b4b47d90..d1c888a185ed9 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; @@ -215,7 +215,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { struct ConnectedRegion { idents: SmallVec<[Symbol; 8]>, - impl_blocks: FxHashSet, + impl_blocks: FxIndexSet, } let mut connected_regions: IndexVec = Default::default(); // Reverse map from the Symbol to the connected region id. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2186952720f33..6dd3a06ef3773 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -23,7 +23,7 @@ mod lint; use std::slice; use rustc_ast::TraitObjectSyntax; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, @@ -2394,8 +2394,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { #[instrument(level = "trace", skip(self, generate_err))] fn validate_late_bound_regions<'cx>( &'cx self, - constrained_regions: FxHashSet, - referenced_regions: FxHashSet, + constrained_regions: FxIndexSet, + referenced_regions: FxIndexSet, generate_err: impl Fn(&str) -> Diag<'cx>, ) { for br in referenced_regions.difference(&constrained_regions) { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 92d85d48a42c0..7d40a7746b97c 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -58,7 +58,6 @@ This API is completely unstable and subject to change. // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2b1212a5eb567..c4a28845085d9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -186,7 +186,7 @@ pub struct ResolverGlobalCtxt { pub proc_macros: Vec, /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. - pub confused_type_with_std_module: FxHashMap, + pub confused_type_with_std_module: FxIndexMap, pub doc_link_resolutions: FxHashMap, pub doc_link_traits_in_scope: FxHashMap>, pub all_macro_rules: FxHashMap>, diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 78d83004c14e1..4efaccefcf74b 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,6 +1,6 @@ use std::ops::ControlFlow; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_type_ir::fold::TypeFoldable; pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; @@ -110,7 +110,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_constrained_late_bound_regions( self, value: Binder<'tcx, T>, - ) -> FxHashSet + ) -> FxIndexSet where T: TypeFoldable>, { @@ -121,7 +121,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_referenced_late_bound_regions( self, value: Binder<'tcx, T>, - ) -> FxHashSet + ) -> FxIndexSet where T: TypeFoldable>, { @@ -132,7 +132,7 @@ impl<'tcx> TyCtxt<'tcx> { self, value: Binder<'tcx, T>, just_constrained: bool, - ) -> FxHashSet + ) -> FxIndexSet where T: TypeFoldable>, { @@ -148,7 +148,7 @@ impl<'tcx> TyCtxt<'tcx> { /// into a hash set. struct LateBoundRegionsCollector { current_index: ty::DebruijnIndex, - regions: FxHashSet, + regions: FxIndexSet, /// `true` if we only want regions that are known to be /// "constrained" when you equate this type with another type. In diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b84408cd0cb7a..0c1a0038f9c79 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1188,7 +1188,7 @@ pub struct Resolver<'ra, 'tcx> { /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec, - confused_type_with_std_module: FxHashMap, + confused_type_with_std_module: FxIndexMap, /// Whether lifetime elision was successful. lifetime_elision_allowed: FxHashSet, diff --git a/tests/ui/generic-associated-types/self-outlives-lint.stderr b/tests/ui/generic-associated-types/self-outlives-lint.stderr index 9e9b2e18abeb8..58172bf06b512 100644 --- a/tests/ui/generic-associated-types/self-outlives-lint.stderr +++ b/tests/ui/generic-associated-types/self-outlives-lint.stderr @@ -108,17 +108,6 @@ LL | type Bar<'b>; = note: this bound is currently required to ensure that impls have maximum flexibility = note: we are soliciting feedback, see issue #87479 for more information -error: missing required bound on `Iterator` - --> $DIR/self-outlives-lint.rs:142:5 - | -LL | type Iterator<'a>: Iterator>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: add the required where clause: `where Self: 'a` - | - = note: this bound is currently required to ensure that impls have maximum flexibility - = note: we are soliciting feedback, see issue #87479 for more information - error: missing required bound on `Item` --> $DIR/self-outlives-lint.rs:140:5 | @@ -130,6 +119,17 @@ LL | type Item<'a>; = note: this bound is currently required to ensure that impls have maximum flexibility = note: we are soliciting feedback, see issue #87479 for more information +error: missing required bound on `Iterator` + --> $DIR/self-outlives-lint.rs:142:5 + | +LL | type Iterator<'a>: Iterator>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: add the required where clause: `where Self: 'a` + | + = note: this bound is currently required to ensure that impls have maximum flexibility + = note: we are soliciting feedback, see issue #87479 for more information + error: missing required bound on `Item` --> $DIR/self-outlives-lint.rs:148:5 | From 9fa120593e2b6325812aa558fa8878d5aa6635b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 2 Oct 2024 08:00:17 +0200 Subject: [PATCH 1153/1301] mpmc doctest: make sure main thread waits for child threads --- library/std/src/sync/mpmc/mod.rs | 46 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 77a67f4fd386d..44e146a89bafb 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -66,29 +66,31 @@ //! use std::thread; //! use std::sync::mpmc::channel; //! -//! // Create a shared channel that can be sent along from many threads -//! // where tx is the sending half (tx for transmission), and rx is the receiving -//! // half (rx for receiving). -//! let (tx, rx) = channel(); -//! for i in 0..10 { -//! let tx = tx.clone(); -//! thread::spawn(move || { -//! tx.send(i).unwrap(); -//! }); -//! } +//! thread::scope(|s| { +//! // Create a shared channel that can be sent along from many threads +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). +//! let (tx, rx) = channel(); +//! for i in 0..10 { +//! let tx = tx.clone(); +//! s.spawn(move || { +//! tx.send(i).unwrap(); +//! }); +//! } //! -//! for _ in 0..5 { -//! let rx1 = rx.clone(); -//! let rx2 = rx.clone(); -//! thread::spawn(move || { -//! let j = rx1.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! }); -//! thread::spawn(move || { -//! let j = rx2.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! }); -//! } +//! for _ in 0..5 { +//! let rx1 = rx.clone(); +//! let rx2 = rx.clone(); +//! s.spawn(move || { +//! let j = rx1.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! s.spawn(move || { +//! let j = rx2.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! }); +//! } +//! }) //! ``` //! //! Propagating panics: From 58c5ac43aea5c94d61e3a7a4e8646e714230574d Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Wed, 2 Oct 2024 00:49:46 -0600 Subject: [PATCH 1154/1301] restore prior more readable suggestion --- compiler/rustc_interface/src/passes.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index b9b65755db5d2..204ae437a3e9a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -578,13 +578,14 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P // If caller requested this information, add special comments about source file checksums. // These are not necessarily the same checksums as was used in the debug files. if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { - for (path, file_len, checksum_hash) in - files.iter().filter_map(|(path, file_len, hash_algo)| { + files + .iter() + .filter_map(|(path, file_len, hash_algo)| { hash_algo.map(|hash_algo| (path, file_len, hash_algo)) }) - { - writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}")?; - } + .try_for_each(|(path, file_len, checksum_hash)| { + writeln!(file, "# checksum:{checksum_hash} file_len:{file_len} {path}") + })?; } Ok(()) From 8760a401bd55205a188fdc174785c6f32f4027fa Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 2 Oct 2024 09:44:51 +0200 Subject: [PATCH 1155/1301] Update hashbrown to 0.15 and adjust some methods as well as removing some from std as they no longer exists in Hashbrown it-self. --- library/Cargo.lock | 4 +- library/std/Cargo.toml | 2 +- library/std/src/collections/hash/map.rs | 137 ++++++++++-------------- library/std/src/collections/hash/set.rs | 32 ------ 4 files changed, 58 insertions(+), 117 deletions(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 877ae0cc1cc93..209e30b304027 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ "allocator-api2", "compiler_builtins", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 7d860b49e8739..63c65b8ef3994 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -20,7 +20,7 @@ core = { path = "../core", public = true } compiler_builtins = { version = "0.1.130" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.14", default-features = false, features = [ +hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', ] } # FIXME(#127890): `object` depends on `memchr`, but `memchr` > v2.5 causes diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 1a18721b15e92..6f2b4100620ee 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -909,8 +909,11 @@ where /// Attempts to get mutable references to `N` values in the map at once. /// /// Returns an array of length `N` with the results of each query. For soundness, at most one - /// mutable reference will be returned to any value. `None` will be returned if any of the - /// keys are duplicates or missing. + /// mutable reference will be returned to any value. `None` will be used if the key is missing. + /// + /// # Panics + /// + /// Panics if any keys are overlapping. /// /// # Examples /// @@ -924,16 +927,23 @@ where /// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691); /// libraries.insert("Library of Congress".to_string(), 1800); /// + /// // Get Athenæum and Bodleian Library + /// let [Some(a), Some(b)] = libraries.get_many_mut([ + /// "Athenæum", + /// "Bodleian Library", + /// ]) else { panic!() }; + /// + /// // Assert values of Athenæum and Library of Congress /// let got = libraries.get_many_mut([ /// "Athenæum", /// "Library of Congress", /// ]); /// assert_eq!( /// got, - /// Some([ - /// &mut 1807, - /// &mut 1800, - /// ]), + /// [ + /// Some(&mut 1807), + /// Some(&mut 1800), + /// ], /// ); /// /// // Missing keys result in None @@ -941,18 +951,31 @@ where /// "Athenæum", /// "New York Public Library", /// ]); - /// assert_eq!(got, None); + /// assert_eq!( + /// got, + /// [ + /// Some(&mut 1807), + /// None + /// ] + /// ); + /// ``` + /// + /// ```should_panic + /// #![feature(map_many_mut)] + /// use std::collections::HashMap; /// - /// // Duplicate keys result in None + /// let mut libraries = HashMap::new(); + /// libraries.insert("Athenæum".to_string(), 1807); + /// + /// // Duplicate keys panic! /// let got = libraries.get_many_mut([ /// "Athenæum", /// "Athenæum", /// ]); - /// assert_eq!(got, None); /// ``` #[inline] #[unstable(feature = "map_many_mut", issue = "97601")] - pub fn get_many_mut(&mut self, ks: [&Q; N]) -> Option<[&'_ mut V; N]> + pub fn get_many_mut(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -963,10 +986,10 @@ where /// Attempts to get mutable references to `N` values in the map at once, without validating that /// the values are unique. /// - /// Returns an array of length `N` with the results of each query. `None` will be returned if - /// any of the keys are missing. + /// Returns an array of length `N` with the results of each query. `None` will be used if + /// the key is missing. /// - /// For a safe alternative see [`get_many_mut`](Self::get_many_mut). + /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). /// /// # Safety /// @@ -987,31 +1010,39 @@ where /// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691); /// libraries.insert("Library of Congress".to_string(), 1800); /// - /// let got = libraries.get_many_mut([ + /// // SAFETY: The keys do not overlap. + /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ + /// "Athenæum", + /// "Bodleian Library", + /// ]) }) else { panic!() }; + /// + /// // SAFETY: The keys do not overlap. + /// let got = unsafe { libraries.get_many_unchecked_mut([ /// "Athenæum", /// "Library of Congress", - /// ]); + /// ]) }; /// assert_eq!( /// got, - /// Some([ - /// &mut 1807, - /// &mut 1800, - /// ]), + /// [ + /// Some(&mut 1807), + /// Some(&mut 1800), + /// ], /// ); /// - /// // Missing keys result in None - /// let got = libraries.get_many_mut([ + /// // SAFETY: The keys do not overlap. + /// let got = unsafe { libraries.get_many_unchecked_mut([ /// "Athenæum", /// "New York Public Library", - /// ]); - /// assert_eq!(got, None); + /// ]) }; + /// // Missing keys result in None + /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] #[unstable(feature = "map_many_mut", issue = "97601")] pub unsafe fn get_many_unchecked_mut( &mut self, ks: [&Q; N], - ) -> Option<[&'_ mut V; N]> + ) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -2978,64 +3009,6 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { pub fn remove(self) -> V { self.base.remove() } - - /// Replaces the entry, returning the old key and value. The new key in the hash map will be - /// the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_entry_replace)] - /// use std::collections::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// map.insert(Rc::new("Stringthing".to_string()), 15); - /// - /// let my_key = Rc::new("Stringthing".to_string()); - /// - /// if let Entry::Occupied(entry) = map.entry(my_key) { - /// // Also replace the key with a handle to our other key. - /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); - /// } - /// - /// ``` - #[inline] - #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_entry(self, value: V) -> (K, V) { - self.base.replace_entry(value) - } - - /// Replaces the key in the hash map with the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_entry_replace)] - /// use std::collections::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// let known_strings: Vec> = Vec::new(); - /// - /// // Initialise known strings, run program, etc. - /// - /// reclaim_memory(&mut map, &known_strings); - /// - /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { - /// for s in known_strings { - /// if let Entry::Occupied(entry) = map.entry(Rc::clone(s)) { - /// // Replaces the entry's key with our version of it in `known_strings`. - /// entry.replace_key(); - /// } - /// } - /// } - /// ``` - #[inline] - #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_key(self) -> K { - self.base.replace_key() - } } impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 4a113ddea3a6b..e69fb0878e7d0 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -724,38 +724,6 @@ where self.base.get_or_insert(value) } - /// Inserts an owned copy of the given `value` into the set if it is not - /// present, then returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set: HashSet = ["cat", "dog", "horse"] - /// .iter().map(|&pet| pet.to_owned()).collect(); - /// - /// assert_eq!(set.len(), 3); - /// for &pet in &["cat", "dog", "fish"] { - /// let value = set.get_or_insert_owned(pet); - /// assert_eq!(value, pet); - /// } - /// assert_eq!(set.len(), 4); // a new "fish" was inserted - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get_or_insert_owned(&mut self, value: &Q) -> &T - where - T: Borrow, - Q: Hash + Eq + ToOwned, - { - // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with - // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.base.get_or_insert_owned(value) - } - /// Inserts a value computed from `f` into the set if the given `value` is /// not present, then returns a reference to the value in the set. /// From 37e1c955c511cc7ee8e53d848006ae8077555009 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 2 Oct 2024 09:45:27 +0200 Subject: [PATCH 1156/1301] Adjust check-cfg get_many_mut usage following API change --- compiler/rustc_session/src/config/cfg.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 44b96d92b1995..ccc01728958b8 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -402,18 +402,18 @@ impl CheckCfg { // Get all values map at once otherwise it would be costly. // (8 values * 220 targets ~= 1760 times, at the time of writing this comment). let [ - values_target_abi, - values_target_arch, - values_target_endian, - values_target_env, - values_target_family, - values_target_os, - values_target_pointer_width, - values_target_vendor, - ] = self - .expecteds - .get_many_mut(VALUES) - .expect("unable to get all the check-cfg values buckets"); + Some(values_target_abi), + Some(values_target_arch), + Some(values_target_endian), + Some(values_target_env), + Some(values_target_family), + Some(values_target_os), + Some(values_target_pointer_width), + Some(values_target_vendor), + ] = self.expecteds.get_many_mut(VALUES) + else { + panic!("unable to get all the check-cfg values buckets"); + }; for target in TARGETS .iter() From a923fc011bf14e94071594fd95a36c970ed8623d Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 2 Oct 2024 11:15:48 +0200 Subject: [PATCH 1157/1301] Fix rustdoc-js-std path-ordering test due to API removal --- tests/rustdoc-js-std/path-ordering.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/rustdoc-js-std/path-ordering.js b/tests/rustdoc-js-std/path-ordering.js index e6b7bfab1e5f5..4bfc6256052ca 100644 --- a/tests/rustdoc-js-std/path-ordering.js +++ b/tests/rustdoc-js-std/path-ordering.js @@ -6,7 +6,6 @@ const EXPECTED = [ { 'path': 'std::collections::hash_set::HashSet', 'name': 'insert' }, { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert' }, { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert_with' }, - { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert_owned' }, ], }, { From e9b2d09ad7905c51e7795730147f8d2303fb4bbd Mon Sep 17 00:00:00 2001 From: bohan Date: Wed, 2 Oct 2024 17:36:31 +0800 Subject: [PATCH 1158/1301] only query `params_in_repr` if def kind is adt --- compiler/rustc_middle/src/values.rs | 2 +- tests/crashes/128327.rs | 5 ----- tests/ui/infinite/auxiliary/alias.rs | 3 +++ tests/ui/infinite/infinite-assoc.rs | 16 ++++++++++++++++ tests/ui/infinite/infinite-assoc.stderr | 25 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 6 deletions(-) delete mode 100644 tests/crashes/128327.rs create mode 100644 tests/ui/infinite/infinite-assoc.rs create mode 100644 tests/ui/infinite/infinite-assoc.stderr diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index c98d88e22d409..48ca38344cf2e 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -358,7 +358,7 @@ fn find_item_ty_spans( match ty.kind { hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { if let Res::Def(kind, def_id) = path.res - && !matches!(kind, DefKind::TyAlias) + && matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union) { let check_params = def_id.as_local().map_or(true, |def_id| { if def_id == needle { diff --git a/tests/crashes/128327.rs b/tests/crashes/128327.rs deleted file mode 100644 index a63f758c317cf..0000000000000 --- a/tests/crashes/128327.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: rust-lang/rust#128327 - -use std::ops::Deref; -struct Apple((Apple, <&'static [f64] as Deref>::Target(Banana ? Citron))); -fn main(){} diff --git a/tests/ui/infinite/auxiliary/alias.rs b/tests/ui/infinite/auxiliary/alias.rs index 59add7eb18bdb..5ae124e8aba60 100644 --- a/tests/ui/infinite/auxiliary/alias.rs +++ b/tests/ui/infinite/auxiliary/alias.rs @@ -1,2 +1,5 @@ pub struct W(T); pub type Wrapper = W; +pub trait Trait { + type T; +} diff --git a/tests/ui/infinite/infinite-assoc.rs b/tests/ui/infinite/infinite-assoc.rs new file mode 100644 index 0000000000000..d128a7e0d2d59 --- /dev/null +++ b/tests/ui/infinite/infinite-assoc.rs @@ -0,0 +1,16 @@ +//@ aux-build: alias.rs + +// issue#128327 + +extern crate alias; + +use alias::Trait; +struct S; +impl Trait for S { + type T = (); +} +struct A((A, ::T)); +//~^ ERROR: invalid `?` in type +//~| ERROR: recursive type `A` has infinite size + +fn main() {} diff --git a/tests/ui/infinite/infinite-assoc.stderr b/tests/ui/infinite/infinite-assoc.stderr new file mode 100644 index 0000000000000..e6b91f1324138 --- /dev/null +++ b/tests/ui/infinite/infinite-assoc.stderr @@ -0,0 +1,25 @@ +error: invalid `?` in type + --> $DIR/infinite-assoc.rs:12:39 + | +LL | struct A((A, ::T)); + | ^ `?` is only allowed on expressions, not types + | +help: if you meant to express that the type might not contain a value, use the `Option` wrapper type + | +LL | struct A((A, ::T>)); + | +++++++ ~ + +error[E0072]: recursive type `A` has infinite size + --> $DIR/infinite-assoc.rs:12:1 + | +LL | struct A((A, ::T)); + | ^^^^^^^^ - recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle + | +LL | struct A((Box, ::T)); + | ++++ + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0072`. From aa3251dd3b3d4550bdfd740ba8b4855a6c83d3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:51:14 +0800 Subject: [PATCH 1159/1301] Replace zero-width whitespace with a visible `\` People tried to copy the `r?` which had an invisible zero-width whitespace, causing rustbot to not recognize the instruction, and which makes it difficult to figure out why it didn't work. --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fd54a153a1609..ecf8f993f90b0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,6 +7,6 @@ tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using - r​? + r\? (with the `\` removed) --> From 7f6150b5777a25ef8975a11266b0205d6ba8ab71 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 2 Oct 2024 19:42:06 +0800 Subject: [PATCH 1160/1301] Improve const traits diagnostics for new desugaring --- compiler/rustc_middle/src/macros.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 46 +++++++--- .../traits/fulfillment_errors.rs | 86 +++++++++++++++---- compiler/rustc_type_ir/src/predicate.rs | 7 ++ .../assoc-type-const-bound-usage-0.stderr | 16 +++- .../assoc-type-const-bound-usage-1.stderr | 16 +++- .../call-const-trait-method-fail.rs | 6 +- .../call-const-trait-method-fail.stderr | 29 ++----- .../call-generic-method-nonconst.rs | 7 +- .../call-generic-method-nonconst.stderr | 29 ++----- .../const-default-method-bodies.rs | 6 +- .../const-default-method-bodies.stderr | 29 ++----- .../const-fns-are-early-bound.stderr | 4 +- .../cross-crate.gatednc.stderr | 8 +- ...ault-method-body-is-const-same-trait-ck.rs | 6 +- ...-method-body-is-const-same-trait-ck.stderr | 29 ++----- .../super-traits-fail-2.rs | 3 +- .../super-traits-fail-2.yn.stderr | 10 +-- .../super-traits-fail-2.yy.stderr | 10 +-- .../super-traits-fail-3.rs | 2 +- .../super-traits-fail-3.yn.stderr | 10 +-- .../trait-where-clause-const.stderr | 16 +--- .../unsatisfied-const-trait-bound.rs | 1 - .../unsatisfied-const-trait-bound.stderr | 14 +-- 24 files changed, 224 insertions(+), 168 deletions(-) diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index d385be007d33f..39816c17b985f 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -67,7 +67,7 @@ macro_rules! TrivialLiftImpls { }; } -/// Used for types that are `Copy` and which **do not care arena +/// Used for types that are `Copy` and which **do not care about arena /// allocated data** (i.e., don't need to be folded). #[macro_export] macro_rules! TrivialTypeTraversalImpls { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 8fd4c30457f88..7ada5fd93ba7d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1951,19 +1951,18 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn pretty_print_bound_constness( &mut self, - trait_ref: ty::TraitRef<'tcx>, + constness: ty::BoundConstness, ) -> Result<(), PrintError> { define_scoped_cx!(self); - let Some(idx) = self.tcx().generics_of(trait_ref.def_id).host_effect_index else { - return Ok(()); - }; - let arg = trait_ref.args.const_at(idx); - - if arg == self.tcx().consts.false_ { - p!("const "); - } else if arg != self.tcx().consts.true_ && !arg.has_infer() { - p!("~const "); + match constness { + ty::BoundConstness::NotConst => {} + ty::BoundConstness::Const => { + p!("const "); + } + ty::BoundConstness::ConstIfConst => { + p!("~const "); + } } Ok(()) } @@ -2948,6 +2947,15 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } } +#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] +pub struct TraitPredPrintWithBoundConstness<'tcx>(ty::TraitPredicate<'tcx>, ty::BoundConstness); + +impl<'tcx> fmt::Debug for TraitPredPrintWithBoundConstness<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + #[extension(pub trait PrintPolyTraitPredicateExt<'tcx>)] impl<'tcx> ty::PolyTraitPredicate<'tcx> { fn print_modifiers_and_trait_path( @@ -2955,6 +2963,13 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> { ) -> ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>> { self.map_bound(TraitPredPrintModifiersAndPath) } + + fn print_with_bound_constness( + self, + constness: ty::BoundConstness, + ) -> ty::Binder<'tcx, TraitPredPrintWithBoundConstness<'tcx>> { + self.map_bound(|trait_pred| TraitPredPrintWithBoundConstness(trait_pred, constness)) + } } #[derive(Debug, Copy, Clone, Lift)] @@ -3052,7 +3067,6 @@ define_print! { ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": "); - p!(pretty_print_bound_constness(self.trait_ref)); if let ty::PredicatePolarity::Negative = self.polarity { p!("!"); } @@ -3184,13 +3198,21 @@ define_print_and_forward_display! { } TraitPredPrintModifiersAndPath<'tcx> { - p!(pretty_print_bound_constness(self.0.trait_ref)); if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!") } p!(print(self.0.trait_ref.print_trait_sugared())); } + TraitPredPrintWithBoundConstness<'tcx> { + p!(print(self.0.trait_ref.self_ty()), ": "); + p!(pretty_print_bound_constness(self.1)); + if let ty::PredicatePolarity::Negative = self.0.polarity { + p!("!"); + } + p!(print(self.0.trait_ref.print_trait_sugared())) + } + PrintClosureAsImpl<'tcx> { p!(pretty_closure_as_impl(self.closure)) } 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 34a0f182ab45f..5af117a3f48c4 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 @@ -19,8 +19,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{ - FmtPrinter, Print, PrintTraitPredicateExt as _, PrintTraitRefExt as _, - with_forced_trimmed_paths, + FmtPrinter, Print, PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, + PrintTraitRefExt as _, with_forced_trimmed_paths, }; use rustc_middle::ty::{ self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, @@ -154,6 +154,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } else { (leaf_trait_predicate, &obligation) }; + + let (main_trait_predicate, leaf_trait_predicate, predicate_constness) = self.get_effects_trait_pred_override(main_trait_predicate, leaf_trait_predicate, span); + let main_trait_ref = main_trait_predicate.to_poly_trait_ref(); let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref(); @@ -164,9 +167,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return guar; } - // FIXME(effects) - let predicate_is_const = false; - if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; @@ -227,7 +227,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let err_msg = self.get_standard_error_message( main_trait_predicate, message, - predicate_is_const, + predicate_constness, append_const_msg, post_message, ); @@ -286,7 +286,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop) - && predicate_is_const + && matches!(predicate_constness, ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const) { err.note("`~const Drop` was renamed to `~const Destruct`"); err.note("See for more details"); @@ -2187,29 +2187,34 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option, - predicate_is_const: bool, + predicate_constness: ty::BoundConstness, append_const_msg: Option, post_message: String, ) -> String { message .and_then(|cannot_do_this| { - match (predicate_is_const, append_const_msg) { + match (predicate_constness, append_const_msg) { // do nothing if predicate is not const - (false, _) => Some(cannot_do_this), + (ty::BoundConstness::NotConst, _) => Some(cannot_do_this), // suggested using default post message - (true, Some(AppendConstMessage::Default)) => { - Some(format!("{cannot_do_this} in const contexts")) - } + ( + ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(AppendConstMessage::Default), + ) => Some(format!("{cannot_do_this} in const contexts")), // overridden post message - (true, Some(AppendConstMessage::Custom(custom_msg, _))) => { - Some(format!("{cannot_do_this}{custom_msg}")) - } + ( + ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(AppendConstMessage::Custom(custom_msg, _)), + ) => Some(format!("{cannot_do_this}{custom_msg}")), // fallback to generic message - (true, None) => None, + (ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, None) => None, } }) .unwrap_or_else(|| { - format!("the trait bound `{trait_predicate}` is not satisfied{post_message}") + format!( + "the trait bound `{}` is not satisfied{post_message}", + trait_predicate.print_with_bound_constness(predicate_constness) + ) }) } @@ -2333,6 +2338,51 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + /// For effects predicates such as `::Effects: Compat`, pretend that the + /// predicate that failed was `u32: Add`. Return the constness of such predicate to later + /// print as `u32: ~const Add`. + fn get_effects_trait_pred_override( + &self, + p: ty::PolyTraitPredicate<'tcx>, + leaf: ty::PolyTraitPredicate<'tcx>, + span: Span, + ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, ty::BoundConstness) { + let trait_ref = p.to_poly_trait_ref(); + if !self.tcx.is_lang_item(trait_ref.def_id(), LangItem::EffectsCompat) { + return (p, leaf, ty::BoundConstness::NotConst); + } + + let Some(ty::Alias(ty::AliasTyKind::Projection, projection)) = + trait_ref.self_ty().no_bound_vars().map(Ty::kind) + else { + return (p, leaf, ty::BoundConstness::NotConst); + }; + + let constness = trait_ref.skip_binder().args.const_at(1); + + let constness = if constness == self.tcx.consts.true_ || constness.is_ct_infer() { + ty::BoundConstness::NotConst + } else if constness == self.tcx.consts.false_ { + ty::BoundConstness::Const + } else if matches!(constness.kind(), ty::ConstKind::Param(_)) { + ty::BoundConstness::ConstIfConst + } else { + self.dcx().span_bug(span, format!("Unknown constness argument: {constness:?}")); + }; + + let new_pred = p.map_bound(|mut trait_pred| { + trait_pred.trait_ref = projection.trait_ref(self.tcx); + trait_pred + }); + + let new_leaf = leaf.map_bound(|mut trait_pred| { + trait_pred.trait_ref = projection.trait_ref(self.tcx); + trait_pred + }); + + (new_pred, new_leaf, constness) + } + fn add_tuple_trait_message( &self, obligation_cause_code: &ObligationCauseCode<'tcx>, diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 76065c10d190e..8146181df6c17 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -755,3 +755,10 @@ impl fmt::Display for BoundConstness { } } } + +impl Lift for BoundConstness { + type Lifted = BoundConstness; + fn lift_to_interner(self, _: I) -> Option { + Some(self) + } +} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr index fb491453b379b..8288c660ce7c8 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-0.stderr @@ -3,11 +3,11 @@ error: using `#![feature(effects)]` without enabling next trait solver globally = note: the next trait solver must be enabled globally for the effects feature to work correctly = help: use `-Znext-solver` to enable -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-0.rs:13:5 | LL | T::Assoc::func() - | ^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-0.rs:6:1 @@ -17,12 +17,16 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | const fn unqualified() -> i32 where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-0.rs:17:5 | LL | ::Assoc::func() - | ^^^^^^^^^^^^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-0.rs:6:1 @@ -32,6 +36,10 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | const fn qualified() -> i32 where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr index 392b310a4c99e..0792d090321d3 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage-1.stderr @@ -3,11 +3,11 @@ error: using `#![feature(effects)]` without enabling next trait solver globally = note: the next trait solver must be enabled globally for the effects feature to work correctly = help: use `-Znext-solver` to enable -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-1.rs:15:44 | LL | fn unqualified() -> Type<{ T::Assoc::func() }> { - | ^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-1.rs:7:1 @@ -17,12 +17,16 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | fn unqualified() -> Type<{ T::Assoc::func() }> where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ -error[E0277]: the trait bound `Trait::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `::Assoc: Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-1.rs:19:42 | LL | fn qualified() -> Type<{ ::Assoc::func() }> { - | ^^^^^^^^^^^^^^^^^^^ the trait `Compat` is not implemented for `Trait::{synthetic#0}` + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `::Assoc` | note: required by a bound in `Trait::func` --> $DIR/assoc-type-const-bound-usage-1.rs:7:1 @@ -32,6 +36,10 @@ LL | #[const_trait] ... LL | fn func() -> i32; | ---- required by a bound in this associated function +help: consider further restricting the associated type + | +LL | fn qualified() -> Type<{ ::Assoc::func() }> where ::Assoc: Trait { + | ++++++++++++++++++++++++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs index bb9e9045f8fb5..878f9a713a0b4 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] #[const_trait] pub trait Plus { @@ -23,7 +25,7 @@ pub const fn add_i32(a: i32, b: i32) -> i32 { pub const fn add_u32(a: u32, b: u32) -> u32 { a.plus(b) - //~^ ERROR the trait bound + //~^ ERROR the trait bound `u32: ~const Plus` } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr index 73ea1422bf9d7..5d2333d94fe89 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr @@ -1,33 +1,22 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/call-const-trait-method-fail.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: ~const Compat` is not satisfied - --> $DIR/call-const-trait-method-fail.rs:25:5 +error[E0277]: the trait bound `u32: ~const Plus` is not satisfied + --> $DIR/call-const-trait-method-fail.rs:27:5 | LL | a.plus(b) - | ^ the trait `~const Compat` is not implemented for `Runtime` + | ^ the trait `Plus` is not implemented for `u32` | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `Plus::plus` - --> $DIR/call-const-trait-method-fail.rs:3:1 + --> $DIR/call-const-trait-method-fail.rs:5:1 | LL | #[const_trait] | ^^^^^^^^^^^^^^ required by this bound in `Plus::plus` LL | pub trait Plus { LL | fn plus(self, rhs: Self) -> Self; | ---- required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub const fn add_u32(a: u32, b: u32) -> u32 where u32: Plus { + | +++++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs index 74e33ca72fffc..f9e79d417521d 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] struct S; @@ -21,7 +23,6 @@ const fn equals_self(t: &T) -> bool { // it not using the impl. pub const EQ: bool = equals_self(&S); -//~^ ERROR: the trait bound `Runtime: const Compat` is not satisfied -// FIXME(effects) diagnostic +//~^ ERROR: the trait bound `S: const Foo` is not satisfied fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr index b2a98041c1cdf..68c9fc400104e 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr @@ -1,32 +1,21 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/call-generic-method-nonconst.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: const Compat` is not satisfied - --> $DIR/call-generic-method-nonconst.rs:23:34 +error[E0277]: the trait bound `S: const Foo` is not satisfied + --> $DIR/call-generic-method-nonconst.rs:25:34 | LL | pub const EQ: bool = equals_self(&S); - | ----------- ^^ the trait `const Compat` is not implemented for `Runtime` + | ----------- ^^ the trait `Foo` is not implemented for `S` | | | required by a bound introduced by this call | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `equals_self` - --> $DIR/call-generic-method-nonconst.rs:16:25 + --> $DIR/call-generic-method-nonconst.rs:18:25 | LL | const fn equals_self(t: &T) -> bool { | ^^^^^^^^^^ required by this bound in `equals_self` +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub const EQ: bool where S: Foo = equals_self(&S); + | ++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs index 2fd58b05178c2..a0333153f857a 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] #[const_trait] trait ConstDefaultFn: Sized { @@ -22,7 +24,7 @@ impl const ConstDefaultFn for ConstImpl { const fn test() { NonConstImpl.a(); - //~^ ERROR the trait bound + //~^ ERROR the trait bound `NonConstImpl: ~const ConstDefaultFn` is not satisfied ConstImpl.a(); } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr index 02f9dffba3239..0809d9c1e1d32 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr @@ -1,33 +1,22 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-default-method-bodies.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: ~const Compat` is not satisfied - --> $DIR/const-default-method-bodies.rs:24:18 +error[E0277]: the trait bound `NonConstImpl: ~const ConstDefaultFn` is not satisfied + --> $DIR/const-default-method-bodies.rs:26:18 | LL | NonConstImpl.a(); - | ^ the trait `~const Compat` is not implemented for `Runtime` + | ^ the trait `ConstDefaultFn` is not implemented for `NonConstImpl` | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `ConstDefaultFn::a` - --> $DIR/const-default-method-bodies.rs:3:1 + --> $DIR/const-default-method-bodies.rs:5:1 | LL | #[const_trait] | ^^^^^^^^^^^^^^ required by this bound in `ConstDefaultFn::a` ... LL | fn a(self) { | - required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | const fn test() where NonConstImpl: ConstDefaultFn { + | ++++++++++++++++++++++++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr index 7aa3aa8c6bbb3..9eda9d98ec552 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-fns-are-early-bound.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `FnOnce<()>::{synthetic#0}: const Compat` is not satisfied +error[E0277]: the trait bound `fn() {foo}: const FnOnce()` is not satisfied --> $DIR/const-fns-are-early-bound.rs:31:17 | LL | is_const_fn(foo); - | ----------- ^^^ the trait `const Compat` is not implemented for `FnOnce<()>::{synthetic#0}` + | ----------- ^^^ the trait `FnOnce()` is not implemented for fn item `fn() {foo}` | | | required by a bound introduced by this call | diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr index b7209827c22c0..a34bae843c816 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `cross_crate::MyTrait::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `cross_crate::NonConst: ~const cross_crate::MyTrait` is not satisfied --> $DIR/cross-crate.rs:19:14 | LL | NonConst.func(); - | ^^^^ the trait `~const Compat` is not implemented for `cross_crate::MyTrait::{synthetic#0}` + | ^^^^ the trait `cross_crate::MyTrait` is not implemented for `cross_crate::NonConst` | note: required by a bound in `func` --> $DIR/auxiliary/cross-crate.rs:5:1 @@ -12,6 +12,10 @@ LL | #[const_trait] ... LL | fn func(self); | ---- required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | const fn const_context() where cross_crate::NonConst: cross_crate::MyTrait { + | +++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs index 64f23824b397d..0c2d93775a4d7 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs @@ -1,4 +1,6 @@ -#![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete +//@ compile-flags: -Znext-solver +#![allow(incomplete_features)] +#![feature(const_trait_impl, effects)] #[const_trait] pub trait Tr { @@ -6,7 +8,7 @@ pub trait Tr { fn b(&self) { ().a() - //~^ ERROR the trait bound + //~^ ERROR the trait bound `(): ~const Tr` is not satisfied } } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr index 1b5aa9c91918c..d0f22c0b9b62b 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr @@ -1,33 +1,22 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/default-method-body-is-const-same-trait-ck.rs:1:30 - | -LL | #![feature(const_trait_impl, effects)] - | ^^^^^^^ - | - = note: see issue #102090 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: using `#![feature(effects)]` without enabling next trait solver globally - | - = note: the next trait solver must be enabled globally for the effects feature to work correctly - = help: use `-Znext-solver` to enable - -error[E0277]: the trait bound `Runtime: ~const Compat` is not satisfied - --> $DIR/default-method-body-is-const-same-trait-ck.rs:8:12 +error[E0277]: the trait bound `(): ~const Tr` is not satisfied + --> $DIR/default-method-body-is-const-same-trait-ck.rs:10:12 | LL | ().a() - | ^ the trait `~const Compat` is not implemented for `Runtime` + | ^ the trait `Tr` is not implemented for `()` | - = help: the trait `Compat` is implemented for `Runtime` note: required by a bound in `Tr::a` - --> $DIR/default-method-body-is-const-same-trait-ck.rs:3:1 + --> $DIR/default-method-body-is-const-same-trait-ck.rs:5:1 | LL | #[const_trait] | ^^^^^^^^^^^^^^ required by this bound in `Tr::a` LL | pub trait Tr { LL | fn a(&self) {} | - required by a bound in this associated function +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub trait Tr where (): Tr { + | ++++++++++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs index 0d659744e700e..93a6f385e47db 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs @@ -17,8 +17,7 @@ trait Bar: ~const Foo {} const fn foo(x: &T) { x.a(); - //[yy,yn]~^ ERROR the trait bound - // FIXME(effects) diagnostic + //[yy,yn]~^ ERROR the trait bound `T: ~const Foo` } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr index d4064e01ef10c..873c57ec71ffe 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr @@ -10,11 +10,11 @@ note: this trait is not a `#[const_trait]`, so it cannot have `~const` trait bou LL | trait Bar: ~const Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `T: ~const Foo` is not satisfied --> $DIR/super-traits-fail-2.rs:19:7 | LL | x.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::a` --> $DIR/super-traits-fail-2.rs:6:25 @@ -24,10 +24,10 @@ LL | #[cfg_attr(any(yy, yn), const_trait)] LL | trait Foo { LL | fn a(&self); | - required by a bound in this associated function -help: consider further restricting the associated type +help: consider further restricting this bound | -LL | const fn foo(x: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ +LL | const fn foo(x: &T) { + | +++++ error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr index 9f9f96c6b486b..bea3aea2f3afb 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `T: ~const Foo` is not satisfied --> $DIR/super-traits-fail-2.rs:19:7 | LL | x.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::a` --> $DIR/super-traits-fail-2.rs:6:25 @@ -12,10 +12,10 @@ LL | #[cfg_attr(any(yy, yn), const_trait)] LL | trait Foo { LL | fn a(&self); | - required by a bound in this associated function -help: consider further restricting the associated type +help: consider further restricting this bound | -LL | const fn foo(x: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ +LL | const fn foo(x: &T) { + | +++++ error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs index c7e224dcce02c..b5643b1170000 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs @@ -20,7 +20,7 @@ trait Bar: ~const Foo {} const fn foo(x: &T) { //[yn,nn]~^ ERROR: `~const` can only be applied to `#[const_trait]` x.a(); - //[yn]~^ ERROR: the trait bound + //[yn]~^ ERROR: the trait bound `T: ~const Foo` is not satisfied } fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr index 0b48633a10e20..bbc95948a59db 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr @@ -16,11 +16,11 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | const fn foo(x: &T) { | ^^^ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied +error[E0277]: the trait bound `T: ~const Foo` is not satisfied --> $DIR/super-traits-fail-3.rs:22:7 | LL | x.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::a` --> $DIR/super-traits-fail-3.rs:8:25 @@ -30,10 +30,10 @@ LL | #[cfg_attr(any(yy, yn), const_trait)] LL | trait Foo { LL | fn a(&self); | - required by a bound in this associated function -help: consider further restricting the associated type +help: consider further restricting this bound | -LL | const fn foo(x: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ +LL | const fn foo(x: &T) { + | +++++ error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr index 979f1e798e0a7..eaa981ec74444 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Foo::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `T: Foo` is not satisfied --> $DIR/trait-where-clause-const.rs:22:5 | LL | T::b(); - | ^ the trait `Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::b` --> $DIR/trait-where-clause-const.rs:13:1 @@ -12,10 +12,6 @@ LL | #[const_trait] ... LL | fn b() where Self: ~const Bar; | - required by a bound in this associated function -help: consider further restricting the associated type - | -LL | const fn test1() where Foo::{synthetic#0}: Compat { - | ++++++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/trait-where-clause-const.rs:22:5 @@ -26,11 +22,11 @@ LL | T::b(); = note: expected constant `host` found constant `true` -error[E0277]: the trait bound `Foo::{synthetic#0}: Compat` is not satisfied +error[E0277]: the trait bound `T: Foo` is not satisfied --> $DIR/trait-where-clause-const.rs:25:5 | LL | T::c::(); - | ^ the trait `Compat` is not implemented for `Foo::{synthetic#0}` + | ^ the trait `Foo` is not implemented for `T` | note: required by a bound in `Foo::c` --> $DIR/trait-where-clause-const.rs:13:1 @@ -40,10 +36,6 @@ LL | #[const_trait] ... LL | fn c(); | - required by a bound in this associated function -help: consider further restricting the associated type - | -LL | const fn test1() where Foo::{synthetic#0}: Compat { - | ++++++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/trait-where-clause-const.rs:25:5 diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs index 5fffe54fc1a99..d336719f52edc 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs @@ -19,7 +19,6 @@ impl Trait for Ty { } fn main() { - // FIXME(effects): improve diagnostics on this require::(); } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr index 0806ffa4b5df6..848aa68689b48 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr @@ -7,7 +7,7 @@ LL | #![feature(const_trait_impl, effects, generic_const_exprs)] = help: remove one of these features error[E0308]: mismatched types - --> $DIR/unsatisfied-const-trait-bound.rs:30:37 + --> $DIR/unsatisfied-const-trait-bound.rs:29:37 | LL | fn accept0(_: Container<{ T::make() }>) {} | ^^^^^^^^^ expected `false`, found `true` @@ -16,7 +16,7 @@ LL | fn accept0(_: Container<{ T::make() }>) {} found constant `true` error[E0308]: mismatched types - --> $DIR/unsatisfied-const-trait-bound.rs:34:50 + --> $DIR/unsatisfied-const-trait-bound.rs:33:50 | LL | const fn accept1(_: Container<{ T::make() }>) {} | ^^^^^^^^^ expected `false`, found `host` @@ -24,17 +24,21 @@ LL | const fn accept1(_: Container<{ T::make() }>) {} = note: expected constant `false` found constant `host` -error[E0277]: the trait bound `Trait::{synthetic#0}: const Compat` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:23:15 +error[E0277]: the trait bound `Ty: const Trait` is not satisfied + --> $DIR/unsatisfied-const-trait-bound.rs:22:15 | LL | require::(); - | ^^ the trait `const Compat` is not implemented for `Trait::{synthetic#0}` + | ^^ the trait `Trait` is not implemented for `Ty` | note: required by a bound in `require` --> $DIR/unsatisfied-const-trait-bound.rs:8:15 | LL | fn require() {} | ^^^^^^^^^^^ required by this bound in `require` +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn main() where Ty: Trait { + | +++++++++++++++ error: aborting due to 4 previous errors From 1a04a317c447210306396e5ad4a100f423b1dfa6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 2 Oct 2024 14:39:43 +0200 Subject: [PATCH 1161/1301] review --- .../src/infer/relate/type_relating.rs | 8 +++++--- compiler/rustc_middle/src/ty/fold.rs | 17 +++++++++-------- .../src/canonicalizer.rs | 12 +++++++----- compiler/rustc_next_trait_solver/src/resolve.rs | 2 ++ .../src/solve/eval_ctxt/mod.rs | 15 ++++++++------- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index c8ced4a2cda85..7acfea643dd1e 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -14,14 +14,16 @@ use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; /// Enforce that `a` is equal to or a subtype of `b`. pub struct TypeRelating<'combine, 'a, 'tcx> { - // Partially mutable. + // Immutable except for the `InferCtxt` and the + // resulting nested `goals`. fields: &'combine mut CombineFields<'a, 'tcx>, - // Immutable fields. + // Immutable field. structurally_relate_aliases: StructurallyRelateAliases, + // Mutable field. ambient_variance: ty::Variance, - /// The cache has only tracks the `ambient_variance` as its the + /// The cache only tracks the `ambient_variance` as it's the /// only field which is mutable and which meaningfully changes /// the result when relating types. /// diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index e152d3f5fbe9f..b5a77b3c9423e 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -210,16 +210,17 @@ where debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST)); ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32()) } - _ if t.has_vars_bound_at_or_above(self.current_index) => { - if let Some(&ty) = self.cache.get(&(self.current_index, t)) { - return ty; + _ => { + if !t.has_vars_bound_at_or_above(self.current_index) { + t + } else if let Some(&t) = self.cache.get(&(self.current_index, t)) { + t + } else { + let res = t.super_fold_with(self); + assert!(self.cache.insert((self.current_index, t), res)); + res } - - let res = t.super_fold_with(self); - assert!(self.cache.insert((self.current_index, t), res)); - res } - _ => t, } } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 5c3c35b7b3e46..0bf9d7b924952 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -42,17 +42,19 @@ pub enum CanonicalizeMode { pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { delegate: &'a D, + + // Immutable field. canonicalize_mode: CanonicalizeMode, + // Mutable fields. variables: &'a mut Vec, - variable_lookup_table: HashMap, - primitive_var_infos: Vec>, + variable_lookup_table: HashMap, binder_index: ty::DebruijnIndex, - /// We only use the debruijn index during lookup as all other fields - /// should not be impacted by whether a type is folded once or multiple - /// times. + /// We only use the debruijn index during lookup. We don't need to + /// track the `variables` as each generic arg only results in a single + /// bound variable regardless of how many times it is encountered. cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>, } diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index a37066cec669f..f2654f7534e1d 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -16,6 +16,8 @@ where I: Interner, { delegate: &'a D, + /// We're able to use a cache here as the folder does not have any + /// mutable state. cache: DelayedMap, } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 12b4b3cb3a9d8..baa00465c0cce 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1057,16 +1057,17 @@ where ); infer_ty } - _ if ty.has_aliases() => { - if let Some(&entry) = self.cache.get(&ty) { + _ => { + if !ty.has_aliases() { + ty + } else if let Some(&entry) = self.cache.get(&ty) { return entry; + } else { + let res = ty.super_fold_with(self); + assert!(self.cache.insert(ty, res).is_none()); + res } - - let res = ty.super_fold_with(self); - assert!(self.cache.insert(ty, res).is_none()); - res } - _ => ty, } } From d868fdce6b9ddef6abcc8de86b3ba8459def36a2 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 18 Jul 2024 13:59:48 +0200 Subject: [PATCH 1162/1301] std: make `thread::current` available in all `thread_local!` destructors --- library/std/src/rt.rs | 34 ++- library/std/src/sys/pal/hermit/mod.rs | 6 +- library/std/src/sys/pal/hermit/thread.rs | 1 + library/std/src/sys/sync/rwlock/queue.rs | 6 +- .../std/src/sys/thread_local/guard/apple.rs | 1 + library/std/src/sys/thread_local/guard/key.rs | 39 ++- .../std/src/sys/thread_local/guard/solid.rs | 5 +- .../std/src/sys/thread_local/guard/windows.rs | 8 +- library/std/src/sys/thread_local/key/xous.rs | 2 + library/std/src/sys/thread_local/mod.rs | 48 ++-- .../std/src/sys/thread_local/native/mod.rs | 31 +++ library/std/src/sys/thread_local/os.rs | 34 ++- library/std/src/sys/thread_local/statik.rs | 33 ++- library/std/src/thread/current.rs | 252 ++++++++++++++++++ library/std/src/thread/local/tests.rs | 33 ++- library/std/src/thread/mod.rs | 128 +++------ 16 files changed, 529 insertions(+), 132 deletions(-) create mode 100644 library/std/src/thread/current.rs diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b6f36931ec28a..0a841f07e3be7 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -21,9 +21,10 @@ pub use crate::panicking::{begin_panic, panic_count}; pub use core::panicking::{panic_display, panic_fmt}; #[rustfmt::skip] +use crate::any::Any; use crate::sync::Once; -use crate::sys; use crate::thread::{self, Thread}; +use crate::{mem, panic, sys}; // Prints to the "panic output", depending on the platform this may be: // - the standard error output @@ -66,6 +67,11 @@ macro_rules! rtunwrap { }; } +fn handle_rt_panic(e: Box) { + mem::forget(e); + rtabort!("initialization or cleanup bug"); +} + // One-time runtime initialization. // Runs before `main`. // SAFETY: must be called only once during runtime initialization. @@ -101,6 +107,20 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { thread::set_current(thread); } +/// Clean up the thread-local runtime state. This *should* be run after all other +/// code managed by the Rust runtime, but will not cause UB if that condition is +/// not fulfilled. Also note that this function is not guaranteed to be run, but +/// skipping it will cause leaks and therefore is to be avoided. +pub(crate) fn thread_cleanup() { + // This function is run in situations where unwinding leads to an abort + // (think `extern "C"` functions). Abort here instead so that we can + // print a nice message. + panic::catch_unwind(|| { + crate::thread::drop_current(); + }) + .unwrap_or_else(handle_rt_panic); +} + // One-time runtime cleanup. // Runs after `main` or at program exit. // NOTE: this is not guaranteed to run, for example when the program aborts. @@ -123,11 +143,6 @@ fn lang_start_internal( argv: *const *const u8, sigpipe: u8, ) -> Result { - use crate::{mem, panic}; - let rt_abort = move |e| { - mem::forget(e); - rtabort!("initialization or cleanup bug"); - }; // Guard against the code called by this function from unwinding outside of the Rust-controlled // code, which is UB. This is a requirement imposed by a combination of how the // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking @@ -139,16 +154,17 @@ fn lang_start_internal( // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. // SAFETY: Only called once during runtime initialization. - panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?; + panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }) + .unwrap_or_else(handle_rt_panic); let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) .map_err(move |e| { mem::forget(e); rtabort!("drop of the panic payload panicked"); }); - panic::catch_unwind(cleanup).map_err(rt_abort)?; + panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic); // Guard against multiple threads calling `libc::exit` concurrently. // See the documentation for `unique_thread_exit` for more information. - panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?; + panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic); ret_code } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index f49ef94717499..b62afb40a615f 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -92,7 +92,11 @@ pub unsafe extern "C" fn runtime_entry( unsafe { crate::sys::thread_local::destructors::run(); } - unsafe { hermit_abi::exit(result) } + crate::rt::thread_cleanup(); + + unsafe { + hermit_abi::exit(result); + } } #[inline] diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 4c0c0919f4799..41f2c3e212355 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -53,6 +53,7 @@ impl Thread { // run all destructors crate::sys::thread_local::destructors::run(); + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index 0e658328c2e2d..733f51cae8c19 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -113,7 +113,7 @@ use crate::mem; use crate::ptr::{self, NonNull, null_mut, without_provenance_mut}; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::thread::{self, Thread}; +use crate::thread::{self, Thread, ThreadId}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the // locking operation will be retried. @@ -200,7 +200,9 @@ impl Node { fn prepare(&mut self) { // Fall back to creating an unnamed `Thread` handle to allow locking in // TLS destructors. - self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed)); + self.thread.get_or_init(|| { + thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new())) + }); self.completed = AtomicBool::new(false); } diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs index 6c27f7ae35cba..fa25b116622fc 100644 --- a/library/std/src/sys/thread_local/guard/apple.rs +++ b/library/std/src/sys/thread_local/guard/apple.rs @@ -26,6 +26,7 @@ pub fn enable() { unsafe extern "C" fn run_dtors(_: *mut u8) { unsafe { destructors::run(); + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs index 77575547c1498..59581e6f281e6 100644 --- a/library/std/src/sys/thread_local/guard/key.rs +++ b/library/std/src/sys/thread_local/guard/key.rs @@ -3,10 +3,12 @@ //! that will run all native TLS destructors in the destructor list. use crate::ptr; -use crate::sys::thread_local::destructors; use crate::sys::thread_local::key::{LazyKey, set}; +#[cfg(target_thread_local)] pub fn enable() { + use crate::sys::thread_local::destructors; + static DTORS: LazyKey = LazyKey::new(Some(run)); // Setting the key value to something other than NULL will result in the @@ -18,6 +20,41 @@ pub fn enable() { unsafe extern "C" fn run(_: *mut u8) { unsafe { destructors::run(); + // On platforms with `__cxa_thread_atexit_impl`, `destructors::run` + // does nothing on newer systems as the TLS destructors are + // registered with the system. But because all of those platforms + // call the destructors of TLS keys after the registered ones, this + // function will still be run last (at the time of writing). + crate::rt::thread_cleanup(); + } + } +} + +/// On platforms with key-based TLS, the system runs the destructors for us. +/// We still have to make sure that [`crate::rt::thread_cleanup`] is called, +/// however. This is done by defering the execution of a TLS destructor to +/// the next round of destruction inside the TLS destructors. +#[cfg(not(target_thread_local))] +pub fn enable() { + const DEFER: *mut u8 = ptr::without_provenance_mut(1); + const RUN: *mut u8 = ptr::without_provenance_mut(2); + + static CLEANUP: LazyKey = LazyKey::new(Some(run)); + + unsafe { set(CLEANUP.force(), DEFER) } + + unsafe extern "C" fn run(state: *mut u8) { + if state == DEFER { + // Make sure that this function is run again in the next round of + // TLS destruction. If there is no futher round, there will be leaks, + // but that's okay, `thread_cleanup` is not guaranteed to be called. + unsafe { set(CLEANUP.force(), RUN) } + } else { + debug_assert_eq!(state, RUN); + // If the state is still RUN in the next round of TLS destruction, + // it means that no other TLS destructors defined by this runtime + // have been run, as they would have set the state to DEFER. + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/guard/solid.rs b/library/std/src/sys/thread_local/guard/solid.rs index 054b2d561c8b4..3bcd46c481dc0 100644 --- a/library/std/src/sys/thread_local/guard/solid.rs +++ b/library/std/src/sys/thread_local/guard/solid.rs @@ -19,6 +19,9 @@ pub fn enable() { } unsafe extern "C" fn tls_dtor(_unused: *mut u8) { - unsafe { destructors::run() }; + unsafe { + destructors::run(); + crate::rt::thread_cleanup(); + } } } diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index bf94f7d6e3d13..7ee8e695c753f 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -80,13 +80,13 @@ pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) { if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH { - #[cfg(target_thread_local)] unsafe { + #[cfg(target_thread_local)] super::super::destructors::run(); - } - #[cfg(not(target_thread_local))] - unsafe { + #[cfg(not(target_thread_local))] super::super::key::run_dtors(); + + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 295fb848b304d..2ab4bba7d8e98 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -212,4 +212,6 @@ unsafe fn run_dtors() { unsafe { cur = (*cur).next }; } } + + crate::rt::thread_cleanup(); } diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 3d1b91a7ea095..31d3b43906004 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -31,12 +31,15 @@ cfg_if::cfg_if! { ))] { mod statik; pub use statik::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use statik::{LocalPointer, local_pointer}; } else if #[cfg(target_thread_local)] { mod native; pub use native::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use native::{LocalPointer, local_pointer}; } else { mod os; pub use os::{Storage, thread_local_inner}; + pub(crate) use os::{LocalPointer, local_pointer}; } } @@ -72,36 +75,47 @@ pub(crate) mod destructors { } /// This module provides a way to schedule the execution of the destructor list -/// on systems without a per-variable destructor system. -mod guard { +/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable` +/// should ensure that these functions are called at the right times. +pub(crate) mod guard { cfg_if::cfg_if! { if #[cfg(all(target_thread_local, target_vendor = "apple"))] { mod apple; - pub(super) use apple::enable; + pub(crate) use apple::enable; } else if #[cfg(target_os = "windows")] { mod windows; - pub(super) use windows::enable; + pub(crate) use windows::enable; } else if #[cfg(any( - all(target_family = "wasm", target_feature = "atomics"), + target_family = "wasm", + target_os = "uefi", + target_os = "zkvm", ))] { - pub(super) fn enable() { - // FIXME: Right now there is no concept of "thread exit", but - // this is likely going to show up at some point in the form of - // an exported symbol that the wasm runtime is going to be - // expected to call. For now we just leak everything, but if - // such a function starts to exist it will probably need to - // iterate the destructor list with this function: + pub(crate) fn enable() { + // FIXME: Right now there is no concept of "thread exit" on + // wasm, but this is likely going to show up at some point in + // the form of an exported symbol that the wasm runtime is going + // to be expected to call. For now we just leak everything, but + // if such a function starts to exist it will probably need to + // iterate the destructor list with these functions: + #[cfg(all(target_family = "wasm", target_feature = "atomics"))] #[allow(unused)] use super::destructors::run; + #[allow(unused)] + use crate::rt::thread_cleanup; } - } else if #[cfg(target_os = "hermit")] { - pub(super) fn enable() {} + } else if #[cfg(any( + target_os = "hermit", + target_os = "xous", + ))] { + // `std` is the only runtime, so it just calls the destructor functions + // itself when the time comes. + pub(crate) fn enable() {} } else if #[cfg(target_os = "solid_asp3")] { mod solid; - pub(super) use solid::enable; - } else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] { + pub(crate) use solid::enable; + } else { mod key; - pub(super) use key::enable; + pub(crate) use key::enable; } } } diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 1cc45fe892dee..f498dee0899f9 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -29,6 +29,9 @@ //! eliminates the `Destroyed` state for these values, which can allow more niche //! optimizations to occur for the `State` enum. For `Drop` types, `()` is used. +use crate::cell::Cell; +use crate::ptr; + mod eager; mod lazy; @@ -107,3 +110,31 @@ pub macro thread_local_inner { $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); }, } + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + #[thread_local] + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + p: Cell<*mut ()>, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { p: Cell::new(ptr::null_mut()) } + } + + pub fn get(&self) -> *mut () { + self.p.get() + } + + pub fn set(&self, p: *mut ()) { + self.p.set(p) + } +} diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index f09c396ef0f1e..26ce3322a16e3 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -1,8 +1,8 @@ -use super::abort_on_dtor_unwind; +use super::key::{Key, LazyKey, get, set}; +use super::{abort_on_dtor_unwind, guard}; use crate::cell::Cell; use crate::marker::PhantomData; use crate::ptr; -use crate::sys::thread_local::key::{Key, LazyKey, get, set}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -138,5 +138,35 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) { drop(ptr); // SAFETY: `key` is the TLS key `ptr` was stored under. unsafe { set(key, ptr::null_mut()) }; + // Make sure that the runtime cleanup will be performed + // after the next round of TLS destruction. + guard::enable(); }); } + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + key: LazyKey, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { key: LazyKey::new(None) } + } + + pub fn get(&'static self) -> *mut () { + unsafe { get(self.key.force()) as *mut () } + } + + pub fn set(&'static self, p: *mut ()) { + unsafe { set(self.key.force(), p as *mut u8) } + } +} diff --git a/library/std/src/sys/thread_local/statik.rs b/library/std/src/sys/thread_local/statik.rs index a3451ab74e04f..ba94caa669027 100644 --- a/library/std/src/sys/thread_local/statik.rs +++ b/library/std/src/sys/thread_local/statik.rs @@ -1,7 +1,8 @@ //! On some targets like wasm there's no threads, so no need to generate //! thread locals and we can instead just use plain statics! -use crate::cell::UnsafeCell; +use crate::cell::{Cell, UnsafeCell}; +use crate::ptr; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -93,3 +94,33 @@ impl LazyStorage { // SAFETY: the target doesn't have threads. unsafe impl Sync for LazyStorage {} + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + p: Cell<*mut ()>, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { p: Cell::new(ptr::null_mut()) } + } + + pub fn get(&self) -> *mut () { + self.p.get() + } + + pub fn set(&self, p: *mut ()) { + self.p.set(p) + } +} + +// SAFETY: the target doesn't have threads. +unsafe impl Sync for LocalPointer {} diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs new file mode 100644 index 0000000000000..b38149a0da740 --- /dev/null +++ b/library/std/src/thread/current.rs @@ -0,0 +1,252 @@ +use super::{Thread, ThreadId}; +use crate::mem::ManuallyDrop; +use crate::ptr; +use crate::sys::thread_local::local_pointer; + +const NONE: *mut () = ptr::null_mut(); +const BUSY: *mut () = ptr::without_provenance_mut(1); +const DESTROYED: *mut () = ptr::without_provenance_mut(2); + +local_pointer! { + static CURRENT; +} + +/// Persistent storage for the thread ID. +/// +/// We store the thread ID so that it never gets destroyed during the lifetime +/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s. +mod id { + use super::*; + + cfg_if::cfg_if! { + if #[cfg(target_thread_local)] { + use crate::cell::Cell; + + #[thread_local] + static ID: Cell> = Cell::new(None); + + pub(super) const CHEAP: bool = true; + + pub(super) fn get() -> Option { + ID.get() + } + + pub(super) fn set(id: ThreadId) { + ID.set(Some(id)) + } + } else if #[cfg(target_pointer_width = "16")] { + local_pointer! { + static ID0; + static ID16; + static ID32; + static ID48; + } + + pub(super) const CHEAP: bool = false; + + pub(super) fn get() -> Option { + let id0 = ID0.get().addr() as u64; + let id16 = ID16.get().addr() as u64; + let id32 = ID32.get().addr() as u64; + let id48 = ID48.get().addr() as u64; + ThreadId::from_u64((id48 << 48) + (id32 << 32) + (id16 << 16) + id0) + } + + pub(super) fn set(id: ThreadId) { + let val = id.as_u64().get(); + ID0.set(ptr::without_provenance_mut(val as usize)); + ID16.set(ptr::without_provenance_mut((val >> 16) as usize)); + ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); + ID48.set(ptr::without_provenance_mut((val >> 48) as usize)); + } + } else if #[cfg(target_pointer_width = "32")] { + local_pointer! { + static ID0; + static ID32; + } + + pub(super) const CHEAP: bool = false; + + pub(super) fn get() -> Option { + let id0 = ID0.get().addr() as u64; + let id32 = ID32.get().addr() as u64; + ThreadId::from_u64((id32 << 32) + id0) + } + + pub(super) fn set(id: ThreadId) { + let val = id.as_u64().get(); + ID0.set(ptr::without_provenance_mut(val as usize)); + ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); + } + } else { + local_pointer! { + static ID; + } + + pub(super) const CHEAP: bool = true; + + pub(super) fn get() -> Option { + let id = ID.get().addr() as u64; + ThreadId::from_u64(id) + } + + pub(super) fn set(id: ThreadId) { + let val = id.as_u64().get(); + ID.set(ptr::without_provenance_mut(val as usize)); + } + } + } + + #[inline] + pub(super) fn get_or_init() -> ThreadId { + get().unwrap_or_else( + #[cold] + || { + let id = ThreadId::new(); + id::set(id); + id + }, + ) + } +} + +/// Sets the thread handle for the current thread. +/// +/// Aborts if the handle or the ID has been set already. +pub(crate) fn set_current(thread: Thread) { + if CURRENT.get() != NONE || id::get().is_some() { + // Using `panic` here can add ~3kB to the binary size. We have complete + // control over where this is called, so just abort if there is a bug. + rtabort!("thread::set_current should only be called once per thread"); + } + + id::set(thread.id()); + + // Make sure that `crate::rt::thread_cleanup` will be run, which will + // call `drop_current`. + crate::sys::thread_local::guard::enable(); + CURRENT.set(thread.into_raw().cast_mut()); +} + +/// Gets the id of the thread that invokes it. +/// +/// This function will always succeed, will always return the same value for +/// one thread and is guaranteed not to call the global allocator. +#[inline] +pub(crate) fn current_id() -> ThreadId { + // If accessing the persistant thread ID takes multiple TLS accesses, try + // to retrieve it from the current thread handle, which will only take one + // TLS access. + if !id::CHEAP { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + let current = ManuallyDrop::new(Thread::from_raw(current)); + return current.id(); + } + } + } + + id::get_or_init() +} + +/// Gets a handle to the thread that invokes it, if the handle has been initialized. +pub(crate) fn try_current() -> Option { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + let current = ManuallyDrop::new(Thread::from_raw(current)); + Some((*current).clone()) + } + } else { + None + } +} + +/// Gets a handle to the thread that invokes it. +/// +/// # Examples +/// +/// Getting a handle to the current thread with `thread::current()`: +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::Builder::new() +/// .name("named thread".into()) +/// .spawn(|| { +/// let handle = thread::current(); +/// assert_eq!(handle.name(), Some("named thread")); +/// }) +/// .unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn current() -> Thread { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + let current = ManuallyDrop::new(Thread::from_raw(current)); + (*current).clone() + } + } else { + init_current(current) + } +} + +#[cold] +fn init_current(current: *mut ()) -> Thread { + if current == NONE { + CURRENT.set(BUSY); + // If the thread ID was initialized already, use it. + let id = id::get_or_init(); + let thread = Thread::new_unnamed(id); + + // Make sure that `crate::rt::thread_cleanup` will be run, which will + // call `drop_current`. + crate::sys::thread_local::guard::enable(); + CURRENT.set(thread.clone().into_raw().cast_mut()); + thread + } else if current == BUSY { + // BUSY exists solely for this check, but as it is in the slow path, the + // extra TLS write above shouldn't matter. The alternative is nearly always + // a stack overflow. + + // If you came across this message, contact the author of your allocator. + // If you are said author: A surprising amount of functions inside the + // standard library (e.g. `Mutex`, `thread_local!`, `File` when using long + // paths, even `panic!` when using unwinding), need memory allocation, so + // you'll get circular dependencies all over the place when using them. + // I (joboet) highly recommend using only APIs from core in your allocator + // and implementing your own system abstractions. Still, if you feel that + // a particular API should be entirely allocation-free, feel free to open + // an issue on the Rust repository, we'll see what we can do. + rtabort!( + "\n + Attempted to access thread-local data while allocating said data.\n + Do not access functions that allocate in the global allocator!\n + This is a bug in the global allocator.\n + " + ) + } else { + debug_assert_eq!(current, DESTROYED); + panic!( + "use of std::thread::current() is not possible after the thread's + local data has been destroyed" + ) + } +} + +/// This should be run in [`crate::rt::thread_cleanup`] to reset the thread +/// handle. +pub(crate) fn drop_current() { + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + CURRENT.set(DESTROYED); + drop(Thread::from_raw(current)); + } + } +} diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 6abb9b85a2e29..9d4f52a09218e 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -1,7 +1,7 @@ use crate::cell::{Cell, UnsafeCell}; use crate::sync::atomic::{AtomicU8, Ordering}; use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread::{self, LocalKey}; +use crate::thread::{self, Builder, LocalKey}; use crate::thread_local; #[derive(Clone, Default)] @@ -343,3 +343,34 @@ fn join_orders_after_tls_destructors() { jh2.join().unwrap(); } } + +// Test that thread::current is still available in TLS destructors. +#[test] +fn thread_current_in_dtor() { + // Go through one round of TLS destruction first. + struct Defer; + impl Drop for Defer { + fn drop(&mut self) { + RETRIEVE.with(|_| {}); + } + } + + struct RetrieveName; + impl Drop for RetrieveName { + fn drop(&mut self) { + *NAME.lock().unwrap() = Some(thread::current().name().unwrap().to_owned()); + } + } + + static NAME: Mutex> = Mutex::new(None); + + thread_local! { + static DEFER: Defer = const { Defer }; + static RETRIEVE: RetrieveName = const { RetrieveName }; + } + + Builder::new().name("test".to_owned()).spawn(|| DEFER.with(|_| {})).unwrap().join().unwrap(); + let name = NAME.lock().unwrap(); + let name = name.as_ref().unwrap(); + assert_eq!(name, "test"); +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 22d655833650b..d1d4eabb9bd45 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -141,7 +141,7 @@ //! [`Result`]: crate::result::Result //! [`Ok`]: crate::result::Result::Ok //! [`Err`]: crate::result::Result::Err -//! [`thread::current`]: current +//! [`thread::current`]: current::current //! [`thread::Result`]: Result //! [`unpark`]: Thread::unpark //! [`thread::park_timeout`]: park_timeout @@ -159,7 +159,7 @@ mod tests; use crate::any::Any; -use crate::cell::{Cell, OnceCell, UnsafeCell}; +use crate::cell::UnsafeCell; use crate::ffi::CStr; use crate::marker::PhantomData; use crate::mem::{self, ManuallyDrop, forget}; @@ -179,6 +179,12 @@ mod scoped; #[stable(feature = "scoped_threads", since = "1.63.0")] pub use scoped::{Scope, ScopedJoinHandle, scope}; +mod current; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use current::current; +pub(crate) use current::{current_id, drop_current, set_current, try_current}; + //////////////////////////////////////////////////////////////////////////////// // Thread-local storage //////////////////////////////////////////////////////////////////////////////// @@ -471,7 +477,11 @@ impl Builder { amt }); - let my_thread = name.map_or_else(Thread::new_unnamed, Thread::new); + let id = ThreadId::new(); + let my_thread = match name { + Some(name) => Thread::new(id, name.into()), + None => Thread::new_unnamed(id), + }; let their_thread = my_thread.clone(); let my_packet: Arc> = Arc::new(Packet { @@ -509,6 +519,9 @@ impl Builder { let f = MaybeDangling::new(f); let main = move || { + // Immediately store the thread handle to avoid setting it or its ID + // twice, which would cause an abort. + set_current(their_thread.clone()); if let Some(name) = their_thread.cname() { imp::Thread::set_name(name); } @@ -516,7 +529,6 @@ impl Builder { crate::io::set_output_capture(output_capture); let f = f.into_inner(); - set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { crate::sys::backtrace::__rust_begin_short_backtrace(f) })); @@ -690,84 +702,6 @@ where Builder::new().spawn(f).expect("failed to spawn thread") } -thread_local! { - // Invariant: `CURRENT` and `CURRENT_ID` will always be initialized together. - // If `CURRENT` is initialized, then `CURRENT_ID` will hold the same value - // as `CURRENT.id()`. - static CURRENT: OnceCell = const { OnceCell::new() }; - static CURRENT_ID: Cell> = const { Cell::new(None) }; -} - -/// Sets the thread handle for the current thread. -/// -/// Aborts if the handle has been set already to reduce code size. -pub(crate) fn set_current(thread: Thread) { - let tid = thread.id(); - // Using `unwrap` here can add ~3kB to the binary size. We have complete - // control over where this is called, so just abort if there is a bug. - CURRENT.with(|current| match current.set(thread) { - Ok(()) => CURRENT_ID.set(Some(tid)), - Err(_) => rtabort!("thread::set_current should only be called once per thread"), - }); -} - -/// Gets a handle to the thread that invokes it. -/// -/// In contrast to the public `current` function, this will not panic if called -/// from inside a TLS destructor. -pub(crate) fn try_current() -> Option { - CURRENT - .try_with(|current| { - current - .get_or_init(|| { - let thread = Thread::new_unnamed(); - CURRENT_ID.set(Some(thread.id())); - thread - }) - .clone() - }) - .ok() -} - -/// Gets the id of the thread that invokes it. -#[inline] -pub(crate) fn current_id() -> ThreadId { - CURRENT_ID.get().unwrap_or_else(|| { - // If `CURRENT_ID` isn't initialized yet, then `CURRENT` must also not be initialized. - // `current()` will initialize both `CURRENT` and `CURRENT_ID` so subsequent calls to - // `current_id()` will succeed immediately. - current().id() - }) -} - -/// Gets a handle to the thread that invokes it. -/// -/// # Examples -/// -/// Getting a handle to the current thread with `thread::current()`: -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::Builder::new() -/// .name("named thread".into()) -/// .spawn(|| { -/// let handle = thread::current(); -/// assert_eq!(handle.name(), Some("named thread")); -/// }) -/// .unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn current() -> Thread { - try_current().expect( - "use of std::thread::current() is not possible \ - after the thread's local data has been destroyed", - ) -} - /// Cooperatively gives up a timeslice to the OS scheduler. /// /// This calls the underlying OS scheduler's yield primitive, signaling @@ -1225,8 +1159,11 @@ pub fn park_timeout(dur: Duration) { pub struct ThreadId(NonZero); impl ThreadId { + // DO NOT rely on this value. + const MAIN_THREAD: ThreadId = ThreadId(unsafe { NonZero::new_unchecked(1) }); + // Generate a new unique thread ID. - fn new() -> ThreadId { + pub(crate) fn new() -> ThreadId { #[cold] fn exhausted() -> ! { panic!("failed to generate unique thread ID: bitspace exhausted") @@ -1236,7 +1173,7 @@ impl ThreadId { if #[cfg(target_has_atomic = "64")] { use crate::sync::atomic::AtomicU64; - static COUNTER: AtomicU64 = AtomicU64::new(0); + static COUNTER: AtomicU64 = AtomicU64::new(1); let mut last = COUNTER.load(Ordering::Relaxed); loop { @@ -1252,7 +1189,7 @@ impl ThreadId { } else { use crate::sync::{Mutex, PoisonError}; - static COUNTER: Mutex = Mutex::new(0); + static COUNTER: Mutex = Mutex::new(1); let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); let Some(id) = counter.checked_add(1) else { @@ -1269,6 +1206,11 @@ impl ThreadId { } } + #[cfg(not(target_thread_local))] + fn from_u64(v: u64) -> Option { + NonZero::new(v).map(ThreadId) + } + /// This returns a numeric identifier for the thread identified by this /// `ThreadId`. /// @@ -1369,27 +1311,27 @@ impl Inner { /// should instead use a function like `spawn` to create new threads, see the /// docs of [`Builder`] and [`spawn`] for more details. /// -/// [`thread::current`]: current +/// [`thread::current`]: current::current pub struct Thread { inner: Pin>, } impl Thread { /// Used only internally to construct a thread object without spawning. - pub(crate) fn new(name: String) -> Thread { - Self::new_inner(ThreadName::Other(name.into())) + pub(crate) fn new(id: ThreadId, name: String) -> Thread { + Self::new_inner(id, ThreadName::Other(name.into())) } - pub(crate) fn new_unnamed() -> Thread { - Self::new_inner(ThreadName::Unnamed) + pub(crate) fn new_unnamed(id: ThreadId) -> Thread { + Self::new_inner(id, ThreadName::Unnamed) } // Used in runtime to construct main thread pub(crate) fn new_main() -> Thread { - Self::new_inner(ThreadName::Main) + Self::new_inner(ThreadId::MAIN_THREAD, ThreadName::Main) } - fn new_inner(name: ThreadName) -> Thread { + fn new_inner(id: ThreadId, name: ThreadName) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // @@ -1399,7 +1341,7 @@ impl Thread { let mut arc = Arc::::new_uninit(); let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); (&raw mut (*ptr).name).write(name); - (&raw mut (*ptr).id).write(ThreadId::new()); + (&raw mut (*ptr).id).write(id); Parker::new_in_place(&raw mut (*ptr).parker); Pin::new_unchecked(arc.assume_init()) }; From a71ba0cf02cdd196b9c88fd1087ccdf7048c4c21 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 20 Jul 2024 13:00:00 +0200 Subject: [PATCH 1163/1301] bless miri tests --- src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr | 6 +++--- src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr | 2 +- src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr | 2 +- .../miri/tests/pass/backtrace/backtrace-global-alloc.stderr | 2 +- src/tools/miri/tests/pass/backtrace/backtrace-std.stderr | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr index b157e9f0b211a..b416f0eb68976 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr @@ -15,9 +15,9 @@ LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; = note: inside `std::panicking::r#try:: i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panic.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panicking::r#try::` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::r#try::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr index b87063431ad88..9849a1aa74ea0 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr @@ -10,7 +10,7 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) -RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#2}) +RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1}) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr index 2c729c49ee05d..48d9649c9201f 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr @@ -10,7 +10,7 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) -RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#2}) +RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1}) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr index d9414aa651835..667ee04e62413 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr @@ -14,7 +14,7 @@ at RUSTLIB/std/src/panicking.rs:LL:CC 7: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 8: std::rt::lang_start_internal::{closure#2} + 8: std::rt::lang_start_internal::{closure#1} at RUSTLIB/std/src/rt.rs:LL:CC 9: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr index d6d69ee837ede..b3a3a9d654a78 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr @@ -22,7 +22,7 @@ at RUSTLIB/std/src/panicking.rs:LL:CC 11: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 12: std::rt::lang_start_internal::{closure#2} + 12: std::rt::lang_start_internal::{closure#1} at RUSTLIB/std/src/rt.rs:LL:CC 13: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC From 746c3225925ee44b37c1665617ada24c8b173b20 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 2 Oct 2024 19:30:55 +0200 Subject: [PATCH 1164/1301] Fix target_vendor for aarch64-nintendo-switch-freestanding Previously set to `target_vendor = "unknown"`, but Nintendo is clearly the vendor of the Switch, and is also reflected in the target name itself. --- .../src/spec/targets/aarch64_nintendo_switch_freestanding.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs index d6d49a4a07044..1f1cd96632675 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { linker: Some("rust-lld".into()), link_script: Some(LINKER_SCRIPT.into()), os: "horizon".into(), + vendor: "nintendo".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, From 3e089b0e16db2779de0c5ca7d54a462d78354fb7 Mon Sep 17 00:00:00 2001 From: Frank Rehwinkel Date: Wed, 2 Oct 2024 07:44:01 -0400 Subject: [PATCH 1165/1301] epoll: add data_race test This test demonstrates the need to synchronize the clock of the thread waking up from an epoll_wait from the thread that issued the epoll awake event. --- .../fail-dep/libc/libc-epoll-blocking.rs | 79 +++++++++++++++++++ .../fail-dep/libc/libc-epoll-blocking.stderr | 29 +++++++ 2 files changed, 108 insertions(+) create mode 100644 src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs new file mode 100644 index 0000000000000..fd9d1875daf83 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs @@ -0,0 +1,79 @@ +//@only-target: linux +// test_epoll_race depends on a deterministic schedule. +//@compile-flags: -Zmiri-preemption-rate=0 + +use std::convert::TryInto; +use std::thread; + +fn main() { + test_epoll_race(); +} + +// Using `as` cast since `EPOLLET` wraps around +const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _; + +#[track_caller] +fn check_epoll_wait( + epfd: i32, + expected_notifications: &[(u32, u64)], + timeout: i32, +) { + let epoll_event = libc::epoll_event { events: 0, u64: 0 }; + let mut array: [libc::epoll_event; N] = [epoll_event; N]; + let maxsize = N; + let array_ptr = array.as_mut_ptr(); + let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) }; + if res < 0 { + panic!("epoll_wait failed: {}", std::io::Error::last_os_error()); + } + assert_eq!( + res, + expected_notifications.len().try_into().unwrap(), + "got wrong number of notifications" + ); + let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) }; + for (return_event, expected_event) in slice.iter().zip(expected_notifications.iter()) { + let event = return_event.events; + let data = return_event.u64; + assert_eq!(event, expected_event.0, "got wrong events"); + assert_eq!(data, expected_event.1, "got wrong data"); + } +} + +// This test shows a data_race before epoll had vector clocks added. +fn test_epoll_race() { + // Create an epoll instance. + let epfd = unsafe { libc::epoll_create1(0) }; + assert_ne!(epfd, -1); + + // Create an eventfd instance. + let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; + let fd = unsafe { libc::eventfd(0, flags) }; + + // Register eventfd with the epoll instance. + let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 }; + let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; + assert_eq!(res, 0); + + static mut VAL: u8 = 0; + let thread1 = thread::spawn(move || { + // Write to the static mut variable. + unsafe { VAL = 1 }; + // Write to the eventfd instance. + let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); + let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + // read returns number of bytes that have been read, which is always 8. + assert_eq!(res, 8); + }); + thread::yield_now(); + // epoll_wait for the event to happen. + let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); + let expected_value = u64::try_from(fd).unwrap(); + check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1); + // Read from the static mut variable. + #[allow(static_mut_refs)] + unsafe { + assert_eq!(VAL, 1) //~ ERROR: Data race detected + }; + thread1.join().unwrap(); +} diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr b/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr new file mode 100644 index 0000000000000..27974981cea7a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr @@ -0,0 +1,29 @@ +error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) retag read of type `u8` on thread `main` at ALLOC. (2) just happened here + --> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC + | +LL | assert_eq!(VAL, 1) + | ^^^^^^^^^^^^^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) retag read of type `u8` on thread `main` at ALLOC. (2) just happened here + | +help: and (1) occurred earlier here + --> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC + | +LL | unsafe { VAL = 1 }; + | ^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write + = 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 + = note: BACKTRACE (of the first span): + = note: inside `test_epoll_race` at RUSTLIB/core/src/macros/mod.rs:LL:CC +note: inside `main` + --> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC + | +LL | test_epoll_race(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 1b622f46728a51b6582b02b84407e5f64262479b Mon Sep 17 00:00:00 2001 From: Frank Rehwinkel Date: Wed, 2 Oct 2024 07:57:36 -0400 Subject: [PATCH 1166/1301] epoll: remove unnecessary instructions A couple of instructions were left over from an earlier rebase it would seem. They don't impact the logic but the ready_list type is about to change in the next commit. Rather than modify one of these lines in the commit that changes ready_list, only to have these lines removed later on, remove them now. They don't impact the tests results. --- src/tools/miri/src/shims/unix/linux/epoll.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index ff0709dd5ac1c..13c44334d5844 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -207,9 +207,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - let mut epoll_instance = Epoll::default(); - epoll_instance.ready_list = Rc::new(RefCell::new(BTreeMap::new())); - let fd = this.machine.fds.insert_new(Epoll::default()); Ok(Scalar::from_i32(fd)) } From 86bb1373aa94aec8d9d4c8fb692cd352eca01d90 Mon Sep 17 00:00:00 2001 From: Frank Rehwinkel Date: Wed, 2 Oct 2024 08:04:25 -0400 Subject: [PATCH 1167/1301] epoll: add vector clock to the epoll ready_list This adds a VClock to the epoll implementation's ready_list and has this VClock synced from the thread that updates an event in the ready_list and then has the VClocks of any threads being made runnable again, out of the calls to epoll_wait, synced from it. --- src/tools/miri/src/shims/unix/linux/epoll.rs | 37 ++++++--- .../fail-dep/libc/libc-epoll-blocking.rs | 79 ------------------- .../fail-dep/libc/libc-epoll-blocking.stderr | 29 ------- .../pass-dep/libc/libc-epoll-blocking.rs | 41 +++++++++- 4 files changed, 67 insertions(+), 119 deletions(-) delete mode 100644 src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs delete mode 100644 src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 13c44334d5844..88fab8aa010ab 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -4,6 +4,7 @@ use std::io; use std::rc::{Rc, Weak}; use std::time::Duration; +use crate::concurrency::VClock; use crate::shims::unix::fd::{FdId, FileDescriptionRef, WeakFileDescriptionRef}; use crate::shims::unix::*; use crate::*; @@ -19,7 +20,7 @@ struct Epoll { /// and file descriptor value. // This is an Rc because EpollInterest need to hold a reference to update // it. - ready_list: Rc>>, + ready_list: Rc, /// A list of thread ids blocked on this epoll instance. thread_id: RefCell>, } @@ -63,7 +64,7 @@ pub struct EpollEventInterest { /// data: u64, /// Ready list of the epoll instance under which this EpollEventInterest is registered. - ready_list: Rc>>, + ready_list: Rc, /// The epoll file description that this EpollEventInterest is registered under. weak_epfd: WeakFileDescriptionRef, } @@ -88,6 +89,12 @@ pub struct EpollReadyEvents { pub epollerr: bool, } +#[derive(Debug, Default)] +struct ReadyList { + mapping: RefCell>, + clock: RefCell, +} + impl EpollReadyEvents { pub fn new() -> Self { EpollReadyEvents { @@ -127,7 +134,7 @@ impl EpollReadyEvents { } impl Epoll { - fn get_ready_list(&self) -> Rc>> { + fn get_ready_list(&self) -> Rc { Rc::clone(&self.ready_list) } } @@ -374,7 +381,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { drop(epoll_interest); // Remove related epoll_interest from ready list. - ready_list.borrow_mut().remove(&epoll_key); + ready_list.mapping.borrow_mut().remove(&epoll_key); // Remove dangling EpollEventInterest from its global table. // .unwrap() below should succeed because the file description id must have registered @@ -469,7 +476,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .downcast::() .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; let binding = epoll_file_description.get_ready_list(); - ready_list_empty = binding.borrow_mut().is_empty(); + ready_list_empty = binding.mapping.borrow_mut().is_empty(); thread_ids = epoll_file_description.thread_id.borrow_mut(); } if timeout == 0 || !ready_list_empty { @@ -558,9 +565,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // holds a strong ref to epoll_interest. let epfd = epoll_interest.borrow().weak_epfd.upgrade().unwrap(); // FIXME: We can randomly pick a thread to unblock. - if let Some(thread_id) = - epfd.downcast::().unwrap().thread_id.borrow_mut().pop() - { + + let epoll = epfd.downcast::().unwrap(); + + // Synchronize running thread to the epoll ready list. + if let Some(clock) = &this.release_clock() { + epoll.ready_list.clock.borrow_mut().join(clock); + } + + if let Some(thread_id) = epoll.thread_id.borrow_mut().pop() { waiter.push(thread_id); }; } @@ -614,7 +627,7 @@ fn check_and_update_one_event_interest<'tcx>( // insert an epoll_return to the ready list. if flags != 0 { let epoll_key = (id, epoll_event_interest.fd_num); - let ready_list = &mut epoll_event_interest.ready_list.borrow_mut(); + let ready_list = &mut epoll_event_interest.ready_list.mapping.borrow_mut(); let event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); // Triggers the notification by inserting it to the ready list. ready_list.insert(epoll_key, event_instance); @@ -641,7 +654,11 @@ fn blocking_epoll_callback<'tcx>( .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; let ready_list = epoll_file_description.get_ready_list(); - let mut ready_list = ready_list.borrow_mut(); + + // Synchronize waking thread from the epoll ready list. + ecx.acquire_clock(&ready_list.clock.borrow()); + + let mut ready_list = ready_list.mapping.borrow_mut(); let mut num_of_events: i32 = 0; let mut array_iter = ecx.project_array_fields(events)?; diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs deleted file mode 100644 index fd9d1875daf83..0000000000000 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.rs +++ /dev/null @@ -1,79 +0,0 @@ -//@only-target: linux -// test_epoll_race depends on a deterministic schedule. -//@compile-flags: -Zmiri-preemption-rate=0 - -use std::convert::TryInto; -use std::thread; - -fn main() { - test_epoll_race(); -} - -// Using `as` cast since `EPOLLET` wraps around -const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _; - -#[track_caller] -fn check_epoll_wait( - epfd: i32, - expected_notifications: &[(u32, u64)], - timeout: i32, -) { - let epoll_event = libc::epoll_event { events: 0, u64: 0 }; - let mut array: [libc::epoll_event; N] = [epoll_event; N]; - let maxsize = N; - let array_ptr = array.as_mut_ptr(); - let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) }; - if res < 0 { - panic!("epoll_wait failed: {}", std::io::Error::last_os_error()); - } - assert_eq!( - res, - expected_notifications.len().try_into().unwrap(), - "got wrong number of notifications" - ); - let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) }; - for (return_event, expected_event) in slice.iter().zip(expected_notifications.iter()) { - let event = return_event.events; - let data = return_event.u64; - assert_eq!(event, expected_event.0, "got wrong events"); - assert_eq!(data, expected_event.1, "got wrong data"); - } -} - -// This test shows a data_race before epoll had vector clocks added. -fn test_epoll_race() { - // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); - - // Create an eventfd instance. - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; - - // Register eventfd with the epoll instance. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; - assert_eq!(res, 0); - - static mut VAL: u8 = 0; - let thread1 = thread::spawn(move || { - // Write to the static mut variable. - unsafe { VAL = 1 }; - // Write to the eventfd instance. - let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; - // read returns number of bytes that have been read, which is always 8. - assert_eq!(res, 8); - }); - thread::yield_now(); - // epoll_wait for the event to happen. - let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); - let expected_value = u64::try_from(fd).unwrap(); - check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1); - // Read from the static mut variable. - #[allow(static_mut_refs)] - unsafe { - assert_eq!(VAL, 1) //~ ERROR: Data race detected - }; - thread1.join().unwrap(); -} diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr b/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr deleted file mode 100644 index 27974981cea7a..0000000000000 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) retag read of type `u8` on thread `main` at ALLOC. (2) just happened here - --> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC - | -LL | assert_eq!(VAL, 1) - | ^^^^^^^^^^^^^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) retag read of type `u8` on thread `main` at ALLOC. (2) just happened here - | -help: and (1) occurred earlier here - --> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC - | -LL | unsafe { VAL = 1 }; - | ^^^^^^^ - = help: retags occur on all (re)borrows and as well as when references are copied or moved - = help: retags permit optimizations that insert speculative reads or writes - = help: therefore from the perspective of data races, a retag has the same implications as a read or write - = 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 - = note: BACKTRACE (of the first span): - = note: inside `test_epoll_race` at RUSTLIB/core/src/macros/mod.rs:LL:CC -note: inside `main` - --> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC - | -LL | test_epoll_race(); - | ^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) - -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-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index eb38529ae57df..d7675a40163c5 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 @@ -1,5 +1,5 @@ //@only-target: linux -// test_epoll_block_then_unblock depends on a deterministic schedule. +// test_epoll_block_then_unblock and test_epoll_race depend on a deterministic schedule. //@compile-flags: -Zmiri-preemption-rate=0 use std::convert::TryInto; @@ -12,6 +12,7 @@ fn main() { test_epoll_block_without_notification(); test_epoll_block_then_unblock(); test_notification_after_timeout(); + test_epoll_race(); } // Using `as` cast since `EPOLLET` wraps around @@ -137,3 +138,41 @@ fn test_notification_after_timeout() { let expected_value = fds[0] as u64; check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10); } + +// This test shows a data_race before epoll had vector clocks added. +fn test_epoll_race() { + // Create an epoll instance. + let epfd = unsafe { libc::epoll_create1(0) }; + assert_ne!(epfd, -1); + + // Create an eventfd instance. + let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; + let fd = unsafe { libc::eventfd(0, flags) }; + + // Register eventfd with the epoll instance. + let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 }; + let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; + assert_eq!(res, 0); + + static mut VAL: u8 = 0; + let thread1 = thread::spawn(move || { + // Write to the static mut variable. + unsafe { VAL = 1 }; + // Write to the eventfd instance. + let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); + let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + // read returns number of bytes that have been read, which is always 8. + assert_eq!(res, 8); + }); + thread::yield_now(); + // epoll_wait for the event to happen. + let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); + let expected_value = u64::try_from(fd).unwrap(); + check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1); + // Read from the static mut variable. + #[allow(static_mut_refs)] + unsafe { + assert_eq!(VAL, 1) + }; + thread1.join().unwrap(); +} From 81202c8b13c6ff64c14fd740757ba3e283adfec7 Mon Sep 17 00:00:00 2001 From: Frank Rehwinkel Date: Wed, 2 Oct 2024 08:13:11 -0400 Subject: [PATCH 1168/1301] epoll: remove extraneous clone of ready_list A simplification that doesn't impact the epoll implementation's logic. It is not necessary to clone the ready_list before reading its `is_empty` state. This avoids the clone step but more importantly avoids the invisible drop step of the clone. --- src/tools/miri/src/shims/unix/linux/epoll.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 88fab8aa010ab..c4bd38c47e54d 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -475,8 +475,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let epoll_file_description = epfd .downcast::() .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; - let binding = epoll_file_description.get_ready_list(); - ready_list_empty = binding.mapping.borrow_mut().is_empty(); + ready_list_empty = epoll_file_description.ready_list.mapping.borrow().is_empty(); thread_ids = epoll_file_description.thread_id.borrow_mut(); } if timeout == 0 || !ready_list_empty { From 033fdda46c55ece534fdd5aabf0c1e70bb73b1ee Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 2 Oct 2024 20:30:51 +0200 Subject: [PATCH 1169/1301] Fix target_env in avr-unknown-gnu-atmega328 The target name itself contains GNU, we should set that in the environment as well. --- compiler/rustc_target/src/spec/base/avr_gnu.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_target/src/spec/base/avr_gnu.rs b/compiler/rustc_target/src/spec/base/avr_gnu.rs index fb97738618b28..4f348af21ad99 100644 --- a/compiler/rustc_target/src/spec/base/avr_gnu.rs +++ b/compiler/rustc_target/src/spec/base/avr_gnu.rs @@ -19,6 +19,8 @@ pub(crate) fn target(target_cpu: &'static str, mmcu: &'static str) -> Target { llvm_target: "avr-unknown-unknown".into(), pointer_width: 16, options: TargetOptions { + env: "gnu".into(), + c_int_width: "16".into(), cpu: target_cpu.into(), exe_suffix: ".elf".into(), From 0ae796440a476a4ffe982fbb7cc54a9097e7f161 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 2 Oct 2024 20:54:09 +0200 Subject: [PATCH 1170/1301] Fix target_abi in SOLID targets The `armv7a-kmc-solid_asp3-eabi` and `armv7a-kmc-solid_asp3-eabihf` targets clearly have the ABI in their name, so it should also be exposed in Rust's `target_abi` cfg variable. --- .../rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs | 1 + .../src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs index 7e14c5efe7191..e5ae1064d9716 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs @@ -14,6 +14,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), options: TargetOptions { + abi: "eabi".into(), linker: Some("arm-kmc-eabi-gcc".into()), features: "+v7,+soft-float,+thumb2,-neon".into(), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs index 1958f4a7c30a2..0879fa24a1b73 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs @@ -14,6 +14,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), options: TargetOptions { + abi: "eabihf".into(), linker: Some("arm-kmc-eabi-gcc".into()), features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), relocation_model: RelocModel::Static, From a0228686d16baea8880320905a484cfbfe36d8bc Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Sep 2024 13:17:27 -0700 Subject: [PATCH 1171/1301] library: Stabilize `const_str_from_utf8_unchecked_mut` Const-stabilizes: - `str::from_utf8_unchecked_mut` --- library/core/src/lib.rs | 1 - library/core/src/str/converts.rs | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 01cadd78cc09d..9edc1a8c1f588 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -152,7 +152,6 @@ #![feature(const_slice_from_ref)] #![feature(const_slice_split_at_mut)] #![feature(const_str_as_mut)] -#![feature(const_str_from_utf8_unchecked_mut)] #![feature(const_strict_overflow_ops)] #![feature(const_swap)] #![feature(const_try)] diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index d6459607221ae..e932b652e2dcc 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -195,7 +195,10 @@ pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { #[inline] #[must_use] #[stable(feature = "str_mut_extras", since = "1.20.0")] -#[rustc_const_unstable(feature = "const_str_from_utf8_unchecked_mut", issue = "91005")] +#[rustc_const_stable( + feature = "const_str_from_utf8_unchecked_mut", + since = "CURRENT_RUSTC_VERSION" +)] #[rustc_diagnostic_item = "str_from_utf8_unchecked_mut"] pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { // SAFETY: the caller must guarantee that the bytes `v` From bcc78bdc29ed8183f60b3275ce8ac292bd771ab4 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Sep 2024 13:30:21 -0700 Subject: [PATCH 1172/1301] library: Stabilize `const_str_as_mut` Const-stabilizes: - `str::as_bytes_mut` - `str::as_mut_ptr` --- library/core/src/lib.rs | 1 - library/core/src/str/converts.rs | 1 + library/core/src/str/mod.rs | 6 ++++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9edc1a8c1f588..ba30c4eab1180 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -151,7 +151,6 @@ #![feature(const_slice_from_raw_parts_mut)] #![feature(const_slice_from_ref)] #![feature(const_slice_split_at_mut)] -#![feature(const_str_as_mut)] #![feature(const_strict_overflow_ops)] #![feature(const_swap)] #![feature(const_try)] diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index e932b652e2dcc..194db56fdafda 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -195,6 +195,7 @@ pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { #[inline] #[must_use] #[stable(feature = "str_mut_extras", since = "1.20.0")] +#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] #[rustc_const_stable( feature = "const_str_from_utf8_unchecked_mut", since = "CURRENT_RUSTC_VERSION" diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 3d535214637f6..e93c52f27999e 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -339,7 +339,8 @@ impl str { /// assert_eq!("🍔∈🌏", s); /// ``` #[stable(feature = "str_mut_extras", since = "1.20.0")] - #[rustc_const_unstable(feature = "const_str_as_mut", issue = "130086")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_str_as_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline(always)] pub const unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { @@ -385,7 +386,8 @@ impl str { /// It is your responsibility to make sure that the string slice only gets /// modified in a way that it remains valid UTF-8. #[stable(feature = "str_as_mut_ptr", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_str_as_mut", issue = "130086")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_str_as_mut", since = "CURRENT_RUSTC_VERSION")] #[rustc_never_returns_null_ptr] #[must_use] #[inline(always)] From 966405d1072bda18810a886418be1ed7da4dac15 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Sep 2024 13:34:06 -0700 Subject: [PATCH 1173/1301] library: Stabilize `const_ptr_as_ref` Const-stabilizes: - `NonNull::as_mut` --- library/core/src/lib.rs | 1 - library/core/src/ptr/non_null.rs | 3 ++- library/core/tests/lib.rs | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ba30c4eab1180..bcc38720ad1a1 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -140,7 +140,6 @@ #![feature(const_option_ext)] #![feature(const_pin)] #![feature(const_pointer_is_aligned)] -#![feature(const_ptr_as_ref)] #![feature(const_ptr_is_null)] #![feature(const_ptr_sub_ptr)] #![feature(const_ptr_write)] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index daa40b3c9d2e5..f89db6a447047 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -394,7 +394,8 @@ impl NonNull { /// /// [the module documentation]: crate::ptr#safety #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_unstable(feature = "const_ptr_as_ref", issue = "91822")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_ptr_as_ref", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline(always)] pub const unsafe fn as_mut<'a>(&mut self) -> &'a mut T { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 604c0d4874362..ae055fd6a2b64 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -30,7 +30,6 @@ #![feature(const_option_ext)] #![feature(const_pin)] #![feature(const_pointer_is_aligned)] -#![feature(const_ptr_as_ref)] #![feature(const_ptr_write)] #![feature(const_result)] #![feature(const_slice_from_ref)] From 75db6b29b5ad03f4ca4a8cfcc18f76feca32731a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Sep 2024 13:49:49 -0700 Subject: [PATCH 1174/1301] library: Stabilize `const_unsafecell_get_mut` Const-stabilizes: - `UnsafeCell::get_mut` --- library/core/src/cell.rs | 3 ++- library/core/src/lib.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c99f1aece4f69..a5cfddd281d45 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -2173,7 +2173,8 @@ impl UnsafeCell { /// ``` #[inline(always)] #[stable(feature = "unsafe_cell_get_mut", since = "1.50.0")] - #[rustc_const_unstable(feature = "const_unsafecell_get_mut", issue = "88836")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_unsafecell_get_mut", since = "CURRENT_RUSTC_VERSION")] pub const fn get_mut(&mut self) -> &mut T { &mut self.value } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index bcc38720ad1a1..dc5716a5bb3d3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -158,7 +158,6 @@ #![feature(const_typed_swap)] #![feature(const_ub_checks)] #![feature(const_unicode_case_lookup)] -#![feature(const_unsafecell_get_mut)] #![feature(coverage_attribute)] #![feature(do_not_recommend)] #![feature(duration_consts_float)] From ac53f1f242433b9cc5c440e85aa40468f9ca256a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Sep 2024 13:50:09 -0700 Subject: [PATCH 1175/1301] library: Stabilize `const_slice_first_last` Const-stabilizes: - `slice::first_mut` - `slice::split_first_mut` - `slice::last_mut` - `slice::split_last_mut` --- library/core/src/slice/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index dd8fa1ae343ae..ae988deb28f5f 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -172,7 +172,8 @@ impl [T] { /// assert_eq!(None, y.first_mut()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_slice_first_last", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const fn first_mut(&mut self) -> Option<&mut T> { @@ -214,7 +215,8 @@ impl [T] { /// assert_eq!(x, &[3, 4, 5]); /// ``` #[stable(feature = "slice_splits", since = "1.5.0")] - #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_slice_first_last", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { @@ -256,7 +258,8 @@ impl [T] { /// assert_eq!(x, &[4, 5, 3]); /// ``` #[stable(feature = "slice_splits", since = "1.5.0")] - #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_slice_first_last", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { @@ -298,7 +301,8 @@ impl [T] { /// assert_eq!(None, y.last_mut()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[rustc_const_stable(feature = "const_slice_first_last", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const fn last_mut(&mut self) -> Option<&mut T> { From f95bdf453e4f4c0a5ac244ff5878264b61ccaa8e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 21:52:44 -0400 Subject: [PATCH 1176/1301] Remove redundant in_trait from hir::TyKind::OpaqueDef --- compiler/rustc_ast_lowering/src/lib.rs | 1 - compiler/rustc_borrowck/src/diagnostics/region_name.rs | 2 +- compiler/rustc_hir/src/hir.rs | 4 ++-- compiler/rustc_hir/src/intravisit.rs | 2 +- compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 2 +- compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs | 2 +- compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 4 ++-- compiler/rustc_middle/src/ty/diagnostics.rs | 2 +- compiler/rustc_passes/src/dead.rs | 2 +- .../infer/nice_region_error/static_impl_trait.rs | 2 +- .../rustc_trait_selection/src/error_reporting/infer/region.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- .../clippy/clippy_lints/src/extra_unused_type_parameters.rs | 2 +- src/tools/clippy/clippy_lints/src/lifetimes.rs | 2 +- src/tools/clippy/clippy_lints/src/manual_async_fn.rs | 2 +- src/tools/clippy/clippy_utils/src/hir_utils.rs | 3 +-- 17 files changed, 18 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c6cb7aa7dd5c2..8f8fe9e85809e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1776,7 +1776,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyKind::OpaqueDef( hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } }, generic_args, - in_trait, ) } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 2f22e1532c196..d4598a1f582b6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -832,7 +832,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { let hir = self.infcx.tcx.hir(); - let hir::TyKind::OpaqueDef(id, _, _) = hir_ty.kind else { + let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else { span_bug!( hir_ty.span, "lowered return type of async fn is not OpaqueDef: {:?}", diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 71216023ecc33..ac23443fee2a9 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2632,7 +2632,7 @@ impl<'hir> Ty<'hir> { } TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty), TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(), - TyKind::OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args), + TyKind::OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args), TyKind::Path(QPath::TypeRelative(ty, segment)) => { ty.is_suggestable_infer_ty() || are_suggestable_generic_args(segment.args().args) } @@ -2856,7 +2856,7 @@ pub enum TyKind<'hir> { /// possibly parameters) that are actually bound on the `impl Trait`. /// /// The last parameter specifies whether this opaque appears in a trait definition. - OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool), + OpaqueDef(ItemId, &'hir [GenericArg<'hir>]), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. TraitObject( diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 4da3224578519..d0a8aaa85bb29 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -894,7 +894,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); } - TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { + TyKind::OpaqueDef(item_id, lifetimes) => { try_visit!(visitor.visit_nested_item(item_id)); walk_list!(visitor, visit_generic_arg, lifetimes); } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 9e9704622052f..33f6623edfdf3 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -332,7 +332,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // and the duplicated parameter, to ensure that they do not get out of sync. if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node { let opaque_ty_node = tcx.parent_hir_node(hir_id); - let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node + let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes), .. }) = opaque_ty_node else { bug!("unexpected {opaque_ty_node:?}") }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index c9b949ad88d54..30944b5e9908b 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -689,7 +689,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }; self.with(scope, |this| this.visit_ty(mt.ty)); } - hir::TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { + hir::TyKind::OpaqueDef(item_id, lifetimes) => { // Resolve the lifetimes in the bounds to the lifetime defs in the generics. // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to // `type MyAnonTy<'b> = impl MyTrait<'b>;` diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2186952720f33..496f54e31d2c0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2087,11 +2087,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); self.lower_path(opt_self_ty, path, hir_ty.hir_id, false) } - &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { + &hir::TyKind::OpaqueDef(item_id, lifetimes) => { let opaque_ty = tcx.hir().item(item_id); match opaque_ty.kind { - hir::ItemKind::OpaqueTy(&hir::OpaqueTy { .. }) => { + hir::ItemKind::OpaqueTy(&hir::OpaqueTy { in_trait, .. }) => { let local_def_id = item_id.owner_id.def_id; // If this is an RPITIT and we are using the new RPITIT lowering scheme, we // generate the def_id of an associated type for the trait and return as diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 992eb264163ba..d98e18c1b0c2e 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -510,7 +510,7 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { ) => { self.0.push(ty); } - hir::TyKind::OpaqueDef(item_id, _, _) => { + hir::TyKind::OpaqueDef(item_id, _) => { self.0.push(ty); let item = self.1.item(item_id); hir::intravisit::walk_item(self, item); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index aa329fc546ecb..100f3e80603e5 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -656,7 +656,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if let TyKind::OpaqueDef(item_id, _, _) = ty.kind { + if let TyKind::OpaqueDef(item_id, _) = ty.kind { let item = self.tcx.hir().item(item_id); intravisit::walk_item(self, item); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 4bde120cba9d0..31256bca55e93 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -284,7 +284,7 @@ pub fn suggest_new_region_bound( } match fn_return.kind { // FIXME(precise_captures): Suggest adding to `use<...>` list instead. - TyKind::OpaqueDef(item_id, _, _) => { + TyKind::OpaqueDef(item_id, _) => { let item = tcx.hir().item(item_id); let ItemKind::OpaqueTy(opaque) = &item.kind else { return; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 41fe8a2bf2266..4c3a011248808 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -857,7 +857,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { - let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else { + let hir::TyKind::OpaqueDef(item_id, _) = ty.kind else { return hir::intravisit::walk_ty(self, ty); }; let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 6726db8bb54df..45fe199c335b0 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -323,7 +323,7 @@ fn associated_types_for_impl_traits_in_associated_fn( impl<'tcx> Visitor<'tcx> for RPITVisitor<'tcx> { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind + if let hir::TyKind::OpaqueDef(item_id, _) = ty.kind && self.rpits.insert(item_id.owner_id.def_id) { let opaque_item = diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 3d845cf878f4a..b79458eaa7890 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1828,7 +1828,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T Array(Box::new(clean_ty(ty, cx)), length.into()) } TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()), - TyKind::OpaqueDef(item_id, _, _) => { + TyKind::OpaqueDef(item_id, _) => { let item = cx.tcx.hir().item(item_id); if let hir::ItemKind::OpaqueTy(ty) = item.kind { ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index bf9388b4a70fd..a7fb9535e7904 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -199,7 +199,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { if let Some((def_id, _)) = t.peel_refs().as_generic_param() { self.ty_params.remove(&def_id); - } else if let TyKind::OpaqueDef(id, _, _) = t.kind { + } else if let TyKind::OpaqueDef(id, _) = t.kind { // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 755afe959b37a..d3cc5ea628c72 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -523,7 +523,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::OpaqueDef(item, bounds, _) => { + TyKind::OpaqueDef(item, bounds) => { let map = self.cx.tcx.hir(); let item = map.item(item); let len = self.lts.len(); diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index fc3bba9e51232..7097c85156c34 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -105,7 +105,7 @@ fn future_trait_ref<'tcx>( cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>, ) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { - if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind + if let TyKind::OpaqueDef(item_id, bounds) = ty.kind && let item = cx.tcx.hir().item(item_id) && let ItemKind::OpaqueTy(opaque) = &item.kind && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 76900379ac787..b2d75125ccdb1 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1115,9 +1115,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, TyKind::Path(ref qpath) => self.hash_qpath(qpath), - TyKind::OpaqueDef(_, arg_list, in_trait) => { + TyKind::OpaqueDef(_, arg_list) => { self.hash_generic_args(arg_list); - in_trait.hash(&mut self.s); }, TyKind::TraitObject(_, lifetime, _) => { self.hash_lifetime(lifetime); From cb7e3695e8e7ea04377f60977b65ba324273b63d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 22:04:18 -0400 Subject: [PATCH 1177/1301] Use named fields for OpaqueTyOrigin --- compiler/rustc_ast_lowering/src/lib.rs | 12 +++++++----- .../src/region_infer/opaque_types.rs | 4 ++-- compiler/rustc_hir/src/hir.rs | 10 ++++++++-- compiler/rustc_hir_analysis/src/check/check.rs | 16 ++++++++-------- .../src/check/compare_impl_item/refine.rs | 4 ++-- .../src/collect/generics_of.rs | 3 ++- .../src/collect/item_bounds.rs | 4 ++-- .../src/collect/resolve_bound_vars.rs | 4 ++-- .../rustc_hir_analysis/src/collect/type_of.rs | 3 ++- compiler/rustc_hir_typeck/src/_match.rs | 3 ++- .../rustc_lint/src/impl_trait_overcaptures.rs | 4 ++-- .../src/opaque_hidden_inferred_bound.rs | 6 +++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 6 +++--- .../src/error_reporting/infer/region.rs | 2 +- .../error_reporting/traits/fulfillment_errors.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 3 ++- compiler/rustc_ty_utils/src/opaque_types.rs | 3 ++- src/librustdoc/visit_ast.rs | 2 +- src/tools/clippy/clippy_lints/src/len_zero.rs | 2 +- 19 files changed, 53 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 8f8fe9e85809e..c8aa8d701b9b0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1555,7 +1555,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .map(|(ident, id, _)| Lifetime { id, ident }) .collect() } - hir::OpaqueTyOrigin::FnReturn(..) => { + hir::OpaqueTyOrigin::FnReturn { .. } => { if matches!( fn_kind.expect("expected RPITs to be lowered with a FnKind"), FnDeclKind::Impl | FnDeclKind::Trait @@ -1576,7 +1576,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime_collector::lifetimes_in_bounds(self.resolver, bounds) } } - hir::OpaqueTyOrigin::AsyncFn(..) => { + hir::OpaqueTyOrigin::AsyncFn { .. } => { unreachable!("should be using `lower_async_fn_ret_ty`") } } @@ -1867,7 +1867,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | FnDeclKind::Inherent | FnDeclKind::Trait | FnDeclKind::Impl => ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)), + origin: hir::OpaqueTyOrigin::FnReturn { + parent: self.local_def_id(fn_node_id), + }, fn_kind: Some(kind), }, FnDeclKind::ExternFn => { @@ -1952,7 +1954,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_ref = self.lower_opaque_inner( opaque_ty_node_id, - hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, matches!(fn_kind, FnDeclKind::Trait), captured_lifetimes, span, @@ -1963,7 +1965,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { coro, opaque_ty_span, ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), + origin: hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id }, fn_kind: Some(fn_kind), }, ); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 3cf21d4a36b83..1f6aa59805948 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -503,8 +503,8 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { let &Self { tcx, def_id, .. } = self; let origin = tcx.opaque_type_origin(def_id); let parent = match origin { - hir::OpaqueTyOrigin::FnReturn(parent) - | hir::OpaqueTyOrigin::AsyncFn(parent) + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(parent); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ac23443fee2a9..68195c46f5c40 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2806,9 +2806,15 @@ pub struct PreciseCapturingNonLifetimeArg { #[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { /// `-> impl Trait` - FnReturn(LocalDefId), + FnReturn { + /// The defining function. + parent: LocalDefId, + }, /// `async fn` - AsyncFn(LocalDefId), + AsyncFn { + /// The defining function. + parent: LocalDefId, + }, /// type aliases: `type Foo = impl Trait;` TyAlias { /// The type alias or associated type parent of the TAIT/ATPIT diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d725772a5b369..feba484229b87 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -336,9 +336,9 @@ fn check_opaque_meets_bounds<'tcx>( origin: &hir::OpaqueTyOrigin, ) -> Result<(), ErrorGuaranteed> { let defining_use_anchor = match *origin { - hir::OpaqueTyOrigin::FnReturn(did) - | hir::OpaqueTyOrigin::AsyncFn(did) - | hir::OpaqueTyOrigin::TyAlias { parent: did, .. } => did, + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } + | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(defining_use_anchor); @@ -346,8 +346,8 @@ fn check_opaque_meets_bounds<'tcx>( let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let args = match *origin { - hir::OpaqueTyOrigin::FnReturn(parent) - | hir::OpaqueTyOrigin::AsyncFn(parent) + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item( tcx, parent, ) @@ -409,7 +409,7 @@ fn check_opaque_meets_bounds<'tcx>( let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; - if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { + if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } = origin { // HACK: this should also fall through to the hidden type check below, but the original // implementation had a bug where equivalent lifetimes are not identical. This caused us // to reject existing stable code that is otherwise completely fine. The real fix is to @@ -736,8 +736,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_opaque_precise_captures(tcx, def_id); let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) - | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin + if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id } = origin && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index e07b587508a68..3375fc303f156 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -94,8 +94,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( if !tcx.hir().get_if_local(impl_opaque.def_id).is_some_and(|node| { matches!( node.expect_item().expect_opaque_ty().origin, - hir::OpaqueTyOrigin::AsyncFn(def_id) | hir::OpaqueTyOrigin::FnReturn(def_id) - if def_id == impl_m.def_id.expect_local() + hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::FnReturn { parent } + if parent == impl_m.def_id.expect_local() ) }) { report_mismatched_rpitit_signature( diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 0a8eef2006d5a..73e98c67cdc30 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -210,7 +210,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Node::Item(item) => match item.kind { ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, in_trait, .. }) => { diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index f44b4728ad533..07a8c3a9e84db 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -388,8 +388,8 @@ pub(super) fn explicit_item_bounds_with_filter( span, .. }) => { - let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) - | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = *origin + let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = *origin else { span_bug!(*span, "RPITIT cannot be a TAIT, but got origin {origin:?}"); }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 30944b5e9908b..60788641da0cd 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -515,8 +515,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn(parent) - | hir::OpaqueTyOrigin::AsyncFn(parent) + hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::TyAlias { parent, .. }, generics, .. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 48b5e87cbd033..313d7dd62654a 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -618,7 +618,8 @@ pub(super) fn type_of_opaque( // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(&OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), + hir::OpaqueTyOrigin::FnReturn { parent: owner } + | hir::OpaqueTyOrigin::AsyncFn { parent: owner }, in_trait, .. }) => { diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index bf8ed017cf703..204138ffd44a4 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -602,7 +602,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(k, _)| (k.def_id, k.args))?, _ => return None, }; - let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = self.tcx.opaque_type_origin(def_id) + let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id } = + self.tcx.opaque_type_origin(def_id) else { return None; }; diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index a073d16f634d8..c13f4c7746d08 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -259,8 +259,8 @@ where // If it's owned by this function && let opaque = self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty() - && let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin - && parent_def_id == self.parent_def_id + && let hir::OpaqueTyOrigin::FnReturn { parent } = opaque.origin + && parent == self.parent_def_id { let opaque_span = self.tcx.def_span(opaque_def_id); let new_capture_rules = diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 83652bbf54631..87fa38a282ed1 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // That's because although we may have an opaque type on the function, // it won't have a hidden type, so proving predicates about it is // not really meaningful. - if let hir::OpaqueTyOrigin::FnReturn(method_def_id) = opaque.origin + if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id } = opaque.origin && let hir::Node::TraitItem(trait_item) = cx.tcx.hir_node_by_def_id(method_def_id) && !trait_item.defaultness.has_value() { @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { && cx.tcx.parent(opaque_ty.def_id) == def_id && matches!( opaque.origin, - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) + hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } ) { return; @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // return type is well-formed in traits even when `Self` isn't sized. if let ty::Param(param_ty) = *proj_term.kind() && param_ty.name == kw::SelfUpper - && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(_)) + && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn { .. }) && opaque.in_trait { return; diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5f756672b049e..61eb7260384ef 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1186,9 +1186,9 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> DefKind::OpaqueTy => { let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) - | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin - && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) + if let hir::OpaqueTyOrigin::FnReturn { parent } + | hir::OpaqueTyOrigin::AsyncFn { parent } = origin + && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(parent) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { false diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 4c3a011248808..673756076b12f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -1271,7 +1271,7 @@ fn suggest_precise_capturing<'tcx>( let hir::OpaqueTy { bounds, origin, .. } = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else { + let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } = *origin else { return; }; 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 34a0f182ab45f..43762b1004baa 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 @@ -2580,7 +2580,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { def_id: DefId, ) -> ErrorGuaranteed { let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { + hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } => { "opaque type".to_string() } hir::OpaqueTyOrigin::TyAlias { .. } => { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 45fe199c335b0..f8cfeb500a5ec 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -379,7 +379,8 @@ fn associated_type_for_impl_trait_in_trait( tcx: TyCtxt<'_>, opaque_ty_def_id: LocalDefId, ) -> LocalDefId { - let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = + let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = tcx.opaque_type_origin(opaque_ty_def_id) else { bug!("expected opaque for {opaque_ty_def_id:?}"); diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index bac0d020d721b..7c4b4887b2d75 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -141,7 +141,8 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local()); trace!(?origin); match origin { - rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} + rustc_hir::OpaqueTyOrigin::FnReturn { .. } + | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {} rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => { if !in_assoc_ty && !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { return; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 2cf703f57c0b6..c44e5ecaba81c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -515,7 +515,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { self.add_to_current_mod(item, renamed, import_id); } hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn(_) | hir::OpaqueTyOrigin::FnReturn(_), + origin: hir::OpaqueTyOrigin::AsyncFn { .. } | hir::OpaqueTyOrigin::FnReturn { .. }, .. }) => { // return-position impl traits are never nameable, and should never be documented. diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 8bc2a56af9938..0cbcdbeb90ae1 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -313,7 +313,7 @@ fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<& kind: ItemKind::OpaqueTy(opaque), .. } = item - && let OpaqueTyOrigin::AsyncFn(_) = opaque.origin + && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds && let Some(segment) = trait_ref.trait_ref.path.segments.last() && let Some(generic_args) = segment.args From 7cd466a03606313dad4fa22fd5cf444204138fc8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 22:21:37 -0400 Subject: [PATCH 1178/1301] Move in_trait into OpaqueTyOrigin --- compiler/rustc_ast_lowering/src/item.rs | 2 - compiler/rustc_ast_lowering/src/lib.rs | 80 +++++++++---------- .../src/region_infer/opaque_types.rs | 4 +- compiler/rustc_hir/src/hir.rs | 14 +++- .../rustc_hir_analysis/src/check/check.rs | 12 +-- .../src/check/compare_impl_item/refine.rs | 2 +- .../src/collect/generics_of.rs | 7 +- .../src/collect/item_bounds.rs | 70 +++++++++------- .../src/collect/resolve_bound_vars.rs | 4 +- .../rustc_hir_analysis/src/collect/type_of.rs | 9 ++- .../src/hir_ty_lowering/mod.rs | 34 ++++++-- compiler/rustc_hir_typeck/src/_match.rs | 2 +- .../rustc_lint/src/impl_trait_overcaptures.rs | 2 +- .../src/opaque_hidden_inferred_bound.rs | 8 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 4 +- compiler/rustc_middle/src/hir/map/mod.rs | 8 +- compiler/rustc_privacy/src/lib.rs | 3 +- .../src/error_reporting/infer/region.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 4 +- 19 files changed, 146 insertions(+), 125 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 7bb3b2fa29095..1273b50dff816 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -286,7 +286,6 @@ impl<'hir> LoweringContext<'_, 'hir> { parent: this.local_def_id(id), in_assoc_ty: false, }, - fn_kind: None, }), }, ); @@ -983,7 +982,6 @@ impl<'hir> LoweringContext<'_, 'hir> { parent: this.local_def_id(i.id), in_assoc_ty: true, }, - fn_kind: None, }); hir::ImplItemKind::Type(ty) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c8aa8d701b9b0..9275308cccbce 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -288,12 +288,7 @@ enum ImplTraitContext { /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`. /// - OpaqueTy { - origin: hir::OpaqueTyOrigin, - /// Only used to change the lifetime capture rules, since - /// RPITIT captures all in scope, RPIT does not. - fn_kind: Option, - }, + OpaqueTy { origin: hir::OpaqueTyOrigin }, /// `impl Trait` is unstably accepted in this position. FeatureGated(ImplTraitPosition, Symbol), /// `impl Trait` is not accepted in this position. @@ -1404,14 +1399,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; match itctx { - ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait( - span, - origin, - *def_node_id, - bounds, - fn_kind, - itctx, - ), + ImplTraitContext::OpaqueTy { origin } => { + self.lower_opaque_impl_trait(span, origin, *def_node_id, bounds, itctx) + } ImplTraitContext::Universal => { if let Some(span) = bounds.iter().find_map(|bound| match *bound { ast::GenericBound::Use(_, span) => Some(span), @@ -1513,7 +1503,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { origin: hir::OpaqueTyOrigin, opaque_ty_node_id: NodeId, bounds: &GenericBounds, - fn_kind: Option, itctx: ImplTraitContext, ) -> hir::TyKind<'hir> { // Make sure we know that some funky desugaring has been going on here. @@ -1555,11 +1544,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .map(|(ident, id, _)| Lifetime { id, ident }) .collect() } - hir::OpaqueTyOrigin::FnReturn { .. } => { - if matches!( - fn_kind.expect("expected RPITs to be lowered with a FnKind"), - FnDeclKind::Impl | FnDeclKind::Trait - ) || self.tcx.features().lifetime_capture_rules_2024 + hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => { + if in_trait_or_impl.is_some() + || self.tcx.features().lifetime_capture_rules_2024 || span.at_least_rust_2024() { // return-position impl trait in trait was decided to capture all @@ -1583,9 +1570,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; debug!(?captured_lifetimes_to_duplicate); - match fn_kind { - // Deny `use<>` on RPITIT in trait/trait-impl for now. - Some(FnDeclKind::Trait | FnDeclKind::Impl) => { + // Feature gate for RPITIT + use<..> + match origin { + rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => { if let Some(span) = bounds.iter().find_map(|bound| match *bound { ast::GenericBound::Use(_, span) => Some(span), _ => None, @@ -1593,20 +1580,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span }); } } - None - | Some( - FnDeclKind::Fn - | FnDeclKind::Inherent - | FnDeclKind::ExternFn - | FnDeclKind::Closure - | FnDeclKind::Pointer, - ) => {} + _ => {} } self.lower_opaque_inner( opaque_ty_node_id, origin, - matches!(fn_kind, Some(FnDeclKind::Trait)), captured_lifetimes_to_duplicate, span, opaque_ty_span, @@ -1618,7 +1597,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, opaque_ty_node_id: NodeId, origin: hir::OpaqueTyOrigin, - in_trait: bool, captured_lifetimes_to_duplicate: FxIndexSet, span: Span, opaque_ty_span: Span, @@ -1747,7 +1725,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds, origin, lifetime_mapping, - in_trait, }; // Generate an `type Foo = impl Trait;` declaration. @@ -1863,14 +1840,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { None => match &decl.output { FnRetTy::Ty(ty) => { let itctx = match kind { - FnDeclKind::Fn - | FnDeclKind::Inherent - | FnDeclKind::Trait - | FnDeclKind::Impl => ImplTraitContext::OpaqueTy { + FnDeclKind::Fn | FnDeclKind::Inherent => ImplTraitContext::OpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn { + parent: self.local_def_id(fn_node_id), + in_trait_or_impl: None, + }, + }, + FnDeclKind::Trait => ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn { parent: self.local_def_id(fn_node_id), + in_trait_or_impl: Some(hir::RpitContext::Trait), + }, + }, + FnDeclKind::Impl => ImplTraitContext::OpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn { + parent: self.local_def_id(fn_node_id), + in_trait_or_impl: Some(hir::RpitContext::TraitImpl), }, - fn_kind: Some(kind), }, FnDeclKind::ExternFn => { ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn) @@ -1952,10 +1938,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .map(|(ident, id, _)| Lifetime { id, ident }) .collect(); + let in_trait_or_impl = match fn_kind { + FnDeclKind::Trait => Some(hir::RpitContext::Trait), + FnDeclKind::Impl => Some(hir::RpitContext::TraitImpl), + FnDeclKind::Fn | FnDeclKind::Inherent => None, + FnDeclKind::ExternFn | FnDeclKind::Closure | FnDeclKind::Pointer => unreachable!(), + }; + let opaque_ty_ref = self.lower_opaque_inner( opaque_ty_node_id, - hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, - matches!(fn_kind, FnDeclKind::Trait), + hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl }, captured_lifetimes, span, opaque_ty_span, @@ -1965,8 +1957,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { coro, opaque_ty_span, ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id }, - fn_kind: Some(fn_kind), + origin: hir::OpaqueTyOrigin::FnReturn { + parent: fn_def_id, + in_trait_or_impl, + }, }, ); arena_vec![this; bound] diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 1f6aa59805948..2f90e91628141 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -503,8 +503,8 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { let &Self { tcx, def_id, .. } = self; let origin = tcx.opaque_type_origin(def_id); let parent = match origin { - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(parent); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 68195c46f5c40..f58ec22aea971 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2762,10 +2762,6 @@ pub struct OpaqueTy<'hir> { /// This mapping associated a captured lifetime (first parameter) with the new /// early-bound lifetime that was generated for the opaque. pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)], - /// Whether the opaque is a return-position impl trait (or async future) - /// originating from a trait method. This makes it so that the opaque is - /// lowered as an associated type. - pub in_trait: bool, } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -2802,6 +2798,12 @@ pub struct PreciseCapturingNonLifetimeArg { pub res: Res, } +#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] +pub enum RpitContext { + Trait, + TraitImpl, +} + /// From whence the opaque type came. #[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { @@ -2809,11 +2811,15 @@ pub enum OpaqueTyOrigin { FnReturn { /// The defining function. parent: LocalDefId, + // Whether this is an RPITIT (return position impl trait in trait) + in_trait_or_impl: Option, }, /// `async fn` AsyncFn { /// The defining function. parent: LocalDefId, + // Whether this is an AFIT (async fn in trait) + in_trait_or_impl: Option, }, /// type aliases: `type Foo = impl Trait;` TyAlias { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index feba484229b87..312212232bcdc 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -336,8 +336,8 @@ fn check_opaque_meets_bounds<'tcx>( origin: &hir::OpaqueTyOrigin, ) -> Result<(), ErrorGuaranteed> { let defining_use_anchor = match *origin { - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(defining_use_anchor); @@ -346,8 +346,8 @@ fn check_opaque_meets_bounds<'tcx>( let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let args = match *origin { - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item( tcx, parent, ) @@ -736,8 +736,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_opaque_precise_captures(tcx, def_id); let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id } = origin + if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = origin && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 3375fc303f156..35c2b7e7ce2fb 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -94,7 +94,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( if !tcx.hir().get_if_local(impl_opaque.def_id).is_some_and(|node| { matches!( node.expect_item().expect_opaque_ty().origin, - hir::OpaqueTyOrigin::AsyncFn { parent } | hir::OpaqueTyOrigin::FnReturn { parent } + hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::FnReturn { parent, .. } if parent == impl_m.def_id.expect_local() ) }) { diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 73e98c67cdc30..8ff9640a87489 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -210,12 +210,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Node::Item(item) => match item.kind { ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }, - in_trait, + hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, in_trait_or_impl } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl }, .. }) => { - if in_trait { + if in_trait_or_impl.is_some() { assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn); } else { assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 07a8c3a9e84db..2418037ae962f 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -370,39 +370,47 @@ pub(super) fn explicit_item_bounds_with_filter( .. }) => associated_type_bounds(tcx, def_id, bounds, *span, filter), hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: false, .. }), + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, origin, .. }), span, .. - }) => { - let args = GenericArgs::identity_for_item(tcx, def_id); - let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); - let bounds = opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter); - assert_only_contains_predicates_from(filter, bounds, item_ty); - bounds - } - // Since RPITITs are lowered as projections in `::lower_ty`, when we're - // asking for the item bounds of the *opaques* in a trait's default method signature, we - // need to map these projections back to opaques. - hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: true, origin, .. }), - span, - .. - }) => { - let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = *origin - else { - span_bug!(*span, "RPITIT cannot be a TAIT, but got origin {origin:?}"); - }; - let args = GenericArgs::identity_for_item(tcx, def_id); - let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); - let bounds = &*tcx.arena.alloc_slice( - &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) - .to_vec() - .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: fn_def_id.to_def_id() }), - ); - assert_only_contains_predicates_from(filter, bounds, item_ty); - bounds - } + }) => match origin { + // Since RPITITs are lowered as projections in `::lower_ty`, + // when we're asking for the item bounds of the *opaques* in a trait's default + // method signature, we need to map these projections back to opaques. + rustc_hir::OpaqueTyOrigin::FnReturn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } + | rustc_hir::OpaqueTyOrigin::AsyncFn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } => { + let args = GenericArgs::identity_for_item(tcx, def_id); + let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + let bounds = &*tcx.arena.alloc_slice( + &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) + .to_vec() + .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: parent.to_def_id() }), + ); + assert_only_contains_predicates_from(filter, bounds, item_ty); + bounds + } + rustc_hir::OpaqueTyOrigin::FnReturn { + parent: _, + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + } + | rustc_hir::OpaqueTyOrigin::AsyncFn { + parent: _, + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + } + | rustc_hir::OpaqueTyOrigin::TyAlias { parent: _, .. } => { + let args = GenericArgs::identity_for_item(tcx, def_id); + let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + let bounds = opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter); + assert_only_contains_predicates_from(filter, bounds, item_ty); + bounds + } + }, hir::Node::Item(hir::Item { kind: hir::ItemKind::TyAlias(..), .. }) => &[], _ => bug!("item_bounds called on {:?}", def_id), }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 60788641da0cd..a15621bf28b9b 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -515,8 +515,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } + hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::TyAlias { parent, .. }, generics, .. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 313d7dd62654a..3af4d1f5edad5 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -618,12 +618,13 @@ pub(super) fn type_of_opaque( // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(&OpaqueTy { origin: - hir::OpaqueTyOrigin::FnReturn { parent: owner } - | hir::OpaqueTyOrigin::AsyncFn { parent: owner }, - in_trait, + hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl } + | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl }, .. }) => { - if in_trait && !tcx.defaultness(owner).has_value() { + if in_trait_or_impl == Some(hir::RpitContext::Trait) + && !tcx.defaultness(owner).has_value() + { span_bug!( tcx.def_span(def_id), "tried to get type of this RPITIT with no definition" diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 496f54e31d2c0..24a8fdf0f6bcc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2091,17 +2091,37 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let opaque_ty = tcx.hir().item(item_id); match opaque_ty.kind { - hir::ItemKind::OpaqueTy(&hir::OpaqueTy { in_trait, .. }) => { + hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin, .. }) => { let local_def_id = item_id.owner_id.def_id; // If this is an RPITIT and we are using the new RPITIT lowering scheme, we // generate the def_id of an associated type for the trait and return as // type a projection. - let def_id = if in_trait { - tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id() - } else { - local_def_id.to_def_id() - }; - self.lower_opaque_ty(def_id, lifetimes, in_trait) + match origin { + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + } => self.lower_opaque_ty( + tcx.associated_type_for_impl_trait_in_trait(local_def_id) + .to_def_id(), + lifetimes, + true, + ), + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::TyAlias { .. } => { + self.lower_opaque_ty(local_def_id.to_def_id(), lifetimes, false) + } + } } ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), } diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 204138ffd44a4..2dbadf8198b3f 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -602,7 +602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(k, _)| (k.def_id, k.args))?, _ => return None, }; - let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id } = + let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id, .. } = self.tcx.opaque_type_origin(def_id) else { return None; diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index c13f4c7746d08..5aeaad420699e 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -259,7 +259,7 @@ where // If it's owned by this function && let opaque = self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty() - && let hir::OpaqueTyOrigin::FnReturn { parent } = opaque.origin + && let hir::OpaqueTyOrigin::FnReturn { parent, .. } = opaque.origin && parent == self.parent_def_id { let opaque_span = self.tcx.def_span(opaque_def_id); diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 87fa38a282ed1..342ebfa0b06cc 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // That's because although we may have an opaque type on the function, // it won't have a hidden type, so proving predicates about it is // not really meaningful. - if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id } = opaque.origin + if let hir::OpaqueTyOrigin::FnReturn { parent: method_def_id, .. } = opaque.origin && let hir::Node::TraitItem(trait_item) = cx.tcx.hir_node_by_def_id(method_def_id) && !trait_item.defaultness.has_value() { @@ -114,8 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // return type is well-formed in traits even when `Self` isn't sized. if let ty::Param(param_ty) = *proj_term.kind() && param_ty.name == kw::SelfUpper - && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn { .. }) - && opaque.in_trait + && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + }) { return; } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 61eb7260384ef..610c682d3a461 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1186,8 +1186,8 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> DefKind::OpaqueTy => { let origin = tcx.opaque_type_origin(def_id); - if let hir::OpaqueTyOrigin::FnReturn { parent } - | hir::OpaqueTyOrigin::AsyncFn { parent } = origin + if let hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } = origin && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(parent) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index e11361a615f4c..72e6c96e6f646 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1139,13 +1139,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { ItemKind::ForeignMod { .. } => "foreign mod", ItemKind::GlobalAsm(..) => "global asm", ItemKind::TyAlias(..) => "ty", - ItemKind::OpaqueTy(opaque) => { - if opaque.in_trait { - "opaque type in trait" - } else { - "opaque type" - } - } + ItemKind::OpaqueTy(..) => "opaque type", ItemKind::Enum(..) => "enum", ItemKind::Struct(..) => "struct", ItemKind::Union(..) => "union", diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9094b00fbfb44..015e45ad67014 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -636,8 +636,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { if self.impl_trait_pass - && let hir::ItemKind::OpaqueTy(opaque) = item.kind - && !opaque.in_trait + && let hir::ItemKind::OpaqueTy(..) = item.kind { // FIXME: This is some serious pessimization intended to workaround deficiencies // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 673756076b12f..a2d717817db14 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -1271,7 +1271,7 @@ fn suggest_precise_capturing<'tcx>( let hir::OpaqueTy { bounds, origin, .. } = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } = *origin else { + let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } = *origin else { return; }; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index f8cfeb500a5ec..e41f2c8ce4869 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -379,8 +379,8 @@ fn associated_type_for_impl_trait_in_trait( tcx: TyCtxt<'_>, opaque_ty_def_id: LocalDefId, ) -> LocalDefId { - let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id }) = + let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) = tcx.opaque_type_origin(opaque_ty_def_id) else { bug!("expected opaque for {opaque_ty_def_id:?}"); From 6e8573c520086db6cc7993a1a2608cc9347832a4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 23:00:08 -0400 Subject: [PATCH 1179/1301] Visit in embargo visitor if trait method has body --- compiler/rustc_privacy/src/lib.rs | 48 ++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 015e45ad67014..d00e7eff75212 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -636,17 +636,45 @@ impl<'tcx> EmbargoVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { if self.impl_trait_pass - && let hir::ItemKind::OpaqueTy(..) = item.kind + && let hir::ItemKind::OpaqueTy(opaque) = item.kind { - // FIXME: This is some serious pessimization intended to workaround deficiencies - // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time - // reachable if they are returned via `impl Trait`, even from private functions. - let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); - self.reach_through_impl_trait(item.owner_id.def_id, pub_ev) - .generics() - .predicates() - .ty(); - return; + let should_visit = match opaque.origin { + hir::OpaqueTyOrigin::FnReturn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } + | hir::OpaqueTyOrigin::AsyncFn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } => match self.tcx.hir_node_by_def_id(parent).expect_trait_item().expect_fn().1 { + hir::TraitFn::Required(_) => false, + hir::TraitFn::Provided(..) => true, + }, + + // Always visit RPITs in functions that have definitions, + // and all TAITs. + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::TyAlias { .. } => true, + }; + + if should_visit { + // FIXME: This is some serious pessimization intended to workaround deficiencies + // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time + // reachable if they are returned via `impl Trait`, even from private functions. + let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); + self.reach_through_impl_trait(item.owner_id.def_id, pub_ev) + .generics() + .predicates() + .ty(); + return; + } } // Update levels of nested things and mark all items From 11f738fcb2ae4ecd6821309290a468a6ebcbaaa9 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 17 Aug 2024 20:33:31 -0400 Subject: [PATCH 1180/1301] impl Default for Hash{Map,Set} iterators that don't already have it --- library/std/src/collections/hash/map.rs | 64 +++++++++++++++++++++++++ library/std/src/collections/hash/set.rs | 16 +++++++ 2 files changed, 80 insertions(+) diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 6f2b4100620ee..f2e523dca779c 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1438,6 +1438,14 @@ impl Clone for Iter<'_, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, K, V> { + #[inline] + fn default() -> Self { + Iter { base: Default::default() } + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Iter<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1476,6 +1484,14 @@ impl<'a, K, V> IterMut<'a, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IterMut<'_, K, V> { + #[inline] + fn default() -> Self { + IterMut { base: Default::default() } + } +} + /// An owning iterator over the entries of a `HashMap`. /// /// This `struct` is created by the [`into_iter`] method on [`HashMap`] @@ -1506,6 +1522,14 @@ impl IntoIter { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoIter { + #[inline] + fn default() -> Self { + IntoIter { base: Default::default() } + } +} + /// An iterator over the keys of a `HashMap`. /// /// This `struct` is created by the [`keys`] method on [`HashMap`]. See its @@ -1538,6 +1562,14 @@ impl Clone for Keys<'_, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Keys<'_, K, V> { + #[inline] + fn default() -> Self { + Keys { inner: Default::default() } + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Keys<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1577,6 +1609,14 @@ impl Clone for Values<'_, K, V> { } } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Values<'_, K, V> { + #[inline] + fn default() -> Self { + Values { inner: Default::default() } + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Values<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1665,6 +1705,14 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V>, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for ValuesMut<'_, K, V> { + #[inline] + fn default() -> Self { + ValuesMut { inner: Default::default() } + } +} + /// An owning iterator over the keys of a `HashMap`. /// /// This `struct` is created by the [`into_keys`] method on [`HashMap`]. @@ -1687,6 +1735,14 @@ pub struct IntoKeys { inner: IntoIter, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoKeys { + #[inline] + fn default() -> Self { + IntoKeys { inner: Default::default() } + } +} + /// An owning iterator over the values of a `HashMap`. /// /// This `struct` is created by the [`into_values`] method on [`HashMap`]. @@ -1709,6 +1765,14 @@ pub struct IntoValues { inner: IntoIter, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoValues { + #[inline] + fn default() -> Self { + IntoValues { inner: Default::default() } + } +} + /// A builder for computing where in a HashMap a key-value pair would be stored. /// /// See the [`HashMap::raw_entry_mut`] docs for usage examples. diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index e69fb0878e7d0..210f57152250c 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -1244,6 +1244,14 @@ pub struct Iter<'a, K: 'a> { base: base::Iter<'a, K>, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, K> { + #[inline] + fn default() -> Self { + Iter { base: Default::default() } + } +} + /// An owning iterator over the items of a `HashSet`. /// /// This `struct` is created by the [`into_iter`] method on [`HashSet`] @@ -1265,6 +1273,14 @@ pub struct IntoIter { base: base::IntoIter, } +#[stable(feature = "default_iters_hash", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoIter { + #[inline] + fn default() -> Self { + IntoIter { base: Default::default() } + } +} + /// A draining iterator over the items of a `HashSet`. /// /// This `struct` is created by the [`drain`] method on [`HashSet`]. From 4b48d72eaa7f4d934f56bcf2b6d0348954977823 Mon Sep 17 00:00:00 2001 From: Jaken Herman Date: Wed, 2 Oct 2024 00:24:42 -0500 Subject: [PATCH 1181/1301] Add `get_line` confusable to `Stdin::read_line()` Add tests for addition of `#[rustc_confusables("get_line")]` --- library/std/src/io/stdio.rs | 1 + tests/ui/attributes/rustc_confusables_std_cases.rs | 4 ++++ .../attributes/rustc_confusables_std_cases.stderr | 13 ++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 6de069a518e3d..bf242e715bd94 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -394,6 +394,7 @@ impl Stdin { /// in which case it will wait for the Enter key to be pressed before /// continuing #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("get_line")] pub fn read_line(&self, buf: &mut String) -> io::Result { self.lock().read_line(buf) } diff --git a/tests/ui/attributes/rustc_confusables_std_cases.rs b/tests/ui/attributes/rustc_confusables_std_cases.rs index d9121695950b2..4f6baea26dfd7 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.rs +++ b/tests/ui/attributes/rustc_confusables_std_cases.rs @@ -23,4 +23,8 @@ fn main() { //~^ HELP you might have meant to use `push_str` String::new().append(""); //~ ERROR E0599 //~^ HELP you might have meant to use `push_str` + let mut buffer = String::new(); + let stdin = std::io::stdin(); + stdin.get_line(&mut buffer).unwrap(); //~ ERROR E0599 + //~^ HELP you might have meant to use `read_line` } diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index f4b6947ccd94a..7bf96241ca725 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -106,7 +106,18 @@ help: you might have meant to use `push_str` LL | String::new().push_str(""); | ~~~~~~~~ -error: aborting due to 8 previous errors +error[E0599]: no method named `get_line` found for struct `Stdin` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:28:11 + | +LL | stdin.get_line(&mut buffer).unwrap(); + | ^^^^^^^^ method not found in `Stdin` + | +help: you might have meant to use `read_line` + | +LL | stdin.read_line(&mut buffer).unwrap(); + | ~~~~~~~~~ + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0599. For more information about an error, try `rustc --explain E0308`. From 3d8bd6bbc5dc51fbcc8d912a46f117611c98ee0d Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Thu, 3 Oct 2024 08:23:30 +0300 Subject: [PATCH 1182/1301] Handle `rustc_metadata` cases of `rustc::potential_query_instability` lint --- compiler/rustc_metadata/src/lib.rs | 1 - compiler/rustc_metadata/src/locator.rs | 20 ++++++++++---------- compiler/rustc_middle/src/ty/mod.rs | 4 ++-- compiler/rustc_resolve/src/lib.rs | 4 ++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 10f2087d1e6f1..f206dba6cf4bc 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(control_flow_enum)] diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 089ac060ba848..99c673b021a90 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -218,7 +218,7 @@ use std::ops::Deref; use std::path::{Path, PathBuf}; use std::{cmp, fmt}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owned_slice::slice_owned; use rustc_data_structures::svh::Svh; @@ -385,7 +385,7 @@ impl<'a> CrateLocator<'a> { let dylib_suffix = &self.target.dll_suffix; let staticlib_suffix = &self.target.staticlib_suffix; - let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> = + let mut candidates: FxIndexMap<_, (FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>)> = Default::default(); // First, find all possible candidate rlibs and dylibs purely based on @@ -460,7 +460,7 @@ impl<'a> CrateLocator<'a> { // A Library candidate is created if the metadata for the set of // libraries corresponds to the crate id and hash criteria that this // search is being performed for. - let mut libraries = FxHashMap::default(); + let mut libraries = FxIndexMap::default(); for (_hash, (rlibs, rmetas, dylibs)) in candidates { if let Some((svh, lib)) = self.extract_lib(rlibs, rmetas, dylibs)? { libraries.insert(svh, lib); @@ -494,9 +494,9 @@ impl<'a> CrateLocator<'a> { fn extract_lib( &mut self, - rlibs: FxHashMap, - rmetas: FxHashMap, - dylibs: FxHashMap, + rlibs: FxIndexMap, + rmetas: FxIndexMap, + dylibs: FxIndexMap, ) -> Result, CrateError> { let mut slot = None; // Order here matters, rmeta should come first. See comment in @@ -534,7 +534,7 @@ impl<'a> CrateLocator<'a> { // The `PathBuf` in `slot` will only be used for diagnostic purposes. fn extract_one( &mut self, - m: FxHashMap, + m: FxIndexMap, flavor: CrateFlavor, slot: &mut Option<(Svh, MetadataBlob, PathBuf)>, ) -> Result, CrateError> { @@ -702,9 +702,9 @@ impl<'a> CrateLocator<'a> { // First, filter out all libraries that look suspicious. We only accept // files which actually exist that have the correct naming scheme for // rlibs/dylibs. - let mut rlibs = FxHashMap::default(); - let mut rmetas = FxHashMap::default(); - let mut dylibs = FxHashMap::default(); + let mut rlibs = FxIndexMap::default(); + let mut rmetas = FxIndexMap::default(); + let mut dylibs = FxIndexMap::default(); for loc in &self.exact_paths { if !loc.canonicalized().exists() { return Err(CrateError::ExternLocationNotExist( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c4a28845085d9..f32daee7c44f2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -187,8 +187,8 @@ pub struct ResolverGlobalCtxt { /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: FxIndexMap, - pub doc_link_resolutions: FxHashMap, - pub doc_link_traits_in_scope: FxHashMap>, + pub doc_link_resolutions: FxIndexMap, + pub doc_link_traits_in_scope: FxIndexMap>, pub all_macro_rules: FxHashMap>, pub stripped_cfg_items: Steal>, } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0c1a0038f9c79..fcbbb523cfc9f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1196,8 +1196,8 @@ pub struct Resolver<'ra, 'tcx> { stripped_cfg_items: Vec>, effective_visibilities: EffectiveVisibilities, - doc_link_resolutions: FxHashMap, - doc_link_traits_in_scope: FxHashMap>, + doc_link_resolutions: FxIndexMap, + doc_link_traits_in_scope: FxIndexMap>, all_macro_rules: FxHashMap, /// Invocation ids of all glob delegations. From be819d415952f877019e6a0fea269e2aaccc552b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 3 Oct 2024 07:47:41 +0200 Subject: [PATCH 1183/1301] Preparing for merge from rustc --- 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 b05c409f82319..fe0421b2075a5 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -76ed7a1fa40c3f54d3fd3f834e12bf9c932d0146 +ad9c494835e746fb7c8a26eeed0ad90e4e834058 From 32d49680d6088623f8d709a90247f65b4b81cfd6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 3 Oct 2024 07:59:28 +0200 Subject: [PATCH 1184/1301] fmt --- src/tools/miri/src/concurrency/init_once.rs | 3 ++- src/tools/miri/src/concurrency/sync.rs | 9 ++++--- src/tools/miri/src/helpers.rs | 24 ++++++++++--------- src/tools/miri/src/shims/time.rs | 3 ++- src/tools/miri/src/shims/unix/fd.rs | 3 ++- .../data_race/mixed_size_read_read_write.rs | 2 +- .../fail/data_race/mixed_size_read_write.rs | 2 +- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 8985135f4e892..7a9b12bbe82c9 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -39,7 +39,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.init_onces, |_| interp_ok(Default::default()), )? - .ok_or_else(|| err_ub_format!("init_once has invalid ID")).into() + .ok_or_else(|| err_ub_format!("init_once has invalid ID")) + .into() } #[inline] diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 3b57af641b589..5627ccdbbea27 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -307,7 +307,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.mutexes, |ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("mutex has invalid ID")).into() + .ok_or_else(|| err_ub_format!("mutex has invalid ID")) + .into() } /// Retrieve the additional data stored for a mutex. @@ -334,7 +335,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.rwlocks, |ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("rwlock has invalid ID")).into() + .ok_or_else(|| err_ub_format!("rwlock has invalid ID")) + .into() } /// Retrieve the additional data stored for a rwlock. @@ -375,7 +377,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |ecx| &mut ecx.machine.sync.condvars, |ecx| initialize_data(ecx).map(|data| Condvar { data, ..Default::default() }), )? - .ok_or_else(|| err_ub_format!("condvar has invalid ID")).into() + .ok_or_else(|| err_ub_format!("condvar has invalid ID")) + .into() } /// Retrieve the additional data stored for a condvar. diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index b12a1939e2580..013bfe03aafa7 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -752,17 +752,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?; let nanoseconds = nanoseconds_scalar.to_target_isize(this)?; - interp_ok(try { - // tv_sec must be non-negative. - let seconds: u64 = seconds.try_into().ok()?; - // tv_nsec must be non-negative. - let nanoseconds: u32 = nanoseconds.try_into().ok()?; - if nanoseconds >= 1_000_000_000 { - // tv_nsec must not be greater than 999,999,999. - None? - } - Duration::new(seconds, nanoseconds) - }) + interp_ok( + try { + // tv_sec must be non-negative. + let seconds: u64 = seconds.try_into().ok()?; + // tv_nsec must be non-negative. + let nanoseconds: u32 = nanoseconds.try_into().ok()?; + if nanoseconds >= 1_000_000_000 { + // tv_nsec must not be greater than 999,999,999. + None? + } + Duration::new(seconds, nanoseconds) + }, + ) } /// Read bytes from a byte slice. diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 21c5421f10941..12c7679608ded 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -11,7 +11,8 @@ use crate::*; /// Returns the time elapsed between the provided time and the unix epoch as a `Duration`. pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> { time.duration_since(SystemTime::UNIX_EPOCH) - .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported")).into() + .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported")) + .into() } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index cf8e629cea1b1..f2d3115f98507 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -517,7 +517,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let start = this.read_scalar(&args[2])?.to_i32()?; match this.machine.fds.get(fd_num) { - Some(fd) => interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), + Some(fd) => + interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))), None => interp_ok(Scalar::from_i32(this.fd_not_found()?)), } } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read_read_write.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read_read_write.rs index ece5bd31274de..e76654806bb1e 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read_read_write.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read_read_write.rs @@ -4,7 +4,7 @@ // Two variants: the atomic store matches the size of the first or second atomic load. //@revisions: match_first_load match_second_load -use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; +use std::sync::atomic::{AtomicU8, AtomicU16, Ordering}; use std::thread; fn convert(a: &AtomicU16) -> &[AtomicU8; 2] { diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read_write.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read_write.rs index acc12427b3fdc..53016bab78045 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read_write.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read_write.rs @@ -4,7 +4,7 @@ // Two revisions, depending on which access goes first. //@revisions: read_write write_read -use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; +use std::sync::atomic::{AtomicU8, AtomicU16, Ordering}; use std::thread; fn convert(a: &AtomicU16) -> &[AtomicU8; 2] { From 9b35886adfe5da4163e95508f586dc071eec4d0f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 3 Oct 2024 08:19:07 +0200 Subject: [PATCH 1185/1301] make sure we also detect mixed-size races that begin at different addresses --- .../mixed_size_write_write.fst.stderr | 22 +++++++++++++++++++ .../fail/data_race/mixed_size_write_write.rs | 4 +++- .../mixed_size_write_write.snd.stderr | 22 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/tools/miri/tests/fail/data_race/mixed_size_write_write.fst.stderr create mode 100644 src/tools/miri/tests/fail/data_race/mixed_size_write_write.snd.stderr diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_write_write.fst.stderr b/src/tools/miri/tests/fail/data_race/mixed_size_write_write.fst.stderr new file mode 100644 index 0000000000000..a353910dcc998 --- /dev/null +++ b/src/tools/miri/tests/fail/data_race/mixed_size_write_write.fst.stderr @@ -0,0 +1,22 @@ +error: Undefined Behavior: Race condition detected between (1) 2-byte atomic store on thread `unnamed-ID` and (2) 1-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here + --> tests/fail/data_race/mixed_size_write_write.rs:LL:CC + | +LL | a8[idx].store(1, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) 2-byte atomic store on thread `unnamed-ID` and (2) 1-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here + | +help: and (1) occurred earlier here + --> tests/fail/data_race/mixed_size_write_write.rs:LL:CC + | +LL | a16.store(1, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: overlapping unsynchronized atomic accesses must use the same access size + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model + = 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 + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside closure at tests/fail/data_race/mixed_size_write_write.rs:LL:CC + +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/fail/data_race/mixed_size_write_write.rs b/src/tools/miri/tests/fail/data_race/mixed_size_write_write.rs index 89afda2fff5c2..545e354a0372c 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_write_write.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_write_write.rs @@ -1,6 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation // Avoid accidental synchronization via address reuse inside `thread::spawn`. //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 +//@revisions: fst snd use std::sync::atomic::{AtomicU8, AtomicU16, Ordering}; use std::thread; @@ -21,7 +22,8 @@ fn main() { a16.store(1, Ordering::SeqCst); }); s.spawn(|| { - a8[0].store(1, Ordering::SeqCst); + let idx = if cfg!(fst) { 0 } else { 1 }; + a8[idx].store(1, Ordering::SeqCst); //~^ ERROR: Race condition detected between (1) 2-byte atomic store on thread `unnamed-1` and (2) 1-byte atomic store on thread `unnamed-2` }); }); diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_write_write.snd.stderr b/src/tools/miri/tests/fail/data_race/mixed_size_write_write.snd.stderr new file mode 100644 index 0000000000000..3b9c0491502a1 --- /dev/null +++ b/src/tools/miri/tests/fail/data_race/mixed_size_write_write.snd.stderr @@ -0,0 +1,22 @@ +error: Undefined Behavior: Race condition detected between (1) 2-byte atomic store on thread `unnamed-ID` and (2) 1-byte atomic store on thread `unnamed-ID` at ALLOC+0x1. (2) just happened here + --> tests/fail/data_race/mixed_size_write_write.rs:LL:CC + | +LL | a8[idx].store(1, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) 2-byte atomic store on thread `unnamed-ID` and (2) 1-byte atomic store on thread `unnamed-ID` at ALLOC+0x1. (2) just happened here + | +help: and (1) occurred earlier here + --> tests/fail/data_race/mixed_size_write_write.rs:LL:CC + | +LL | a16.store(1, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: overlapping unsynchronized atomic accesses must use the same access size + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model + = 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 + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside closure at tests/fail/data_race/mixed_size_write_write.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From a8f9a32650122dd7c54ad20d1230bcb62c6506ff Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 30 Sep 2024 17:49:51 +0200 Subject: [PATCH 1186/1301] interpret: Immediate::offset: use shared sanity-check function to ensure invariant --- .../rustc_const_eval/src/interpret/operand.rs | 64 ++++++++++--------- .../rustc_const_eval/src/interpret/place.rs | 8 ++- .../src/interpret/projection.rs | 4 ++ 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 291664d556af4..3b5af113e9963 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -113,28 +113,47 @@ impl Immediate { } /// Assert that this immediate is a valid value for the given ABI. - pub fn assert_matches_abi(self, abi: Abi, cx: &impl HasDataLayout) { + pub fn assert_matches_abi(self, abi: Abi, msg: &str, cx: &impl HasDataLayout) { match (self, abi) { (Immediate::Scalar(scalar), Abi::Scalar(s)) => { - assert_eq!(scalar.size(), s.size(cx)); + assert_eq!(scalar.size(), s.size(cx), "{msg}: scalar value has wrong size"); if !matches!(s.primitive(), abi::Pointer(..)) { // This is not a pointer, it should not carry provenance. - assert!(matches!(scalar, Scalar::Int(..))); + assert!( + matches!(scalar, Scalar::Int(..)), + "{msg}: scalar value should be an integer, but has provenance" + ); } } (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert_eq!(a_val.size(), a.size(cx)); + assert_eq!( + a_val.size(), + a.size(cx), + "{msg}: first component of scalar pair has wrong size" + ); if !matches!(a.primitive(), abi::Pointer(..)) { - assert!(matches!(a_val, Scalar::Int(..))); + assert!( + matches!(a_val, Scalar::Int(..)), + "{msg}: first component of scalar pair should be an integer, but has provenance" + ); } - assert_eq!(b_val.size(), b.size(cx)); + assert_eq!( + b_val.size(), + b.size(cx), + "{msg}: second component of scalar pair has wrong size" + ); if !matches!(b.primitive(), abi::Pointer(..)) { - assert!(matches!(b_val, Scalar::Int(..))); + assert!( + matches!(b_val, Scalar::Int(..)), + "{msg}: second component of scalar pair should be an integer, but has provenance" + ); } } - (Immediate::Uninit, _) => {} + (Immediate::Uninit, _) => { + assert!(abi.is_sized(), "{msg}: unsized immediates are not a thing"); + } _ => { - bug!("value {self:?} does not match ABI {abi:?})",) + bug!("{msg}: value {self:?} does not match ABI {abi:?})",) } } } @@ -241,6 +260,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline(always)] pub fn from_immediate(imm: Immediate, layout: TyAndLayout<'tcx>) -> Self { + // Without a `cx` we cannot call `assert_matches_abi`. debug_assert!( match (imm, layout.abi) { (Immediate::Scalar(..), Abi::Scalar(..)) => true, @@ -261,7 +281,6 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self { - assert_eq!(s.size(), layout.size); Self::from_scalar(Scalar::from(s), layout) } @@ -334,7 +353,10 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { /// given layout. // Not called `offset` to avoid confusion with the trait method. fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { - debug_assert!(layout.is_sized(), "unsized immediates are not a thing"); + // Verify that the input matches its type. + if cfg!(debug_assertions) { + self.assert_matches_abi(self.layout.abi, "invalid input to Immediate::offset", cx); + } // `ImmTy` have already been checked to be in-bounds, so we can just check directly if this // remains in-bounds. This cannot actually be violated since projections are type-checked // and bounds-checked. @@ -368,32 +390,14 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { // the field covers the entire type _ if layout.size == self.layout.size => { assert_eq!(offset.bytes(), 0); - assert!( - match (self.layout.abi, layout.abi) { - (Abi::Scalar(l), Abi::Scalar(r)) => l.size(cx) == r.size(cx), - (Abi::ScalarPair(l1, l2), Abi::ScalarPair(r1, r2)) => - l1.size(cx) == r1.size(cx) && l2.size(cx) == r2.size(cx), - _ => false, - }, - "cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}", - self.layout.ty, - layout.ty, - self.layout.abi, - layout.abi, - ); **self } // extract fields from types with `ScalarPair` ABI (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert_matches!(layout.abi, Abi::Scalar(..)); Immediate::from(if offset.bytes() == 0 { - // It is "okay" to transmute from `usize` to a pointer (GVN relies on that). - // So only compare the size. - assert_eq!(layout.size, a.size(cx)); a_val } else { assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi)); - assert_eq!(layout.size, b.size(cx)); b_val }) } @@ -405,6 +409,8 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { self.layout ), }; + // Ensure the new layout matches the new value. + inner_val.assert_matches_abi(layout.abi, "invalid field type in Immediate::offset", cx); ImmTy::from_immediate(inner_val, layout) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 49656e10f2a21..81b926a1b65fa 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -658,7 +658,11 @@ where // Things can ge wrong in quite weird ways when this is violated. // Unfortunately this is too expensive to do in release builds. if cfg!(debug_assertions) { - src.assert_matches_abi(local_layout.abi, self); + src.assert_matches_abi( + local_layout.abi, + "invalid immediate for given destination place", + self, + ); } } Left(mplace) => { @@ -679,7 +683,7 @@ where ) -> InterpResult<'tcx> { // We use the sizes from `value` below. // Ensure that matches the type of the place it is written to. - value.assert_matches_abi(layout.abi, self); + value.assert_matches_abi(layout.abi, "invalid immediate for given destination place", self); // Note that it is really important that the type here is the right one, and matches the // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here // to handle padding properly, which is only correct if we never look at this data with the diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index cc49d7cebf450..e636090a324ce 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -82,6 +82,10 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx) } + /// This does an offset-by-zero, which is effectively a transmute. Note however that + /// not all transmutes are supported by all projectables -- specifically, if this is an + /// `OpTy` or `ImmTy`, the new layout must have almost the same ABI as the old one + /// (only changing the `valid_range` is allowed and turning integers into pointers). fn transmute>( &self, layout: TyAndLayout<'tcx>, From 2cf155924fb6f7d4f85f49c0642b2d40b22a13b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 4 Sep 2024 11:22:43 +0200 Subject: [PATCH 1187/1301] Do not include `libstd.so` in sysroot when we statically link to libstd --- src/bootstrap/src/bin/rustc.rs | 1 - src/bootstrap/src/core/build_steps/compile.rs | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index d04e2fbeb7853..780979ed98121 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -95,7 +95,6 @@ fn main() { // When statically linking `std` into `rustc_driver`, remove `-C prefer-dynamic` if env::var("RUSTC_LINK_STD_INTO_RUSTC_DRIVER").unwrap() == "1" && crate_name == Some("rustc_driver") - && stage != "0" { if let Some(pos) = args.iter().enumerate().position(|(i, a)| { a == "-C" && args.get(i + 1).map(|a| a == "prefer-dynamic").unwrap_or(false) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index eaa982d4e2bbd..c274bcc2df73d 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1923,8 +1923,24 @@ impl Step for Assemble { let src_libdir = builder.sysroot_libdir(build_compiler, host); for f in builder.read_dir(&src_libdir) { let filename = f.file_name().into_string().unwrap(); - if (is_dylib(&filename) || is_debug_info(&filename)) && !proc_macros.contains(&filename) + + let is_proc_macro = proc_macros.contains(&filename); + let is_dylib_or_debug = is_dylib(&filename) || is_debug_info(&filename); + + // If we link statically to stdlib, do not copy the libstd dynamic library file + // Currently, we do not avoid the copy on Windows, as it seems to be causing issues in + // post-optimization stage0 tests. + let can_be_rustc_dynamic_dep = if builder + .link_std_into_rustc_driver(target_compiler.host) + && !target_compiler.host.is_windows() { + let is_std = filename.starts_with("std-") || filename.starts_with("libstd-"); + !is_std + } else { + true + }; + + if is_dylib_or_debug && can_be_rustc_dynamic_dep && !is_proc_macro { builder.copy_link(&f.path(), &rustc_libdir.join(&filename)); } } From 3a0ed0c6e3209ae1bce3079eea944c07b58f225f Mon Sep 17 00:00:00 2001 From: Orion Gonzalez Date: Wed, 2 Oct 2024 23:35:17 +0200 Subject: [PATCH 1188/1301] add direnv to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index d6d8b99f6023c..2f88b27530128 100644 --- a/.gitignore +++ b/.gitignore @@ -86,4 +86,8 @@ package.json ## Rustdoc GUI tests tests/rustdoc-gui/src/**.lock +## direnv +.envrc +.direnv/ + # Before adding new lines, see the comment at the top. From 510e262deb6c5eba94e0aad2f609448271f80264 Mon Sep 17 00:00:00 2001 From: Orion Gonzalez Date: Wed, 2 Oct 2024 23:35:48 +0200 Subject: [PATCH 1189/1301] add nix files to gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 2f88b27530128..b170dca88fa95 100644 --- a/.gitignore +++ b/.gitignore @@ -90,4 +90,9 @@ tests/rustdoc-gui/src/**.lock .envrc .direnv/ +## nix +flake.nix +flake.lock +default.nix + # Before adding new lines, see the comment at the top. From 83d0d9f292d4c98d26cd007f294ab91789b1378d Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Thu, 3 Oct 2024 12:47:08 +0300 Subject: [PATCH 1190/1301] Handle `rustc_query_impl` cases of `rustc::potential_query_instability` lint --- compiler/rustc_query_impl/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 69742bb49b1e8..df898e0587ff9 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -2,7 +2,7 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![allow(rustc::potential_query_instability, unused_parens)] +#![allow(unused_parens)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(min_specialization)] From 8e382ba022fc7f8f6f7e0abca5e8052039a81202 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 3 Oct 2024 16:53:51 +1000 Subject: [PATCH 1191/1301] Avoid ICE in coverage builds with bad `#[coverage(..)]` attributes This code can sometimes witness malformed coverage attributes in builds that are going to fail, so use `span_delayed_bug` to avoid an inappropriate ICE in that case. --- .../rustc_mir_transform/src/coverage/query.rs | 3 ++- tests/crashes/127880.rs | 5 ---- .../ui/coverage-attr/bad-attr-ice.feat.stderr | 15 +++++++++++ .../coverage-attr/bad-attr-ice.nofeat.stderr | 26 +++++++++++++++++++ tests/ui/coverage-attr/bad-attr-ice.rs | 16 ++++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) delete mode 100644 tests/crashes/127880.rs create mode 100644 tests/ui/coverage-attr/bad-attr-ice.feat.stderr create mode 100644 tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr create mode 100644 tests/ui/coverage-attr/bad-attr-ice.rs diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index e65a5fdd5e7f1..df151f8cca35e 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -63,7 +63,8 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { Some([item]) if item.has_name(sym::on) => return true, Some(_) | None => { // Other possibilities should have been rejected by `rustc_parse::validate_attr`. - tcx.dcx().span_bug(attr.span, "unexpected value of coverage attribute"); + // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880). + tcx.dcx().span_delayed_bug(attr.span, "unexpected value of coverage attribute"); } } } diff --git a/tests/crashes/127880.rs b/tests/crashes/127880.rs deleted file mode 100644 index 6c625eac6914a..0000000000000 --- a/tests/crashes/127880.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: #127880 -//@ compile-flags: -Cinstrument-coverage - -#[coverage] -fn main() {} diff --git a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr new file mode 100644 index 0000000000000..9e3cd41c2777d --- /dev/null +++ b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr @@ -0,0 +1,15 @@ +error: malformed `coverage` attribute input + --> $DIR/bad-attr-ice.rs:10:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(off)] + | +LL | #[coverage(on)] + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr new file mode 100644 index 0000000000000..d73636e158bae --- /dev/null +++ b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr @@ -0,0 +1,26 @@ +error: malformed `coverage` attribute input + --> $DIR/bad-attr-ice.rs:10:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(off)] + | +LL | #[coverage(on)] + | + +error[E0658]: the `#[coverage]` attribute is an experimental feature + --> $DIR/bad-attr-ice.rs:10:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | + = note: see issue #84605 for more information + = help: add `#![feature(coverage_attribute)]` 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 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/coverage-attr/bad-attr-ice.rs b/tests/ui/coverage-attr/bad-attr-ice.rs new file mode 100644 index 0000000000000..ae4d27d65ebd6 --- /dev/null +++ b/tests/ui/coverage-attr/bad-attr-ice.rs @@ -0,0 +1,16 @@ +#![cfg_attr(feat, feature(coverage_attribute))] +//@ revisions: feat nofeat +//@ compile-flags: -Cinstrument-coverage +//@ needs-profiler-support + +// Malformed `#[coverage(..)]` attributes should not cause an ICE when built +// with `-Cinstrument-coverage`. +// Regression test for . + +#[coverage] +//~^ ERROR malformed `coverage` attribute input +//[nofeat]~| the `#[coverage]` attribute is an experimental feature +fn main() {} + +// FIXME(#130766): When the `#[coverage(..)]` attribute is stabilized, +// get rid of the revisions and just make this a normal test. From 8918a9d265c38945fe442a8fcec71995f6e52b5a Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Thu, 3 Oct 2024 13:15:48 +0200 Subject: [PATCH 1192/1301] Fix needless_lifetimes in stable_mir --- compiler/stable_mir/src/mir/visit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index aeae866e9d344..e2d1ff7fdd3a4 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -76,9 +76,9 @@ pub trait MirVisitor { self.super_place(place, ptx, location) } - fn visit_projection_elem<'a>( + fn visit_projection_elem( &mut self, - place_ref: PlaceRef<'a>, + place_ref: PlaceRef<'_>, elem: &ProjectionElem, ptx: PlaceContext, location: Location, From c994a60fd4f848005beeef17bf6a13e1347103b8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 3 Oct 2024 14:48:03 +0200 Subject: [PATCH 1193/1301] Bump nightly version -> 2024-10-03 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index b431599c224ef..f0c8651efce4e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-09-22" +channel = "nightly-2024-10-03" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 9c91a4ef1652e5f01742185f43541ab22e6f01db Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 3 Oct 2024 09:40:53 -0400 Subject: [PATCH 1194/1301] Failing diff test --- ...itwise_not.JumpThreading.panic-unwind.diff | 47 +++++++++++++++++++ tests/mir-opt/jump_threading.rs | 11 +++++ 2 files changed, 58 insertions(+) create mode 100644 tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff diff --git a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff new file mode 100644 index 0000000000000..fb64fff7e11e5 --- /dev/null +++ b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff @@ -0,0 +1,47 @@ +- // MIR for `bitwise_not` before JumpThreading ++ // MIR for `bitwise_not` after JumpThreading + + fn bitwise_not() -> i32 { + let mut _0: i32; + let mut _1: i32; + let mut _2: bool; + let mut _3: i32; + let mut _4: i32; + scope 1 { + debug a => _1; + } + + bb0: { + StorageLive(_1); + _1 = const 0_i32; + _1 = const 1_i32; + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + _3 = Not(move _4); + StorageDead(_4); + _2 = Eq(move _3, const 0_i32); +- switchInt(move _2) -> [0: bb2, otherwise: bb1]; ++ goto -> bb1; + } + + bb1: { + StorageDead(_3); + _0 = const 1_i32; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + _0 = const 0_i32; + goto -> bb3; + } + + bb3: { + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index 9487a4e7e5ffe..4094dec0cbf1e 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -531,6 +531,16 @@ fn floats() -> u32 { if x == 0.0 { 0 } else { 1 } } +pub fn bitwise_not() -> i32 { + // CHECK-LABEL: fn bitwise_not( + // CHECK: switchInt( + + // Test for #131195, which was optimizing `!a == b` into `a != b`. + let mut a: i32 = 0; + a = 1; + if !a == 0 { 1 } else { 0 } +} + fn main() { // CHECK-LABEL: fn main( too_complex(Ok(0)); @@ -562,3 +572,4 @@ fn main() { // EMIT_MIR jump_threading.assume.JumpThreading.diff // EMIT_MIR jump_threading.aggregate_copy.JumpThreading.diff // EMIT_MIR jump_threading.floats.JumpThreading.diff +// EMIT_MIR jump_threading.bitwise_not.JumpThreading.diff From d47e388843f682f57262f020edd9909c850a0c49 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Thu, 3 Oct 2024 22:15:52 +0800 Subject: [PATCH 1195/1301] Avoid emptiness check in `PeekMut::pop` --- library/alloc/src/collections/binary_heap/mod.rs | 5 ++++- tests/codegen/binary-heap-peek-mut-pop-no-panic.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/codegen/binary-heap-peek-mut-pop-no-panic.rs diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 5e59abf54ee0f..59f10b09c73fd 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -374,7 +374,10 @@ impl<'a, T: Ord, A: Allocator> PeekMut<'a, T, A> { // the caller could've mutated the element. It is removed from the // heap on the next line and pop() is not sensitive to its value. } - this.heap.pop().unwrap() + + // SAFETY: Have a `PeekMut` element proves that the associated binary heap being non-empty, + // so the `pop` operation will not fail. + unsafe { this.heap.pop().unwrap_unchecked() } } } diff --git a/tests/codegen/binary-heap-peek-mut-pop-no-panic.rs b/tests/codegen/binary-heap-peek-mut-pop-no-panic.rs new file mode 100644 index 0000000000000..9cf4f210e527a --- /dev/null +++ b/tests/codegen/binary-heap-peek-mut-pop-no-panic.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -O +//@ ignore-debug +#![crate_type = "lib"] + +use std::collections::binary_heap::PeekMut; + +// CHECK-LABEL: @peek_mut_pop +#[no_mangle] +pub fn peek_mut_pop(peek_mut: PeekMut) -> u32 { + // CHECK-NOT: panic + // CHECK-NOT: unwrap_failed + PeekMut::pop(peek_mut) +} From 456be106b714b3b2f5f3d43c801e28bb69fcc789 Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Thu, 3 Oct 2024 15:22:44 +0100 Subject: [PATCH 1196/1301] bootstrap: Consolidate editor LSP setup Consolidate LSP setup for different editors into one `./x setup editor`. --- src/bootstrap/src/core/build_steps/setup.rs | 116 ++++++++++++-------- src/bootstrap/src/core/builder.rs | 10 +- src/bootstrap/src/core/config/flags.rs | 9 +- src/bootstrap/src/utils/change_tracker.rs | 2 +- src/etc/completions/x.py.sh | 2 +- 5 files changed, 77 insertions(+), 62 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 3aa8d3a67d8bd..519649779336c 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -528,6 +528,35 @@ enum EditorKind { } impl EditorKind { + fn prompt_user() -> io::Result> { + let prompt_str = "Available editors: +1. vscode +2. vim +3. emacs +4. helix + +Select which editor you would like to set up [default: None]: "; + + let mut input = String::new(); + loop { + print!("{}", prompt_str); + io::stdout().flush()?; + input.clear(); + io::stdin().read_line(&mut input)?; + match input.trim().to_lowercase().as_str() { + "1" | "vscode" => return Ok(Some(EditorKind::Vscode)), + "2" | "vim" => return Ok(Some(EditorKind::Vim)), + "3" | "emacs" => return Ok(Some(EditorKind::Emacs)), + "4" | "helix" => return Ok(Some(EditorKind::Helix)), + "" => return Ok(None), + _ => { + eprintln!("ERROR: unrecognized option '{}'", input.trim()); + eprintln!("NOTE: press Ctrl+C to exit"); + } + }; + } + } + /// A list of historical hashes of each LSP settings file /// New entries should be appended whenever this is updated so we can detect /// outdated vs. user-modified settings files. @@ -568,10 +597,10 @@ impl EditorKind { fn settings_folder(&self) -> PathBuf { match self { - EditorKind::Vscode => PathBuf::new().join(".vscode"), - EditorKind::Vim => PathBuf::new().join(".vim"), + EditorKind::Vscode => PathBuf::from(".vscode"), + EditorKind::Vim => PathBuf::from(".vim"), EditorKind::Emacs => PathBuf::new(), - EditorKind::Helix => PathBuf::new().join(".helix"), + EditorKind::Helix => PathBuf::from(".helix"), } } @@ -590,48 +619,47 @@ impl EditorKind { } } -/// Helper macro for implementing the necessary Step to support editor LSP setup -/// The first argument must match the argument set up in `flags.rs` -/// The second argument must match the name of some `EditorKind` variant -/// After using the macro, the editor needs to be registered in `builder.rs` with describe!() -macro_rules! impl_editor_support { - ( $($editor:ident, $kind:ident),+ ) => {$( - #[doc = concat!(" Sets up or displays the LSP config for ", stringify!($editor), ".")] - #[derive(Clone, Debug, Eq, PartialEq, Hash)] - pub struct $kind; - - impl Step for $kind { - type Output = (); - const DEFAULT: bool = true; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.alias(stringify!($editor)) - } - fn make_run(run: RunConfig<'_>) { - if run.builder.config.dry_run() { - return; - } - if let [cmd] = &run.paths[..] { - if cmd.assert_single_path().path.as_path().as_os_str() == stringify!($editor) { - run.builder.ensure($kind); - } - } +/// Sets up or displays the LSP config for one of the supported editors +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct Editor; + +impl Step for Editor { + type Output = (); + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("editor") + } + + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == "editor" { + run.builder.ensure(Editor); } - fn run(self, builder: &Builder<'_>) -> Self::Output { - let config = &builder.config; - if config.dry_run() { - return; + } + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + match EditorKind::prompt_user() { + Ok(editor_kind) => { + if let Some(editor_kind) = editor_kind { + while !t!(create_editor_settings_maybe(config, editor_kind.clone())) {} + } else { + println!("Ok, skipping editor setup!"); } - while !t!(create_editor_settings_maybe(config, EditorKind::$kind)) {} } + Err(e) => eprintln!("Could not determine the editor: {e}"), } - )+}; + } } -impl_editor_support!(vscode, Vscode); -impl_editor_support!(vim, Vim); -impl_editor_support!(emacs, Emacs); -impl_editor_support!(helix, Helix); - /// Create the recommended editor LSP config file for rustc development, or just print it /// If this method should be re-called, it returns `false`. fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Result { @@ -662,13 +690,11 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu ); match mismatched_settings { - Some(true) => eprintln!( - "WARNING: existing `{}` is out of date, x.py will update it", - settings_filename - ), + Some(true) => { + eprintln!("WARNING: existing `{settings_filename}` is out of date, x.py will update it") + } Some(false) => eprintln!( - "WARNING: existing `{}` has been modified by user, x.py will back it up and replace it", - settings_filename + "WARNING: existing `{settings_filename}` has been modified by user, x.py will back it up and replace it" ), _ => (), } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 67f9a3ab3edfc..abc7a22889357 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1001,15 +1001,7 @@ impl<'a> Builder<'a> { run::GenerateCompletions, ), Kind::Setup => { - describe!( - setup::Profile, - setup::Hook, - setup::Link, - setup::Vscode, - setup::Emacs, - setup::Helix, - setup::Vim - ) + describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index fb27d1ebb182d..3aefe517a5be6 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -447,17 +447,14 @@ Arguments: The profile is optional and you will be prompted interactively if it is not given. The following profiles are available: {} - To only set up the git hook, VS Code config or toolchain link, you may use + To only set up the git hook, editor config or toolchain link, you may use ./x.py setup hook - ./x.py setup vscode - ./x.py setup vim - ./x.py setup emacs - ./x.py setup helix + ./x.py setup editor ./x.py setup link", Profile::all_for_help(" ").trim_end()))] Setup { /// Either the profile for `config.toml` or another setup action. /// May be omitted to set up interactively - #[arg(value_name = "|hook|vscode|vim|emacs|helix|link")] + #[arg(value_name = "|hook|editor|link")] profile: Option, }, /// Suggest a subset of tests to run, based on modified files diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index e6f5363dbf2f9..b37786496cb56 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -273,6 +273,6 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ ChangeInfo { change_id: 131075, severity: ChangeSeverity::Info, - summary: "New options for ./x setup added - ./x setup [vim|emacs|helix]", + summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.", }, ]; diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 913b0e820cb51..ba7f2c9fb5d66 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -2741,7 +2741,7 @@ _x.py() { return 0 ;; x.py__setup) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|vscode|vim|emacs|helix|link] [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [|hook|editor|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 From bc5f9520c117b372b7ad7326fda1252cd9da3f4e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 3 Oct 2024 15:19:23 -0400 Subject: [PATCH 1197/1301] Remove crashes, add comment --- .../traits/fulfillment_errors.rs | 2 ++ tests/crashes/125881.rs | 8 ----- tests/crashes/126377.rs | 29 ------------------- tests/crashes/130413.rs | 17 ----------- 4 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 tests/crashes/125881.rs delete mode 100644 tests/crashes/126377.rs delete mode 100644 tests/crashes/130413.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 f494d9637f324..e2d067e012c77 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 @@ -2224,6 +2224,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> GetSafeTransmuteErrorAndReason { use rustc_transmute::Answer; + // We don't assemble a transmutability candidate for types that are generic + // and we should have ambiguity for types that still have non-region infer. if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { return GetSafeTransmuteErrorAndReason::Default; } diff --git a/tests/crashes/125881.rs b/tests/crashes/125881.rs deleted file mode 100644 index a38f1891b61d9..0000000000000 --- a/tests/crashes/125881.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: rust-lang/rust#125881 -#![crate_type = "lib"] -#![feature(transmutability)] -#![feature(unboxed_closures,effects)] - -const fn test() -> impl std::mem::TransmuteFrom() { - || {} -} diff --git a/tests/crashes/126377.rs b/tests/crashes/126377.rs deleted file mode 100644 index f6727bcc0a494..0000000000000 --- a/tests/crashes/126377.rs +++ /dev/null @@ -1,29 +0,0 @@ -//@ known-bug: rust-lang/rust#126377 - -#![feature(effects)] -#![feature(generic_const_exprs)] - -mod assert { - use std::mem::{Assume, TransmuteFrom}; - - pub fn is_transmutable< - Src, - Dst, - const ASSUME_ALIGNMENT: bool, - const ASSUME_LIFETIMES: bool, - const ASSUME_SAFETY: bool, - const ASSUME_VALIDITY: bool, - >() - where - Dst: TransmuteFrom< - Src, - { } - >, - {} -} - -const fn from_options() -> Assume { - #[repr(C)] struct Src; - #[repr(C)] struct Dst; - assert::is_transmutable::(); -} diff --git a/tests/crashes/130413.rs b/tests/crashes/130413.rs deleted file mode 100644 index 08435ac6450c0..0000000000000 --- a/tests/crashes/130413.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: #130413 - -#![feature(transmutability)] -trait Aaa { - type Y; -} - -trait Bbb { - type B: std::mem::TransmuteFrom<()>; -} - -impl Bbb for T -where - T: Aaa, -{ - type B = T::Y; -} From f0bfba258330884df9c9d14a0aafd741ca9bbdc9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 3 Oct 2024 09:47:06 -0400 Subject: [PATCH 1198/1301] Disable jump threading UnOp::Not for non-bool --- .../rustc_mir_transform/src/jump_threading.rs | 8 ++++ ...bitwise_not.JumpThreading.panic-abort.diff | 46 +++++++++++++++++++ ...itwise_not.JumpThreading.panic-unwind.diff | 3 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 1844b97887ab9..9b9b0b705bfec 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -494,8 +494,16 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } // Transfer the conditions on the copy rhs, after inversing polarity. Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { + if !place.ty(self.body, self.tcx).ty.is_bool() { + // Constructing the conditions by inverting the polarity + // of equality is only correct for bools. That is to say, + // `!a == b` is not `a != b` for integers greater than 1 bit. + return; + } let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; let Some(place) = self.map.find(place.as_ref()) else { return }; + // FIXME: I think This could be generalized to not bool if we + // actually perform a logical not on the condition's value. let conds = conditions.map(self.arena, Condition::inv); state.insert_value_idx(place, conds, &self.map); } diff --git a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff new file mode 100644 index 0000000000000..047441e609913 --- /dev/null +++ b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-abort.diff @@ -0,0 +1,46 @@ +- // MIR for `bitwise_not` before JumpThreading ++ // MIR for `bitwise_not` after JumpThreading + + fn bitwise_not() -> i32 { + let mut _0: i32; + let mut _1: i32; + let mut _2: bool; + let mut _3: i32; + let mut _4: i32; + scope 1 { + debug a => _1; + } + + bb0: { + StorageLive(_1); + _1 = const 0_i32; + _1 = const 1_i32; + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + _3 = Not(move _4); + StorageDead(_4); + _2 = Eq(move _3, const 0_i32); + switchInt(move _2) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + StorageDead(_3); + _0 = const 1_i32; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + _0 = const 0_i32; + goto -> bb3; + } + + bb3: { + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff index fb64fff7e11e5..047441e609913 100644 --- a/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.bitwise_not.JumpThreading.panic-unwind.diff @@ -22,8 +22,7 @@ _3 = Not(move _4); StorageDead(_4); _2 = Eq(move _3, const 0_i32); -- switchInt(move _2) -> [0: bb2, otherwise: bb1]; -+ goto -> bb1; + switchInt(move _2) -> [0: bb2, otherwise: bb1]; } bb1: { From 0aab10135dd3d4e43e44ba5c375f5fae382cae49 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 3 Oct 2024 16:20:26 +1000 Subject: [PATCH 1199/1301] Merge `rustc_infer::infer::relate::{glb,lub}`. Most of the code in these two modules is duplicated in the other module. This commit eliminates the duplication by replacing them with a new module `lattice_op`. The new `LatticeOpKind` enum is used to distinguish between glb and lub in the few places where the behaviour differs. --- .../rustc_infer/src/infer/relate/combine.rs | 13 +- compiler/rustc_infer/src/infer/relate/glb.rs | 159 -------------- .../rustc_infer/src/infer/relate/lattice.rs | 197 +++++++++++++++++- compiler/rustc_infer/src/infer/relate/lub.rs | 158 -------------- compiler/rustc_infer/src/infer/relate/mod.rs | 2 - 5 files changed, 196 insertions(+), 333 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/relate/glb.rs delete mode 100644 compiler/rustc_infer/src/infer/relate/lub.rs diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index e75d7b7db1453..d9e59d67ba32b 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -1,4 +1,4 @@ -//! There are four type combiners: [TypeRelating], [Lub], and [Glb], +//! There are four type combiners: [TypeRelating], `Lub`, and `Glb`, //! and `NllTypeRelating` in rustc_borrowck, which is only used for NLL. //! //! Each implements the trait [TypeRelation] and contains methods for @@ -26,8 +26,7 @@ use rustc_middle::ty::{self, InferConst, IntType, Ty, TyCtxt, TypeVisitableExt, pub use rustc_next_trait_solver::relate::combine::*; use tracing::debug; -use super::glb::Glb; -use super::lub::Lub; +use super::lattice::{LatticeOp, LatticeOpKind}; use super::type_relating::TypeRelating; use super::{RelateResult, StructurallyRelateAliases}; use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace, relate}; @@ -298,12 +297,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { TypeRelating::new(self, StructurallyRelateAliases::No, ty::Contravariant) } - pub fn lub<'a>(&'a mut self) -> Lub<'a, 'infcx, 'tcx> { - Lub::new(self) + pub(crate) fn lub<'a>(&'a mut self) -> LatticeOp<'a, 'infcx, 'tcx> { + LatticeOp::new(self, LatticeOpKind::Lub) } - pub fn glb<'a>(&'a mut self) -> Glb<'a, 'infcx, 'tcx> { - Glb::new(self) + pub(crate) fn glb<'a>(&'a mut self) -> LatticeOp<'a, 'infcx, 'tcx> { + LatticeOp::new(self, LatticeOpKind::Glb) } pub fn register_obligations( diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs deleted file mode 100644 index ed108f42969db..0000000000000 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! Greatest lower bound. See [`lattice`]. - -use rustc_middle::traits::solve::Goal; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::Span; -use tracing::{debug, instrument}; - -use super::StructurallyRelateAliases; -use super::combine::{CombineFields, PredicateEmittingRelation}; -use super::lattice::{self, LatticeDir}; -use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::ObligationCause; - -/// "Greatest lower bound" (common subtype) -pub struct Glb<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, -} - -impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { - pub fn new(fields: &'combine mut CombineFields<'infcx, 'tcx>) -> Glb<'combine, 'infcx, 'tcx> { - Glb { fields } - } -} - -impl<'tcx> TypeRelation> for Glb<'_, '_, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.fields.tcx() - } - - fn relate_with_variance>>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - match variance { - ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b), - ty::Covariant => self.relate(a, b), - // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a), - ty::Contravariant => self.fields.lub().relate(a, b), - } - } - - #[instrument(skip(self), level = "trace")] - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - lattice::super_lattice_tys(self, a, b) - } - - #[instrument(skip(self), level = "trace")] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); - // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( - self.cx(), - origin, - a, - b, - )) - } - - #[instrument(skip(self), level = "trace")] - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate>, - { - // GLB of a binder and itself is just itself - if a == b { - return Ok(a); - } - - debug!("binders(a={:?}, b={:?})", a, b); - if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - // When higher-ranked types are involved, computing the GLB is - // very challenging, switch to invariance. This is obviously - // overly conservative but works ok in practice. - self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; - Ok(a) - } else { - Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) - } - } -} - -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { - let mut sub = self.fields.sub(); - sub.relate(v, a)?; - sub.relate(v, b)?; - Ok(()) - } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } -} - -impl<'tcx> PredicateEmittingRelation> for Glb<'_, '_, 'tcx> { - fn span(&self) -> Span { - self.fields.trace.span() - } - - fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { - StructurallyRelateAliases::No - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn register_predicates( - &mut self, - obligations: impl IntoIterator, ty::Predicate<'tcx>>>, - ) { - self.fields.register_predicates(obligations); - } - - fn register_goals( - &mut self, - obligations: impl IntoIterator>>, - ) { - self.fields.register_obligations(obligations); - } - - fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - // FIXME(deferred_projection_equality): This isn't right, I think? - ty::AliasRelationDirection::Equate, - ))]); - } -} diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 1d3f45465d63d..1e4db70898686 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -17,12 +17,15 @@ //! //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) -use rustc_middle::ty::relate::RelateResult; -use rustc_middle::ty::{self, Ty, TyVar}; -use tracing::instrument; +use rustc_middle::traits::solve::Goal; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt, TyVar, TypeVisitableExt}; +use rustc_span::Span; +use tracing::{debug, instrument}; -use super::combine::PredicateEmittingRelation; -use crate::infer::{DefineOpaqueTypes, InferCtxt}; +use super::StructurallyRelateAliases; +use super::combine::{CombineFields, PredicateEmittingRelation}; +use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; use crate::traits::ObligationCause; /// Trait for returning data about a lattice, and for abstracting @@ -30,7 +33,7 @@ use crate::traits::ObligationCause; /// /// GLB moves "down" the lattice (to smaller values); LUB moves /// "up" the lattice (to bigger values). -pub(crate) trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation> { +trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation> { fn infcx(&self) -> &'f InferCtxt<'tcx>; fn cause(&self) -> &ObligationCause<'tcx>; @@ -48,7 +51,7 @@ pub(crate) trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation /// Relates two types using a given lattice. #[instrument(skip(this), level = "debug")] -pub fn super_lattice_tys<'a, 'tcx: 'a, L>( +fn super_lattice_tys<'a, 'tcx: 'a, L>( this: &mut L, a: Ty<'tcx>, b: Ty<'tcx>, @@ -113,3 +116,183 @@ where _ => infcx.super_combine_tys(this, a, b), } } + +#[derive(Clone, Copy)] +pub(crate) enum LatticeOpKind { + Glb, + Lub, +} + +impl LatticeOpKind { + fn invert(self) -> Self { + match self { + LatticeOpKind::Glb => LatticeOpKind::Lub, + LatticeOpKind::Lub => LatticeOpKind::Glb, + } + } +} + +/// A greatest lower bound" (common subtype) or least upper bound (common supertype). +pub(crate) struct LatticeOp<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + kind: LatticeOpKind, +} + +impl<'combine, 'infcx, 'tcx> LatticeOp<'combine, 'infcx, 'tcx> { + pub(crate) fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + kind: LatticeOpKind, + ) -> LatticeOp<'combine, 'infcx, 'tcx> { + LatticeOp { fields, kind } + } +} + +impl<'tcx> TypeRelation> for LatticeOp<'_, '_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn relate_with_variance>>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b), + ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + ty::Bivariant => Ok(a), + ty::Contravariant => { + self.kind = self.kind.invert(); + let res = self.relate(a, b); + self.kind = self.kind.invert(); + res + } + } + } + + #[instrument(skip(self), level = "trace")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + super_lattice_tys(self, a, b) + } + + #[instrument(skip(self), level = "trace")] + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + let mut inner = self.fields.infcx.inner.borrow_mut(); + let mut constraints = inner.unwrap_region_constraints(); + Ok(match self.kind { + // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 + LatticeOpKind::Glb => constraints.lub_regions(self.cx(), origin, a, b), + + // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 + LatticeOpKind::Lub => constraints.glb_regions(self.cx(), origin, a, b), + }) + } + + #[instrument(skip(self), level = "trace")] + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate>, + { + // GLB/LUB of a binder and itself is just itself + if a == b { + return Ok(a); + } + + debug!("binders(a={:?}, b={:?})", a, b); + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the GLB/LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; + Ok(a) + } else { + Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for LatticeOp<'combine, 'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'tcx> { + self.fields.infcx + } + + fn cause(&self) -> &ObligationCause<'tcx> { + &self.fields.trace.cause + } + + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let mut sub = self.fields.sub(); + match self.kind { + LatticeOpKind::Glb => { + sub.relate(v, a)?; + sub.relate(v, b)?; + } + LatticeOpKind::Lub => { + sub.relate(a, v)?; + sub.relate(b, v)?; + } + } + Ok(()) + } + + fn define_opaque_types(&self) -> DefineOpaqueTypes { + self.fields.define_opaque_types + } +} + +impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, '_, 'tcx> { + fn span(&self) -> Span { + self.fields.trace.span() + } + + fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { + StructurallyRelateAliases::No + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn register_predicates( + &mut self, + obligations: impl IntoIterator, ty::Predicate<'tcx>>>, + ) { + self.fields.register_predicates(obligations); + } + + fn register_goals( + &mut self, + obligations: impl IntoIterator>>, + ) { + self.fields.register_obligations(obligations); + } + + fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + // FIXME(deferred_projection_equality): This isn't right, I think? + ty::AliasRelationDirection::Equate, + ))]); + } +} diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs deleted file mode 100644 index 35c7ab5000d70..0000000000000 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Least upper bound. See [`lattice`]. - -use rustc_middle::traits::solve::Goal; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::Span; -use tracing::{debug, instrument}; - -use super::StructurallyRelateAliases; -use super::combine::{CombineFields, PredicateEmittingRelation}; -use super::lattice::{self, LatticeDir}; -use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::ObligationCause; - -/// "Least upper bound" (common supertype) -pub struct Lub<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, -} - -impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { - pub fn new(fields: &'combine mut CombineFields<'infcx, 'tcx>) -> Lub<'combine, 'infcx, 'tcx> { - Lub { fields } - } -} - -impl<'tcx> TypeRelation> for Lub<'_, '_, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.fields.tcx() - } - - fn relate_with_variance>>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - match variance { - ty::Invariant => self.fields.equate(StructurallyRelateAliases::No).relate(a, b), - ty::Covariant => self.relate(a, b), - // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a), - ty::Contravariant => self.fields.glb().relate(a, b), - } - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - lattice::super_lattice_tys(self, a, b) - } - - #[instrument(skip(self), level = "trace")] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); - // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( - self.cx(), - origin, - a, - b, - )) - } - - #[instrument(skip(self), level = "trace")] - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate>, - { - // LUB of a binder and itself is just itself - if a == b { - return Ok(a); - } - - debug!("binders(a={:?}, b={:?})", a, b); - if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - // When higher-ranked types are involved, computing the LUB is - // very challenging, switch to invariance. This is obviously - // overly conservative but works ok in practice. - self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; - Ok(a) - } else { - Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) - } - } -} - -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { - let mut sub = self.fields.sub(); - sub.relate(a, v)?; - sub.relate(b, v)?; - Ok(()) - } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } -} - -impl<'tcx> PredicateEmittingRelation> for Lub<'_, '_, 'tcx> { - fn span(&self) -> Span { - self.fields.trace.span() - } - - fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { - StructurallyRelateAliases::No - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn register_predicates( - &mut self, - obligations: impl IntoIterator, ty::Predicate<'tcx>>>, - ) { - self.fields.register_predicates(obligations); - } - - fn register_goals( - &mut self, - obligations: impl IntoIterator>>, - ) { - self.fields.register_obligations(obligations) - } - - fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - // FIXME(deferred_projection_equality): This isn't right, I think? - ty::AliasRelationDirection::Equate, - ))]); - } -} diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index 183ea5b3309a9..edc0c4f078ab2 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -10,8 +10,6 @@ pub use self::combine::{CombineFields, PredicateEmittingRelation}; #[allow(hidden_glob_reexports)] pub(super) mod combine; mod generalize; -mod glb; mod higher_ranked; mod lattice; -mod lub; mod type_relating; From ee227dec8c84fc5a65ef798af93863a97794502e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 3 Oct 2024 17:41:32 +1000 Subject: [PATCH 1200/1301] Remove `LatticeDir` trait. It's no longer necessary now that the `glb` and `lub` modules have been merged. --- .../rustc_infer/src/infer/relate/lattice.rs | 173 +++++++----------- 1 file changed, 69 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 1e4db70898686..9564baa6ab287 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -26,96 +26,6 @@ use tracing::{debug, instrument}; use super::StructurallyRelateAliases; use super::combine::{CombineFields, PredicateEmittingRelation}; use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::ObligationCause; - -/// Trait for returning data about a lattice, and for abstracting -/// over the "direction" of the lattice operation (LUB/GLB). -/// -/// GLB moves "down" the lattice (to smaller values); LUB moves -/// "up" the lattice (to bigger values). -trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation> { - fn infcx(&self) -> &'f InferCtxt<'tcx>; - - fn cause(&self) -> &ObligationCause<'tcx>; - - fn define_opaque_types(&self) -> DefineOpaqueTypes; - - // Relates the type `v` to `a` and `b` such that `v` represents - // the LUB/GLB of `a` and `b` as appropriate. - // - // Subtle hack: ordering *may* be significant here. This method - // relates `v` to `a` first, which may help us to avoid unnecessary - // type variable obligations. See caller for details. - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>; -} - -/// Relates two types using a given lattice. -#[instrument(skip(this), level = "debug")] -fn super_lattice_tys<'a, 'tcx: 'a, L>( - this: &mut L, - a: Ty<'tcx>, - b: Ty<'tcx>, -) -> RelateResult<'tcx, Ty<'tcx>> -where - L: LatticeDir<'a, 'tcx>, -{ - if a == b { - return Ok(a); - } - - let infcx = this.infcx(); - - let a = infcx.shallow_resolve(a); - let b = infcx.shallow_resolve(b); - - match (a.kind(), b.kind()) { - // If one side is known to be a variable and one is not, - // create a variable (`v`) to represent the LUB. Make sure to - // relate `v` to the non-type-variable first (by passing it - // first to `relate_bound`). Otherwise, we would produce a - // subtype obligation that must then be processed. - // - // Example: if the LHS is a type variable, and RHS is - // `Box`, then we current compare `v` to the RHS first, - // which will instantiate `v` with `Box`. Then when `v` - // is compared to the LHS, we instantiate LHS with `Box`. - // But if we did in reverse order, we would create a `v <: - // LHS` (or vice versa) constraint and then instantiate - // `v`. This would require further processing to achieve same - // end-result; in particular, this screws up some of the logic - // in coercion, which expects LUB to figure out that the LHS - // is (e.g.) `Box`. A more obvious solution might be to - // iterate on the subtype obligations that are returned, but I - // think this suffices. -nmatsakis - (&ty::Infer(TyVar(..)), _) => { - let v = infcx.next_ty_var(this.cause().span); - this.relate_bound(v, b, a)?; - Ok(v) - } - (_, &ty::Infer(TyVar(..))) => { - let v = infcx.next_ty_var(this.cause().span); - this.relate_bound(v, a, b)?; - Ok(v) - } - - ( - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b), - - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if this.define_opaque_types() == DefineOpaqueTypes::Yes - && def_id.is_local() - && !this.infcx().next_trait_solver() => - { - this.register_goals(infcx.handle_opaque_type(a, b, this.span(), this.param_env())?); - Ok(a) - } - - _ => infcx.super_combine_tys(this, a, b), - } -} #[derive(Clone, Copy)] pub(crate) enum LatticeOpKind { @@ -173,9 +83,70 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, '_, 'tcx> { } } + /// Relates two types using a given lattice. #[instrument(skip(self), level = "trace")] fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - super_lattice_tys(self, a, b) + if a == b { + return Ok(a); + } + + let infcx = self.fields.infcx; + + let a = infcx.shallow_resolve(a); + let b = infcx.shallow_resolve(b); + + match (a.kind(), b.kind()) { + // If one side is known to be a variable and one is not, + // create a variable (`v`) to represent the LUB. Make sure to + // relate `v` to the non-type-variable first (by passing it + // first to `relate_bound`). Otherwise, we would produce a + // subtype obligation that must then be processed. + // + // Example: if the LHS is a type variable, and RHS is + // `Box`, then we current compare `v` to the RHS first, + // which will instantiate `v` with `Box`. Then when `v` + // is compared to the LHS, we instantiate LHS with `Box`. + // But if we did in reverse order, we would create a `v <: + // LHS` (or vice versa) constraint and then instantiate + // `v`. This would require further processing to achieve same + // end-result; in particular, this screws up some of the logic + // in coercion, which expects LUB to figure out that the LHS + // is (e.g.) `Box`. A more obvious solution might be to + // iterate on the subtype obligations that are returned, but I + // think this suffices. -nmatsakis + (&ty::Infer(TyVar(..)), _) => { + let v = infcx.next_ty_var(self.fields.trace.cause.span); + self.relate_bound(v, b, a)?; + Ok(v) + } + (_, &ty::Infer(TyVar(..))) => { + let v = infcx.next_ty_var(self.fields.trace.cause.span); + self.relate_bound(v, a, b)?; + Ok(v) + } + + ( + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b), + + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if self.fields.define_opaque_types == DefineOpaqueTypes::Yes + && def_id.is_local() + && !infcx.next_trait_solver() => + { + self.register_goals(infcx.handle_opaque_type( + a, + b, + self.span(), + self.param_env(), + )?); + Ok(a) + } + + _ => infcx.super_combine_tys(self, a, b), + } } #[instrument(skip(self), level = "trace")] @@ -231,15 +202,13 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, '_, 'tcx> { } } -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for LatticeOp<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - +impl<'combine, 'infcx, 'tcx> LatticeOp<'combine, 'infcx, 'tcx> { + // Relates the type `v` to `a` and `b` such that `v` represents + // the LUB/GLB of `a` and `b` as appropriate. + // + // Subtle hack: ordering *may* be significant here. This method + // relates `v` to `a` first, which may help us to avoid unnecessary + // type variable obligations. See caller for details. fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { let mut sub = self.fields.sub(); match self.kind { @@ -254,10 +223,6 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for LatticeOp<'combine, 'i } Ok(()) } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } } impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, '_, 'tcx> { From d19bd66a4e88116af18a1f5a6ab4ffe2f5bd1956 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 4 Oct 2024 05:00:12 +0000 Subject: [PATCH 1201/1301] Preparing for merge from rustc --- 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 fe0421b2075a5..eb4dfcf57cf00 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ad9c494835e746fb7c8a26eeed0ad90e4e834058 +7067e4aee45c18cfa1c6af3bf79bd097684fb294 From 253fec494fa336ee27c6cb027ffdb3d6e0b632c3 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 3 Oct 2024 17:42:08 -0700 Subject: [PATCH 1202/1301] rustdoc: prevent ctors from resolving --- compiler/rustc_resolve/src/lib.rs | 4 +- .../intra-doc/disambiguator-mismatch.rs | 4 +- .../intra-doc/disambiguator-mismatch.stderr | 6 +- tests/rustdoc-ui/intra-doc/value-ctor.rs | 35 +++++++++++ tests/rustdoc-ui/intra-doc/value-ctor.stderr | 62 +++++++++++++++++++ 5 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 tests/rustdoc-ui/intra-doc/value-ctor.rs create mode 100644 tests/rustdoc-ui/intra-doc/value-ctor.stderr diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index fcbbb523cfc9f..24a0c252e55ad 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2148,7 +2148,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match self.maybe_resolve_path(&segments, Some(ns), &parent_scope, None) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), - PathResult::NonModule(path_res) => path_res.full_res(), + PathResult::NonModule(path_res) => { + path_res.full_res().filter(|res| !matches!(res, Res::Def(DefKind::Ctor(..), _))) + } PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => { None } diff --git a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.rs b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.rs index 8142bd83877c3..f63a9e87497d0 100644 --- a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.rs +++ b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.rs @@ -91,7 +91,7 @@ struct X { //~| HELP prefix with `field@` /// Link to [field@S::A] -//~^ ERROR incompatible link kind for `S::A` -//~| NOTE this link resolved +//~^ ERROR unresolved link to `S::A` +//~| NOTE this link resolves //~| HELP prefix with `variant@` pub fn f() {} diff --git a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr index 488120304fde0..66b910eed8135 100644 --- a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr +++ b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr @@ -160,13 +160,13 @@ help: to link to the field, prefix with `field@` LL | /// Link to [field@X::y] | ~~~~~~ -error: incompatible link kind for `S::A` +error: unresolved link to `S::A` --> $DIR/disambiguator-mismatch.rs:93:14 | LL | /// Link to [field@S::A] - | ^^^^^^^^^^ this link resolved to a unit variant, which is not a field + | ^^^^^^^^^^ this link resolves to the variant `A`, which is not in the value namespace | -help: to link to the unit variant, prefix with `variant@` +help: to link to the variant, prefix with `variant@` | LL | /// Link to [variant@S::A] | ~~~~~~~~ diff --git a/tests/rustdoc-ui/intra-doc/value-ctor.rs b/tests/rustdoc-ui/intra-doc/value-ctor.rs new file mode 100644 index 0000000000000..6f57b7c6f1082 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/value-ctor.rs @@ -0,0 +1,35 @@ +// https://github.com/rust-lang/rust/issues/130591 +#![deny(rustdoc::broken_intra_doc_links)] +#![crate_name = "foo"] + +/// [value@Foo::X] //~ERROR broken +pub enum Foo { + X, +} + +/// [tst][value@MyStruct] //~ERROR broken +pub struct MyStruct; + +pub enum MyEnum { + Internals, +} + +pub use MyEnum::*; + +/// In this context, [a][type@Internals] is a struct, +/// while [b][value@Internals] fails. //~ERROR broken +/// Also, [c][struct@Internals] is a struct, +/// while [d][variant@Internals] fails. //~ERROR broken +pub struct Internals { + foo: (), +} + +pub mod inside { + pub struct Internals2; +} + +use inside::*; + +/// In this context, [a][type@Internals2] is an enum, +/// while [b][value@Internals2] fails. //~ERROR broken +pub enum Internals2 {} diff --git a/tests/rustdoc-ui/intra-doc/value-ctor.stderr b/tests/rustdoc-ui/intra-doc/value-ctor.stderr new file mode 100644 index 0000000000000..8d2a6649f4c01 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/value-ctor.stderr @@ -0,0 +1,62 @@ +error: unresolved link to `Foo::X` + --> $DIR/value-ctor.rs:5:6 + | +LL | /// [value@Foo::X] + | ^^^^^^^^^^^^ this link resolves to the variant `X`, which is not in the value namespace + | +note: the lint level is defined here + --> $DIR/value-ctor.rs:2:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to link to the variant, prefix with `variant@` + | +LL | /// [variant@Foo::X] + | ~~~~~~~~ + +error: unresolved link to `MyStruct` + --> $DIR/value-ctor.rs:10:11 + | +LL | /// [tst][value@MyStruct] + | ^^^^^^^^^^^^^^ this link resolves to the struct `MyStruct`, which is not in the value namespace + | +help: to link to the struct, prefix with `struct@` + | +LL | /// [tst][struct@MyStruct] + | ~~~~~~~ + +error: unresolved link to `Internals` + --> $DIR/value-ctor.rs:20:15 + | +LL | /// while [b][value@Internals] fails. + | ^^^^^^^^^^^^^^^ this link resolves to the struct `Internals`, which is not in the value namespace + | +help: to link to the struct, prefix with `struct@` + | +LL | /// while [b][struct@Internals] fails. + | ~~~~~~~ + +error: incompatible link kind for `Internals` + --> $DIR/value-ctor.rs:22:15 + | +LL | /// while [d][variant@Internals] fails. + | ^^^^^^^^^^^^^^^^^ this link resolved to a struct, which is not a variant + | +help: to link to the struct, prefix with `struct@` + | +LL | /// while [d][struct@Internals] fails. + | ~~~~~~~ + +error: unresolved link to `Internals2` + --> $DIR/value-ctor.rs:34:15 + | +LL | /// while [b][value@Internals2] fails. + | ^^^^^^^^^^^^^^^^ this link resolves to the enum `Internals2`, which is not in the value namespace + | +help: to link to the enum, prefix with `enum@` + | +LL | /// while [b][enum@Internals2] fails. + | ~~~~~ + +error: aborting due to 5 previous errors + From 62ef411631efb25134e29da76fcdb7802aa94bd4 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 19 Sep 2024 11:19:35 +0200 Subject: [PATCH 1203/1301] Feature gate boolean lit support in cfg predicates --- compiler/rustc_attr/src/builtin.rs | 20 ++++++++- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + .../language-features/cfg-boolean-literals.md | 22 ++++++++++ tests/ui/cfg/raw-true-false.rs | 19 ++++++++ tests/ui/cfg/true-false.rs | 1 + .../feature-gate-cfg-boolean-literals.rs | 10 +++++ .../feature-gate-cfg-boolean-literals.stderr | 43 +++++++++++++++++++ 8 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/cfg-boolean-literals.md create mode 100644 tests/ui/cfg/raw-true-false.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 930fdeb975d87..a3c6132d484d2 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -18,7 +18,7 @@ use rustc_session::parse::feature_err; use rustc_session::{RustcVersion, Session}; use rustc_span::Span; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::{Symbol, kw, sym}; use crate::fluent_generated; use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; @@ -603,7 +603,23 @@ pub fn eval_condition( let cfg = match cfg { ast::NestedMetaItem::MetaItem(meta_item) => meta_item, - ast::NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => return *b, + ast::NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { + if let Some(features) = features { + // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` + // and `true`, and we want to keep the former working without feature gate + gate_cfg( + &(( + if *b { kw::True } else { kw::False }, + sym::cfg_boolean_literals, + |features: &Features| features.cfg_boolean_literals, + )), + cfg.span(), + sess, + features, + ); + } + return *b; + } _ => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: cfg.span(), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1ffd35dbf91d1..643a95e6ea1ef 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -371,6 +371,8 @@ declare_features! ( (unstable, async_for_loop, "1.77.0", Some(118898)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), + /// Allows the use of `#[cfg()]`. + (unstable, cfg_boolean_literals, "CURRENT_RUSTC_VERSION", Some(131204)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8f226b26befda..ea8c5f135abf7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -543,6 +543,7 @@ symbols! { cfg_accessible, cfg_attr, cfg_attr_multi, + cfg_boolean_literals, cfg_doctest, cfg_eval, cfg_fmt_debug, diff --git a/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md b/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md new file mode 100644 index 0000000000000..ad795ff9d9b26 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md @@ -0,0 +1,22 @@ +# `cfg_boolean_literals` + +The tracking issue for this feature is: [#131204] + +[#131204]: https://github.com/rust-lang/rust/issues/131204 + +------------------------ + +The `cfg_boolean_literals` feature makes it possible to use the `true`/`false` +literal as cfg predicate. They always evaluate to true/false respectively. + +## Examples + +```rust +#![feature(cfg_boolean_literals)] + +#[cfg(true)] +const A: i32 = 5; + +#[cfg(all(false))] +const A: i32 = 58 * 89; +``` diff --git a/tests/ui/cfg/raw-true-false.rs b/tests/ui/cfg/raw-true-false.rs new file mode 100644 index 0000000000000..4cb8bb71c924b --- /dev/null +++ b/tests/ui/cfg/raw-true-false.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ compile-flags: --cfg false --check-cfg=cfg(r#false) + +#![deny(warnings)] + +#[expect(unexpected_cfgs)] +mod a { + #[cfg(r#true)] + pub fn foo() {} +} + +mod b { + #[cfg(r#false)] + pub fn bar() {} +} + +fn main() { + b::bar() +} diff --git a/tests/ui/cfg/true-false.rs b/tests/ui/cfg/true-false.rs index 0bd1cf427fafd..03d96fbafecbe 100644 --- a/tests/ui/cfg/true-false.rs +++ b/tests/ui/cfg/true-false.rs @@ -1,6 +1,7 @@ //@ run-pass #![feature(link_cfg)] +#![feature(cfg_boolean_literals)] #[cfg(true)] fn foo() -> bool { diff --git a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs new file mode 100644 index 0000000000000..6784b4450490b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs @@ -0,0 +1,10 @@ +#[cfg(true)] //~ ERROR `cfg(true)` is experimental +fn foo() {} + +#[cfg_attr(true, cfg(false))] //~ ERROR `cfg(true)` is experimental +//~^ ERROR `cfg(false)` is experimental +fn foo() {} + +fn main() { + cfg!(false); //~ ERROR `cfg(false)` is experimental +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr new file mode 100644 index 0000000000000..64491464f1d43 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr @@ -0,0 +1,43 @@ +error[E0658]: `cfg(true)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:1:7 + | +LL | #[cfg(true)] + | ^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` 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]: `cfg(true)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:4:12 + | +LL | #[cfg_attr(true, cfg(false))] + | ^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` 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]: `cfg(false)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:4:22 + | +LL | #[cfg_attr(true, cfg(false))] + | ^^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` 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]: `cfg(false)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:9:10 + | +LL | cfg!(false); + | ^^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` 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 4 previous errors + +For more information about this error, try `rustc --explain E0658`. From 781f1840cd37348ce4d76fff2b04e1ada531864e Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 29 Sep 2024 23:40:06 +0200 Subject: [PATCH 1204/1301] Adjust rustdoc for literal boolean support --- src/librustdoc/clean/cfg.rs | 10 +++-- src/librustdoc/clean/cfg/tests.rs | 51 ++++++++++++++++--------- src/librustdoc/clean/types.rs | 5 ++- src/librustdoc/visit_ast.rs | 2 +- tests/rustdoc-ui/cfg-boolean-literal.rs | 19 +++++++++ 5 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 tests/rustdoc-ui/cfg-boolean-literal.rs diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 267392190858c..53830016a80bb 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -6,7 +6,7 @@ use std::fmt::{self, Write}; use std::{mem, ops}; -use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem}; +use rustc_ast::{LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashSet; use rustc_feature::Features; use rustc_session::parse::ParseSess; @@ -48,6 +48,10 @@ impl Cfg { ) -> Result, InvalidCfgError> { match nested_cfg { NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude), + NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b { + true => Ok(Some(Cfg::True)), + false => Ok(Some(Cfg::False)), + }, NestedMetaItem::Lit(ref lit) => { Err(InvalidCfgError { msg: "unexpected literal", span: lit.span }) } @@ -120,8 +124,8 @@ impl Cfg { /// /// If the content is not properly formatted, it will return an error indicating what and where /// the error is. - pub(crate) fn parse(cfg: &MetaItem) -> Result { - Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap()) + pub(crate) fn parse(cfg: &NestedMetaItem) -> Result { + Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap()) } /// Checks whether the given configuration can be matched in the current session. diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 0ab655103e293..d4b11451c8941 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,4 +1,5 @@ -use rustc_ast::{MetaItemLit, Path, Safety, StrStyle}; +use rustc_ast::ast::LitIntType; +use rustc_ast::{MetaItemLit, NestedMetaItem, Path, Safety, StrStyle}; use rustc_span::symbol::{Ident, kw}; use rustc_span::{DUMMY_SP, create_default_session_globals_then}; use thin_vec::thin_vec; @@ -13,52 +14,52 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg { Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value))) } -fn dummy_meta_item_word(name: &str) -> MetaItem { - MetaItem { +fn dummy_lit(symbol: Symbol, kind: LitKind) -> NestedMetaItem { + NestedMetaItem::Lit(MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }) +} + +fn dummy_meta_item_word(name: &str) -> NestedMetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::Word, span: DUMMY_SP, - } + }) } -fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem { +fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> NestedMetaItem { let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }; - MetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::NameValue(lit), span: DUMMY_SP, - } + }) } macro_rules! dummy_meta_item_list { ($name:ident, [$($list:ident),* $(,)?]) => { - MetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( - NestedMetaItem::MetaItem( - dummy_meta_item_word(stringify!($list)), - ), + dummy_meta_item_word(stringify!($list)), )* ]), span: DUMMY_SP, - } + }) }; ($name:ident, [$($list:expr),* $(,)?]) => { - MetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ - $( - NestedMetaItem::MetaItem($list), - )* + $($list,)* ]), span: DUMMY_SP, - } + }) }; } @@ -251,6 +252,14 @@ fn test_cfg_or() { #[test] fn test_parse_ok() { create_default_session_globals_then(|| { + let r#true = Symbol::intern("true"); + let mi = dummy_lit(r#true, LitKind::Bool(true)); + assert_eq!(Cfg::parse(&mi), Ok(Cfg::True)); + + let r#false = Symbol::intern("false"); + let mi = dummy_lit(r#false, LitKind::Bool(false)); + assert_eq!(Cfg::parse(&mi), Ok(Cfg::False)); + let mi = dummy_meta_item_word("all"); assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all"))); @@ -309,6 +318,14 @@ fn test_parse_err() { let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(foo, []),]); assert!(Cfg::parse(&mi).is_err()); + + let c = Symbol::intern("e"); + let mi = dummy_lit(c, LitKind::Char('e')); + assert!(Cfg::parse(&mi).is_err()); + + let five = Symbol::intern("5"); + let mi = dummy_lit(five, LitKind::Int(5.into(), LitIntType::Unsuffixed)); + assert!(Cfg::parse(&mi).is_err()); }) } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index b9c5e8e787b29..7e822011a67d0 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, OnceLock as OnceCell}; use std::{fmt, iter}; use arrayvec::ArrayVec; +use rustc_ast::NestedMetaItem; use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince}; use rustc_const_eval::const_eval::is_unstable_const_fn; @@ -1016,7 +1017,7 @@ pub(crate) trait AttributesExt { .peekable(); if doc_cfg.peek().is_some() && doc_cfg_active { doc_cfg - .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok()) + .filter_map(|attr| Cfg::parse(&attr).ok()) .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) } else if doc_auto_cfg_active { // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because @@ -1072,7 +1073,7 @@ pub(crate) trait AttributesExt { let mut meta = attr.meta_item().unwrap().clone(); meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); - if let Ok(feat_cfg) = Cfg::parse(&meta) { + if let Ok(feat_cfg) = Cfg::parse(&NestedMetaItem::MetaItem(meta)) { cfg &= feat_cfg; } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 2cf703f57c0b6..2fcb147688dc9 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -164,7 +164,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { .unwrap_or(&[]) .iter() .filter_map(|attr| { - Cfg::parse(attr.meta_item()?) + Cfg::parse(attr) .map_err(|e| self.cx.sess().dcx().span_err(e.span, e.msg)) .ok() }) diff --git a/tests/rustdoc-ui/cfg-boolean-literal.rs b/tests/rustdoc-ui/cfg-boolean-literal.rs new file mode 100644 index 0000000000000..4d4e599bfeef2 --- /dev/null +++ b/tests/rustdoc-ui/cfg-boolean-literal.rs @@ -0,0 +1,19 @@ +//@ check-pass + +#![feature(cfg_boolean_literals)] +#![feature(doc_cfg)] + +#[doc(cfg(false))] +pub fn foo() {} + +#[doc(cfg(true))] +pub fn bar() {} + +#[doc(cfg(any(true)))] +pub fn zoo() {} + +#[doc(cfg(all(true)))] +pub fn toy() {} + +#[doc(cfg(not(true)))] +pub fn nay() {} From a3ffa1eae507809628decc6250b28db6ab167b11 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 1 Oct 2024 10:25:18 +0200 Subject: [PATCH 1205/1301] Improve non-boolean literal error in cfg predicate --- compiler/rustc_attr/messages.ftl | 2 ++ compiler/rustc_attr/src/builtin.rs | 3 ++- compiler/rustc_attr/src/session_diagnostics.rs | 1 + tests/ui/macros/cfg.rs | 2 +- tests/ui/macros/cfg.stderr | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_attr/messages.ftl b/compiler/rustc_attr/messages.ftl index 5d9ac23ec490e..adabf18ca85d6 100644 --- a/compiler/rustc_attr/messages.ftl +++ b/compiler/rustc_attr/messages.ftl @@ -107,6 +107,8 @@ attr_unknown_version_literal = attr_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change +attr_unsupported_literal_cfg_boolean = + literal in `cfg` predicate value must be a boolean attr_unsupported_literal_cfg_string = literal in `cfg` predicate value must be a string attr_unsupported_literal_deprecated_kv_pair = diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index a3c6132d484d2..af73281c3160a 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -36,6 +36,7 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool { pub(crate) enum UnsupportedLiteralReason { Generic, CfgString, + CfgBoolean, DeprecatedString, DeprecatedKvPair, } @@ -623,7 +624,7 @@ pub fn eval_condition( _ => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: cfg.span(), - reason: UnsupportedLiteralReason::Generic, + reason: UnsupportedLiteralReason::CfgBoolean, is_bytestr: false, start_point_span: sess.source_map().start_point(cfg.span()), }); diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs index 959a5a4bba752..626840aa6a3cd 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr/src/session_diagnostics.rs @@ -206,6 +206,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { let mut diag = Diag::new(dcx, level, match self.reason { UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic, UnsupportedLiteralReason::CfgString => fluent::attr_unsupported_literal_cfg_string, + UnsupportedLiteralReason::CfgBoolean => fluent::attr_unsupported_literal_cfg_boolean, UnsupportedLiteralReason::DeprecatedString => { fluent::attr_unsupported_literal_deprecated_string } diff --git a/tests/ui/macros/cfg.rs b/tests/ui/macros/cfg.rs index 387cc0ba8e25c..50998572274ec 100644 --- a/tests/ui/macros/cfg.rs +++ b/tests/ui/macros/cfg.rs @@ -1,6 +1,6 @@ fn main() { cfg!(); //~ ERROR macro requires a cfg-pattern - cfg!(123); //~ ERROR unsupported literal + cfg!(123); //~ ERROR literal in `cfg` predicate value must be a boolean cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string cfg!(foo, bar); //~ ERROR expected 1 cfg-pattern } diff --git a/tests/ui/macros/cfg.stderr b/tests/ui/macros/cfg.stderr index 5c21497dc5dd6..5332691486562 100644 --- a/tests/ui/macros/cfg.stderr +++ b/tests/ui/macros/cfg.stderr @@ -6,7 +6,7 @@ LL | cfg!(); | = note: this error originates in the macro `cfg` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0565]: unsupported literal +error[E0565]: literal in `cfg` predicate value must be a boolean --> $DIR/cfg.rs:3:10 | LL | cfg!(123); From eefe3200cd26a1810a50c7016d07d28862e0744e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20D=C3=B6nszelmann?= Date: Fri, 4 Oct 2024 09:38:14 +0200 Subject: [PATCH 1206/1301] remove blank issue template --- .github/ISSUE_TEMPLATE/blank_issue.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/blank_issue.md diff --git a/.github/ISSUE_TEMPLATE/blank_issue.md b/.github/ISSUE_TEMPLATE/blank_issue.md deleted file mode 100644 index 9aef3ebe637a1..0000000000000 --- a/.github/ISSUE_TEMPLATE/blank_issue.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: Blank Issue -about: Create a blank issue. ---- From dc88a6a788ebbcc2c342b2cfb584f48b2b78b43f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 4 Oct 2024 10:12:05 +0200 Subject: [PATCH 1207/1301] clippy --- src/tools/miri/cargo-miri/src/phases.rs | 1 - src/tools/miri/src/concurrency/vector_clock.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 52bc8e1a3b6d7..f1f76fd338cea 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -668,7 +668,6 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner RunnerPhase::Rustdoc => { cmd.stdin(std::process::Stdio::piped()); // the warning is wrong, we have a `wait` inside the `scope` closure. - #[expect(clippy::zombie_processes)] let mut child = cmd.spawn().expect("failed to spawn process"); let child_stdin = child.stdin.take().unwrap(); // Write stdin in a background thread, as it may block. diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs index f9025e06c684e..345726634299b 100644 --- a/src/tools/miri/src/concurrency/vector_clock.rs +++ b/src/tools/miri/src/concurrency/vector_clock.rs @@ -151,7 +151,7 @@ impl VClock { /// Load the internal timestamp slice in the vector clock #[inline] pub(super) fn as_slice(&self) -> &[VTimestamp] { - debug_assert!(!self.0.last().is_some_and(|t| t.time() == 0)); + debug_assert!(self.0.last().is_none_or(|t| t.time() != 0)); self.0.as_slice() } From 32099dbc1e112e34047a8ee458bf35aae76c7b46 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 4 Oct 2024 12:11:23 +0200 Subject: [PATCH 1208/1301] Enable `--no-sandbox` option by default for rustdoc GUI tests --- .../host-x86_64/x86_64-gnu-tools/Dockerfile | 2 +- src/tools/rustdoc-gui/tester.js | 26 +------------------ tests/rustdoc-gui/README.md | 7 ----- 3 files changed, 2 insertions(+), 33 deletions(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 6a09ab3065ffa..145f41f21e1f4 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -100,4 +100,4 @@ RUN /scripts/build-gccjit.sh /scripts # the local version of the package is different than the one used by the CI. ENV SCRIPT /tmp/checktools.sh ../x.py && \ npm install browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true && \ - python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--no-sandbox --jobs 1'" + python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--jobs 1'" diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js index 8f6626f62967a..c5874b4ee02de 100644 --- a/src/tools/rustdoc-gui/tester.js +++ b/src/tools/rustdoc-gui/tester.js @@ -19,7 +19,6 @@ function showHelp() { console.log(" --debug : show extra information about script run"); console.log(" --show-text : render font in pages"); console.log(" --no-headless : disable headless mode"); - console.log(" --no-sandbox : disable sandbox mode"); console.log(" --help : show this message then quit"); console.log(" --tests-folder [PATH] : location of the .GOML tests folder"); console.log(" --jobs [NUMBER] : number of threads to run tests on"); @@ -40,7 +39,6 @@ function parseOptions(args) { "no_headless": false, "jobs": -1, "executable_path": null, - "no_sandbox": false, }; const correspondences = { "--doc-folder": "doc_folder", @@ -49,7 +47,6 @@ function parseOptions(args) { "--show-text": "show_text", "--no-headless": "no_headless", "--executable-path": "executable_path", - "--no-sandbox": "no_sandbox", }; for (let i = 0; i < args.length; ++i) { @@ -80,9 +77,6 @@ function parseOptions(args) { } else if (arg === "--help") { showHelp(); process.exit(0); - } else if (arg === "--no-sandbox") { - console.log("`--no-sandbox` is being used. Be very careful!"); - opts[correspondences[arg]] = true; } else if (correspondences[arg]) { opts[correspondences[arg]] = true; } else { @@ -203,6 +197,7 @@ async function main(argv) { const args = [ "--variable", "DOC_PATH", opts["doc_folder"].split("\\").join("/"), "--enable-fail-on-js-error", "--allow-file-access-from-files", + "--no-sandbox", ]; if (opts["debug"]) { debug = true; @@ -211,9 +206,6 @@ async function main(argv) { if (opts["show_text"]) { args.push("--show-text"); } - if (opts["no_sandbox"]) { - args.push("--no-sandbox"); - } if (opts["no_headless"]) { args.push("--no-headless"); headless = false; @@ -262,19 +254,6 @@ async function main(argv) { console.log(`Running ${files.length} rustdoc-gui ...`); } - // We catch this "event" to display a nicer message in case of unexpected exit (because of a - // missing `--no-sandbox`). - const exitHandling = () => { - if (!opts["no_sandbox"]) { - console.log(""); - console.log( - "`browser-ui-test` crashed unexpectedly. Please try again with adding `--test-args \ ---no-sandbox` at the end. For example: `x.py test tests/rustdoc-gui --test-args --no-sandbox`"); - console.log(""); - } - }; - process.on("exit", exitHandling); - const originalFilesLen = files.length; const results = createEmptyResults(); const status_bar = char_printer(files.length); @@ -299,9 +278,6 @@ async function main(argv) { Array.prototype.push.apply(results.failed, new_results.failed); Array.prototype.push.apply(results.errored, new_results.errored); - // We don't need this listener anymore. - process.removeListener("exit", exitHandling); - if (debug) { results.successful.sort(by_filename); results.successful.forEach(r => { diff --git a/tests/rustdoc-gui/README.md b/tests/rustdoc-gui/README.md index 1126a72ab67ea..efd513715d495 100644 --- a/tests/rustdoc-gui/README.md +++ b/tests/rustdoc-gui/README.md @@ -23,12 +23,5 @@ $ ./x.py test tests/rustdoc-gui --stage 1 --test-args --no-headless To see the supported options, use `--help`. -Important to be noted: if the chromium instance crashes when you run it, you might need to -use `--no-sandbox` to make it work: - -```bash -$ ./x.py test tests/rustdoc-gui --stage 1 --test-args --no-sandbox -``` - [browser-ui-test]: https://github.com/GuillaumeGomez/browser-UI-test/ [puppeteer]: https://pptr.dev/ From 0369ee4c9b4cb51a774976ae54b052a71d370270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Fri, 4 Oct 2024 11:20:38 +0000 Subject: [PATCH 1209/1301] Week off of reviews to focus on docs Dedicating a week to work on rustc-dev-guide. --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index aba3669282806..8f3af1f25bd1b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -925,6 +925,7 @@ users_on_vacation = [ "jhpratt", "jyn514", "oli-obk", + "jieyouxu", ] [assign.adhoc_groups] From 018ba0528fa5d22712397e520351295f8582a525 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 3 Oct 2024 15:05:23 +0200 Subject: [PATCH 1210/1301] Use wide pointers consistenly across the compiler --- .../example/std_example.rs | 4 +-- compiler/rustc_codegen_cranelift/src/base.rs | 10 +++---- .../rustc_codegen_cranelift/src/common.rs | 2 +- .../src/debuginfo/types.rs | 2 +- .../rustc_codegen_gcc/src/intrinsic/simd.rs | 4 +-- compiler/rustc_codegen_gcc/src/type_of.rs | 2 +- compiler/rustc_codegen_llvm/src/abi.rs | 2 +- .../src/debuginfo/metadata.rs | 30 +++++++++---------- .../rustc_codegen_llvm/src/debuginfo/utils.rs | 18 +++++------ compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +-- compiler/rustc_codegen_llvm/src/type_of.rs | 2 +- compiler/rustc_codegen_ssa/messages.ftl | 2 +- compiler/rustc_codegen_ssa/src/errors.rs | 4 +-- compiler/rustc_codegen_ssa/src/mir/mod.rs | 6 ++-- compiler/rustc_codegen_ssa/src/mir/operand.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 14 ++++----- .../rustc_const_eval/src/interpret/cast.rs | 6 ++-- .../src/error_codes/E0607.md | 10 +++---- .../rustc_hir_analysis/src/check/check.rs | 2 +- .../src/coherence/builtin.rs | 2 +- compiler/rustc_hir_typeck/messages.ftl | 8 ++--- compiler/rustc_hir_typeck/src/cast.rs | 20 ++++++------- compiler/rustc_hir_typeck/src/errors.rs | 4 +-- compiler/rustc_hir_typeck/src/expectation.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 2 +- compiler/rustc_middle/src/ty/adjustment.rs | 6 ++-- compiler/rustc_middle/src/ty/layout.rs | 14 ++++----- compiler/rustc_middle/src/ty/sty.rs | 4 +-- compiler/rustc_mir_transform/src/gvn.rs | 6 ++-- compiler/rustc_monomorphize/src/collector.rs | 8 ++--- compiler/rustc_target/src/abi/call/mod.rs | 2 +- compiler/rustc_target/src/abi/mod.rs | 2 +- compiler/rustc_ty_utils/src/abi.rs | 14 ++++----- compiler/stable_mir/src/mir/body.rs | 6 ++-- tests/ui/cast/fat-ptr-cast.stderr | 2 +- ...e_elem_ty_mismatch_in_unsizing_cast.stderr | 2 +- tests/ui/error-codes/E0607.stderr | 2 +- tests/ui/error-festival.stderr | 2 +- tests/ui/issues/issue-31511.rs | 2 +- tests/ui/issues/issue-31511.stderr | 2 +- tests/ui/mismatched_types/cast-rfc0401.stderr | 2 +- 41 files changed, 120 insertions(+), 120 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index ebaa9c69c810a..3078288c28615 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -168,7 +168,7 @@ fn main() { foo(I64X2([0, 0])); - transmute_fat_pointer(); + transmute_wide_pointer(); rust_call_abi(); @@ -192,7 +192,7 @@ type TwoPtrs = i64; #[cfg(target_pointer_width = "64")] type TwoPtrs = i128; -fn transmute_fat_pointer() -> TwoPtrs { +fn transmute_wide_pointer() -> TwoPtrs { unsafe { transmute::<_, TwoPtrs>("true !") } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1ce0aacab4998..0968062206940 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -713,17 +713,17 @@ fn codegen_stmt<'tcx>( let from_ty = operand.layout().ty; let to_ty = fx.monomorphize(to_ty); - fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { + fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { ty.builtin_deref(true) .is_some_and(|pointee_ty| has_ptr_meta(fx.tcx, pointee_ty)) } - if is_fat_ptr(fx, from_ty) { - if is_fat_ptr(fx, to_ty) { - // fat-ptr -> fat-ptr + if is_wide_ptr(fx, from_ty) { + if is_wide_ptr(fx, to_ty) { + // wide-ptr -> wide-ptr lval.write_cvalue(fx, operand.cast_pointer_to(dest_layout)); } else { - // fat-ptr -> thin-ptr + // wide-ptr -> thin-ptr let (ptr, _extra) = operand.load_scalar_pair(fx); lval.write_cvalue(fx, CValue::by_val(ptr, dest_layout)) } diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index e78ba5a341531..69a32cc3d43c9 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -101,7 +101,7 @@ fn clif_pair_type_from_ty<'tcx>( }) } -/// Is a pointer to this type a fat ptr? +/// Is a pointer to this type a wide ptr? pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { if ty.is_sized(tcx, ParamEnv::reveal_all()) { return false; diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs index a710701e72c4b..714742aeaffe0 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -139,7 +139,7 @@ impl DebugContext { pointer_type_id } else { - // FIXME implement debuginfo for fat pointers + // FIXME implement debuginfo for wide pointers self.placeholder_for_type(tcx, type_dbg, ptr_type) } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index eeab2a5f15597..4e1b99fdebfdf 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -478,7 +478,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastFatPointer { + require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { span, name, ty: in_elem @@ -493,7 +493,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastFatPointer { + require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { span, name, ty: out_elem diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index cb45bbde2c27d..5b0d862ae6d4e 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -207,7 +207,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { // layout. if let Abi::Scalar(ref scalar) = self.abi { // Use a different cache for scalars because pointers to DSTs - // can be either fat or thin (data pointers of fat pointers). + // can be either wide or thin (data pointers of wide pointers). if let Some(&ty) = cx.scalar_types.borrow().get(&self.ty) { return ty; } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 3d75393bf06fd..6a29eb5fa04ab 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -7,7 +7,7 @@ use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; -pub(crate) use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; +pub(crate) use rustc_middle::ty::layout::{WIDE_PTR_ADDR, WIDE_PTR_EXTRA}; use rustc_middle::{bug, ty}; use rustc_session::config; pub(crate) use rustc_target::abi::call::*; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index b7a6f80956dd3..15d441a986d88 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -34,7 +34,7 @@ use super::utils::{ }; use crate::common::CodegenCx; use crate::debuginfo::metadata::type_map::build_type_with_children; -use crate::debuginfo::utils::{FatPtrKind, fat_pointer_kind}; +use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; use crate::llvm::debuginfo::{ DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, DebugNameTableKind, @@ -161,7 +161,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { // The debuginfo generated by this function is only valid if `ptr_type` is really just - // a (fat) pointer. Make sure it is not called for e.g. `Box`. + // a (wide) pointer. Make sure it is not called for e.g. `Box`. assert_eq!( cx.size_and_align_of(ptr_type), cx.size_and_align_of(Ty::new_mut_ptr(cx.tcx, pointee_type)) @@ -174,7 +174,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( let data_layout = &cx.tcx.data_layout; let ptr_type_debuginfo_name = compute_debuginfo_type_name(cx.tcx, ptr_type, true); - match fat_pointer_kind(cx, pointee_type) { + match wide_pointer_kind(cx, pointee_type) { None => { // This is a thin pointer. Create a regular pointer type and give it the correct name. assert_eq!( @@ -197,7 +197,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( DINodeCreationResult { di_node, already_stored_in_typemap: false } } - Some(fat_pointer_kind) => { + Some(wide_pointer_kind) => { type_map::build_type_with_children( cx, type_map::stub( @@ -210,7 +210,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, owner| { - // FIXME: If this fat pointer is a `Box` then we don't want to use its + // FIXME: If this wide pointer is a `Box` then we don't want to use its // type layout and instead use the layout of the raw pointer inside // of it. // The proper way to handle this is to not treat Box as a pointer @@ -227,16 +227,16 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( }; let layout = cx.layout_of(layout_type); - let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); - let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); + let addr_field = layout.field(cx, abi::WIDE_PTR_ADDR); + let extra_field = layout.field(cx, abi::WIDE_PTR_EXTRA); - let (addr_field_name, extra_field_name) = match fat_pointer_kind { - FatPtrKind::Dyn => ("pointer", "vtable"), - FatPtrKind::Slice => ("data_ptr", "length"), + let (addr_field_name, extra_field_name) = match wide_pointer_kind { + WidePtrKind::Dyn => ("pointer", "vtable"), + WidePtrKind::Slice => ("data_ptr", "length"), }; - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); + assert_eq!(abi::WIDE_PTR_ADDR, 0); + assert_eq!(abi::WIDE_PTR_EXTRA, 1); // The data pointer type is a regular, thin pointer, regardless of whether this // is a slice or a trait object. @@ -258,7 +258,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( owner, addr_field_name, (addr_field.size, addr_field.align.abi), - layout.fields.offset(abi::FAT_PTR_ADDR), + layout.fields.offset(abi::WIDE_PTR_ADDR), DIFlags::FlagZero, data_ptr_type_di_node, ), @@ -267,7 +267,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( owner, extra_field_name, (extra_field.size, extra_field.align.abi), - layout.fields.offset(abi::FAT_PTR_EXTRA), + layout.fields.offset(abi::WIDE_PTR_EXTRA), DIFlags::FlagZero, type_di_node(cx, extra_field.ty), ), @@ -391,7 +391,7 @@ fn build_dyn_type_di_node<'ll, 'tcx>( /// /// NOTE: We currently emit just emit the debuginfo for the element type here /// (i.e. `T` for slices and `u8` for `str`), so that we end up with -/// `*const T` for the `data_ptr` field of the corresponding fat-pointer +/// `*const T` for the `data_ptr` field of the corresponding wide-pointer /// debuginfo of `&[T]`. /// /// It would be preferable and more accurate if we emitted a DIArray of T diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index acb15449ce323..960487ada16ad 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -49,23 +49,23 @@ pub(crate) fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId } #[derive(Debug, PartialEq, Eq)] -pub(crate) enum FatPtrKind { +pub(crate) enum WidePtrKind { Slice, Dyn, } /// Determines if `pointee_ty` is slice-like or trait-object-like, i.e. -/// if the second field of the fat pointer is a length or a vtable-pointer. -/// If `pointee_ty` does not require a fat pointer (because it is Sized) then +/// if the second field of the wide pointer is a length or a vtable-pointer. +/// If `pointee_ty` does not require a wide pointer (because it is Sized) then /// the function returns `None`. -pub(crate) fn fat_pointer_kind<'ll, 'tcx>( +pub(crate) fn wide_pointer_kind<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, pointee_ty: Ty<'tcx>, -) -> Option { +) -> Option { let pointee_tail_ty = cx.tcx.struct_tail_for_codegen(pointee_ty, cx.param_env()); let layout = cx.layout_of(pointee_tail_ty); trace!( - "fat_pointer_kind: {:?} has layout {:?} (is_unsized? {})", + "wide_pointer_kind: {:?} has layout {:?} (is_unsized? {})", pointee_tail_ty, layout, layout.is_unsized() @@ -76,8 +76,8 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>( } match *pointee_tail_ty.kind() { - ty::Str | ty::Slice(_) => Some(FatPtrKind::Slice), - ty::Dynamic(..) => Some(FatPtrKind::Dyn), + ty::Str | ty::Slice(_) => Some(WidePtrKind::Slice), + ty::Dynamic(..) => Some(WidePtrKind::Dyn), ty::Foreign(_) => { // Assert that pointers to foreign types really are thin: assert_eq!( @@ -90,7 +90,7 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>( // For all other pointee types we should already have returned None // at the beginning of the function. panic!( - "fat_pointer_kind() - Encountered unexpected `pointee_tail_ty`: {pointee_tail_ty:?}" + "wide_pointer_kind() - Encountered unexpected `pointee_tail_ty`: {pointee_tail_ty:?}" ) } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c66c80da9fcca..30c6f08e894b7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2185,7 +2185,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastFatPointer { + require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { span, name, ty: in_elem @@ -2200,7 +2200,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastFatPointer { + require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { span, name, ty: out_elem diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 6e429a1674ade..7071dd86ee0d2 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -199,7 +199,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { // layout. if let Abi::Scalar(scalar) = self.abi { // Use a different cache for scalars because pointers to DSTs - // can be either fat or thin (data pointers of fat pointers). + // can be either wide or thin (data pointers of wide pointers). if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { return llty; } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 9091602d75b71..f02b0f7267430 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -82,7 +82,7 @@ codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphizati codegen_ssa_invalid_monomorphization_cannot_return = invalid monomorphization of `{$name}` intrinsic: cannot return `{$ret_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` -codegen_ssa_invalid_monomorphization_cast_fat_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast fat pointer `{$ty}` +codegen_ssa_invalid_monomorphization_cast_wide_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast wide pointer `{$ty}` codegen_ssa_invalid_monomorphization_expected_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of second argument `{$second_arg}` to be a pointer to the element type `{$in_elem}` of the first argument `{$in_ty}`, found `{$expected_element}` != `{$mutability} {$in_elem}` diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 08b326e3ac3c6..ab909abcead8e 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -916,8 +916,8 @@ pub enum InvalidMonomorphization<'tcx> { ret_ty: Ty<'tcx>, }, - #[diag(codegen_ssa_invalid_monomorphization_cast_fat_pointer, code = E0511)] - CastFatPointer { + #[diag(codegen_ssa_invalid_monomorphization_cast_wide_pointer, code = E0511)] + CastWidePointer { #[primary_span] span: Span, name: Symbol, diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index c4fb24aa625d9..8bd172a9ce6d3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -133,9 +133,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { enum LocalRef<'tcx, V> { Place(PlaceRef<'tcx, V>), /// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place). - /// `*p` is the fat pointer that references the actual unsized place. + /// `*p` is the wide pointer that references the actual unsized place. /// Every time it is initialized, we have to reallocate the place - /// and update the fat pointer. That's the reason why it is indirect. + /// and update the wide pointer. That's the reason why it is indirect. UnsizedPlace(PlaceRef<'tcx, V>), /// The backend [`OperandValue`] has already been generated. Operand(OperandRef<'tcx, V>), @@ -429,7 +429,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Unsized indirect qrguments PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { // As the storage for the indirect argument lives during - // the whole function call, we just copy the fat pointer. + // the whole function call, we just copy the wide pointer. let llarg = bx.get_param(llarg_idx); llarg_idx += 1; let llextra = bx.get_param(llarg_idx); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 17f66c12a0332..0bcd7d6d08150 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -41,7 +41,7 @@ pub enum OperandValue { /// The backend value in this variant must be the *immediate* backend type, /// as returned by [`LayoutTypeCodegenMethods::immediate_backend_type`]. Immediate(V), - /// A pair of immediate LLVM values. Used by fat pointers too. + /// A pair of immediate LLVM values. Used by wide pointers too. /// /// An `OperandValue` *must* be this variant for any type for which /// [`LayoutTypeCodegenMethods::is_backend_scalar_pair`] returns `true`. diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f9c0f3ce94146..a132ca6954050 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -38,10 +38,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ref source, _, ) => { - // The destination necessarily contains a fat pointer, so if - // it's a scalar pair, it's a fat pointer or newtype thereof. + // The destination necessarily contains a wide pointer, so if + // it's a scalar pair, it's a wide pointer or newtype thereof. if bx.cx().is_backend_scalar_pair(dest.layout) { - // Into-coerce of a thin pointer to a fat pointer -- just + // Into-coerce of a thin pointer to a wide pointer -- just // use the operand path. let temp = self.codegen_rvalue_operand(bx, rvalue); temp.val.store(bx, dest); @@ -519,7 +519,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if bx.cx().is_backend_scalar_pair(cast) { OperandValue::Pair(data_ptr, meta) } else { - // Cast of fat-ptr to thin-ptr is an extraction of data-ptr. + // Cast of wide-ptr to thin-ptr is an extraction of data-ptr. OperandValue::Immediate(data_ptr) } } else { @@ -622,7 +622,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ( OperandValue::Pair(lhs_addr, lhs_extra), OperandValue::Pair(rhs_addr, rhs_extra), - ) => self.codegen_fat_ptr_binop( + ) => self.codegen_wide_ptr_binop( bx, op, lhs_addr, @@ -984,7 +984,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn codegen_fat_ptr_binop( + fn codegen_wide_ptr_binop( &mut self, bx: &mut Bx, op: mir::BinOp, @@ -1021,7 +1021,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.or(lhs, rhs) } _ => { - bug!("unexpected fat ptr binop"); + bug!("unexpected wide ptr binop"); } } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 565a7d1624249..30b5a8d70bc33 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -204,12 +204,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { assert!(src.layout.ty.is_any_ptr()); assert!(cast_to.ty.is_unsafe_ptr()); - // Handle casting any ptr to raw ptr (might be a fat ptr). + // Handle casting any ptr to raw ptr (might be a wide ptr). if cast_to.size == src.layout.size { - // Thin or fat pointer that just has the ptr kind of target type changed. + // Thin or wide pointer that just has the ptr kind of target type changed. return interp_ok(ImmTy::from_immediate(**src, cast_to)); } else { - // Casting the metadata away from a fat ptr. + // Casting the metadata away from a wide ptr. assert_eq!(src.layout.size, 2 * self.pointer_size()); assert_eq!(cast_to.size, self.pointer_size()); assert!(src.layout.ty.is_unsafe_ptr()); diff --git a/compiler/rustc_error_codes/src/error_codes/E0607.md b/compiler/rustc_error_codes/src/error_codes/E0607.md index 0545246929f48..8ebc227114d95 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0607.md +++ b/compiler/rustc_error_codes/src/error_codes/E0607.md @@ -1,4 +1,4 @@ -A cast between a thin and a fat pointer was attempted. +A cast between a thin and a wide pointer was attempted. Erroneous code example: @@ -7,18 +7,18 @@ let v = core::ptr::null::(); v as *const [u8]; ``` -First: what are thin and fat pointers? +First: what are thin and wide pointers? Thin pointers are "simple" pointers: they are purely a reference to a memory address. -Fat pointers are pointers referencing Dynamically Sized Types (also called +Wide pointers are pointers referencing Dynamically Sized Types (also called DSTs). DSTs don't have a statically known size, therefore they can only exist behind some kind of pointer that contains additional information. For example, slices and trait objects are DSTs. In the case of slices, the additional -information the fat pointer holds is their size. +information the wide pointer holds is their size. -To fix this error, don't try to cast directly between thin and fat pointers. +To fix this error, don't try to cast directly between thin and wide pointers. For more information about type casts, take a look at the section of the [The Rust Reference][1] on type cast expressions. diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d725772a5b369..3692555a6c6be 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1105,7 +1105,7 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { // Check that we use types valid for use in the lanes of a SIMD "vector register" // These are scalar types which directly match a "machine" type // Yes: Integers, floats, "thin" pointers - // No: char, "fat" pointers, compound types + // No: char, "wide" pointers, compound types match element_ty.kind() { ty::Param(_) => (), // pass struct([T; 4]) through, let monomorphization catch errors ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct([u8; 4]) is ok diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index bea8d28a9f756..76c75d976ee0f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -424,7 +424,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( // Here `U = [i32; 3]` and `V = [i32]`. At runtime, // when this coercion occurs, we would be changing the // field `ptr` from a thin pointer of type `*mut [i32; - // 3]` to a fat pointer of type `*mut [i32]` (with + // 3]` to a wide pointer of type `*mut [i32]` (with // extra data `3`). **The purpose of this check is to // make sure that we know how to do this conversion.** // diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 39d430cf73b76..3669100ed9167 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -23,17 +23,17 @@ hir_typeck_cannot_cast_to_bool = cannot cast `{$expr_ty}` as `bool` hir_typeck_cast_enum_drop = cannot cast enum `{$expr_ty}` into integer `{$cast_ty}` because it implements `Drop` -hir_typeck_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_ty}` to fat pointer `{$cast_ty}` +hir_typeck_cast_thin_pointer_to_wide_pointer = cannot cast thin pointer `{$expr_ty}` to wide pointer `{$cast_ty}` .teach_help = Thin pointers are "simple" pointers: they are purely a reference to a memory address. - Fat pointers are pointers referencing "Dynamically Sized Types" (also + Wide pointers are pointers referencing "Dynamically Sized Types" (also called DST). DST don't have a statically known size, therefore they can only exist behind some kind of pointers that contain additional information. Slices and trait objects are DSTs. In the case of slices, - the additional information the fat pointer holds is their size. + the additional information the wide pointer holds is their size. - To fix this error, don't try to cast directly between thin and fat + To fix this error, don't try to cast directly between thin and wide pointers. For more information about casts, take a look at The Book: diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index fcd2940b83ae3..12fcbc7e50aed 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -66,7 +66,7 @@ pub(crate) struct CastCheck<'tcx> { } /// The kind of pointer and associated metadata (thin, length or vtable) - we -/// only allow casts between fat pointers if their metadata have the same +/// only allow casts between wide pointers if their metadata have the same /// kind. #[derive(Debug, Copy, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] enum PointerKind<'tcx> { @@ -162,7 +162,7 @@ enum CastError<'tcx> { src_kind: PointerKind<'tcx>, dst_kind: PointerKind<'tcx>, }, - /// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`). + /// Cast of thin to wide raw ptr (e.g., `*const () as *const [u8]`). SizedUnsizedCast, IllegalCast, NeedDeref, @@ -172,12 +172,12 @@ enum CastError<'tcx> { NonScalar, UnknownExprPtrKind, UnknownCastPtrKind, - /// Cast of int to (possibly) fat raw pointer. + /// Cast of int to (possibly) wide raw pointer. /// /// Argument is the specific name of the metadata in plain words, such as "a vtable" /// or "a length". If this argument is None, then the metadata is unknown, for example, /// when we're typechecking a type parameter with a ?Sized bound. - IntToFatCast(Option<&'static str>), + IntToWideCast(Option<&'static str>), ForeignNonExhaustiveAdt, } @@ -545,14 +545,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.emit(); } CastError::SizedUnsizedCast => { - fcx.dcx().emit_err(errors::CastThinPointerToFatPointer { + fcx.dcx().emit_err(errors::CastThinPointerToWidePointer { span: self.span, expr_ty: self.expr_ty, cast_ty: fcx.ty_to_string(self.cast_ty), teach: fcx.tcx.sess.teach(E0607), }); } - CastError::IntToFatCast(known_metadata) => { + CastError::IntToWideCast(known_metadata) => { let expr_if_nightly = fcx.tcx.sess.is_nightly_build().then_some(self.expr_span); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); let expr_ty = fcx.ty_to_string(self.expr_ty); @@ -861,7 +861,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { return Ok(CastKind::PtrPtrCast); } - // We can't cast to fat pointer if source pointer kind is unknown + // We can't cast to wide pointer if source pointer kind is unknown let Some(src_kind) = src_kind else { return Err(CastError::UnknownCastPtrKind); }; @@ -1054,10 +1054,10 @@ impl<'a, 'tcx> CastCheck<'tcx> { match fcx.pointer_kind(m_cast.ty, self.span)? { None => Err(CastError::UnknownCastPtrKind), Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), - Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))), - Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))), + Some(PointerKind::VTable(_)) => Err(CastError::IntToWideCast(Some("a vtable"))), + Some(PointerKind::Length) => Err(CastError::IntToWideCast(Some("a length"))), Some(PointerKind::OfAlias(_) | PointerKind::OfParam(_)) => { - Err(CastError::IntToFatCast(None)) + Err(CastError::IntToWideCast(None)) } } } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index a692642ccfcc2..cceaabaff65b6 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -699,8 +699,8 @@ pub(crate) struct ReplaceWithName { } #[derive(Diagnostic)] -#[diag(hir_typeck_cast_thin_pointer_to_fat_pointer, code = E0607)] -pub(crate) struct CastThinPointerToFatPointer<'tcx> { +#[diag(hir_typeck_cast_thin_pointer_to_wide_pointer, code = E0607)] +pub(crate) struct CastThinPointerToWidePointer<'tcx> { #[primary_span] pub span: Span, pub expr_ty: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index 67f4dbee3cb4a..4653458b5ddcc 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -55,7 +55,7 @@ impl<'a, 'tcx> Expectation<'tcx> { /// be checked higher up, as is the case with `&expr` and `box expr`), but /// is useful in determining the concrete type. /// - /// The primary use case is where the expected type is a fat pointer, + /// The primary use case is where the expected type is a wide pointer, /// like `&[isize]`. For example, consider the following statement: /// /// let x: &[isize] = &[1, 2, 3]; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index b34ed4640db12..e3c7dded0ca6e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -413,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. - // For example, dereferences of a fat pointer and + // For example, dereferences of a wide pointer and // the last field of a struct can be unsized. ExpectHasType(*ty) } else { diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 41a20e89cbf78..71833eea5c000 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -25,10 +25,10 @@ pub enum PointerCoercion { ArrayToPointer, /// Unsize a pointer/reference value, e.g., `&[T; n]` to - /// `&[T]`. Note that the source could be a thin or fat pointer. - /// This will do things like convert thin pointers to fat + /// `&[T]`. Note that the source could be a thin or wide pointer. + /// This will do things like convert thin pointers to wide /// pointers, or convert structs containing thin pointers to - /// structs containing fat pointers, or convert between fat + /// structs containing wide pointers, or convert between wide /// pointers. We don't store the details of how the transform is /// done (in fact, we don't know that, because it might depend on /// the precise type parameters). We just store the target diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index cf0c29e0c8c39..4ba2a9b1d73bc 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -164,17 +164,17 @@ impl Primitive { } } -/// The first half of a fat pointer. +/// The first half of a wide pointer. /// /// - For a trait object, this is the address of the box. /// - For a slice, this is the base address. -pub const FAT_PTR_ADDR: usize = 0; +pub const WIDE_PTR_ADDR: usize = 0; -/// The second half of a fat pointer. +/// The second half of a wide pointer. /// /// - For a trait object, this is the address of the vtable. /// - For a slice, this is the length. -pub const FAT_PTR_EXTRA: usize = 1; +pub const WIDE_PTR_EXTRA: usize = 1; /// The maximum supported number of lanes in a SIMD vector. /// @@ -312,7 +312,7 @@ pub enum SizeSkeleton<'tcx> { /// that another SizeSkeleton is of equal size. Generic(ty::Const<'tcx>), - /// A potentially-fat pointer. + /// A potentially-wide pointer. Pointer { /// If true, this pointer is never null. non_zero: bool, @@ -785,11 +785,11 @@ where bug!("TyAndLayout::field({:?}): not applicable", this) } - // Potentially-fat pointers. + // Potentially-wide pointers. ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { assert!(i < this.fields.count()); - // Reuse the fat `*T` type as its own thin pointer data field. + // Reuse the wide `*T` type as its own thin pointer data field. // This provides information about, e.g., DST struct pointees // (which may have no non-DST form), and will work as long // as the `Abi` or `FieldsShape` is checked by users. diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index db9978a7f5334..489bd49f3e4b6 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1589,7 +1589,7 @@ impl<'tcx> Ty<'tcx> { .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap()) } - /// Returns the type of metadata for (potentially fat) pointers to this type, + /// Returns the type of metadata for (potentially wide) pointers to this type, /// or the struct tail if the metadata type cannot be determined. pub fn ptr_metadata_ty_or_tail( self, @@ -1648,7 +1648,7 @@ impl<'tcx> Ty<'tcx> { } } - /// Returns the type of metadata for (potentially fat) pointers to this type. + /// Returns the type of metadata for (potentially wide) pointers to this type. /// Causes an ICE if the metadata type cannot be determined. pub fn ptr_metadata_ty( self, diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 50c9702cb9b02..0bb46675e9a2b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1137,7 +1137,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => { return Some(fields[1]); } - // We have an unsizing cast, which assigns the length to fat pointer metadata. + // We have an unsizing cast, which assigns the length to wide pointer metadata. ( UnOp::PtrMetadata, Value::Cast { @@ -1421,7 +1421,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let mut inner = self.simplify_place_value(place, location)?; - // The length information is stored in the fat pointer. + // The length information is stored in the wide pointer. // Reborrowing copies length information from one pointer to the other. while let Value::Address { place: borrowed, .. } = self.get(inner) && let [PlaceElem::Deref] = borrowed.projection[..] @@ -1430,7 +1430,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { inner = borrowed; } - // We have an unsizing cast, which assigns the length to fat pointer metadata. + // We have an unsizing cast, which assigns the length to wide pointer metadata. if let Value::Cast { kind, from, to, .. } = self.get(inner) && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind && let Some(from) = from.builtin_deref(true) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 05b3859e554ea..b4d084d4dffc4 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -119,7 +119,7 @@ //! //! #### Unsizing Casts //! A subtle way of introducing use edges is by casting to a trait object. -//! Since the resulting fat-pointer contains a reference to a vtable, we need to +//! Since the resulting wide-pointer contains a reference to a vtable, we need to //! instantiate all dyn-compatible methods of the trait, as we need to store //! pointers to these functions even if they never get called anywhere. This can //! be seen as a special case of taking a function reference. @@ -661,7 +661,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let span = self.body.source_info(location).span; match *rvalue { - // When doing an cast from a regular pointer to a fat pointer, we + // When doing an cast from a regular pointer to a wide pointer, we // have to instantiate all methods of the trait being cast to, so we // can build the appropriate vtable. mir::Rvalue::Cast( @@ -985,7 +985,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) - /// ``` /// /// Then the output of this function would be (SomeStruct, SomeTrait) since for -/// constructing the `target` fat-pointer we need the vtable for that pair. +/// constructing the `target` wide-pointer we need the vtable for that pair. /// /// Things can get more complicated though because there's also the case where /// the unsized type occurs as a field: @@ -999,7 +999,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) - /// ``` /// /// In this case, if `T` is sized, `&ComplexStruct` is a thin pointer. If `T` -/// is unsized, `&SomeStruct` is a fat pointer, and the vtable it points to is +/// is unsized, `&SomeStruct` is a wide pointer, and the vtable it points to is /// for the pair of `T` (which is a trait) and the concrete type that `T` was /// originally coerced from: /// diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index f4469467249f4..352861c5ccb49 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -637,7 +637,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } } - /// Pass this argument indirectly, by passing a (thin or fat) pointer to the argument instead. + /// Pass this argument indirectly, by passing a (thin or wide) pointer to the argument instead. /// This is valid for both sized and unsized arguments. pub fn make_indirect(&mut self) { match self.mode { diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 3eaff65261869..fc92e755feaea 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -135,7 +135,7 @@ impl<'a> Layout<'a> { /// Note that the layout is NOT guaranteed to always be identical /// to that obtained from `layout_of(ty)`, as we need to produce /// layouts for which Rust types do not exist, such as enum variants -/// or synthetic fields of enums (i.e., discriminants) and fat pointers. +/// or synthetic fields of enums (i.e., discriminants) and wide pointers. #[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)] pub struct TyAndLayout<'a, Ty> { pub ty: Ty, diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index f23c2cf2c07a7..3d6c09bf89c7e 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -357,7 +357,7 @@ fn fn_abi_of_instance<'tcx>( ) } -// Handle safe Rust thin and fat pointers. +// Handle safe Rust thin and wide pointers. fn adjust_for_rust_scalar<'tcx>( cx: LayoutCx<'tcx>, attrs: &mut ArgAttributes, @@ -810,7 +810,7 @@ fn make_thin_self_ptr<'tcx>( layout: TyAndLayout<'tcx>, ) -> TyAndLayout<'tcx> { let tcx = cx.tcx(); - let fat_pointer_ty = if layout.is_unsized() { + let wide_pointer_ty = if layout.is_unsized() { // unsized `self` is passed as a pointer to `self` // FIXME (mikeyhew) change this to use &own if it is ever added to the language Ty::new_mut_ptr(tcx, layout.ty) @@ -825,15 +825,15 @@ fn make_thin_self_ptr<'tcx>( // elsewhere in the compiler as a method on a `dyn Trait`. // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we // get a built-in pointer type - let mut fat_pointer_layout = layout; - while !fat_pointer_layout.ty.is_unsafe_ptr() && !fat_pointer_layout.ty.is_ref() { - fat_pointer_layout = fat_pointer_layout + let mut wide_pointer_layout = layout; + while !wide_pointer_layout.ty.is_unsafe_ptr() && !wide_pointer_layout.ty.is_ref() { + wide_pointer_layout = wide_pointer_layout .non_1zst_field(cx) .expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type") .1 } - fat_pointer_layout.ty + wide_pointer_layout.ty }; // we now have a type like `*mut RcBox` @@ -842,7 +842,7 @@ fn make_thin_self_ptr<'tcx>( let unit_ptr_ty = Ty::new_mut_ptr(tcx, tcx.types.unit); TyAndLayout { - ty: fat_pointer_ty, + ty: wide_pointer_ty, // NOTE(eddyb) using an empty `ParamEnv`, and `unwrap`-ing the `Result` // should always work because the type is always `*mut ()`. diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index ab9fc218d19fb..9f4db7e483335 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -946,10 +946,10 @@ pub enum PointerCoercion { ArrayToPointer, /// Unsize a pointer/reference value, e.g., `&[T; n]` to - /// `&[T]`. Note that the source could be a thin or fat pointer. - /// This will do things like convert thin pointers to fat + /// `&[T]`. Note that the source could be a thin or wide pointer. + /// This will do things like convert thin pointers to wide /// pointers, or convert structs containing thin pointers to - /// structs containing fat pointers, or convert between fat + /// structs containing wide pointers, or convert between wide /// pointers. Unsize, } diff --git a/tests/ui/cast/fat-ptr-cast.stderr b/tests/ui/cast/fat-ptr-cast.stderr index 18e7b68ff3c22..2b0bceebf15ca 100644 --- a/tests/ui/cast/fat-ptr-cast.stderr +++ b/tests/ui/cast/fat-ptr-cast.stderr @@ -44,7 +44,7 @@ LL | p as usize; | = help: cast through a thin pointer first -error[E0607]: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]` +error[E0607]: cannot cast thin pointer `*const i32` to wide pointer `*const [i32]` --> $DIR/fat-ptr-cast.rs:19:5 | LL | q as *const [i32]; diff --git a/tests/ui/consts/slice_elem_ty_mismatch_in_unsizing_cast.stderr b/tests/ui/consts/slice_elem_ty_mismatch_in_unsizing_cast.stderr index 3b861d784d8b8..19e7a723a22fa 100644 --- a/tests/ui/consts/slice_elem_ty_mismatch_in_unsizing_cast.stderr +++ b/tests/ui/consts/slice_elem_ty_mismatch_in_unsizing_cast.stderr @@ -1,4 +1,4 @@ -error[E0607]: cannot cast thin pointer `*const [i64; 0]` to fat pointer `*const [u8]` +error[E0607]: cannot cast thin pointer `*const [i64; 0]` to wide pointer `*const [u8]` --> $DIR/slice_elem_ty_mismatch_in_unsizing_cast.rs:1:31 | LL | const FOO: &str = unsafe { &*(1_usize as *const [i64; 0] as *const [u8] as *const str) }; diff --git a/tests/ui/error-codes/E0607.stderr b/tests/ui/error-codes/E0607.stderr index 835ababf44953..3fa134c20283a 100644 --- a/tests/ui/error-codes/E0607.stderr +++ b/tests/ui/error-codes/E0607.stderr @@ -1,4 +1,4 @@ -error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]` +error[E0607]: cannot cast thin pointer `*const u8` to wide pointer `*const [u8]` --> $DIR/E0607.rs:3:5 | LL | v as *const [u8]; diff --git a/tests/ui/error-festival.stderr b/tests/ui/error-festival.stderr index 9d75671c4e6b4..26393352b2b65 100644 --- a/tests/ui/error-festival.stderr +++ b/tests/ui/error-festival.stderr @@ -81,7 +81,7 @@ help: dereference the expression LL | let y: u32 = *x as u32; | + -error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]` +error[E0607]: cannot cast thin pointer `*const u8` to wide pointer `*const [u8]` --> $DIR/error-festival.rs:41:5 | LL | v as *const [u8]; diff --git a/tests/ui/issues/issue-31511.rs b/tests/ui/issues/issue-31511.rs index 53fecb0166351..6c8df1157c6c8 100644 --- a/tests/ui/issues/issue-31511.rs +++ b/tests/ui/issues/issue-31511.rs @@ -1,6 +1,6 @@ fn cast_thin_to_fat(x: *const ()) { x as *const [u8]; - //~^ ERROR: cannot cast thin pointer `*const ()` to fat pointer `*const [u8]` + //~^ ERROR: cannot cast thin pointer `*const ()` to wide pointer `*const [u8]` } fn main() {} diff --git a/tests/ui/issues/issue-31511.stderr b/tests/ui/issues/issue-31511.stderr index 177b78754cc3c..2048bd7ec1666 100644 --- a/tests/ui/issues/issue-31511.stderr +++ b/tests/ui/issues/issue-31511.stderr @@ -1,4 +1,4 @@ -error[E0607]: cannot cast thin pointer `*const ()` to fat pointer `*const [u8]` +error[E0607]: cannot cast thin pointer `*const ()` to wide pointer `*const [u8]` --> $DIR/issue-31511.rs:2:5 | LL | x as *const [u8]; diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr index 3d12ba9899bc5..2e5cbb900364a 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.stderr +++ b/tests/ui/mismatched_types/cast-rfc0401.stderr @@ -158,7 +158,7 @@ LL | let _ = 42usize as *const [u8]; | | | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` -error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]` +error[E0607]: cannot cast thin pointer `*const u8` to wide pointer `*const [u8]` --> $DIR/cast-rfc0401.rs:52:13 | LL | let _ = v as *const [u8]; From f59c8fffe3c821f93ced12e628404f9ba7eec3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 9 Sep 2024 10:24:35 +0200 Subject: [PATCH 1211/1301] Avoid dynamic linking to libstd in `command-current-dir` test --- src/bootstrap/src/core/build_steps/compile.rs | 4 ++-- tests/ui/command/command-current-dir.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index c274bcc2df73d..b5be7d841dd93 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1928,8 +1928,8 @@ impl Step for Assemble { let is_dylib_or_debug = is_dylib(&filename) || is_debug_info(&filename); // If we link statically to stdlib, do not copy the libstd dynamic library file - // Currently, we do not avoid the copy on Windows, as it seems to be causing issues in - // post-optimization stage0 tests. + // FIXME: Also do this for Windows once incremental post-optimization stage0 tests + // work without std.dll (see https://github.com/rust-lang/rust/pull/131188). let can_be_rustc_dynamic_dep = if builder .link_std_into_rustc_driver(target_compiler.host) && !target_compiler.host.is_windows() diff --git a/tests/ui/command/command-current-dir.rs b/tests/ui/command/command-current-dir.rs index 95c16bce6e8b8..23269e4123182 100644 --- a/tests/ui/command/command-current-dir.rs +++ b/tests/ui/command/command-current-dir.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ no-prefer-dynamic We move the binary around, so do not depend dynamically on libstd //@ ignore-wasm32 no processes //@ ignore-sgx no processes //@ ignore-fuchsia Needs directory creation privilege From ea3d336bbb6bb0808a01aeb361cc7a43b128b215 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 4 Oct 2024 15:46:19 +0200 Subject: [PATCH 1212/1301] Remove mw from triagebot.toml cc https://github.com/rust-lang/team/pull/1565 --- triagebot.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index aba3669282806..1695b8ddfe636 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -938,7 +938,6 @@ compiler-team = [ "@oli-obk", "@pnkfelix", "@wesleywiser", - "@michaelwoerister", ] compiler-team-contributors = [ "@TaKO8Ki", @@ -989,7 +988,6 @@ query-system = [ "@cjgillot", ] incremental = [ - "@michaelwoerister", "@wesleywiser", ] diagnostics = [ @@ -1048,7 +1046,6 @@ ast_lowering = [ "@spastorino", ] debuginfo = [ - "@michaelwoerister", "@davidtwco" ] fallback = [ From c6774f19e7093633749ad6fa2474c04e7e2fcdb8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 4 Oct 2024 23:33:30 +0900 Subject: [PATCH 1213/1301] Fix typo in csky-unknown-linux-gnuabiv2.md --- .../rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md index e72bfb8bae767..32e4f85531375 100644 --- a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md +++ b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md @@ -80,7 +80,7 @@ cd hello_world ```sh CARGO_TARGET_CSKY_UNKNOWN_LINUX_GNUABIV2_RUNNER=${QEMU_PATH}/bin/qemu-cskyv2 -L ${TOOLCHAIN_PATH}/csky-linux-gnuabiv2/libc \ CARGO_TARGET_CSKY_UNKNOWN_LINUX_GNUABIV2_LINKER=${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc \ -RUSTFLAGS="-C target-features=+crt-static" \ +RUSTFLAGS="-C target-feature=+crt-static" \ cargo +stage2 run --target csky-unknown-linux-gnuabiv2 ``` From d00b754721a1cfb4feaa1e06d65c836f856e3c4e Mon Sep 17 00:00:00 2001 From: Tobias Decking Date: Tue, 17 Sep 2024 15:16:55 +0200 Subject: [PATCH 1214/1301] Implement LLVM x86 gfni intrinsics --- src/tools/miri/src/shims/x86/gfni.rs | 196 +++++++ src/tools/miri/src/shims/x86/mod.rs | 8 + .../pass/shims/x86/intrinsics-x86-gfni.rs | 518 ++++++++++++++++++ 3 files changed, 722 insertions(+) create mode 100644 src/tools/miri/src/shims/x86/gfni.rs create mode 100644 src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs diff --git a/src/tools/miri/src/shims/x86/gfni.rs b/src/tools/miri/src/shims/x86/gfni.rs new file mode 100644 index 0000000000000..c91b8c835f2e9 --- /dev/null +++ b/src/tools/miri/src/shims/x86/gfni.rs @@ -0,0 +1,196 @@ +use rustc_span::Symbol; +use rustc_target::spec::abi::Abi; + +use crate::*; + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_x86_gfni_intrinsic( + &mut self, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + + // Prefix should have already been checked. + let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap(); + + this.expect_target_feature_for_intrinsic(link_name, "gfni")?; + if unprefixed_name.ends_with(".256") { + this.expect_target_feature_for_intrinsic(link_name, "avx")?; + } else if unprefixed_name.ends_with(".512") { + this.expect_target_feature_for_intrinsic(link_name, "avx512f")?; + } + + match unprefixed_name { + // Used to implement the `_mm{, 256, 512}_gf2p8affine_epi64_epi8` functions. + // See `affine_transform` for details. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_ + "vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => { + let [left, right, imm8] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + affine_transform(this, left, right, imm8, dest, /* inverse */ false)?; + } + // Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions. + // See `affine_transform` for details. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv + "vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => { + let [left, right, imm8] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + affine_transform(this, left, right, imm8, dest, /* inverse */ true)?; + } + // Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions. + // Multiplies packed 8-bit integers in `left` and `right` in the finite field GF(2^8) + // and store the results in `dst`. The field GF(2^8) is represented in + // polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul + "vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + + assert_eq!(left_len, right_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u8()?; + let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?; + let dest = this.project_index(&dest, i)?; + this.write_scalar(Scalar::from_u8(gf2p8_mul(left, right)), &dest)?; + } + } + _ => return interp_ok(EmulateItemResult::NotSupported), + } + interp_ok(EmulateItemResult::NeedsReturn) + } +} + +/// Calculates the affine transformation `right * left + imm8` inside the finite field GF(2^8). +/// `right` is an 8x8 bit matrix, `left` and `imm8` are bit vectors. +/// If `inverse` is set, then the inverse transformation with respect to the reduction polynomial +/// x^8 + x^4 + x^3 + x + 1 is performed instead. +fn affine_transform<'tcx>( + this: &mut MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + imm8: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + inverse: bool, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + + assert_eq!(dest_len, right_len); + assert_eq!(dest_len, left_len); + + let imm8 = this.read_scalar(imm8)?.to_u8()?; + + // Each 8x8 bit matrix gets multiplied with eight bit vectors. + // Therefore, the iteration is done in chunks of eight. + for i in (0..dest_len).step_by(8) { + // Get the bit matrix. + let mut matrix = [0u8; 8]; + for j in 0..8 { + matrix[usize::try_from(j).unwrap()] = + this.read_scalar(&this.project_index(&right, i.wrapping_add(j))?)?.to_u8()?; + } + + // Multiply the matrix with the vector and perform the addition. + for j in 0..8 { + let index = i.wrapping_add(j); + let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u8()?; + let left = if inverse { TABLE[usize::from(left)] } else { left }; + + let mut res = 0; + + // Do the matrix multiplication. + for bit in 0u8..8 { + let mut b = matrix[usize::from(bit)] & left; + + // Calculate the parity bit. + b = (b & 0b1111) ^ (b >> 4); + b = (b & 0b11) ^ (b >> 2); + b = (b & 0b1) ^ (b >> 1); + + res |= b << 7u8.wrapping_sub(bit); + } + + // Perform the addition. + res ^= imm8; + + let dest = this.project_index(&dest, index)?; + this.write_scalar(Scalar::from_u8(res), &dest)?; + } + } + + interp_ok(()) +} + +/// A lookup table for computing the inverse byte for the inverse affine transformation. +// This is a evaluated at compile time. Trait based conversion is not available. +/// See for the +/// definition of `gf_inv` which was used for the creation of this table. +#[allow(clippy::cast_possible_truncation)] +static TABLE: [u8; 256] = { + let mut array = [0; 256]; + + let mut i = 1; + while i < 256 { + let mut x = i as u8; + let mut y = gf2p8_mul(x, x); + x = y; + let mut j = 2; + while j < 8 { + x = gf2p8_mul(x, x); + y = gf2p8_mul(x, y); + j += 1; + } + array[i] = y; + i += 1; + } + + array +}; + +/// Multiplies packed 8-bit integers in `left` and `right` in the finite field GF(2^8) +/// and store the results in `dst`. The field GF(2^8) is represented in +/// polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1. +/// See for details. +// This is a const function. Trait based conversion is not available. +#[allow(clippy::cast_possible_truncation)] +const fn gf2p8_mul(left: u8, right: u8) -> u8 { + // This implementation is based on the `gf2p8mul_byte` definition found inside the Intel intrinsics guide. + // See https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul + // for more information. + + const POLYNOMIAL: u32 = 0x11b; + + let left = left as u32; + let right = right as u32; + + let mut result = 0u32; + + let mut i = 0u32; + while i < 8 { + if left & (1 << i) != 0 { + result ^= right << i; + } + i = i.wrapping_add(1); + } + + let mut i = 14u32; + while i >= 8 { + if result & (1 << i) != 0 { + result ^= POLYNOMIAL << i.wrapping_sub(8); + } + i = i.wrapping_sub(1); + } + + result as u8 +} diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 19c678cb7faca..9339d301aeecd 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -15,6 +15,7 @@ mod aesni; mod avx; mod avx2; mod bmi; +mod gfni; mod sha; mod sse; mod sse2; @@ -106,6 +107,13 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this, link_name, abi, args, dest, ); } + // The GFNI extension does not get its own namespace. + // Check for instruction names instead. + name if name.starts_with("vgf2p8affine") || name.starts_with("vgf2p8mulb") => { + return gfni::EvalContextExt::emulate_x86_gfni_intrinsic( + this, link_name, abi, args, dest, + ); + } name if name.starts_with("sha") => { return sha::EvalContextExt::emulate_x86_sha_intrinsic( this, link_name, abi, args, dest, diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs new file mode 100644 index 0000000000000..a629e2acfe998 --- /dev/null +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -0,0 +1,518 @@ +// We're testing x86 target specific features +//@only-target: x86_64 i686 +//@compile-flags: -C target-feature=+gfni,+avx512f + +// The constants in the tests below are just bit patterns. They should not +// be interpreted as integers; signedness does not make sense for them, but +// __mXXXi happens to be defined in terms of signed integers. +#![allow(overflowing_literals)] +#![feature(avx512_target_feature)] +#![feature(stdarch_x86_avx512)] + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; +use std::hint::black_box; +use std::mem::{size_of, transmute}; + +const IDENTITY_BYTE: i32 = 0; +const CONSTANT_BYTE: i32 = 0x63; + +fn main() { + // Mostly copied from library/stdarch/crates/core_arch/src/x86/gfni.rs + + assert!(is_x86_feature_detected!("avx512f")); + assert!(is_x86_feature_detected!("gfni")); + + unsafe { + let byte_mul_test_data = generate_byte_mul_test_data(); + let affine_mul_test_data_identity = generate_affine_mul_test_data(IDENTITY_BYTE as u8); + let affine_mul_test_data_constant = generate_affine_mul_test_data(CONSTANT_BYTE as u8); + let inv_tests_data = generate_inv_tests_data(); + + test_mm512_gf2p8mul_epi8(&byte_mul_test_data); + test_mm256_gf2p8mul_epi8(&byte_mul_test_data); + test_mm_gf2p8mul_epi8(&byte_mul_test_data); + test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); + test_mm256_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); + test_mm_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); + test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + test_mm256_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + test_mm_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + } +} + +#[target_feature(enable = "gfni,avx512f")] +unsafe fn test_mm512_gf2p8mul_epi8( + byte_mul_test_data: &([u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]), +) { + let (left, right, expected) = byte_mul_test_data; + + for i in 0..NUM_TEST_WORDS_512 { + let left = load_m512i_word(left, i); + let right = load_m512i_word(right, i); + let expected = load_m512i_word(expected, i); + let result = _mm512_gf2p8mul_epi8(left, right); + assert_eq_m512i(result, expected); + } +} + +#[target_feature(enable = "gfni,avx")] +unsafe fn test_mm256_gf2p8mul_epi8( + byte_mul_test_data: &([u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]), +) { + let (left, right, expected) = byte_mul_test_data; + + for i in 0..NUM_TEST_WORDS_256 { + let left = load_m256i_word(left, i); + let right = load_m256i_word(right, i); + let expected = load_m256i_word(expected, i); + let result = _mm256_gf2p8mul_epi8(left, right); + assert_eq_m256i(result, expected); + } +} + +#[target_feature(enable = "gfni")] +unsafe fn test_mm_gf2p8mul_epi8( + byte_mul_test_data: &([u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]), +) { + let (left, right, expected) = byte_mul_test_data; + + for i in 0..NUM_TEST_WORDS_128 { + let left = load_m128i_word(left, i); + let right = load_m128i_word(right, i); + let expected = load_m128i_word(expected, i); + let result = _mm_gf2p8mul_epi8(left, right); + assert_eq_m128i(result, expected); + } +} + +#[target_feature(enable = "gfni,avx512f")] +unsafe fn test_mm512_gf2p8affine_epi64_epi8( + byte_mul_test_data: &([u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]), + affine_mul_test_data_identity: &( + [u64; NUM_TEST_WORDS_64], + [u8; NUM_TEST_ENTRIES], + [u8; NUM_TEST_ENTRIES], + ), +) { + let identity: i64 = 0x01_02_04_08_10_20_40_80; + let constant: i64 = 0; + let identity = _mm512_set1_epi64(identity); + let constant = _mm512_set1_epi64(constant); + let constant_reference = _mm512_set1_epi8(CONSTANT_BYTE as i8); + + let (bytes, more_bytes, _) = byte_mul_test_data; + let (matrices, vectors, references) = affine_mul_test_data_identity; + + for i in 0..NUM_TEST_WORDS_512 { + let data = load_m512i_word(bytes, i); + let result = _mm512_gf2p8affine_epi64_epi8::(data, identity); + assert_eq_m512i(result, data); + let result = _mm512_gf2p8affine_epi64_epi8::(data, constant); + assert_eq_m512i(result, constant_reference); + let data = load_m512i_word(more_bytes, i); + let result = _mm512_gf2p8affine_epi64_epi8::(data, identity); + assert_eq_m512i(result, data); + let result = _mm512_gf2p8affine_epi64_epi8::(data, constant); + assert_eq_m512i(result, constant_reference); + + let matrix = load_m512i_word(matrices, i); + let vector = load_m512i_word(vectors, i); + let reference = load_m512i_word(references, i); + + let result = _mm512_gf2p8affine_epi64_epi8::(vector, matrix); + assert_eq_m512i(result, reference); + } +} + +#[target_feature(enable = "gfni,avx")] +unsafe fn test_mm256_gf2p8affine_epi64_epi8( + byte_mul_test_data: &([u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]), + affine_mul_test_data_identity: &( + [u64; NUM_TEST_WORDS_64], + [u8; NUM_TEST_ENTRIES], + [u8; NUM_TEST_ENTRIES], + ), +) { + let identity: i64 = 0x01_02_04_08_10_20_40_80; + let constant: i64 = 0; + let identity = _mm256_set1_epi64x(identity); + let constant = _mm256_set1_epi64x(constant); + let constant_reference = _mm256_set1_epi8(CONSTANT_BYTE as i8); + + let (bytes, more_bytes, _) = byte_mul_test_data; + let (matrices, vectors, references) = affine_mul_test_data_identity; + + for i in 0..NUM_TEST_WORDS_256 { + let data = load_m256i_word(bytes, i); + let result = _mm256_gf2p8affine_epi64_epi8::(data, identity); + assert_eq_m256i(result, data); + let result = _mm256_gf2p8affine_epi64_epi8::(data, constant); + assert_eq_m256i(result, constant_reference); + let data = load_m256i_word(more_bytes, i); + let result = _mm256_gf2p8affine_epi64_epi8::(data, identity); + assert_eq_m256i(result, data); + let result = _mm256_gf2p8affine_epi64_epi8::(data, constant); + assert_eq_m256i(result, constant_reference); + + let matrix = load_m256i_word(matrices, i); + let vector = load_m256i_word(vectors, i); + let reference = load_m256i_word(references, i); + + let result = _mm256_gf2p8affine_epi64_epi8::(vector, matrix); + assert_eq_m256i(result, reference); + } +} + +#[target_feature(enable = "gfni")] +unsafe fn test_mm_gf2p8affine_epi64_epi8( + byte_mul_test_data: &([u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]), + affine_mul_test_data_identity: &( + [u64; NUM_TEST_WORDS_64], + [u8; NUM_TEST_ENTRIES], + [u8; NUM_TEST_ENTRIES], + ), +) { + let identity: i64 = 0x01_02_04_08_10_20_40_80; + let constant: i64 = 0; + let identity = _mm_set1_epi64x(identity); + let constant = _mm_set1_epi64x(constant); + let constant_reference = _mm_set1_epi8(CONSTANT_BYTE as i8); + + let (bytes, more_bytes, _) = byte_mul_test_data; + let (matrices, vectors, references) = affine_mul_test_data_identity; + + for i in 0..NUM_TEST_WORDS_128 { + let data = load_m128i_word(bytes, i); + let result = _mm_gf2p8affine_epi64_epi8::(data, identity); + assert_eq_m128i(result, data); + let result = _mm_gf2p8affine_epi64_epi8::(data, constant); + assert_eq_m128i(result, constant_reference); + let data = load_m128i_word(more_bytes, i); + let result = _mm_gf2p8affine_epi64_epi8::(data, identity); + assert_eq_m128i(result, data); + let result = _mm_gf2p8affine_epi64_epi8::(data, constant); + assert_eq_m128i(result, constant_reference); + + let matrix = load_m128i_word(matrices, i); + let vector = load_m128i_word(vectors, i); + let reference = load_m128i_word(references, i); + + let result = _mm_gf2p8affine_epi64_epi8::(vector, matrix); + assert_eq_m128i(result, reference); + } +} + +#[target_feature(enable = "gfni,avx512f")] +unsafe fn test_mm512_gf2p8affineinv_epi64_epi8( + inv_tests_data: &([u8; NUM_BYTES], [u8; NUM_BYTES]), + affine_mul_test_data_constant: &( + [u64; NUM_TEST_WORDS_64], + [u8; NUM_TEST_ENTRIES], + [u8; NUM_TEST_ENTRIES], + ), +) { + let identity: i64 = 0x01_02_04_08_10_20_40_80; + let identity = _mm512_set1_epi64(identity); + + // validate inversion + let (inputs, results) = inv_tests_data; + + for i in 0..NUM_BYTES_WORDS_512 { + let input = load_m512i_word(inputs, i); + let reference = load_m512i_word(results, i); + let result = _mm512_gf2p8affineinv_epi64_epi8::(input, identity); + let remultiplied = _mm512_gf2p8mul_epi8(result, input); + assert_eq_m512i(remultiplied, reference); + } + + // validate subsequent affine operation + let (matrices, vectors, _affine_expected) = affine_mul_test_data_constant; + + for i in 0..NUM_TEST_WORDS_512 { + let vector = load_m512i_word(vectors, i); + let matrix = load_m512i_word(matrices, i); + + let inv_vec = _mm512_gf2p8affineinv_epi64_epi8::(vector, identity); + let reference = _mm512_gf2p8affine_epi64_epi8::(inv_vec, matrix); + let result = _mm512_gf2p8affineinv_epi64_epi8::(vector, matrix); + assert_eq_m512i(result, reference); + } + + // validate everything by virtue of checking against the AES SBox + const AES_S_BOX_MATRIX: i64 = 0xF1_E3_C7_8F_1F_3E_7C_F8; + let sbox_matrix = _mm512_set1_epi64(AES_S_BOX_MATRIX); + + for i in 0..NUM_BYTES_WORDS_512 { + let reference = load_m512i_word(&AES_S_BOX, i); + let input = load_m512i_word(inputs, i); + let result = _mm512_gf2p8affineinv_epi64_epi8::(input, sbox_matrix); + assert_eq_m512i(result, reference); + } +} + +#[target_feature(enable = "gfni,avx")] +unsafe fn test_mm256_gf2p8affineinv_epi64_epi8( + inv_tests_data: &([u8; NUM_BYTES], [u8; NUM_BYTES]), + affine_mul_test_data_constant: &( + [u64; NUM_TEST_WORDS_64], + [u8; NUM_TEST_ENTRIES], + [u8; NUM_TEST_ENTRIES], + ), +) { + let identity: i64 = 0x01_02_04_08_10_20_40_80; + let identity = _mm256_set1_epi64x(identity); + + // validate inversion + let (inputs, results) = inv_tests_data; + + for i in 0..NUM_BYTES_WORDS_256 { + let input = load_m256i_word(inputs, i); + let reference = load_m256i_word(results, i); + let result = _mm256_gf2p8affineinv_epi64_epi8::(input, identity); + let remultiplied = _mm256_gf2p8mul_epi8(result, input); + assert_eq_m256i(remultiplied, reference); + } + + // validate subsequent affine operation + let (matrices, vectors, _affine_expected) = affine_mul_test_data_constant; + + for i in 0..NUM_TEST_WORDS_256 { + let vector = load_m256i_word(vectors, i); + let matrix = load_m256i_word(matrices, i); + + let inv_vec = _mm256_gf2p8affineinv_epi64_epi8::(vector, identity); + let reference = _mm256_gf2p8affine_epi64_epi8::(inv_vec, matrix); + let result = _mm256_gf2p8affineinv_epi64_epi8::(vector, matrix); + assert_eq_m256i(result, reference); + } + + // validate everything by virtue of checking against the AES SBox + const AES_S_BOX_MATRIX: i64 = 0xF1_E3_C7_8F_1F_3E_7C_F8; + let sbox_matrix = _mm256_set1_epi64x(AES_S_BOX_MATRIX); + + for i in 0..NUM_BYTES_WORDS_256 { + let reference = load_m256i_word(&AES_S_BOX, i); + let input = load_m256i_word(inputs, i); + let result = _mm256_gf2p8affineinv_epi64_epi8::(input, sbox_matrix); + assert_eq_m256i(result, reference); + } +} + +#[target_feature(enable = "gfni")] +unsafe fn test_mm_gf2p8affineinv_epi64_epi8( + inv_tests_data: &([u8; NUM_BYTES], [u8; NUM_BYTES]), + affine_mul_test_data_constant: &( + [u64; NUM_TEST_WORDS_64], + [u8; NUM_TEST_ENTRIES], + [u8; NUM_TEST_ENTRIES], + ), +) { + let identity: i64 = 0x01_02_04_08_10_20_40_80; + let identity = _mm_set1_epi64x(identity); + + // validate inversion + let (inputs, results) = inv_tests_data; + + for i in 0..NUM_BYTES_WORDS_128 { + let input = load_m128i_word(inputs, i); + let reference = load_m128i_word(results, i); + let result = _mm_gf2p8affineinv_epi64_epi8::(input, identity); + let remultiplied = _mm_gf2p8mul_epi8(result, input); + assert_eq_m128i(remultiplied, reference); + } + + // validate subsequent affine operation + let (matrices, vectors, _affine_expected) = affine_mul_test_data_constant; + + for i in 0..NUM_TEST_WORDS_128 { + let vector = load_m128i_word(vectors, i); + let matrix = load_m128i_word(matrices, i); + + let inv_vec = _mm_gf2p8affineinv_epi64_epi8::(vector, identity); + let reference = _mm_gf2p8affine_epi64_epi8::(inv_vec, matrix); + let result = _mm_gf2p8affineinv_epi64_epi8::(vector, matrix); + assert_eq_m128i(result, reference); + } + + // validate everything by virtue of checking against the AES SBox + const AES_S_BOX_MATRIX: i64 = 0xF1_E3_C7_8F_1F_3E_7C_F8; + let sbox_matrix = _mm_set1_epi64x(AES_S_BOX_MATRIX); + + for i in 0..NUM_BYTES_WORDS_128 { + let reference = load_m128i_word(&AES_S_BOX, i); + let input = load_m128i_word(inputs, i); + let result = _mm_gf2p8affineinv_epi64_epi8::(input, sbox_matrix); + assert_eq_m128i(result, reference); + } +} + +/* Various utilities for processing SIMD values. */ + +#[target_feature(enable = "sse2")] +unsafe fn load_m128i_word(data: &[T], word_index: usize) -> __m128i { + let byte_offset = word_index * 16 / size_of::(); + let pointer = data.as_ptr().add(byte_offset) as *const __m128i; + _mm_loadu_si128(black_box(pointer)) +} + +#[target_feature(enable = "avx")] +unsafe fn load_m256i_word(data: &[T], word_index: usize) -> __m256i { + let byte_offset = word_index * 32 / size_of::(); + let pointer = data.as_ptr().add(byte_offset) as *const __m256i; + _mm256_loadu_si256(black_box(pointer)) +} + +#[target_feature(enable = "avx512f")] +unsafe fn load_m512i_word(data: &[T], word_index: usize) -> __m512i { + let byte_offset = word_index * 64 / size_of::(); + let pointer = data.as_ptr().add(byte_offset) as *const i32; + _mm512_loadu_si512(black_box(pointer)) +} + +#[track_caller] +#[target_feature(enable = "sse2")] +unsafe fn assert_eq_m128i(a: __m128i, b: __m128i) { + assert_eq!(transmute::<_, [u64; 2]>(a), transmute::<_, [u64; 2]>(b)) +} + +#[track_caller] +#[target_feature(enable = "avx")] +unsafe fn assert_eq_m256i(a: __m256i, b: __m256i) { + assert_eq!(transmute::<_, [u64; 4]>(a), transmute::<_, [u64; 4]>(b)) +} + +#[track_caller] +#[target_feature(enable = "avx512f")] +unsafe fn assert_eq_m512i(a: __m512i, b: __m512i) { + assert_eq!(transmute::<_, [u64; 8]>(a), transmute::<_, [u64; 8]>(b)) +} + +/* Software implementation of the hardware intrinsics. */ + +fn mulbyte(left: u8, right: u8) -> u8 { + // this implementation follows the description in + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_gf2p8mul_epi8 + const REDUCTION_POLYNOMIAL: u16 = 0x11b; + let left: u16 = left.into(); + let right: u16 = right.into(); + let mut carryless_product: u16 = 0; + + // Carryless multiplication + for i in 0..8 { + if ((left >> i) & 0x01) != 0 { + carryless_product ^= right << i; + } + } + + // reduction, adding in "0" where appropriate to clear out high bits + // note that REDUCTION_POLYNOMIAL is zero in this context + for i in (8..=14).rev() { + if ((carryless_product >> i) & 0x01) != 0 { + carryless_product ^= REDUCTION_POLYNOMIAL << (i - 8); + } + } + + carryless_product as u8 +} + +/// Calculates the bitwise XOR of all bits inside a byte. +fn parity(input: u8) -> u8 { + let mut accumulator = 0; + for i in 0..8 { + accumulator ^= (input >> i) & 0x01; + } + accumulator +} + +/// Calculates `matrix * x + b` inside the finite field GF(2). +fn mat_vec_multiply_affine(matrix: u64, x: u8, b: u8) -> u8 { + // this implementation follows the description in + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_gf2p8affine_epi64_epi8 + let mut accumulator = 0; + + for bit in 0..8 { + accumulator |= parity(x & matrix.to_le_bytes()[bit]) << (7 - bit); + } + + accumulator ^ b +} + +/* Test data generation. */ + +const NUM_TEST_WORDS_512: usize = 4; +const NUM_TEST_WORDS_256: usize = NUM_TEST_WORDS_512 * 2; +const NUM_TEST_WORDS_128: usize = NUM_TEST_WORDS_256 * 2; +const NUM_TEST_ENTRIES: usize = NUM_TEST_WORDS_512 * 64; +const NUM_TEST_WORDS_64: usize = NUM_TEST_WORDS_128 * 2; +const NUM_BYTES: usize = 256; +const NUM_BYTES_WORDS_128: usize = NUM_BYTES / 16; +const NUM_BYTES_WORDS_256: usize = NUM_BYTES_WORDS_128 / 2; +const NUM_BYTES_WORDS_512: usize = NUM_BYTES_WORDS_256 / 2; + +fn generate_affine_mul_test_data( + immediate: u8, +) -> ([u64; NUM_TEST_WORDS_64], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]) { + let mut left: [u64; NUM_TEST_WORDS_64] = [0; NUM_TEST_WORDS_64]; + let mut right: [u8; NUM_TEST_ENTRIES] = [0; NUM_TEST_ENTRIES]; + let mut result: [u8; NUM_TEST_ENTRIES] = [0; NUM_TEST_ENTRIES]; + + for i in 0..NUM_TEST_WORDS_64 { + left[i] = (i as u64) * 103 * 101; + for j in 0..8 { + let j64 = j as u64; + right[i * 8 + j] = ((left[i] + j64) % 256) as u8; + result[i * 8 + j] = mat_vec_multiply_affine(left[i], right[i * 8 + j], immediate); + } + } + + (left, right, result) +} + +fn generate_inv_tests_data() -> ([u8; NUM_BYTES], [u8; NUM_BYTES]) { + let mut input: [u8; NUM_BYTES] = [0; NUM_BYTES]; + let mut result: [u8; NUM_BYTES] = [0; NUM_BYTES]; + + for i in 0..NUM_BYTES { + input[i] = (i % 256) as u8; + result[i] = if i == 0 { 0 } else { 1 }; + } + + (input, result) +} + +const AES_S_BOX: [u8; NUM_BYTES] = [ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +]; + +fn generate_byte_mul_test_data() +-> ([u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES], [u8; NUM_TEST_ENTRIES]) { + let mut left: [u8; NUM_TEST_ENTRIES] = [0; NUM_TEST_ENTRIES]; + let mut right: [u8; NUM_TEST_ENTRIES] = [0; NUM_TEST_ENTRIES]; + let mut result: [u8; NUM_TEST_ENTRIES] = [0; NUM_TEST_ENTRIES]; + + for i in 0..NUM_TEST_ENTRIES { + left[i] = (i % 256) as u8; + right[i] = left[i].wrapping_mul(101); + result[i] = mulbyte(left[i], right[i]); + } + + (left, right, result) +} From cc36e0d9832349a8834b4800f6b6ca27ab9cba56 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 4 Oct 2024 17:53:44 +0200 Subject: [PATCH 1215/1301] Fix list margins --- src/librustdoc/html/static/css/rustdoc.css | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index beac7e73c62d2..5f6f3c65c7ff7 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -961,10 +961,13 @@ pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers { } .docblock li { - margin-bottom: .8em; + margin-bottom: .4em; } -.docblock li p { - margin-bottom: .1em; +.docblock li p:not(:last-child) { + /* This margin is voluntarily smaller than `.docblock li` to keep the visual + list element items separated while also having a visual separation (although + smaller) for paragraphs. */ + margin-bottom: .3em; } /* "where ..." clauses with block display are also smaller */ From 1c63ec9fa002980fb792d8630661e931ebf9ac06 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 3 Oct 2024 18:09:00 +0200 Subject: [PATCH 1216/1301] Add GUI regression test for #130622 and for #131223 --- tests/rustdoc-gui/list-margins.goml | 11 +++++++++++ tests/rustdoc-gui/src/test_docs/lib.rs | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/rustdoc-gui/list-margins.goml diff --git a/tests/rustdoc-gui/list-margins.goml b/tests/rustdoc-gui/list-margins.goml new file mode 100644 index 0000000000000..c83f5898e8e46 --- /dev/null +++ b/tests/rustdoc-gui/list-margins.goml @@ -0,0 +1,11 @@ +// This test ensures that the documentation list markers are correctly placed. +// It also serves as a regression test for . + +go-to: "file://" + |DOC_PATH| + "/test_docs/long_list/index.html" +show-text: true + +// 0.3em +assert-css: (".docblock li p:not(last-child)", {"margin-bottom": "4.8px"}) +assert-css: (".docblock li p + p:last-child", {"margin-bottom": "0px"}) +// 0.4em +assert-css: (".docblock li", {"margin-bottom": "6.4px"}) diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 7397992c0ab0f..352995c49030b 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -628,3 +628,27 @@ pub mod short_docs { /// subt_vec_num(x: &[f64], y: f64) pub fn subt_vec_num() {} } + +pub mod long_list { + //! bla + //! + //! * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque et libero ut leo + //! interdum laoreet vitae a mi. Aliquam erat volutpat. Suspendisse volutpat non quam non + //! commodo. + //! + //! Praesent enim neque, imperdiet sed nisl at, lobortis egestas augue. Sed vitae tristique + //! augue. Phasellus vel pretium lectus. + //! * Praesent enim neque, imperdiet sed nisl at, lobortis egestas augue. Sed vitae tristique + //! augue. Phasellus vel pretium lectus. + //! * Praesent enim neque, imperdiet sed nisl at, lobortis egestas augue. Sed vitae tristique + //! augue. Phasellus vel pretium lectus. + //! + //! Another list: + //! + //! * [`TryFromBytes`](#a) indicates that a type may safely be converted from certain byte + //! sequence (conditional on runtime checks) + //! * [`FromZeros`](#a) indicates that a sequence of zero bytes represents a valid instance of + //! a type + //! * [`FromBytes`](#a) indicates that a type may safely be converted from an arbitrary byte + //! sequence +} From 3686e59913967072e47d64a6a466a6f5d9386c09 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 4 Oct 2024 09:45:34 -0700 Subject: [PATCH 1217/1301] rustdoc: cleaner errors on disambiguator/namespace mismatches --- .../passes/collect_intra_doc_links.rs | 21 ++++++++++++++----- .../intra-doc/disambiguator-mismatch.stderr | 6 +++--- tests/rustdoc-ui/intra-doc/errors.rs | 2 +- tests/rustdoc-ui/intra-doc/errors.stderr | 4 ++-- .../issue-110495-suffix-with-space.stderr | 4 ++-- .../rustdoc-ui/intra-doc/weird-syntax.stderr | 20 +++++++++--------- 6 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 0dba16cbaf32f..def9ac3ce4b5a 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1996,11 +1996,22 @@ fn resolution_failure( &diag_info, ); - format!( - "this link resolves to {}, which is not in the {} namespace", - item(res), - expected_ns.descr() - ) + if let Some(disambiguator) = disambiguator + && !matches!(disambiguator, Disambiguator::Namespace(..)) + { + format!( + "this link resolves to {}, which is not {} {}", + item(res), + disambiguator.article(), + disambiguator.descr() + ) + } else { + format!( + "this link resolves to {}, which is not in the {} namespace", + item(res), + expected_ns.descr() + ) + } } }; if let Some(span) = sp { diff --git a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr index 66b910eed8135..ef7fec77b1e0d 100644 --- a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr +++ b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr @@ -74,7 +74,7 @@ error: unresolved link to `m` --> $DIR/disambiguator-mismatch.rs:52:14 | LL | /// Link to [m()] - | ^^^ this link resolves to the macro `m`, which is not in the value namespace + | ^^^ this link resolves to the macro `m`, which is not a function | help: to link to the macro, add an exclamation mark | @@ -142,7 +142,7 @@ error: unresolved link to `std` --> $DIR/disambiguator-mismatch.rs:83:14 | LL | /// Link to [fn@std] - | ^^^^^^ this link resolves to the crate `std`, which is not in the value namespace + | ^^^^^^ this link resolves to the crate `std`, which is not a function | help: to link to the crate, prefix with `mod@` | @@ -164,7 +164,7 @@ error: unresolved link to `S::A` --> $DIR/disambiguator-mismatch.rs:93:14 | LL | /// Link to [field@S::A] - | ^^^^^^^^^^ this link resolves to the variant `A`, which is not in the value namespace + | ^^^^^^^^^^ this link resolves to the variant `A`, which is not a field | help: to link to the variant, prefix with `variant@` | diff --git a/tests/rustdoc-ui/intra-doc/errors.rs b/tests/rustdoc-ui/intra-doc/errors.rs index f37f49c24ccc5..e885a3b35f6c7 100644 --- a/tests/rustdoc-ui/intra-doc/errors.rs +++ b/tests/rustdoc-ui/intra-doc/errors.rs @@ -98,7 +98,7 @@ pub trait T { /// [m()] //~^ ERROR unresolved link //~| HELP to link to the macro -//~| NOTE not in the value namespace +//~| NOTE not a function #[macro_export] macro_rules! m { () => {}; diff --git a/tests/rustdoc-ui/intra-doc/errors.stderr b/tests/rustdoc-ui/intra-doc/errors.stderr index a982bba009591..07d328f99a308 100644 --- a/tests/rustdoc-ui/intra-doc/errors.stderr +++ b/tests/rustdoc-ui/intra-doc/errors.stderr @@ -104,7 +104,7 @@ error: unresolved link to `S` --> $DIR/errors.rs:68:6 | LL | /// [S!] - | ^^ this link resolves to the struct `S`, which is not in the macro namespace + | ^^ this link resolves to the struct `S`, which is not a macro | help: to link to the struct, prefix with `struct@` | @@ -158,7 +158,7 @@ error: unresolved link to `m` --> $DIR/errors.rs:98:6 | LL | /// [m()] - | ^^^ this link resolves to the macro `m`, which is not in the value namespace + | ^^^ this link resolves to the macro `m`, which is not a function | help: to link to the macro, add an exclamation mark | diff --git a/tests/rustdoc-ui/intra-doc/issue-110495-suffix-with-space.stderr b/tests/rustdoc-ui/intra-doc/issue-110495-suffix-with-space.stderr index 6c834fd0a1b61..a347044bfe979 100644 --- a/tests/rustdoc-ui/intra-doc/issue-110495-suffix-with-space.stderr +++ b/tests/rustdoc-ui/intra-doc/issue-110495-suffix-with-space.stderr @@ -2,7 +2,7 @@ error: unresolved link to `Clone` --> $DIR/issue-110495-suffix-with-space.rs:3:6 | LL | //! [Clone ()]. - | ^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | note: the lint level is defined here --> $DIR/issue-110495-suffix-with-space.rs:2:9 @@ -31,7 +31,7 @@ error: unresolved link to `Clone` --> $DIR/issue-110495-suffix-with-space.rs:5:7 | LL | //! [`Clone ()`]. - | ^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr index f50feb57fccf2..17bcbc783fd9e 100644 --- a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr +++ b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr @@ -40,7 +40,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:27:9 | LL | /// [ `Clone ()` ] - | ^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -52,7 +52,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:30:7 | LL | /// [`Clone ()` ] - | ^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -64,7 +64,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:33:9 | LL | /// [ `Clone ()`] - | ^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -76,7 +76,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:36:9 | LL | /// [```Clone ()```] - | ^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -88,7 +88,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:42:13 | LL | /// [ ``` Clone () ``` ] - | ^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -122,7 +122,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:74:9 | LL | /// [x][Clone()] - | ^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -134,7 +134,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:77:9 | LL | /// [x][Clone ()] - | ^^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -176,7 +176,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:97:9 | LL | /// [w](Clone\(\)) - | ^^^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -188,7 +188,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:103:9 | LL | /// [w](Clone()) - | ^^^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^^^ this link resolves to the trait `Clone`, which is not a function | help: to link to the trait, prefix with `trait@` | @@ -256,7 +256,7 @@ error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:132:9 | LL | /// The [cln][] link here will produce a plain text suggestion - | ^^^^^ this link resolves to the trait `Clone`, which is not in the value namespace + | ^^^^^ this link resolves to the trait `Clone`, which is not a function | = help: to link to the trait, prefix with `trait@`: trait@Clone From be2540a1f0cd5c791a8f9662691f4ce5d85553d5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 4 Oct 2024 14:02:09 -0400 Subject: [PATCH 1218/1301] Fix some pub(crate) that were undetected bc of instrument --- compiler/rustc_borrowck/src/renumber.rs | 2 +- .../src/type_check/constraint_conversion.rs | 2 +- compiler/rustc_hir_typeck/src/_match.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 2 +- compiler/rustc_hir_typeck/src/closure.rs | 2 +- compiler/rustc_hir_typeck/src/demand.rs | 4 ++-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 22 +++++++++++-------- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 1 - .../src/fn_ctxt/suggestions.rs | 1 - compiler/rustc_hir_typeck/src/method/mod.rs | 8 +++---- compiler/rustc_hir_typeck/src/method/probe.rs | 4 ++-- .../rustc_hir_typeck/src/method/suggest.rs | 2 +- .../rustc_mir_dataflow/src/elaborate_drops.rs | 2 +- .../src/cfi/typeid/itanium_cxx_abi/encode.rs | 2 +- .../cfi/typeid/itanium_cxx_abi/transform.rs | 2 +- .../src/maybe_transmutable/mod.rs | 2 +- 16 files changed, 31 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index 0a375c7fae8e2..b7aef71eb54a0 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -11,7 +11,7 @@ use crate::BorrowckInferCtxt; /// Replaces all free regions appearing in the MIR with fresh /// inference variables, returning the number of variables created. #[instrument(skip(infcx, body, promoted), level = "debug")] -pub fn renumber_mir<'tcx>( +pub(crate) fn renumber_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &mut Body<'tcx>, promoted: &mut IndexSlice>, diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index fc1600ea4a611..6c86968389aa0 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -97,7 +97,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { /// that we computed for the closure. This has the effect of adding new outlives obligations /// to existing region variables in `closure_args`. #[instrument(skip(self), level = "debug")] - pub fn apply_closure_requirements( + pub(crate) fn apply_closure_requirements( &mut self, closure_requirements: &ClosureRegionRequirements<'tcx>, closure_def_id: DefId, diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 2dbadf8198b3f..0d9d1910ae0c1 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -15,7 +15,7 @@ use crate::{Diverges, Expectation, FnCtxt, Needs}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug", ret)] - pub fn check_match( + pub(crate) fn check_match( &self, expr: &'tcx hir::Expr<'tcx>, scrut: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index fcd2940b83ae3..71047b5e01d93 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -671,7 +671,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } #[instrument(skip(fcx), level = "debug")] - pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { + pub(crate) fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { self.expr_ty = fcx.structurally_resolve_type(self.expr_span, self.expr_ty); self.cast_ty = fcx.structurally_resolve_type(self.cast_span, self.cast_ty); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 3e7ce2955fc05..fcaa5751152b6 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -44,7 +44,7 @@ struct ClosureSignatures<'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, closure), level = "debug")] - pub fn check_expr_closure( + pub(crate) fn check_expr_closure( &self, closure: &hir::Closure<'tcx>, expr_span: Span, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index cfa8fc4bbf2e6..777248ff873de 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -182,7 +182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(skip(self), level = "debug")] - pub fn demand_suptype_with_origin( + pub(crate) fn demand_suptype_with_origin( &'a self, cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, @@ -247,7 +247,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!` /// will be permitted if the diverges flag is currently "always". #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] - pub fn demand_coerce_diag( + pub(crate) fn demand_coerce_diag( &'a self, mut expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 62107877283a0..380d91269642a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -187,7 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - pub fn write_method_call_and_enforce_effects( + pub(crate) fn write_method_call_and_enforce_effects( &self, hir_id: HirId, span: Span, @@ -214,7 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// occurred**, so that annotations like `Vec<_>` are preserved /// properly. #[instrument(skip(self), level = "debug")] - pub fn write_user_type_annotation_from_args( + pub(crate) fn write_user_type_annotation_from_args( &self, hir_id: HirId, def_id: DefId, @@ -235,7 +235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(skip(self), level = "debug")] - pub fn write_user_type_annotation( + pub(crate) fn write_user_type_annotation( &self, hir_id: HirId, canonical_user_type_annotation: CanonicalUserType<'tcx>, @@ -254,7 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(skip(self, expr), level = "debug")] - pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { + pub(crate) fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { debug!("expr = {:#?}", expr); if adj.is_empty() { @@ -448,7 +448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip_all)] - pub fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { let ty = self.lower_ty(hir_ty); debug!(?ty); @@ -736,7 +736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Resolves an associated value path into a base type and associated constant, or method /// resolution. The newly resolved definition is written into `type_dependent_defs`. #[instrument(level = "trace", skip(self), ret)] - pub fn resolve_ty_and_res_fully_qualified_call( + pub(crate) fn resolve_ty_and_res_fully_qualified_call( &self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, @@ -995,7 +995,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Instantiates the given path, which must refer to an item with the given // number of type parameters and type. #[instrument(skip(self, span), level = "debug")] - pub fn instantiate_value_path( + pub(crate) fn instantiate_value_path( &self, segments: &'tcx [hir::PathSegment<'tcx>], self_ty: Option>, @@ -1446,7 +1446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// variable. This is different from `structurally_resolve_type` which errors /// in this case. #[instrument(level = "debug", skip(self, sp), ret)] - pub fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + pub(crate) fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let ty = self.resolve_vars_with_obligations(ty); if self.next_trait_solver() @@ -1471,7 +1471,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self, sp), ret)] - pub fn try_structurally_resolve_const(&self, sp: Span, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + pub(crate) fn try_structurally_resolve_const( + &self, + sp: Span, + ct: ty::Const<'tcx>, + ) -> ty::Const<'tcx> { // FIXME(min_const_generic_exprs): We could process obligations here if `ct` is a var. if self.next_trait_solver() diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 550c58b5a17c6..fa471647d02d0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -984,7 +984,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_deref_unwrap_or( &mut err, - error_span, callee_ty, call_ident, expected_ty, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 487cc7e55cd9e..d0e91ec07f6d2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1462,7 +1462,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_deref_unwrap_or( &self, err: &mut Diag<'_>, - error_span: Span, callee_ty: Option>, call_ident: Option, expected_ty: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 3e9cb0ac2c80c..cb8b1df2c6e47 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -94,7 +94,7 @@ pub(crate) enum CandidateSource { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Determines whether the type `self_ty` supports a visible method named `method_name` or not. #[instrument(level = "debug", skip(self))] - pub fn method_exists_for_diagnostic( + pub(crate) fn method_exists_for_diagnostic( &self, method_name: Ident, self_ty: Ty<'tcx>, @@ -178,7 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `self_expr`: the self expression (`foo`) /// * `args`: the expressions of the arguments (`a, b + 1, ...`) #[instrument(level = "debug", skip(self))] - pub fn lookup_method( + pub(crate) fn lookup_method( &self, self_ty: Ty<'tcx>, segment: &'tcx hir::PathSegment<'tcx>, @@ -281,7 +281,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self, call_expr))] - pub fn lookup_probe( + pub(crate) fn lookup_probe( &self, method_name: Ident, self_ty: Ty<'tcx>, @@ -498,7 +498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `self_ty_span` the span for the type being searched within (span of `Foo`) /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call #[instrument(level = "debug", skip(self), ret)] - pub fn resolve_fully_qualified_call( + pub(crate) fn resolve_fully_qualified_call( &self, span: Span, method_name: Ident, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3bb7070d61dfa..3005945a20a17 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -224,7 +224,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would use to decide if a method is a plausible fit for /// ambiguity purposes). #[instrument(level = "debug", skip(self, candidate_filter))] - pub fn probe_for_return_type_for_diagnostic( + pub(crate) fn probe_for_return_type_for_diagnostic( &self, span: Span, mode: Mode, @@ -267,7 +267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - pub fn probe_for_name( + pub(crate) fn probe_for_name( &self, mode: Mode, item_name: Ident, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a37e9744293c3..4f726f3ed3866 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -183,7 +183,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - pub fn report_method_error( + pub(crate) fn report_method_error( &self, call_id: HirId, rcvr_ty: Ty<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index f2541c37167f0..a3b117a3f1966 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -225,7 +225,7 @@ where // FIXME: I think we should just control the flags externally, // and then we do not need this machinery. #[instrument(level = "debug")] - pub fn elaborate_drop(&mut self, bb: BasicBlock) { + fn elaborate_drop(&mut self, bb: BasicBlock) { match self.elaborator.drop_style(self.path, DropFlagMode::Deep) { DropStyle::Dead => { self.elaborator diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 320d66163848f..53834198f6328 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -315,7 +315,7 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, /// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for /// Rust types that are not used at the FFI boundary. #[instrument(level = "trace", skip(tcx, dict))] -pub fn encode_ty<'tcx>( +pub(crate) fn encode_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, dict: &mut FxHashMap, usize>, diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 5f7184a42407c..83dcceeaa84fc 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -291,7 +291,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc /// the Fn trait that defines the method (for being attached as a secondary type id). /// #[instrument(level = "trace", skip(tcx))] -pub fn transform_instance<'tcx>( +pub(crate) fn transform_instance<'tcx>( tcx: TyCtxt<'tcx>, mut instance: Instance<'tcx>, options: TransformTyOptions, diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index 1e5da4ec49d09..9dabcea706f46 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -40,7 +40,7 @@ mod rustc { /// This method begins by converting `src` and `dst` from `Ty`s to `Tree`s, /// then computes an answer using those trees. #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] - pub fn answer(self) -> Answer< as QueryContext>::Ref> { + pub(crate) fn answer(self) -> Answer< as QueryContext>::Ref> { let Self { src, dst, assume, context } = self; let layout_cx = LayoutCx::new(context, ParamEnv::reveal_all()); From f46e1624cbee1558cccf10f0b392ea22c4165e45 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 4 Oct 2024 20:11:26 +0200 Subject: [PATCH 1219/1301] Prefer refutable slice patterns over len check + index op --- .../src/borrow_tracker/tree_borrows/tree.rs | 5 ++-- src/tools/miri/src/shims/unix/fd.rs | 14 ++++----- src/tools/miri/src/shims/unix/fs.rs | 8 ++--- .../src/shims/unix/linux/foreign_items.rs | 10 +++---- src/tools/miri/src/shims/unix/linux/sync.rs | 30 +++++++++---------- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 410f4a58ac530..15cefab1a68e7 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -859,14 +859,15 @@ impl Tree { ) -> Option { let node = self.nodes.get(idx).unwrap(); + let [child_idx] = node.children[..] else { return None }; + // We never want to replace the root node, as it is also kept in `root_ptr_tags`. - if node.children.len() != 1 || live.contains(&node.tag) || node.parent.is_none() { + if live.contains(&node.tag) || node.parent.is_none() { return None; } // Since protected nodes are never GC'd (see `borrow_tracker::FrameExtra::visit_provenance`), // we know that `node` is not protected because otherwise `live` would // have contained `node.tag`. - let child_idx = node.children[0]; let child = self.nodes.get(child_idx).unwrap(); // Check that for that one child, `can_be_replaced_by_child` holds for the permission // on all locations. diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index f2d3115f98507..34e29760da783 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -481,14 +481,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - if args.len() < 2 { + let [fd_num, cmd, ..] = args else { throw_ub_format!( "incorrect number of arguments for fcntl: got {}, expected at least 2", args.len() ); - } - let fd_num = this.read_scalar(&args[0])?.to_i32()?; - let cmd = this.read_scalar(&args[1])?.to_i32()?; + }; + let fd_num = this.read_scalar(fd_num)?.to_i32()?; + let cmd = this.read_scalar(cmd)?.to_i32()?; // We only support getting the flags for a descriptor. if cmd == this.eval_libc_i32("F_GETFD") { @@ -508,13 +508,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // because exec() isn't supported. The F_DUPFD and F_DUPFD_CLOEXEC commands only // differ in whether the FD_CLOEXEC flag is pre-set on the new file descriptor, // thus they can share the same implementation here. - if args.len() < 3 { + let [_, _, start, ..] = args else { throw_ub_format!( "incorrect number of arguments for fcntl with cmd=`F_DUPFD`/`F_DUPFD_CLOEXEC`: got {}, expected at least 3", args.len() ); - } - let start = this.read_scalar(&args[2])?.to_i32()?; + }; + let start = this.read_scalar(start)?.to_i32()?; match this.machine.fds.get(fd_num) { Some(fd) => diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 2df178c4385af..6c9a2beac2d2a 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -433,18 +433,18 @@ fn maybe_sync_file( impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { - if args.len() < 2 { + let [path_raw, flag, ..] = args else { throw_ub_format!( "incorrect number of arguments for `open`: got {}, expected at least 2", args.len() ); - } + }; let this = self.eval_context_mut(); - let path_raw = this.read_pointer(&args[0])?; + let path_raw = this.read_pointer(path_raw)?; let path = this.read_path_from_c_str(path_raw)?; - let flag = this.read_scalar(&args[1])?.to_i32()?; + let flag = this.read_scalar(flag)?.to_i32()?; let mut options = OpenOptions::new(); diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 9726dac7e516b..4b5f3b6c81bae 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -122,19 +122,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { id if id == sys_getrandom => { // Used by getrandom 0.1 // The first argument is the syscall id, so skip over it. - if args.len() < 4 { + let [_, ptr, len, flags, ..] = args else { throw_ub_format!( "incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4", args.len() ); - } + }; - let ptr = this.read_pointer(&args[1])?; - let len = this.read_target_usize(&args[2])?; + let ptr = this.read_pointer(ptr)?; + let len = this.read_target_usize(len)?; // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, // neither of which have any effect on our current PRNG. // See for a discussion of argument sizes. - let _flags = this.read_scalar(&args[3])?.to_i32()?; + let _flags = this.read_scalar(flags)?.to_i32()?; this.gen_random(ptr, len)?; this.write_scalar(Scalar::from_target_usize(len, this), dest)?; diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index 404479e76e39b..5833ec64fc68f 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -15,19 +15,19 @@ pub fn futex<'tcx>( // may or may not be left out from the `syscall()` call. // Therefore we don't use `check_arg_count` here, but only check for the // number of arguments to fall within a range. - if args.len() < 3 { + let [addr, op, val, ..] = args else { throw_ub_format!( "incorrect number of arguments for `futex` syscall: got {}, expected at least 3", args.len() ); - } + }; // The first three arguments (after the syscall number itself) are the same to all futex operations: // (int *addr, int op, int val). // We checked above that these definitely exist. - let addr = this.read_pointer(&args[0])?; - let op = this.read_scalar(&args[1])?.to_i32()?; - let val = this.read_scalar(&args[2])?.to_i32()?; + let addr = this.read_pointer(addr)?; + let op = this.read_scalar(op)?.to_i32()?; + let val = this.read_scalar(val)?.to_i32()?; // This is a vararg function so we have to bring our own type for this pointer. let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32); @@ -55,15 +55,15 @@ pub fn futex<'tcx>( let wait_bitset = op & !futex_realtime == futex_wait_bitset; let bitset = if wait_bitset { - if args.len() < 6 { + let [_, _, _, timeout, uaddr2, bitset, ..] = args else { throw_ub_format!( "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT_BITSET`: got {}, expected at least 6", args.len() ); - } - let _timeout = this.read_pointer(&args[3])?; - let _uaddr2 = this.read_pointer(&args[4])?; - this.read_scalar(&args[5])?.to_u32()? + }; + let _timeout = this.read_pointer(timeout)?; + let _uaddr2 = this.read_pointer(uaddr2)?; + this.read_scalar(bitset)?.to_u32()? } else { if args.len() < 4 { throw_ub_format!( @@ -183,15 +183,15 @@ pub fn futex<'tcx>( // Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up. op if op == futex_wake || op == futex_wake_bitset => { let bitset = if op == futex_wake_bitset { - if args.len() < 6 { + let [_, _, _, timeout, uaddr2, bitset, ..] = args else { throw_ub_format!( "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAKE_BITSET`: got {}, expected at least 6", args.len() ); - } - let _timeout = this.read_pointer(&args[3])?; - let _uaddr2 = this.read_pointer(&args[4])?; - this.read_scalar(&args[5])?.to_u32()? + }; + let _timeout = this.read_pointer(timeout)?; + let _uaddr2 = this.read_pointer(uaddr2)?; + this.read_scalar(bitset)?.to_u32()? } else { u32::MAX }; From f51d8e3276b783a51b04139bf8e7bff099fb3769 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 2 Oct 2024 21:20:01 +0200 Subject: [PATCH 1220/1301] Fix target_abi in sparc-unknown-none-elf This was previously set to `target_abi = "elf"`, but `elf` is not used elsewhere as a target ABI (even though there's many targets that have it in their name). --- .../rustc_target/src/spec/targets/sparc_unknown_none_elf.rs | 1 - tests/ui/check-cfg/well-known-values.stderr | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs index 0cd4faefd6bb1..0157d03f854b7 100644 --- a/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs @@ -7,7 +7,6 @@ pub(crate) fn target() -> Target { linker: Some("sparc-elf-gcc".into()), endian: Endian::Big, cpu: "v7".into(), - abi: "elf".into(), max_atomic_width: Some(32), atomic_cas: true, panic_strategy: PanicStrategy::Abort, diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 3c99fdd3821c5..c6d403104ea6b 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elf`, `fortanix`, `ilp32`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `fortanix`, `ilp32`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From e08002f6d0bfcea06357137dd52ee2c163757154 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:56:15 -0400 Subject: [PATCH 1221/1301] Stabilize `BufRead::skip_until` --- library/std/src/io/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index dd6458c38c6e9..8fedcb241d095 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2382,8 +2382,6 @@ pub trait BufRead: Read { /// about Ferris from a binary string, skipping the fun fact: /// /// ``` - /// #![feature(bufread_skip_until)] - /// /// use std::io::{self, BufRead}; /// /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0"); @@ -2407,7 +2405,7 @@ pub trait BufRead: Read { /// assert_eq!(num_bytes, 11); /// assert_eq!(animal, b"Crustacean\0"); /// ``` - #[unstable(feature = "bufread_skip_until", issue = "111735")] + #[stable(feature = "bufread_skip_until", since = "CURRENT_RUSTC_VERSION")] fn skip_until(&mut self, byte: u8) -> Result { skip_until(self, byte) } From d0a467a89f513c1eaef5e99cb446c66dc37c2837 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 4 Oct 2024 16:37:09 -0400 Subject: [PATCH 1222/1301] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 80d82ca22abbe..ad074abe3a18c 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 80d82ca22abbee5fb7b51fa1abeb1ae34e99e88a +Subproject commit ad074abe3a18ce8444c06f962ceecfd056acfc73 From ae5f58d906e755bff56dd9667c1d8b13ca236286 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 14 Sep 2024 15:43:03 -0400 Subject: [PATCH 1223/1301] Check elaborated projections from dyn don't mention unconstrained late bound lifetimes --- .../src/hir_ty_lowering/dyn_compatibility.rs | 50 +++++++++++++++++++ ...ted-predicates-unconstrained-late-bound.rs | 24 +++++++++ ...predicates-unconstrained-late-bound.stderr | 9 ++++ tests/ui/traits/object/pretty.rs | 4 +- tests/ui/traits/object/pretty.stderr | 13 +---- 5 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.rs create mode 100644 tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index e7b8e6e69b0c6..f8ae9adebe3d4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -174,6 +174,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Include projections defined on supertraits. projection_bounds.push((pred, span)); } + + self.check_elaborated_projection_mentions_input_lifetimes(pred, span); } _ => (), } @@ -360,6 +362,54 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_dynamic(tcx, existential_predicates, region_bound, representation) } + + /// Check that elaborating the principal of a trait ref doesn't lead to projections + /// that are unconstrained. This can happen because an otherwise unconstrained + /// *type variable* can be substituted with a type that has late-bound regions. See + /// `elaborated-predicates-unconstrained-late-bound.rs` for a test. + fn check_elaborated_projection_mentions_input_lifetimes( + &self, + pred: ty::PolyProjectionPredicate<'tcx>, + span: Span, + ) { + let tcx = self.tcx(); + + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref or assoc_item. These are not well-formed. + // + // Example: + // + // for<'a> ::Item = &'a str // <-- 'a is bad + // for<'a> >::Output = &'a str // <-- 'a is ok + let late_bound_in_projection_term = + tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term)); + let late_bound_in_term = + tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term)); + debug!(?late_bound_in_projection_term); + debug!(?late_bound_in_term); + + // FIXME: point at the type params that don't have appropriate lifetimes: + // struct S1 Fn(&i32, &i32) -> &'a i32>(F); + // ---- ---- ^^^^^^^ + // NOTE(associated_const_equality): This error should be impossible to trigger + // with associated const equality constraints. + self.validate_late_bound_regions( + late_bound_in_projection_term, + late_bound_in_term, + |br_name| { + let item_name = tcx.item_name(pred.projection_def_id()); + struct_span_code_err!( + self.dcx(), + span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + item_name, + br_name + ) + }, + ); + } } fn replace_dummy_self_with_error<'tcx, T: TypeFoldable>>( diff --git a/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.rs b/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.rs new file mode 100644 index 0000000000000..b174776c596ee --- /dev/null +++ b/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.rs @@ -0,0 +1,24 @@ +// Make sure that when elaborating the principal of a dyn trait for projection predicates +// we don't end up in a situation where we have an unconstrained late-bound lifetime in +// the output of a projection. + +// Fix for . + +trait A: B {} + +trait B { + type T; +} + +struct Erase(T::T); + +fn main() { + let x = { + let x = String::from("hello"); + + Erase:: A<&'a _>>(x.as_str()) + //~^ ERROR binding for associated type `T` references lifetime `'a`, which does not appear in the trait input types + }; + + dbg!(x.0); +} diff --git a/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr b/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr new file mode 100644 index 0000000000000..bb3cc4b3fd786 --- /dev/null +++ b/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr @@ -0,0 +1,9 @@ +error[E0582]: binding for associated type `T` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/elaborated-predicates-unconstrained-late-bound.rs:19:21 + | +LL | Erase:: A<&'a _>>(x.as_str()) + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0582`. diff --git a/tests/ui/traits/object/pretty.rs b/tests/ui/traits/object/pretty.rs index 6660ff040f753..603d7af526002 100644 --- a/tests/ui/traits/object/pretty.rs +++ b/tests/ui/traits/object/pretty.rs @@ -13,7 +13,7 @@ trait SuperGeneric<'a> { } trait AnyGeneric<'a>: SuperGeneric<'a> {} trait FixedGeneric1<'a>: SuperGeneric<'a, Assoc2 = &'a u8> {} -trait FixedGeneric2<'a>: Super {} +// trait FixedGeneric2<'a>: Super {} // Unsound! trait FixedHrtb: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> {} trait AnyDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super {} trait FixedDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super {} @@ -32,7 +32,7 @@ fn dyn_fixed_static(x: &dyn FixedStatic) { x } //~ERROR mismatched types fn dyn_super_generic(x: &dyn for<'a> SuperGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types fn dyn_any_generic(x: &dyn for<'a> AnyGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x } //~ERROR mismatched types -fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } //~ERROR mismatched types +// fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } // Unsound! fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>) { x } //~ERROR mismatched types fn dyn_fixed_hrtb(x: &dyn FixedHrtb) { x } //~ERROR mismatched types fn dyn_any_different_binders(x: &dyn AnyDifferentBinders) { x } //~ERROR mismatched types diff --git a/tests/ui/traits/object/pretty.stderr b/tests/ui/traits/object/pretty.stderr index ca56bdbb67a56..af941e69c5f84 100644 --- a/tests/ui/traits/object/pretty.stderr +++ b/tests/ui/traits/object/pretty.stderr @@ -106,17 +106,6 @@ LL | fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x } = note: expected unit type `()` found reference `&dyn for<'a> FixedGeneric1<'a>` -error[E0308]: mismatched types - --> $DIR/pretty.rs:35:60 - | -LL | fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } - | - ^ expected `()`, found `&dyn FixedGeneric2<'a>` - | | - | help: try adding a return type: `-> &dyn for<'a> FixedGeneric2<'a>` - | - = note: expected unit type `()` - found reference `&dyn for<'a> FixedGeneric2<'a>` - error[E0308]: mismatched types --> $DIR/pretty.rs:36:79 | @@ -172,6 +161,6 @@ LL | fn dyn_has_gat(x: &dyn HasGat = ()>) { x } = note: expected unit type `()` found reference `&dyn HasGat = ()>` -error: aborting due to 15 previous errors; 1 warning emitted +error: aborting due to 14 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0308`. From fd7ee484f9da99120116dc560452dd551d4c6496 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 14 Sep 2024 15:55:16 -0400 Subject: [PATCH 1224/1301] Elaborate supertrait span correctly to label the error better --- .../src/hir_ty_lowering/dyn_compatibility.rs | 20 +++++++--- compiler/rustc_middle/src/ty/predicate.rs | 4 ++ compiler/rustc_type_ir/src/elaborate.rs | 40 +++++++++++++++++++ compiler/rustc_type_ir/src/inherent.rs | 2 + ...predicates-unconstrained-late-bound.stderr | 3 ++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index f8ae9adebe3d4..394a263fbb595 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::{ use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations}; +use rustc_type_ir::elaborate::ClauseWithSupertraitSpan; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -124,16 +125,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .into_iter() .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); - for (base_trait_ref, span) in regular_traits_refs_spans { + for (base_trait_ref, original_span) in regular_traits_refs_spans { let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx); - for pred in traits::elaborate(tcx, [base_pred]).filter_only_self() { + for ClauseWithSupertraitSpan { pred, original_span, supertrait_span } in + traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(base_pred, original_span)]) + .filter_only_self() + { debug!("observing object predicate `{pred:?}`"); let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { let pred = bound_predicate.rebind(pred); - associated_types.entry(span).or_default().extend( + associated_types.entry(original_span).or_default().extend( tcx.associated_items(pred.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) @@ -172,10 +176,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // the discussion in #56288 for alternatives. if !references_self { // Include projections defined on supertraits. - projection_bounds.push((pred, span)); + projection_bounds.push((pred, original_span)); } - self.check_elaborated_projection_mentions_input_lifetimes(pred, span); + self.check_elaborated_projection_mentions_input_lifetimes( + pred, + original_span, + supertrait_span, + ); } _ => (), } @@ -371,6 +379,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, pred: ty::PolyProjectionPredicate<'tcx>, span: Span, + supertrait_span: Span, ) { let tcx = self.tcx(); @@ -407,6 +416,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { item_name, br_name ) + .with_span_label(supertrait_span, "due to this supertrait") }, ); } diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index fd4e8f1cd4e71..d20cb368278b3 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -179,6 +179,10 @@ pub struct Clause<'tcx>( ); impl<'tcx> rustc_type_ir::inherent::Clause> for Clause<'tcx> { + fn as_predicate(self) -> Predicate<'tcx> { + self.as_predicate() + } + fn instantiate_supertrait(self, tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Self { self.instantiate_supertrait(tcx, trait_ref) } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 61736633cfa28..dac45ff2aba3f 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -44,6 +44,46 @@ pub trait Elaboratable { ) -> Self; } +pub struct ClauseWithSupertraitSpan { + pub pred: I::Predicate, + // Span of the original elaborated predicate. + pub original_span: I::Span, + // Span of the supertrait predicatae that lead to this clause. + pub supertrait_span: I::Span, +} +impl ClauseWithSupertraitSpan { + pub fn new(pred: I::Predicate, span: I::Span) -> Self { + ClauseWithSupertraitSpan { pred, original_span: span, supertrait_span: span } + } +} +impl Elaboratable for ClauseWithSupertraitSpan { + fn predicate(&self) -> ::Predicate { + self.pred + } + + fn child(&self, clause: ::Clause) -> Self { + ClauseWithSupertraitSpan { + pred: clause.as_predicate(), + original_span: self.original_span, + supertrait_span: self.supertrait_span, + } + } + + fn child_with_derived_cause( + &self, + clause: ::Clause, + supertrait_span: ::Span, + _parent_trait_pred: crate::Binder>, + _index: usize, + ) -> Self { + ClauseWithSupertraitSpan { + pred: clause.as_predicate(), + original_span: self.original_span, + supertrait_span: supertrait_span, + } + } +} + pub fn elaborate>( cx: I, obligations: impl IntoIterator, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 59a83ea5412d5..69665df4bfc26 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -460,6 +460,8 @@ pub trait Clause>: + IntoKind>> + Elaboratable { + fn as_predicate(self) -> I::Predicate; + fn as_trait_clause(self) -> Option>> { self.kind() .map_bound(|clause| if let ty::ClauseKind::Trait(t) = clause { Some(t) } else { None }) diff --git a/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr b/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr index bb3cc4b3fd786..067eaf5e7f30f 100644 --- a/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr +++ b/tests/ui/traits/object/elaborated-predicates-unconstrained-late-bound.stderr @@ -1,6 +1,9 @@ error[E0582]: binding for associated type `T` references lifetime `'a`, which does not appear in the trait input types --> $DIR/elaborated-predicates-unconstrained-late-bound.rs:19:21 | +LL | trait A: B {} + | ----- due to this supertrait +... LL | Erase:: A<&'a _>>(x.as_str()) | ^^^^^^^^^^^^^^^^ From b955480d05dcbe0fc29e329862c827eb28b0b189 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 3 Oct 2024 17:57:12 -0400 Subject: [PATCH 1225/1301] Update compiler-builtins to 0.1.132 This commit updates compiler-builtins from 0.1.130 to 0.1.132. PRs in the delta: - rust-lang/compiler-builtins#698 - rust-lang/compiler-builtins#699 - rust-lang/compiler-builtins#701 - rust-lang/compiler-builtins#704 - rust-lang/compiler-builtins#627 - rust-lang/compiler-builtins#706 --- library/Cargo.lock | 4 ++-- library/alloc/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 209e30b304027..66ded59ae3661 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.130" +version = "0.1.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64c30475571756801eff60a811520c3d18e0ceb9c56c97bad2047ae601f6709" +checksum = "88f58330b127df19326d19e9debbb722578589cb1b9c061b5cdc4036d038b54b" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 11db50a0fdf3c..fa18c7aec743a 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.130", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.132", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 63c65b8ef3994..6bf70e1d82abe 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.130" } +compiler_builtins = { version = "0.1.132" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ From e057c43382a1eacc5941bc4e78b36dc122b32419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 4 Oct 2024 22:59:03 +0000 Subject: [PATCH 1226/1301] Account for `impl Trait {` when `impl Trait for Type {` was intended On editions where bare traits are never allowed, detect if the user has written `impl Trait` with no type, silence any dyn-compatibility errors, and provide a structured suggestion for the potentially missing type: ``` error[E0782]: trait objects must include the `dyn` keyword --> $DIR/missing-for-type-in-impl.rs:8:6 | LL | impl Foo { | ^^^^^^^^ | help: add `dyn` keyword before this trait | LL | impl dyn Foo { | +++ help: you might have intended to implement this trait for a given type | LL | impl Foo for /* Type */ { | ++++++++++++++ ``` --- .../src/hir_ty_lowering/lint.rs | 17 +++-- .../traits/fulfillment_errors.rs | 22 +++++- .../missing-for-type-in-impl.e2015.stderr | 74 +++++++++++++++++++ .../missing-for-type-in-impl.e2021.stderr | 31 ++++++++ tests/ui/traits/missing-for-type-in-impl.rs | 22 ++++++ 5 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 tests/ui/traits/missing-for-type-in-impl.e2015.stderr create mode 100644 tests/ui/traits/missing-for-type-in-impl.e2021.stderr create mode 100644 tests/ui/traits/missing-for-type-in-impl.rs diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index a70f881f5fedc..5607fe873f652 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -108,17 +108,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; if let hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Impl(hir::Impl { - self_ty: impl_self_ty, - of_trait: Some(of_trait_ref), - generics, - .. - }), + kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), .. }) = tcx.hir_node_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id { + let Some(of_trait_ref) = of_trait else { + diag.span_suggestion_verbose( + impl_self_ty.span.shrink_to_hi(), + "you might have intended to implement this trait for a given type", + format!(" for /* Type */"), + Applicability::HasPlaceholders, + ); + return; + }; if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { return; } 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 1889ecc7670ee..824c25db07d2e 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 @@ -1,6 +1,7 @@ use core::ops::ControlFlow; use std::borrow::Cow; +use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; @@ -573,7 +574,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::PredicateKind::DynCompatible(trait_def_id) => { let violations = self.tcx.dyn_compatibility_violations(trait_def_id); - report_dyn_incompatibility(self.tcx, span, None, trait_def_id, violations) + let mut err = report_dyn_incompatibility( + self.tcx, + span, + None, + trait_def_id, + violations, + ); + if let hir::Node::Item(item) = + self.tcx.hir_node_by_def_id(obligation.cause.body_id) + && let hir::ItemKind::Impl(impl_) = item.kind + && let None = impl_.of_trait + && let hir::TyKind::TraitObject(_, _, syntax) = impl_.self_ty.kind + && let TraitObjectSyntax::None = syntax + && impl_.self_ty.span.edition().at_least_rust_2021() + { + // Silence the dyn-compatibility error in favor of the missing dyn on + // self type error. #131051. + err.downgrade_to_delayed_bug(); + } + err } ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { diff --git a/tests/ui/traits/missing-for-type-in-impl.e2015.stderr b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr new file mode 100644 index 0000000000000..541b49b024fda --- /dev/null +++ b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr @@ -0,0 +1,74 @@ +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/missing-for-type-in-impl.rs:8:6 + | +LL | impl Foo { + | ^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: `#[warn(bare_trait_objects)]` on by default +help: if this is a dyn-compatible trait, use `dyn` + | +LL | impl dyn Foo { + | +++ +help: you might have intended to implement this trait for a given type + | +LL | impl Foo for /* Type */ { + | ++++++++++++++ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/missing-for-type-in-impl.rs:8:6 + | +LL | impl Foo { + | ^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: if this is a dyn-compatible trait, use `dyn` + | +LL | impl dyn Foo { + | +++ +help: you might have intended to implement this trait for a given type + | +LL | impl Foo for /* Type */ { + | ++++++++++++++ + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/missing-for-type-in-impl.rs:8:6 + | +LL | impl Foo { + | ^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/missing-for-type-in-impl.rs:4:8 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | fn id(me: T) -> T; + | ^^ ...because associated function `id` has no `self` parameter +help: consider turning `id` into a method by giving it a `&self` argument + | +LL | fn id(&self, me: T) -> T; + | ++++++ +help: alternatively, consider constraining `id` so it does not apply to trait objects + | +LL | fn id(me: T) -> T where Self: Sized; + | +++++++++++++++++ + +error[E0277]: the trait bound `i64: Foo` is not satisfied + --> $DIR/missing-for-type-in-impl.rs:19:19 + | +LL | let x: i64 = >::id(10); + | ^^^ the trait `Foo` is not implemented for `i64` + | +help: this trait has no implementations, consider adding one + --> $DIR/missing-for-type-in-impl.rs:3:1 + | +LL | trait Foo { + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/missing-for-type-in-impl.e2021.stderr b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr new file mode 100644 index 0000000000000..b5a607a54cb54 --- /dev/null +++ b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `i64: Foo` is not satisfied + --> $DIR/missing-for-type-in-impl.rs:19:19 + | +LL | let x: i64 = >::id(10); + | ^^^ the trait `Foo` is not implemented for `i64` + | +help: this trait has no implementations, consider adding one + --> $DIR/missing-for-type-in-impl.rs:3:1 + | +LL | trait Foo { + | ^^^^^^^^^^^^ + +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/missing-for-type-in-impl.rs:8:6 + | +LL | impl Foo { + | ^^^^^^^^ + | +help: add `dyn` keyword before this trait + | +LL | impl dyn Foo { + | +++ +help: you might have intended to implement this trait for a given type + | +LL | impl Foo for /* Type */ { + | ++++++++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0782. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/missing-for-type-in-impl.rs b/tests/ui/traits/missing-for-type-in-impl.rs new file mode 100644 index 0000000000000..7d4ad479e773c --- /dev/null +++ b/tests/ui/traits/missing-for-type-in-impl.rs @@ -0,0 +1,22 @@ +//@revisions: e2021 e2015 +//@[e2021]edition: 2021 +trait Foo { + fn id(me: T) -> T; +} + +/* note the "missing" for ... (in this case for i64, in order for this to compile) */ +impl Foo { +//[e2021]~^ ERROR trait objects must include the `dyn` keyword +//[e2015]~^^ WARNING trait objects without an explicit `dyn` are deprecated +//[e2015]~| WARNING trait objects without an explicit `dyn` are deprecated +//[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! +//[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! +//[e2015]~| ERROR the trait `Foo` cannot be made into an object + fn id(me: i64) -> i64 {me} +} + +fn main() { + let x: i64 = >::id(10); + //~^ ERROR the trait bound `i64: Foo` is not satisfied + println!("{}", x); +} From 99144726a43b6ed5a5010837080f2a7c70c4ec52 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 18 Aug 2024 15:48:22 +0000 Subject: [PATCH 1227/1301] Make query backtrace more useful. --- compiler/rustc_middle/src/query/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 989fbd711c3c8..9b7dfaab601a1 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1738,16 +1738,16 @@ rustc_queries! { /// Does lifetime resolution on items. Importantly, we can't resolve /// lifetimes directly on things like trait methods, because of trait params. /// See `rustc_resolve::late::lifetimes` for details. - query resolve_bound_vars(_: hir::OwnerId) -> &'tcx ResolveBoundVars { + query resolve_bound_vars(def_id: hir::OwnerId) -> &'tcx ResolveBoundVars { arena_cache - desc { "resolving lifetimes" } + desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(def_id) } } - query named_variable_map(_: hir::OwnerId) -> + query named_variable_map(def_id: hir::OwnerId) -> Option<&'tcx FxIndexMap> { - desc { "looking up a named region" } + desc { |tcx| "looking up a named region inside `{}`", tcx.def_path_str(def_id) } } - query is_late_bound_map(_: hir::OwnerId) -> Option<&'tcx FxIndexSet> { - desc { "testing if a region is late bound" } + query is_late_bound_map(def_id: hir::OwnerId) -> Option<&'tcx FxIndexSet> { + desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(def_id) } } /// For a given item's generic parameter, gets the default lifetimes to be used /// for each parameter if a trait object were to be passed for that parameter. @@ -1758,9 +1758,9 @@ rustc_queries! { desc { "looking up lifetime defaults for generic parameter `{}`", tcx.def_path_str(key) } separate_provide_extern } - query late_bound_vars_map(_: hir::OwnerId) + query late_bound_vars_map(def_id: hir::OwnerId) -> Option<&'tcx FxIndexMap>> { - desc { "looking up late bound vars" } + desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(def_id) } } /// Computes the visibility of the provided `def_id`. From 4ec7839afa30aae41ee01326b0273ca1ba8e027b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 22 Aug 2024 00:55:35 +0000 Subject: [PATCH 1228/1301] Make naming more consistent. --- compiler/rustc_middle/src/query/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9b7dfaab601a1..1f0aab6d6b321 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1738,29 +1738,29 @@ rustc_queries! { /// Does lifetime resolution on items. Importantly, we can't resolve /// lifetimes directly on things like trait methods, because of trait params. /// See `rustc_resolve::late::lifetimes` for details. - query resolve_bound_vars(def_id: hir::OwnerId) -> &'tcx ResolveBoundVars { + query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars { arena_cache - desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(def_id) } + desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) } } - query named_variable_map(def_id: hir::OwnerId) -> + query named_variable_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexMap> { - desc { |tcx| "looking up a named region inside `{}`", tcx.def_path_str(def_id) } + desc { |tcx| "looking up a named region inside `{}`", tcx.def_path_str(owner_id) } } - query is_late_bound_map(def_id: hir::OwnerId) -> Option<&'tcx FxIndexSet> { - desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(def_id) } + query is_late_bound_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexSet> { + desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(owner_id) } } /// For a given item's generic parameter, gets the default lifetimes to be used /// for each parameter if a trait object were to be passed for that parameter. /// For example, for `T` in `struct Foo<'a, T>`, this would be `'static`. /// For `T` in `struct Foo<'a, T: 'a>`, this would instead be `'a`. /// This query will panic if passed something that is not a type parameter. - query object_lifetime_default(key: DefId) -> ObjectLifetimeDefault { - desc { "looking up lifetime defaults for generic parameter `{}`", tcx.def_path_str(key) } + query object_lifetime_default(def_id: DefId) -> ObjectLifetimeDefault { + desc { "looking up lifetime defaults for generic parameter `{}`", tcx.def_path_str(def_id) } separate_provide_extern } - query late_bound_vars_map(def_id: hir::OwnerId) + query late_bound_vars_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexMap>> { - desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(def_id) } + desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) } } /// Computes the visibility of the provided `def_id`. From d6f247f3d52f529e7145eb355ac9660a49d0d8c9 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 9 Aug 2024 20:43:30 -0700 Subject: [PATCH 1229/1301] rm `ItemKind::OpaqueTy` This introduce an additional collection of opaques on HIR, as they can no longer be listed using the free item list. --- compiler/rustc_ast_lowering/src/index.rs | 8 ++ compiler/rustc_ast_lowering/src/lib.rs | 27 +++---- .../src/diagnostics/region_name.rs | 10 +-- .../src/region_infer/opaque_types.rs | 4 +- compiler/rustc_hir/src/hir.rs | 15 ++-- compiler/rustc_hir/src/intravisit.rs | 25 +++++-- compiler/rustc_hir/src/target.rs | 5 -- .../rustc_hir_analysis/src/check/check.rs | 16 ++-- .../src/check/compare_impl_item/refine.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 40 +++------- .../rustc_hir_analysis/src/collect/dump.rs | 11 +-- .../src/collect/generics_of.rs | 57 +++++++-------- .../src/collect/item_bounds.rs | 13 +--- .../src/collect/predicates_of.rs | 2 +- .../src/collect/resolve_bound_vars.rs | 73 ++++++++----------- .../rustc_hir_analysis/src/collect/type_of.rs | 62 ++++++---------- .../src/hir_ty_lowering/mod.rs | 67 ++++++++--------- .../rustc_hir_analysis/src/variance/dump.rs | 13 ++-- compiler/rustc_hir_pretty/src/lib.rs | 15 ++-- .../src/fn_ctxt/suggestions.rs | 5 +- compiler/rustc_lint/src/async_fn_in_trait.rs | 7 +- .../rustc_lint/src/impl_trait_overcaptures.rs | 2 +- .../src/opaque_hidden_inferred_bound.rs | 6 +- compiler/rustc_lint/src/types.rs | 1 - compiler/rustc_middle/src/hir/map/mod.rs | 31 +++++++- compiler/rustc_middle/src/hir/mod.rs | 12 +++ compiler/rustc_middle/src/ty/context.rs | 11 +-- compiler/rustc_middle/src/ty/diagnostics.rs | 10 +-- compiler/rustc_middle/src/ty/sty.rs | 2 + compiler/rustc_passes/src/check_attr.rs | 2 - compiler/rustc_passes/src/dead.rs | 10 +-- compiler/rustc_passes/src/hir_stats.rs | 1 - compiler/rustc_passes/src/reachable.rs | 4 +- compiler/rustc_privacy/src/lib.rs | 18 ++--- .../nice_region_error/static_impl_trait.rs | 9 +-- .../error_reporting/infer/note_and_explain.rs | 2 +- .../src/error_reporting/infer/region.rs | 11 +-- .../src/error_reporting/infer/suggest.rs | 8 +- .../src/error_reporting/traits/suggestions.rs | 18 +++-- compiler/rustc_ty_utils/src/assoc.rs | 15 ++-- compiler/rustc_ty_utils/src/opaque_types.rs | 1 + src/librustdoc/clean/mod.rs | 12 +-- src/librustdoc/html/render/span_map.rs | 1 - src/librustdoc/visit_ast.rs | 10 --- .../clippy_utils/src/check_proc_macro.rs | 2 +- 45 files changed, 307 insertions(+), 369 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index e77c0fb3a3ed3..6289966561f58 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -226,6 +226,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_opaque_ty(&mut self, opaq: &'hir OpaqueTy<'hir>) { + self.insert(opaq.span, opaq.hir_id, Node::OpaqueTy(opaq)); + + self.with_parent(opaq.hir_id, |this| { + intravisit::walk_opaque_ty(this, opaq); + }); + } + fn visit_anon_const(&mut self, constant: &'hir AnonConst) { // FIXME: use real span? self.insert(DUMMY_SP, constant.hir_id, Node::AnonConst(constant)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 9275308cccbce..b26797f42032f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1603,7 +1603,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>], ) -> hir::TyKind<'hir> { let opaque_ty_def_id = self.local_def_id(opaque_ty_node_id); - debug!(?opaque_ty_def_id); + let opaque_ty_hir_id = self.lower_node_id(opaque_ty_node_id); + debug!(?opaque_ty_def_id, ?opaque_ty_hir_id); // Map from captured (old) lifetime to synthetic (new) lifetime. // Used to resolve lifetimes in the bounds of the opaque. @@ -1676,7 +1677,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - self.with_hir_id_owner(opaque_ty_node_id, |this| { + let opaque_ty_def = self.with_def_id_parent(opaque_ty_def_id, |this| { // Install the remapping from old to new (if any). This makes sure that // any lifetimes that would have resolved to the def-id of captured // lifetimes are remapped to the new *synthetic* lifetimes of the opaque. @@ -1714,7 +1715,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let lifetime_mapping = self.arena.alloc_slice(&synthesized_lifetime_args); - let opaque_ty_item = hir::OpaqueTy { + trace!("registering opaque type with id {:#?}", opaque_ty_def_id); + let opaque_ty_def = hir::OpaqueTy { + hir_id: opaque_ty_hir_id, + def_id: opaque_ty_def_id, generics: this.arena.alloc(hir::Generics { params: generic_params, predicates: &[], @@ -1725,19 +1729,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds, origin, lifetime_mapping, - }; - - // Generate an `type Foo = impl Trait;` declaration. - trace!("registering opaque type with id {:#?}", opaque_ty_def_id); - let opaque_ty_item = hir::Item { - owner_id: hir::OwnerId { def_id: opaque_ty_def_id }, - ident: Ident::empty(), - kind: hir::ItemKind::OpaqueTy(this.arena.alloc(opaque_ty_item)), - vis_span: this.lower_span(span.shrink_to_lo()), span: this.lower_span(opaque_ty_span), }; - - hir::OwnerNode::Item(this.arena.alloc(opaque_ty_item)) + this.arena.alloc(opaque_ty_def) }); let generic_args = self.arena.alloc_from_iter( @@ -1750,10 +1744,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Foo = impl Trait` is, internally, created as a child of the // async fn, so the *type parameters* are inherited. It's // only the lifetime parameters that we must supply. - hir::TyKind::OpaqueDef( - hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } }, - generic_args, - ) + hir::TyKind::OpaqueDef(opaque_ty_def, generic_args) } fn lower_precise_capturing_args( diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index d4598a1f582b6..1a5f9bdb154c0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -830,20 +830,14 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// /// [`OpaqueDef`]: hir::TyKind::OpaqueDef fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - let hir = self.infcx.tcx.hir(); - - let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else { + let hir::TyKind::OpaqueDef(opaque_ty, _) = hir_ty.kind else { span_bug!( hir_ty.span, "lowered return type of async fn is not OpaqueDef: {:?}", hir_ty ); }; - let opaque_ty = hir.item(id); - if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { - bounds: [hir::GenericBound::Trait(trait_ref, _)], - .. - }) = opaque_ty.kind + if let hir::OpaqueTy { bounds: [hir::GenericBound::Trait(trait_ref, _)], .. } = opaque_ty && let Some(segment) = trait_ref.trait_ref.path.segments.last() && let Some(args) = segment.args && let [constraint] = args.constraints diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 2f90e91628141..a16c1931a552b 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -329,8 +329,8 @@ fn check_opaque_type_well_formed<'tcx>( ) -> Result, ErrorGuaranteed> { // Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs` // on stable and we'd break that. - let opaque_ty_hir = tcx.hir().expect_item(def_id); - let OpaqueTyOrigin::TyAlias { .. } = opaque_ty_hir.expect_opaque_ty().origin else { + let opaque_ty_hir = tcx.hir().expect_opaque_ty(def_id); + let OpaqueTyOrigin::TyAlias { .. } = opaque_ty_hir.origin else { return Ok(definition_ty); }; let param_env = tcx.param_env(def_id); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f58ec22aea971..2ef6fa53f4edd 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2749,6 +2749,8 @@ pub struct BareFnTy<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct OpaqueTy<'hir> { + pub hir_id: HirId, + pub def_id: LocalDefId, pub generics: &'hir Generics<'hir>, pub bounds: GenericBounds<'hir>, pub origin: OpaqueTyOrigin, @@ -2762,6 +2764,7 @@ pub struct OpaqueTy<'hir> { /// This mapping associated a captured lifetime (first parameter) with the new /// early-bound lifetime that was generated for the opaque. pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)], + pub span: Span, } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -2868,7 +2871,7 @@ pub enum TyKind<'hir> { /// possibly parameters) that are actually bound on the `impl Trait`. /// /// The last parameter specifies whether this opaque appears in a trait definition. - OpaqueDef(ItemId, &'hir [GenericArg<'hir>]), + OpaqueDef(&'hir OpaqueTy<'hir>, &'hir [GenericArg<'hir>]), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. TraitObject( @@ -3337,8 +3340,6 @@ impl<'hir> Item<'hir> { expect_ty_alias, (&'hir Ty<'hir>, &'hir Generics<'hir>), ItemKind::TyAlias(ty, generics), (ty, generics); - expect_opaque_ty, &OpaqueTy<'hir>, ItemKind::OpaqueTy(ty), ty; - expect_enum, (&EnumDef<'hir>, &'hir Generics<'hir>), ItemKind::Enum(def, generics), (def, generics); expect_struct, (&VariantData<'hir>, &'hir Generics<'hir>), @@ -3451,8 +3452,6 @@ pub enum ItemKind<'hir> { GlobalAsm(&'hir InlineAsm<'hir>), /// A type alias, e.g., `type Foo = Bar`. TyAlias(&'hir Ty<'hir>, &'hir Generics<'hir>), - /// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`. - OpaqueTy(&'hir OpaqueTy<'hir>), /// An enum definition, e.g., `enum Foo {C, D}`. Enum(EnumDef<'hir>, &'hir Generics<'hir>), /// A struct definition, e.g., `struct Foo {x: A}`. @@ -3496,7 +3495,6 @@ impl ItemKind<'_> { ItemKind::Fn(_, ref generics, _) | ItemKind::TyAlias(_, ref generics) | ItemKind::Const(_, ref generics, _) - | ItemKind::OpaqueTy(OpaqueTy { ref generics, .. }) | ItemKind::Enum(_, ref generics) | ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) @@ -3519,7 +3517,6 @@ impl ItemKind<'_> { ItemKind::ForeignMod { .. } => "extern block", ItemKind::GlobalAsm(..) => "global asm item", ItemKind::TyAlias(..) => "type alias", - ItemKind::OpaqueTy(..) => "opaque type", ItemKind::Enum(..) => "enum", ItemKind::Struct(..) => "struct", ItemKind::Union(..) => "union", @@ -3806,6 +3803,7 @@ pub enum Node<'hir> { Ty(&'hir Ty<'hir>), AssocItemConstraint(&'hir AssocItemConstraint<'hir>), TraitRef(&'hir TraitRef<'hir>), + OpaqueTy(&'hir OpaqueTy<'hir>), Pat(&'hir Pat<'hir>), PatField(&'hir PatField<'hir>), Arm(&'hir Arm<'hir>), @@ -3871,6 +3869,7 @@ impl<'hir> Node<'hir> { | Node::Crate(..) | Node::Ty(..) | Node::TraitRef(..) + | Node::OpaqueTy(..) | Node::Infer(..) | Node::WhereBoundPredicate(..) | Node::ArrayLenInfer(..) @@ -3996,6 +3995,7 @@ impl<'hir> Node<'hir> { | Node::TraitItem(TraitItem { generics, .. }) | Node::ImplItem(ImplItem { generics, .. }) => Some(generics), Node::Item(item) => item.kind.generics(), + Node::OpaqueTy(opaque) => Some(opaque.generics), _ => None, } } @@ -4055,6 +4055,7 @@ impl<'hir> Node<'hir> { expect_ty, &'hir Ty<'hir>, Node::Ty(n), n; expect_assoc_item_constraint, &'hir AssocItemConstraint<'hir>, Node::AssocItemConstraint(n), n; expect_trait_ref, &'hir TraitRef<'hir>, Node::TraitRef(n), n; + expect_opaque_ty, &'hir OpaqueTy<'hir>, Node::OpaqueTy(n), n; expect_pat, &'hir Pat<'hir>, Node::Pat(n), n; expect_pat_field, &'hir PatField<'hir>, Node::PatField(n), n; expect_arm, &'hir Arm<'hir>, Node::Arm(n), n; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d0a8aaa85bb29..58916d0586593 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -111,6 +111,7 @@ impl<'a> FnKind<'a> { pub trait Map<'hir> { /// Retrieves the `Node` corresponding to `id`. fn hir_node(&self, hir_id: HirId) -> Node<'hir>; + fn hir_node_by_def_id(&self, def_id: LocalDefId) -> Node<'hir>; fn body(&self, id: BodyId) -> &'hir Body<'hir>; fn item(&self, id: ItemId) -> &'hir Item<'hir>; fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir>; @@ -123,6 +124,9 @@ impl<'hir> Map<'hir> for ! { fn hir_node(&self, _: HirId) -> Node<'hir> { *self; } + fn hir_node_by_def_id(&self, _: LocalDefId) -> Node<'hir> { + *self; + } fn body(&self, _: BodyId) -> &'hir Body<'hir> { *self; } @@ -423,6 +427,9 @@ pub trait Visitor<'v>: Sized { fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>) -> Self::Result { walk_poly_trait_ref(self, t) } + fn visit_opaque_ty(&mut self, opaque: &'v OpaqueTy<'v>) -> Self::Result { + walk_opaque_ty(self, opaque) + } fn visit_variant_data(&mut self, s: &'v VariantData<'v>) -> Self::Result { walk_struct_def(self, s) } @@ -536,11 +543,6 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_generics(generics)); } - ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => { - try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(walk_generics(visitor, generics)); - walk_list!(visitor, visit_param_bound, bounds); - } ItemKind::Enum(ref enum_definition, ref generics) => { try_visit!(visitor.visit_generics(generics)); // `visit_enum_def()` takes care of visiting the `Item`'s `HirId`. @@ -894,8 +896,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); } - TyKind::OpaqueDef(item_id, lifetimes) => { - try_visit!(visitor.visit_nested_item(item_id)); + TyKind::OpaqueDef(opaque, lifetimes) => { + try_visit!(visitor.visit_opaque_ty(opaque)); walk_list!(visitor, visit_generic_arg, lifetimes); } TyKind::Array(ref ty, ref length) => { @@ -1185,6 +1187,15 @@ pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>( visitor.visit_trait_ref(&trait_ref.trait_ref) } +pub fn walk_opaque_ty<'v, V: Visitor<'v>>(visitor: &mut V, opaque: &'v OpaqueTy<'v>) -> V::Result { + let &OpaqueTy { hir_id, def_id: _, generics, bounds, origin: _, lifetime_mapping: _, span: _ } = + opaque; + try_visit!(visitor.visit_id(hir_id)); + try_visit!(walk_generics(visitor, generics)); + walk_list!(visitor, visit_param_bound, bounds); + V::Result::output() +} + pub fn walk_struct_def<'v, V: Visitor<'v>>( visitor: &mut V, struct_definition: &'v VariantData<'v>, diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 155270ca6a7c2..6ff57396b4a19 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -34,7 +34,6 @@ pub enum Target { ForeignMod, GlobalAsm, TyAlias, - OpaqueTy, Enum, Variant, Struct, @@ -79,7 +78,6 @@ impl Target { | Target::ForeignMod | Target::GlobalAsm | Target::TyAlias - | Target::OpaqueTy | Target::Enum | Target::Variant | Target::Struct @@ -114,7 +112,6 @@ impl Target { ItemKind::ForeignMod { .. } => Target::ForeignMod, ItemKind::GlobalAsm(..) => Target::GlobalAsm, ItemKind::TyAlias(..) => Target::TyAlias, - ItemKind::OpaqueTy(..) => Target::OpaqueTy, ItemKind::Enum(..) => Target::Enum, ItemKind::Struct(..) => Target::Struct, ItemKind::Union(..) => Target::Union, @@ -137,7 +134,6 @@ impl Target { DefKind::ForeignMod => Target::ForeignMod, DefKind::GlobalAsm => Target::GlobalAsm, DefKind::TyAlias => Target::TyAlias, - DefKind::OpaqueTy => Target::OpaqueTy, DefKind::Enum => Target::Enum, DefKind::Struct => Target::Struct, DefKind::Union => Target::Union, @@ -191,7 +187,6 @@ impl Target { Target::ForeignMod => "foreign module", Target::GlobalAsm => "global asm", Target::TyAlias => "type alias", - Target::OpaqueTy => "opaque type", Target::Enum => "enum", Target::Variant => "enum variant", Target::Struct => "struct", diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 785258d011c1a..eb62ff86c7179 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -252,10 +252,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` /// projections that would result in "inheriting lifetimes". fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let item = tcx.hir().expect_item(def_id); - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { - tcx.dcx().span_bug(item.span, "expected opaque item"); - }; + let hir::OpaqueTy { origin, .. } = tcx.hir().expect_opaque_ty(def_id); // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting // `async-std` (and `pub async fn` in general). @@ -265,16 +262,16 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { return; } - let span = tcx.def_span(item.owner_id.def_id); + let span = tcx.def_span(def_id); - if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() { + if tcx.type_of(def_id).instantiate_identity().references_error() { return; } - if check_opaque_for_cycles(tcx, item.owner_id.def_id, span).is_err() { + if check_opaque_for_cycles(tcx, def_id, span).is_err() { return; } - let _ = check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, origin); + let _ = check_opaque_meets_bounds(tcx, def_id, span, origin); } /// Checks that an opaque type does not contain cycles. @@ -481,8 +478,7 @@ fn sanity_check_found_hidden_type<'tcx>( /// 2. Checking that all lifetimes that are implicitly captured are mentioned. /// 3. Asserting that all parameters mentioned in the captures list are invariant. fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) { - let hir::OpaqueTy { bounds, .. } = - *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let hir::OpaqueTy { bounds, .. } = *tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty(); let Some(precise_capturing_args) = bounds.iter().find_map(|bound| match *bound { hir::GenericBound::Use(bounds, ..) => Some(bounds), _ => None, diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 35c2b7e7ce2fb..80334c6efe7d6 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -93,7 +93,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( // it's a refinement to a TAIT. if !tcx.hir().get_if_local(impl_opaque.def_id).is_some_and(|node| { matches!( - node.expect_item().expect_opaque_ty().origin, + node.expect_opaque_ty().origin, hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::FnReturn { parent, .. } if parent == impl_m.def_id.expect_local() ) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 93b021be24584..bb517eec64dcd 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -260,8 +260,8 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( | hir::ItemKind::Trait(_, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Struct(_, generics) => (generics, true), - hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }) - | hir::ItemKind::TyAlias(_, generics) => (generics, false), + // FIXME: how to handle opaque types since no longer items + hir::ItemKind::TyAlias(_, generics) => (generics, false), // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. _ => return, }; @@ -731,18 +731,8 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } - // Don't call `type_of` on opaque types, since that depends on type - // checking function bodies. `check_item_type` ensures that it's called - // instead. - hir::ItemKind::OpaqueTy(..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().predicates_of(def_id); - tcx.ensure().explicit_item_bounds(def_id); - tcx.ensure().explicit_item_super_predicates(def_id); - tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); - } - + // FIXME: ok to ignore opaque tys in collection? + // hir::ItemKind::TyAlias(..) => { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); @@ -1852,12 +1842,8 @@ fn coroutine_for_closure(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DefId { } fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { - match tcx.hir_node_by_def_id(def_id) { - Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(opaque), .. }) => { - matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias { .. }) - } - _ => bug!("tried getting opaque_ty_origin for non-opaque: {:?}", def_id), - } + let opaque = tcx.hir().expect_opaque_ty(def_id); + matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias { .. }) } fn rendered_precise_capturing_args<'tcx>( @@ -1870,12 +1856,10 @@ fn rendered_precise_capturing_args<'tcx>( return tcx.rendered_precise_capturing_args(opaque_def_id); } - tcx.hir_node_by_def_id(def_id).expect_item().expect_opaque_ty().bounds.iter().find_map( - |bound| match bound { - hir::GenericBound::Use(args, ..) => { - Some(&*tcx.arena.alloc_from_iter(args.iter().map(|arg| arg.name()))) - } - _ => None, - }, - ) + tcx.hir_node_by_def_id(def_id).expect_opaque_ty().bounds.iter().find_map(|bound| match bound { + hir::GenericBound::Use(args, ..) => { + Some(&*tcx.arena.alloc_from_iter(args.iter().map(|arg| arg.name()))) + } + _ => None, + }) } diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index d76d9213129fa..8648a7d1e3220 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,4 +1,3 @@ -use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::intravisit; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -10,12 +9,10 @@ pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { return; } - for id in tcx.hir().items() { - let DefKind::OpaqueTy = tcx.def_kind(id.owner_id) else { continue }; - - let ty = tcx.type_of(id.owner_id).instantiate_identity(); - - tcx.dcx().emit_err(crate::errors::TypeOf { span: tcx.def_span(id.owner_id), ty }); + for id in tcx.hir_crate_items(()).opaques() { + let ty = tcx.type_of(id).instantiate_identity(); + let span = tcx.def_span(id); + tcx.dcx().emit_err(crate::errors::TypeOf { span, ty }); } } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 8ff9640a87489..14b6b17ed18a7 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -24,6 +24,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) = tcx.opt_rpitit_info(def_id.to_def_id()) { + debug!("RPITIT fn_def_id={fn_def_id:?} opaque_def_id={opaque_def_id:?}"); let trait_def_id = tcx.parent(fn_def_id); let opaque_ty_generics = tcx.generics_of(opaque_def_id); let opaque_ty_parent_count = opaque_ty_generics.parent_count; @@ -207,36 +208,33 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { | Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { Some(tcx.typeck_root_def_id(def_id.to_def_id())) } - Node::Item(item) => match item.kind { - ItemKind::OpaqueTy(&hir::OpaqueTy { - origin: - hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, in_trait_or_impl } - | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl }, - .. - }) => { - if in_trait_or_impl.is_some() { - assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn); - } else { - assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn); - } - Some(fn_def_id.to_def_id()) + Node::OpaqueTy(&hir::OpaqueTy { + origin: + hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, in_trait_or_impl } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl }, + .. + }) => { + if in_trait_or_impl.is_some() { + assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn); + } else { + assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn); } - ItemKind::OpaqueTy(&hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty }, - .. - }) => { - if in_assoc_ty { - assert_matches!(tcx.def_kind(parent), DefKind::AssocTy); - } else { - assert_matches!(tcx.def_kind(parent), DefKind::TyAlias); - } - debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent); - // Opaque types are always nested within another item, and - // inherit the generics of the item. - Some(parent.to_def_id()) + Some(fn_def_id.to_def_id()) + } + Node::OpaqueTy(&hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty }, + .. + }) => { + if in_assoc_ty { + assert_matches!(tcx.def_kind(parent), DefKind::AssocTy); + } else { + assert_matches!(tcx.def_kind(parent), DefKind::TyAlias); } - _ => None, - }, + debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent); + // Opaque types are always nested within another item, and + // inherit the generics of the item. + Some(parent.to_def_id()) + } _ => None, }; @@ -272,13 +270,14 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) - | ItemKind::OpaqueTy(..) | ItemKind::Union(..) => (None, Defaults::Allowed), ItemKind::Const(..) => (None, Defaults::Deny), _ => (None, Defaults::FutureCompatDisallowed), } } + Node::OpaqueTy(..) => (None, Defaults::Allowed), + // GATs Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => { (None, Defaults::Deny) diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 2418037ae962f..4346504450d0b 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -335,8 +335,7 @@ pub(super) fn explicit_item_bounds_with_filter( // RPITIT's bounds are the same as opaque type bounds, but with // a projection self type. Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { - let item = tcx.hir_node_by_def_id(opaque_def_id.expect_local()).expect_item(); - let opaque_ty = item.expect_opaque_ty(); + let opaque_ty = tcx.hir_node_by_def_id(opaque_def_id.expect_local()).expect_opaque_ty(); let item_ty = Ty::new_projection_from_args( tcx, def_id.to_def_id(), @@ -347,7 +346,7 @@ pub(super) fn explicit_item_bounds_with_filter( opaque_def_id.expect_local(), opaque_ty.bounds, item_ty, - item.span, + opaque_ty.span, filter, ); assert_only_contains_predicates_from(filter, bounds, item_ty); @@ -369,11 +368,7 @@ pub(super) fn explicit_item_bounds_with_filter( span, .. }) => associated_type_bounds(tcx, def_id, bounds, *span, filter), - hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, origin, .. }), - span, - .. - }) => match origin { + hir::Node::OpaqueTy(hir::OpaqueTy { bounds, origin, span, .. }) => match origin { // Since RPITITs are lowered as projections in `::lower_ty`, // when we're asking for the item bounds of the *opaques* in a trait's default // method signature, we need to map these projections back to opaques. @@ -412,7 +407,7 @@ pub(super) fn explicit_item_bounds_with_filter( } }, hir::Node::Item(hir::Item { kind: hir::ItemKind::TyAlias(..), .. }) => &[], - _ => bug!("item_bounds called on {:?}", def_id), + node => bug!("item_bounds called on {def_id:?} => {node:?}"), }; ty::EarlyBinder::bind(bounds) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 33f6623edfdf3..6d30f7c7b9d07 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -330,7 +330,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // Opaque types duplicate some of their generic parameters. // We create bi-directional Outlives predicates between the original // and the duplicated parameter, to ensure that they do not get out of sync. - if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node { + if let Node::OpaqueTy(..) = node { let opaque_ty_node = tcx.parent_hir_node(hir_id); let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes), .. }) = opaque_ty_node else { diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index a15621bf28b9b..513d4e9b4a555 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -486,6 +486,31 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] + fn visit_opaque_ty(&mut self, opaque: &'tcx rustc_hir::OpaqueTy<'tcx>) { + // We want to start our early-bound indices at the end of the parent scope, + // not including any parent `impl Trait`s. + let mut bound_vars = FxIndexMap::default(); + debug!(?opaque.generics.params); + for param in opaque.generics.params { + let (def_id, reg) = ResolvedArg::early(param); + bound_vars.insert(def_id, reg); + } + + let hir_id = self.tcx.local_def_id_to_hir_id(opaque.def_id); + let scope = Scope::Binder { + hir_id, + bound_vars, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| intravisit::walk_opaque_ty(this, opaque)) + }) + } + #[instrument(level = "debug", skip(self))] fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { match &item.kind { @@ -513,38 +538,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // These sorts of items have no lifetime parameters at all. intravisit::walk_item(self, item); } - hir::ItemKind::OpaqueTy(&hir::OpaqueTy { - origin: - hir::OpaqueTyOrigin::FnReturn { parent, .. } - | hir::OpaqueTyOrigin::AsyncFn { parent, .. } - | hir::OpaqueTyOrigin::TyAlias { parent, .. }, - generics, - .. - }) => { - // We want to start our early-bound indices at the end of the parent scope, - // not including any parent `impl Trait`s. - let mut bound_vars = FxIndexMap::default(); - debug!(?generics.params); - for param in generics.params { - let (def_id, reg) = ResolvedArg::early(param); - bound_vars.insert(def_id, reg); - } - - let scope = Scope::Root { opt_parent_item: Some(parent) }; - self.with(scope, |this| { - let scope = Scope::Binder { - hir_id: item.hir_id(), - bound_vars, - s: this.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; - this.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| intravisit::walk_item(this, item)) - }); - }) - } hir::ItemKind::TyAlias(_, generics) | hir::ItemKind::Const(_, generics, _) | hir::ItemKind::Enum(_, generics) @@ -689,17 +682,14 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }; self.with(scope, |this| this.visit_ty(mt.ty)); } - hir::TyKind::OpaqueDef(item_id, lifetimes) => { + hir::TyKind::OpaqueDef(opaque_ty, lifetimes) => { + self.visit_opaque_ty(opaque_ty); + // Resolve the lifetimes in the bounds to the lifetime defs in the generics. // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to // `type MyAnonTy<'b> = impl MyTrait<'b>;` // ^ ^ this gets resolved in the scope of // the opaque_ty generics - let opaque_ty = self.tcx.hir().item(item_id); - match &opaque_ty.kind { - hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: _, .. }) => {} - i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), - }; // Resolve the lifetimes that are applied to the opaque type. // These are resolved in the current scope. @@ -722,9 +712,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { { // Opaques do not declare their own lifetimes, so if a lifetime comes from an opaque // it must be a reified late-bound lifetime from a trait goal. - hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy { .. }, .. - }) => "higher-ranked lifetime from outer `impl Trait`", + hir::Node::OpaqueTy(_) => "higher-ranked lifetime from outer `impl Trait`", // Other items are fine. hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => { continue; @@ -740,8 +728,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let (span, label) = if lifetime.ident.span == self.tcx.def_span(lifetime_def_id) { - let opaque_span = self.tcx.def_span(item_id.owner_id); - (opaque_span, Some(opaque_span)) + (opaque_ty.span, Some(opaque_ty.span)) } else { (lifetime.ident.span, None) }; diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 3af4d1f5edad5..470bcaeded167 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -529,10 +529,6 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ let args = ty::GenericArgs::identity_for_item(tcx, def_id); Ty::new_adt(tcx, def, args) } - ItemKind::OpaqueTy(..) => tcx.type_of_opaque(def_id).map_or_else( - |CyclePlaceholder(guar)| Ty::new_error(tcx, guar), - |ty| ty.instantiate_identity(), - ), ItemKind::Trait(..) | ItemKind::TraitAlias(..) | ItemKind::Macro(..) @@ -545,6 +541,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ } }, + Node::OpaqueTy(..) => tcx.type_of_opaque(def_id).map_or_else( + |CyclePlaceholder(guar)| Ty::new_error(tcx, guar), + |ty| ty.instantiate_identity(), + ), + Node::ForeignItem(foreign_item) => match foreign_item.kind { ForeignItemKind::Fn(..) => { let args = ty::GenericArgs::identity_for_item(tcx, def_id); @@ -603,42 +604,25 @@ pub(super) fn type_of_opaque( def_id: DefId, ) -> Result>, CyclePlaceholder> { if let Some(def_id) = def_id.as_local() { - use rustc_hir::*; - - Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) { - Node::Item(item) => match item.kind { - ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. }, - .. - }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id), - ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. }, - .. - }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id), - // Opaque types desugared from `impl Trait`. - ItemKind::OpaqueTy(&OpaqueTy { - origin: - hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl } - | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl }, - .. - }) => { - if in_trait_or_impl == Some(hir::RpitContext::Trait) - && !tcx.defaultness(owner).has_value() - { - span_bug!( - tcx.def_span(def_id), - "tried to get type of this RPITIT with no definition" - ); - } - opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) - } - _ => { - span_bug!(item.span, "type_of_opaque: unexpected item type: {:?}", item.kind); + Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin { + hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => { + opaque::find_opaque_ty_constraints_for_tait(tcx, def_id) + } + hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => { + opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id) + } + // Opaque types desugared from `impl Trait`. + hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl } + | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => { + if in_trait_or_impl == Some(hir::RpitContext::Trait) + && !tcx.defaultness(owner).has_value() + { + span_bug!( + tcx.def_span(def_id), + "tried to get type of this RPITIT with no definition" + ); } - }, - - x => { - bug!("unexpected sort of node in type_of_opaque(): {:?}", x); + opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } })) } else { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 95f83836d7573..28a1fc887412f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2087,43 +2087,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); self.lower_path(opt_self_ty, path, hir_ty.hir_id, false) } - &hir::TyKind::OpaqueDef(item_id, lifetimes) => { - let opaque_ty = tcx.hir().item(item_id); - - match opaque_ty.kind { - hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin, .. }) => { - let local_def_id = item_id.owner_id.def_id; - // If this is an RPITIT and we are using the new RPITIT lowering scheme, we - // generate the def_id of an associated type for the trait and return as - // type a projection. - match origin { - hir::OpaqueTyOrigin::FnReturn { - in_trait_or_impl: Some(hir::RpitContext::Trait), - .. - } - | hir::OpaqueTyOrigin::AsyncFn { - in_trait_or_impl: Some(hir::RpitContext::Trait), - .. - } => self.lower_opaque_ty( - tcx.associated_type_for_impl_trait_in_trait(local_def_id) - .to_def_id(), - lifetimes, - true, - ), - hir::OpaqueTyOrigin::FnReturn { - in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), - .. - } - | hir::OpaqueTyOrigin::AsyncFn { - in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), - .. - } - | hir::OpaqueTyOrigin::TyAlias { .. } => { - self.lower_opaque_ty(local_def_id.to_def_id(), lifetimes, false) - } - } + &hir::TyKind::OpaqueDef(opaque_ty, lifetimes) => { + let local_def_id = opaque_ty.def_id; + + // If this is an RPITIT and we are using the new RPITIT lowering scheme, we + // generate the def_id of an associated type for the trait and return as + // type a projection. + match opaque_ty.origin { + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + } => self.lower_opaque_ty( + tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id(), + lifetimes, + true, + ), + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::TyAlias { .. } => { + self.lower_opaque_ty(local_def_id.to_def_id(), lifetimes, false) } - ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), } } // If we encounter a type relative path with RTN generics, then it must have @@ -2289,7 +2282,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span_bug!( tcx.def_span(param.def_id), "only expected lifetime for opaque's own generics, got {:?}", - param.kind + param ); }; let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { diff --git a/compiler/rustc_hir_analysis/src/variance/dump.rs b/compiler/rustc_hir_analysis/src/variance/dump.rs index dbaf9c2c6f075..a0fdf95a83173 100644 --- a/compiler/rustc_hir_analysis/src/variance/dump.rs +++ b/compiler/rustc_hir_analysis/src/variance/dump.rs @@ -1,6 +1,5 @@ use std::fmt::Write; -use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_middle::ty::{GenericArgs, TyCtxt}; use rustc_span::symbol::sym; @@ -24,18 +23,18 @@ fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { } pub(crate) fn variances(tcx: TyCtxt<'_>) { - if tcx.has_attr(CRATE_DEF_ID, sym::rustc_variance_of_opaques) { - for id in tcx.hir().items() { - let DefKind::OpaqueTy = tcx.def_kind(id.owner_id) else { continue }; + let crate_items = tcx.hir_crate_items(()); + if tcx.has_attr(CRATE_DEF_ID, sym::rustc_variance_of_opaques) { + for id in crate_items.opaques() { tcx.dcx().emit_err(crate::errors::VariancesOf { - span: tcx.def_span(id.owner_id), - variances: format_variances(tcx, id.owner_id.def_id), + span: tcx.def_span(id), + variances: format_variances(tcx, id), }); } } - for id in tcx.hir().items() { + for id in crate_items.free_items() { if !tcx.has_attr(id.owner_id, sym::rustc_variance) { continue; } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 1c52283d537d3..9fe6a8ee3425a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -96,6 +96,7 @@ impl<'a> State<'a> { Node::Ty(a) => self.print_type(a), Node::AssocItemConstraint(a) => self.print_assoc_item_constraint(a), Node::TraitRef(a) => self.print_trait_ref(a), + Node::OpaqueTy(o) => self.print_opaque_ty(o), Node::Pat(a) => self.print_pat(a), Node::PatField(a) => self.print_patfield(a), Node::Arm(a) => self.print_arm(a), @@ -568,11 +569,6 @@ impl<'a> State<'a> { state.print_type(ty); }); } - hir::ItemKind::OpaqueTy(opaque_ty) => { - self.print_item_type(item, opaque_ty.generics, |state| { - state.print_bounds("= impl", opaque_ty.bounds) - }); - } hir::ItemKind::Enum(ref enum_definition, params) => { self.print_enum_def(enum_definition, params, item.ident.name, item.span); } @@ -665,6 +661,15 @@ impl<'a> State<'a> { self.print_path(t.path, false); } + fn print_opaque_ty(&mut self, o: &hir::OpaqueTy<'_>) { + self.head("opaque"); + self.print_generic_params(o.generics.params); + self.print_where_clause(o.generics); + self.word("{"); + self.print_bounds("impl", o.bounds); + self.word("}"); + } + fn print_formal_generic_params(&mut self, generic_params: &[hir::GenericParam<'_>]) { if !generic_params.is_empty() { self.word("for"); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 487cc7e55cd9e..2c5f8adcb5d2b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -847,11 +847,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } hir::FnRetTy::Return(hir_ty) => { - if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind + if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind // FIXME: account for RPITIT. - && let hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(op_ty), .. - }) = self.tcx.hir_node(item_id.hir_id()) && let [hir::GenericBound::Trait(trait_ref, _)] = op_ty.bounds && let Some(hir::PathSegment { args: Some(generic_args), .. }) = trait_ref.trait_ref.path.segments.last() diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs index d904020730019..63a8a949e9699 100644 --- a/compiler/rustc_lint/src/async_fn_in_trait.rs +++ b/compiler/rustc_lint/src/async_fn_in_trait.rs @@ -104,8 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for AsyncFnInTrait { return; } - let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) = - sig.decl.output + let hir::FnRetTy::Return(hir::Ty { + kind: hir::TyKind::OpaqueDef(opaq_def, ..), .. + }) = sig.decl.output else { // This should never happen, but let's not ICE. return; @@ -114,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for AsyncFnInTrait { cx.tcx, sig, body, - def.owner_id.def_id, + opaq_def.def_id, " + Send", ); cx.tcx.emit_node_span_lint( diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 5aeaad420699e..d029ad934079b 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -258,7 +258,7 @@ where && self.seen.insert(opaque_def_id) // If it's owned by this function && let opaque = - self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty() + self.tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty() && let hir::OpaqueTyOrigin::FnReturn { parent, .. } = opaque.origin && parent == self.parent_def_id { diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 342ebfa0b06cc..ffbcf7f808ebe 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -68,8 +68,8 @@ declare_lint! { declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]); impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - let hir::ItemKind::OpaqueTy(opaque) = &item.kind else { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) { + let hir::TyKind::OpaqueDef(opaque, _) = &ty.kind else { return; }; @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { return; } - let def_id = item.owner_id.def_id.to_def_id(); + let def_id = opaque.def_id.to_def_id(); let infcx = &cx.tcx.infer_ctxt().build(); // For every projection predicate in the opaque type's explicit bounds, // check that the type that we're assigning actually satisfies the bounds diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 75611d7102545..db4413149a49f 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1419,7 +1419,6 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { hir::ItemKind::Impl(..) | hir::ItemKind::TraitAlias(..) | hir::ItemKind::Trait(..) - | hir::ItemKind::OpaqueTy(..) | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::Mod(..) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 72e6c96e6f646..8fd5ff1f369d4 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -732,6 +732,19 @@ impl<'hir> Map<'hir> { } } + #[track_caller] + pub fn expect_opaque_ty(self, id: LocalDefId) -> &'hir OpaqueTy<'hir> { + match self.tcx.hir_node_by_def_id(id) { + Node::OpaqueTy(opaq) => opaq, + _ => { + bug!( + "expected opaque type definition, found {}", + self.node_to_string(self.tcx.local_def_id_to_hir_id(id)) + ) + } + } + } + pub fn expect_expr(self, id: HirId) -> &'hir Expr<'hir> { match self.tcx.hir_node(id) { Node::Expr(expr) => expr, @@ -923,6 +936,7 @@ impl<'hir> Map<'hir> { Node::Ty(ty) => ty.span, Node::AssocItemConstraint(constraint) => constraint.span, Node::TraitRef(tr) => tr.path.span, + Node::OpaqueTy(op) => op.span, Node::Pat(pat) => pat.span, Node::PatField(field) => field.span, Node::Arm(arm) => arm.span, @@ -1006,6 +1020,10 @@ impl<'hir> intravisit::Map<'hir> for Map<'hir> { self.tcx.hir_node(hir_id) } + fn hir_node_by_def_id(&self, def_id: LocalDefId) -> Node<'hir> { + self.tcx.hir_node_by_def_id(def_id) + } + fn body(&self, id: BodyId) -> &'hir Body<'hir> { (*self).body(id) } @@ -1139,7 +1157,6 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { ItemKind::ForeignMod { .. } => "foreign mod", ItemKind::GlobalAsm(..) => "global asm", ItemKind::TyAlias(..) => "ty", - ItemKind::OpaqueTy(..) => "opaque type", ItemKind::Enum(..) => "enum", ItemKind::Struct(..) => "struct", ItemKind::Union(..) => "union", @@ -1191,6 +1208,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Ty(_) => node_str("type"), Node::AssocItemConstraint(_) => node_str("assoc item constraint"), Node::TraitRef(_) => node_str("trait ref"), + Node::OpaqueTy(_) => node_str("opaque type"), Node::Pat(_) => node_str("pat"), Node::PatField(_) => node_str("pattern field"), Node::Param(_) => node_str("param"), @@ -1228,6 +1246,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod impl_items, foreign_items, body_owners, + opaques, .. } = collector; ModuleItems { @@ -1237,6 +1256,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), body_owners: body_owners.into_boxed_slice(), + opaques: opaques.into_boxed_slice(), } } @@ -1256,6 +1276,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { impl_items, foreign_items, body_owners, + opaques, .. } = collector; @@ -1266,6 +1287,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), body_owners: body_owners.into_boxed_slice(), + opaques: opaques.into_boxed_slice(), } } @@ -1280,6 +1302,7 @@ struct ItemCollector<'tcx> { impl_items: Vec, foreign_items: Vec, body_owners: Vec, + opaques: Vec, } impl<'tcx> ItemCollector<'tcx> { @@ -1293,6 +1316,7 @@ impl<'tcx> ItemCollector<'tcx> { impl_items: Vec::default(), foreign_items: Vec::default(), body_owners: Vec::default(), + opaques: Vec::default(), } } } @@ -1338,6 +1362,11 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { intravisit::walk_inline_const(self, c) } + fn visit_opaque_ty(&mut self, o: &'hir OpaqueTy<'hir>) { + self.opaques.push(o.def_id); + intravisit::walk_opaque_ty(self, o) + } + fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { if let ExprKind::Closure(closure) = ex.kind { self.body_owners.push(closure.def_id); diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 7a07ef80dedb4..ad0d70152e1ad 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -28,6 +28,7 @@ pub struct ModuleItems { trait_items: Box<[TraitItemId]>, impl_items: Box<[ImplItemId]>, foreign_items: Box<[ForeignItemId]>, + opaques: Box<[LocalDefId]>, body_owners: Box<[LocalDefId]>, } @@ -65,6 +66,10 @@ impl ModuleItems { .chain(self.foreign_items.iter().map(|id| id.owner_id)) } + pub fn opaques(&self) -> impl Iterator + '_ { + self.opaques.iter().copied() + } + pub fn definitions(&self) -> impl Iterator + '_ { self.owners().map(|id| id.def_id) } @@ -96,6 +101,13 @@ impl ModuleItems { ) -> Result<(), ErrorGuaranteed> { try_par_for_each_in(&self.foreign_items[..], |&id| f(id)) } + + pub fn par_opaques( + &self, + f: impl Fn(LocalDefId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, + ) -> Result<(), ErrorGuaranteed> { + try_par_for_each_in(&self.opaques[..], |&id| f(id)) + } } impl<'tcx> TyCtxt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 2ffb273cb6fc9..d6547b51186b6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -56,7 +56,7 @@ use rustc_type_ir::lang_items::TraitSolverLangItem; pub use rustc_type_ir::lift::Lift; use rustc_type_ir::solve::SolverMode; use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, search_graph}; -use tracing::{debug, instrument}; +use tracing::{debug, trace}; use crate::arena::Arena; use crate::dep_graph::{DepGraph, DepKindStruct}; @@ -2073,9 +2073,11 @@ impl<'tcx> TyCtxt<'tcx> { } /// Returns the origin of the opaque type `def_id`. - #[instrument(skip(self), level = "trace", ret)] + #[track_caller] pub fn opaque_type_origin(self, def_id: LocalDefId) -> hir::OpaqueTyOrigin { - self.hir().expect_item(def_id).expect_opaque_ty().origin + let origin = self.hir().expect_opaque_ty(def_id).origin; + trace!("opaque_type_origin({def_id:?}) => {origin:?}"); + origin } } @@ -3031,8 +3033,7 @@ impl<'tcx> TyCtxt<'tcx> { loop { let parent = self.local_parent(opaque_lifetime_param_def_id); - let hir::OpaqueTy { lifetime_mapping, .. } = - self.hir_node_by_def_id(parent).expect_item().expect_opaque_ty(); + let hir::OpaqueTy { lifetime_mapping, .. } = self.hir().expect_opaque_ty(parent); let Some((lifetime, _)) = lifetime_mapping .iter() diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index d98e18c1b0c2e..751f0c71eb4b5 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -507,14 +507,8 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { .. }, _, - ) => { - self.0.push(ty); - } - hir::TyKind::OpaqueDef(item_id, _) => { - self.0.push(ty); - let item = self.1.item(item_id); - hir::intravisit::walk_item(self, item); - } + ) + | hir::TyKind::OpaqueDef(..) => self.0.push(ty), _ => {} } hir::intravisit::walk_ty(self, ty); diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 489bd49f3e4b6..a55ca5a01736f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -21,6 +21,7 @@ use rustc_target::spec::abi; use rustc_type_ir::TyKind::*; use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind}; +use tracing::instrument; use ty::util::{AsyncDropGlueMorphology, IntTypeExt}; use super::GenericParamDefKind; @@ -500,6 +501,7 @@ impl<'tcx> Ty<'tcx> { } #[inline] + #[instrument(level = "debug", skip(tcx))] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { Ty::new_alias(tcx, ty::Opaque, AliasTy::new_from_args(tcx, def_id, args)) } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a24259a1e358f..b3334bb70aa8d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -814,7 +814,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::Mod | Target::GlobalAsm | Target::TyAlias - | Target::OpaqueTy | Target::Enum | Target::Variant | Target::Struct @@ -1328,7 +1327,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) { let article = match target { Target::ExternCrate - | Target::OpaqueTy | Target::Enum | Target::Impl | Target::Expression diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 100f3e80603e5..af17fbf7e4df3 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -41,6 +41,7 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { | Node::TraitItem(..) | Node::Variant(..) | Node::AnonConst(..) + | Node::OpaqueTy(..) ) } @@ -494,6 +495,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { Node::ForeignItem(foreign_item) => { intravisit::walk_foreign_item(self, foreign_item); } + Node::OpaqueTy(opaq) => intravisit::walk_opaque_ty(self, opaq), _ => {} } self.repr_has_repr_simd = had_repr_simd; @@ -655,14 +657,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { intravisit::walk_path(self, path); } - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if let TyKind::OpaqueDef(item_id, _) = ty.kind { - let item = self.tcx.hir().item(item_id); - intravisit::walk_item(self, item); - } - intravisit::walk_ty(self, ty); - } - fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { // When inline const blocks are used in pattern position, paths // referenced by it should be considered as used. diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 903fb1147440c..8ad14b6eb74b2 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -230,7 +230,6 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { ForeignMod, GlobalAsm, TyAlias, - OpaqueTy, Enum, Struct, Union, diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 925ee26202283..056318fbcb793 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -236,7 +236,6 @@ impl<'tcx> ReachableContext<'tcx> { // worklist, as determined by the privacy pass hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) - | hir::ItemKind::OpaqueTy(..) | hir::ItemKind::TyAlias(..) | hir::ItemKind::Macro(..) | hir::ItemKind::Mod(..) @@ -287,7 +286,8 @@ impl<'tcx> ReachableContext<'tcx> { | Node::Field(_) | Node::Ty(_) | Node::Crate(_) - | Node::Synthetic => {} + | Node::Synthetic + | Node::OpaqueTy(..) => {} _ => { bug!( "found unexpected node kind in worklist: {} ({:?})", diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index d00e7eff75212..36eada4c1d766 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -634,10 +634,8 @@ impl<'tcx> EmbargoVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - if self.impl_trait_pass - && let hir::ItemKind::OpaqueTy(opaque) = item.kind - { + fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) { + if self.impl_trait_pass { let should_visit = match opaque.origin { hir::OpaqueTyOrigin::FnReturn { parent, @@ -669,14 +667,16 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time // reachable if they are returned via `impl Trait`, even from private functions. let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); - self.reach_through_impl_trait(item.owner_id.def_id, pub_ev) - .generics() - .predicates() - .ty(); + self.reach_through_impl_trait(opaque.def_id, pub_ev).generics().predicates().ty(); return; } } + // Visit nested items. + intravisit::walk_opaque_ty(self, opaque) + } + + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { // Update levels of nested things and mark all items // in interfaces of reachable items as reachable. let item_ev = self.get(item.owner_id.def_id); @@ -686,7 +686,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { | hir::ItemKind::ExternCrate(..) | hir::ItemKind::GlobalAsm(..) => {} // The interface is empty, and all nested items are processed by `visit_item`. - hir::ItemKind::Mod(..) | hir::ItemKind::OpaqueTy(..) => {} + hir::ItemKind::Mod(..) => {} hir::ItemKind::Macro(macro_def, _) => { if let Some(item_ev) = item_ev { self.update_reachability_from_macro(item.owner_id.def_id, macro_def, item_ev); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 31256bca55e93..a6ecd1cc9871b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -284,14 +284,9 @@ pub fn suggest_new_region_bound( } match fn_return.kind { // FIXME(precise_captures): Suggest adding to `use<...>` list instead. - TyKind::OpaqueDef(item_id, _) => { - let item = tcx.hir().item(item_id); - let ItemKind::OpaqueTy(opaque) = &item.kind else { - return; - }; - + TyKind::OpaqueDef(opaque, _) => { // Get the identity type for this RPIT - let did = item_id.owner_id.to_def_id(); + let did = opaque.def_id.to_def_id(); let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did)); if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 7802d5bf7a6b9..cf0ab630f2e21 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -720,7 +720,7 @@ fn foo(&self) -> Self::T { String::new() } if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() { let opaque_local_def_id = def_id.as_local(); let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { - tcx.hir().expect_item(opaque_local_def_id).expect_opaque_ty() + tcx.hir().expect_opaque_ty(opaque_local_def_id) } else { return false; }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index a2d717817db14..94610a9e0e653 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -842,14 +842,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { lifetime: Region<'tcx>, add_lt_suggs: &mut Vec<(Span, String)>, ) -> String { - struct LifetimeReplaceVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, + struct LifetimeReplaceVisitor<'a> { needle: hir::LifetimeName, new_lt: &'a str, add_lt_suggs: &'a mut Vec<(Span, String)>, } - impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_, 'tcx> { + impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> { fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) { if lt.res == self.needle { self.add_lt_suggs.push(lt.suggestion(self.new_lt)); @@ -857,10 +856,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { - let hir::TyKind::OpaqueDef(item_id, _) = ty.kind else { + let hir::TyKind::OpaqueDef(opaque_ty, _) = ty.kind else { return hir::intravisit::walk_ty(self, ty); }; - let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); if let Some(&(_, b)) = opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle) { @@ -905,7 +903,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let mut visitor = LifetimeReplaceVisitor { - tcx: self.tcx, needle: hir::LifetimeName::Param(lifetime_def_id), add_lt_suggs, new_lt: &new_lt, @@ -1269,7 +1266,7 @@ fn suggest_precise_capturing<'tcx>( diag: &mut Diag<'_>, ) { let hir::OpaqueTy { bounds, origin, .. } = - tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty(); let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } = *origin else { return; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index 6c3f3afce11b9..709b6eb18e35f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -731,12 +731,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let exp_local_id = exp_def_id.as_local()?; match ( - &self.tcx.hir().expect_item(last_local_id).kind, - &self.tcx.hir().expect_item(exp_local_id).kind, + &self.tcx.hir().expect_opaque_ty(last_local_id), + &self.tcx.hir().expect_opaque_ty(exp_local_id), ) { ( - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), + hir::OpaqueTy { bounds: last_bounds, .. }, + hir::OpaqueTy { bounds: exp_bounds, .. }, ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match ( left, right, ) { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 6df7fac949c2f..87834c329e19f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -355,12 +355,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | hir::ItemKind::Fn(_, generics, _) | hir::ItemKind::TyAlias(_, generics) | hir::ItemKind::Const(_, generics, _) - | hir::ItemKind::TraitAlias(generics, _) - | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), + | hir::ItemKind::TraitAlias(generics, _), .. }) | hir::Node::TraitItem(hir::TraitItem { generics, .. }) | hir::Node::ImplItem(hir::ImplItem { generics, .. }) + | hir::Node::OpaqueTy(hir::OpaqueTy { generics, .. }) if param_ty => { // We skip the 0'th arg (self) because we do not want @@ -421,10 +421,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | hir::ItemKind::Fn(_, generics, _) | hir::ItemKind::TyAlias(_, generics) | hir::ItemKind::Const(_, generics, _) - | hir::ItemKind::TraitAlias(generics, _) - | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), + | hir::ItemKind::TraitAlias(generics, _), .. - }) if !param_ty => { + }) + | hir::Node::OpaqueTy(hir::OpaqueTy { generics, .. }) + if !param_ty => + { // Missing generic type parameter bound. if suggest_arbitrary_trait_bound( self.tcx, @@ -4542,7 +4544,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // ... whose signature is `async` (i.e. this is an AFIT) let (sig, body) = item.expect_fn(); - let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) = + let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(opaq_def, ..), .. }) = sig.decl.output else { // This should never happen, but let's not ICE. @@ -4551,7 +4553,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Check that this is *not* a nested `impl Future` RPIT in an async fn // (i.e. `async fn foo() -> impl Future`) - if def.owner_id.to_def_id() != opaque_def_id { + if opaq_def.def_id.to_def_id() != opaque_def_id { return; } @@ -5159,7 +5161,7 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( }; let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span); - let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let future = tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty(); let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { // `async fn` should always lower to a single bound... but don't ICE. return None; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index e41f2c8ce4869..a057caa932952 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -316,19 +316,16 @@ fn associated_types_for_impl_traits_in_associated_fn( match tcx.def_kind(parent_def_id) { DefKind::Trait => { - struct RPITVisitor<'tcx> { + struct RPITVisitor { rpits: FxIndexSet, - tcx: TyCtxt<'tcx>, } - impl<'tcx> Visitor<'tcx> for RPITVisitor<'tcx> { + impl<'tcx> Visitor<'tcx> for RPITVisitor { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if let hir::TyKind::OpaqueDef(item_id, _) = ty.kind - && self.rpits.insert(item_id.owner_id.def_id) + if let hir::TyKind::OpaqueDef(opaq, _) = ty.kind + && self.rpits.insert(opaq.def_id) { - let opaque_item = - self.tcx.hir().expect_item(item_id.owner_id.def_id).expect_opaque_ty(); - for bound in opaque_item.bounds { + for bound in opaq.bounds { intravisit::walk_param_bound(self, bound); } } @@ -336,7 +333,7 @@ fn associated_types_for_impl_traits_in_associated_fn( } } - let mut visitor = RPITVisitor { tcx, rpits: FxIndexSet::default() }; + let mut visitor = RPITVisitor { rpits: FxIndexSet::default() }; if let Some(output) = tcx.hir().get_fn_output(fn_def_id) { visitor.visit_fn_ret_ty(output); diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 7c4b4887b2d75..5e2232ff47d73 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -132,6 +132,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { TaitInBodyFinder { collector: self }.visit_expr(body); } + #[instrument(level = "debug", skip(self))] fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) { if !self.seen.insert(alias_ty.def_id.expect_local()) { return; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b79458eaa7890..fa73733360ca1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1828,13 +1828,8 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T Array(Box::new(clean_ty(ty, cx)), length.into()) } TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()), - TyKind::OpaqueDef(item_id, _) => { - let item = cx.tcx.hir().item(item_id); - if let hir::ItemKind::OpaqueTy(ty) = item.kind { - ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) - } else { - unreachable!() - } + TyKind::OpaqueDef(ty, _) => { + ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, lifetime, _) => { @@ -2736,9 +2731,6 @@ fn clean_maybe_renamed_item<'tcx>( type_: clean_ty(ty, cx), kind: ConstantKind::Local { body: body_id, def_id }, })), - // clean_ty changes types which reference an OpaqueTy item to instead be - // an ImplTrait, so it's ok to return nothing here. - ItemKind::OpaqueTy(_) => return vec![], ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 2143f1ff23650..7e1ea5cde8320 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -243,7 +243,6 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { | ItemKind::ExternCrate(_) | ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm(_) - | ItemKind::OpaqueTy(_) // We already have "visit_mod" above so no need to check it here. | ItemKind::Mod(_) => {} } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 6defe91d38303..f789aca73784d 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -505,21 +505,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::TyAlias(..) - | hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { .. }, - .. - }) | hir::ItemKind::Static(..) | hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) => { self.add_to_current_mod(item, renamed, import_id); } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn { .. } | hir::OpaqueTyOrigin::FnReturn { .. }, - .. - }) => { - // return-position impl traits are never nameable, and should never be documented. - } hir::ItemKind::Const(..) => { // Underscore constants do not correspond to a nameable item and // so are never useful in documentation. diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 9143d292f670f..b18997e6ee420 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -220,7 +220,7 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) { ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), ItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")), - ItemKind::TyAlias(..) | ItemKind::OpaqueTy(_) => (Pat::Str("type"), Pat::Str(";")), + ItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")), ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")), ItemKind::Struct(VariantData::Struct { .. }, _) => (Pat::Str("struct"), Pat::Str("}")), ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")), From 68f7ed4495ac5b000a5dc17fc0fd8d8760e68727 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 18 Aug 2024 14:33:03 +0000 Subject: [PATCH 1230/1301] WfCheck opaques. --- .../rustc_hir_analysis/src/check/wfcheck.rs | 30 +++++++++++-------- compiler/rustc_hir_analysis/src/collect.rs | 13 ++++++++ compiler/rustc_middle/src/query/mod.rs | 2 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 02d23b95d46e6..3a9d2640eee93 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -185,15 +185,16 @@ where } } -fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> { - let node = tcx.hir_owner_node(def_id); +fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { + let node = tcx.hir_node_by_def_id(def_id); let mut res = match node { - hir::OwnerNode::Crate(_) => bug!("check_well_formed cannot be applied to the crate root"), - hir::OwnerNode::Item(item) => check_item(tcx, item), - hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item), - hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item), - hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item), - hir::OwnerNode::Synthetic => unreachable!(), + hir::Node::Crate(_) => bug!("check_well_formed cannot be applied to the crate root"), + hir::Node::Item(item) => check_item(tcx, item), + hir::Node::TraitItem(item) => check_trait_item(tcx, item), + hir::Node::ImplItem(item) => check_impl_item(tcx, item), + hir::Node::ForeignItem(item) => check_foreign_item(tcx, item), + hir::Node::OpaqueTy(_) => Ok(crate::check::check::check_item_type(tcx, def_id)), + _ => unreachable!(), }; if let Some(generics) = node.generics() { @@ -201,6 +202,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorG res = res.and(check_param_wf(tcx, param)); } } + res } @@ -2172,10 +2174,14 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), ErrorGuaranteed> { let items = tcx.hir_module_items(module); - let mut res = items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id)); - res = res.and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id))); - res = res.and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id))); - res = res.and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id))); + let mut res = items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)); + res = + res.and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id))); + res = + res.and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id))); + res = res + .and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id))); + res = res.and(items.par_opaques(|item| tcx.ensure().check_well_formed(item))); if module == LocalModDefId::CRATE_DEF_ID { super::entry::check_for_entry_fn(tcx); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index bb517eec64dcd..02ad5355d252b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -328,6 +328,19 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { intravisit::walk_expr(self, expr); } + /// Don't call `type_of` on opaque types, since that depends on type checking function bodies. + /// `check_item_type` ensures that it's called instead. + fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) { + let def_id = opaque.def_id; + self.tcx.ensure().generics_of(def_id); + self.tcx.ensure().predicates_of(def_id); + self.tcx.ensure().explicit_item_bounds(def_id); + self.tcx.ensure().explicit_item_super_predicates(def_id); + self.tcx.ensure().item_bounds(def_id); + self.tcx.ensure().item_super_predicates(def_id); + intravisit::walk_opaque_ty(self, opaque); + } + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { lower_trait_item(self.tcx, trait_item.trait_item_id()); intravisit::walk_trait_item(self, trait_item); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1f0aab6d6b321..bd6a7578a6864 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1552,7 +1552,7 @@ rustc_queries! { feedable } - query check_well_formed(key: hir::OwnerId) -> Result<(), ErrorGuaranteed> { + query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } ensure_forwards_result_if_red } From e740c7b6241f5396db081d34a33894fb8bcfbffe Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 18 Aug 2024 15:25:42 +0000 Subject: [PATCH 1231/1301] Visit opaques for visibilities. --- compiler/rustc_privacy/src/lib.rs | 98 +++++++++++++++---------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 36eada4c1d766..f67c4cb922cee 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -402,8 +402,6 @@ struct EmbargoVisitor<'tcx> { /// n::p::f() /// } macro_reachable: FxHashSet<(LocalModDefId, LocalModDefId)>, - /// Preliminary pass for marking all underlying types of `impl Trait`s as reachable. - impl_trait_pass: bool, /// Has something changed in the level map? changed: bool, } @@ -634,48 +632,6 @@ impl<'tcx> EmbargoVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { - fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) { - if self.impl_trait_pass { - let should_visit = match opaque.origin { - hir::OpaqueTyOrigin::FnReturn { - parent, - in_trait_or_impl: Some(hir::RpitContext::Trait), - } - | hir::OpaqueTyOrigin::AsyncFn { - parent, - in_trait_or_impl: Some(hir::RpitContext::Trait), - } => match self.tcx.hir_node_by_def_id(parent).expect_trait_item().expect_fn().1 { - hir::TraitFn::Required(_) => false, - hir::TraitFn::Provided(..) => true, - }, - - // Always visit RPITs in functions that have definitions, - // and all TAITs. - hir::OpaqueTyOrigin::FnReturn { - in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), - .. - } - | hir::OpaqueTyOrigin::AsyncFn { - in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), - .. - } - | hir::OpaqueTyOrigin::TyAlias { .. } => true, - }; - - if should_visit { - // FIXME: This is some serious pessimization intended to workaround deficiencies - // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time - // reachable if they are returned via `impl Trait`, even from private functions. - let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); - self.reach_through_impl_trait(opaque.def_id, pub_ev).generics().predicates().ty(); - return; - } - } - - // Visit nested items. - intravisit::walk_opaque_ty(self, opaque) - } - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { // Update levels of nested things and mark all items // in interfaces of reachable items as reachable. @@ -1752,19 +1708,59 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { tcx, effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(), macro_reachable: Default::default(), - // HACK(jynelson): trying to infer the type of `impl Trait` breaks `async-std` (and - // `pub async fn` in general). Since rustdoc never needs to do codegen and doesn't - // care about link-time reachability, keep them unreachable (issue #75100). - impl_trait_pass: !tcx.sess.opts.actually_rustdoc, changed: false, }; visitor.effective_visibilities.check_invariants(tcx); - if visitor.impl_trait_pass { + + // HACK(jynelson): trying to infer the type of `impl Trait` breaks `async-std` (and + // `pub async fn` in general). Since rustdoc never needs to do codegen and doesn't + // care about link-time reachability, keep them unreachable (issue #75100). + let impl_trait_pass = !tcx.sess.opts.actually_rustdoc; + if impl_trait_pass { // Underlying types of `impl Trait`s are marked as reachable unconditionally, // so this pass doesn't need to be a part of the fixed point iteration below. - tcx.hir().visit_all_item_likes_in_crate(&mut visitor); - visitor.impl_trait_pass = false; + let krate = tcx.hir_crate_items(()); + for id in krate.opaques() { + let opaque = tcx.hir_node_by_def_id(id).expect_opaque_ty(); + let should_visit = match opaque.origin { + hir::OpaqueTyOrigin::FnReturn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } + | hir::OpaqueTyOrigin::AsyncFn { + parent, + in_trait_or_impl: Some(hir::RpitContext::Trait), + } => match tcx.hir_node_by_def_id(parent).expect_trait_item().expect_fn().1 { + hir::TraitFn::Required(_) => false, + hir::TraitFn::Provided(..) => true, + }, + + // Always visit RPITs in functions that have definitions, + // and all TAITs. + hir::OpaqueTyOrigin::FnReturn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), + .. + } + | hir::OpaqueTyOrigin::TyAlias { .. } => true, + }; + if should_visit { + // FIXME: This is some serious pessimization intended to workaround deficiencies + // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time + // reachable if they are returned via `impl Trait`, even from private functions. + let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public); + visitor + .reach_through_impl_trait(opaque.def_id, pub_ev) + .generics() + .predicates() + .ty(); + } + } + visitor.changed = false; } From d9f15faf3a7ceebca239f511d5f06ada5a87b281 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 22 Aug 2024 00:55:09 +0000 Subject: [PATCH 1232/1301] Bless ui tests. --- .../associated-type-bounds/duplicate.stderr | 66 ++++++------- .../async-await/async-fn/edition-2015.stderr | 8 +- .../inference_var_self_argument.stderr | 18 ++-- tests/ui/async-await/issue-66312.stderr | 12 +-- tests/ui/const-generics/opaque_types.stderr | 16 ++-- tests/ui/delegation/unsupported.stderr | 32 +++---- ...ature-gate-impl_trait_in_assoc_type.stderr | 16 ++-- .../impl-fn-predefined-lifetimes.stderr | 5 +- .../ui/impl-trait/issues/issue-78722-2.stderr | 12 +-- ...s-impl-trait-declaration-too-subtle.stderr | 44 ++++----- tests/ui/impl-trait/where-allowed.stderr | 94 +++++++++---------- tests/ui/privacy/private-type-in-interface.rs | 2 - .../privacy/private-type-in-interface.stderr | 16 +--- .../const-impl-trait.stderr | 56 +++++------ tests/ui/self/arbitrary-self-opaque.stderr | 18 ++-- ...f_types_pin_lifetime_mismatch-async.stderr | 12 +-- tests/ui/self/elision/lt-ref-self-async.fixed | 12 +-- .../ui/self/elision/lt-ref-self-async.stderr | 24 ++--- tests/ui/self/elision/ref-assoc-async.stderr | 20 ++-- .../ui/self/elision/ref-mut-self-async.stderr | 24 ++--- .../self/elision/ref-mut-struct-async.stderr | 20 ++-- tests/ui/self/elision/ref-self-async.stderr | 28 +++--- tests/ui/self/elision/ref-struct-async.stderr | 20 ++-- .../const_generic_type.no_infer.stderr | 16 ++-- .../constrain_inputs.stderr | 26 ++--- .../generic_underconstrained.stderr | 22 ++--- .../generic_underconstrained2.stderr | 44 ++++----- ...iled-to-resolve-instance-for-110696.stderr | 12 +-- .../in-where-clause.stderr | 42 ++++----- .../ui/type-alias-impl-trait/issue-53092-2.rs | 9 +- .../issue-53092-2.stderr | 66 +++---------- .../issue-84660-unsoundness.current.stderr | 18 ++-- .../issue-84660-unsoundness.next.stderr | 18 ++-- .../nested-in-anon-const.stderr | 16 ++-- ...alias-impl-trait-with-cycle-error-4.stderr | 14 +-- .../ui/typeck/typeck_type_placeholder_item.rs | 4 +- .../typeck_type_placeholder_item.stderr | 91 +++++++++--------- 37 files changed, 454 insertions(+), 519 deletions(-) diff --git a/tests/ui/associated-type-bounds/duplicate.stderr b/tests/ui/associated-type-bounds/duplicate.stderr index cf4809991c370..0dabcbdce1bbf 100644 --- a/tests/ui/associated-type-bounds/duplicate.stderr +++ b/tests/ui/associated-type-bounds/duplicate.stderr @@ -208,17 +208,6 @@ LL | fn FRPIT1() -> impl Iterator { | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0282]: type annotations needed - --> $DIR/duplicate.rs:136:5 - | -LL | iter::empty() - | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` - | -help: consider specifying the generic argument - | -LL | iter::empty::() - | +++++ - error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified --> $DIR/duplicate.rs:139:42 | @@ -237,17 +226,6 @@ LL | fn FRPIT2() -> impl Iterator { | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0282]: type annotations needed - --> $DIR/duplicate.rs:142:5 - | -LL | iter::empty() - | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` - | -help: consider specifying the generic argument - | -LL | iter::empty::() - | +++++ - error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified --> $DIR/duplicate.rs:145:45 | @@ -266,17 +244,6 @@ LL | fn FRPIT3() -> impl Iterator { | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0282]: type annotations needed - --> $DIR/duplicate.rs:148:5 - | -LL | iter::empty() - | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` - | -help: consider specifying the generic argument - | -LL | iter::empty::() - | +++++ - error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified --> $DIR/duplicate.rs:151:40 | @@ -697,6 +664,39 @@ LL | type A: Iterator; | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error[E0282]: type annotations needed + --> $DIR/duplicate.rs:136:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + +error[E0282]: type annotations needed + --> $DIR/duplicate.rs:142:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + +error[E0282]: type annotations needed + --> $DIR/duplicate.rs:148:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` + | +help: consider specifying the generic argument + | +LL | iter::empty::() + | +++++ + error: aborting due to 81 previous errors Some errors have detailed explanations: E0282, E0719. diff --git a/tests/ui/async-await/async-fn/edition-2015.stderr b/tests/ui/async-await/async-fn/edition-2015.stderr index 23ffee0d0a6b2..358bb3e112ed4 100644 --- a/tests/ui/async-await/async-fn/edition-2015.stderr +++ b/tests/ui/async-await/async-fn/edition-2015.stderr @@ -39,20 +39,20 @@ LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } = help: to use an async block, remove the `||`: `async {` error[E0658]: use of unstable library feature 'async_closure' - --> $DIR/edition-2015.rs:1:22 + --> $DIR/edition-2015.rs:1:42 | LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } - | ^^^^ + | ^^^^ | = note: see issue #62290 for more information = help: add `#![feature(async_closure)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature 'async_closure' - --> $DIR/edition-2015.rs:1:42 + --> $DIR/edition-2015.rs:1:22 | LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } - | ^^^^ + | ^^^^ | = note: see issue #62290 for more information = help: add `#![feature(async_closure)]` to the crate attributes to enable diff --git a/tests/ui/async-await/inference_var_self_argument.stderr b/tests/ui/async-await/inference_var_self_argument.stderr index 7bfa9be66dd37..7b7b3dbc757f1 100644 --- a/tests/ui/async-await/inference_var_self_argument.stderr +++ b/tests/ui/async-await/inference_var_self_argument.stderr @@ -1,3 +1,12 @@ +error[E0307]: invalid `self` parameter type: `&dyn Foo` + --> $DIR/inference_var_self_argument.rs:5:24 + | +LL | async fn foo(self: &dyn Foo) { + | ^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/inference_var_self_argument.rs:5:5 | @@ -13,15 +22,6 @@ LL | async fn foo(self: &dyn Foo) { | ^^^ ...because method `foo` is `async` = help: consider moving `foo` to another trait -error[E0307]: invalid `self` parameter type: `&dyn Foo` - --> $DIR/inference_var_self_argument.rs:5:24 - | -LL | async fn foo(self: &dyn Foo) { - | ^^^^^^^^ - | - = note: type of `self` must be `Self` or a type that dereferences to it - = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) - error: aborting due to 2 previous errors Some errors have detailed explanations: E0038, E0307. diff --git a/tests/ui/async-await/issue-66312.stderr b/tests/ui/async-await/issue-66312.stderr index 702e0b375e577..c95ae1147df36 100644 --- a/tests/ui/async-await/issue-66312.stderr +++ b/tests/ui/async-await/issue-66312.stderr @@ -1,9 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/issue-66312.rs:9:8 - | -LL | if x.is_some() { - | ^^^^^^^^^^^ expected `bool`, found `()` - error[E0307]: invalid `self` parameter type: `T` --> $DIR/issue-66312.rs:4:22 | @@ -13,6 +7,12 @@ LL | fn is_some(self: T); = note: type of `self` must be `Self` or a type that dereferences to it = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) +error[E0308]: mismatched types + --> $DIR/issue-66312.rs:9:8 + | +LL | if x.is_some() { + | ^^^^^^^^^^^ expected `bool`, found `()` + error: aborting due to 2 previous errors Some errors have detailed explanations: E0307, E0308. diff --git a/tests/ui/const-generics/opaque_types.stderr b/tests/ui/const-generics/opaque_types.stderr index 847f1da16f612..a060488b3287e 100644 --- a/tests/ui/const-generics/opaque_types.stderr +++ b/tests/ui/const-generics/opaque_types.stderr @@ -1,3 +1,11 @@ +error: `Foo` is forbidden as the type of a const generic parameter + --> $DIR/opaque_types.rs:7:17 + | +LL | fn foo() {} + | ^^^ + | + = note: the only supported types are integers, `bool`, and `char` + error: item does not constrain `Foo::{opaque#0}`, but has it in its signature --> $DIR/opaque_types.rs:7:4 | @@ -68,14 +76,6 @@ LL | type Foo = impl Sized; | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: `Foo` is forbidden as the type of a const generic parameter - --> $DIR/opaque_types.rs:7:17 - | -LL | fn foo() {} - | ^^^ - | - = note: the only supported types are integers, `bool`, and `char` - error[E0391]: cycle detected when computing type of opaque `Foo::{opaque#0}` --> $DIR/opaque_types.rs:3:12 | diff --git a/tests/ui/delegation/unsupported.stderr b/tests/ui/delegation/unsupported.stderr index 03ded300bb446..6a627be3b64e7 100644 --- a/tests/ui/delegation/unsupported.stderr +++ b/tests/ui/delegation/unsupported.stderr @@ -3,22 +3,6 @@ error: using `#![feature(effects)]` without enabling next trait solver globally = note: the next trait solver must be enabled globally for the effects feature to work correctly = help: use `-Znext-solver` to enable -warning: this function depends on never type fallback being `()` - --> $DIR/unsupported.rs:20:9 - | -LL | fn opaque_ret() -> impl Trait { unimplemented!() } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 #123748 - = help: specify the types explicitly -note: in edition 2024, the requirement `!: opaque::Trait` will fail - --> $DIR/unsupported.rs:20:28 - | -LL | fn opaque_ret() -> impl Trait { unimplemented!() } - | ^^^^^^^^^^ - = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default - error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` --> $DIR/unsupported.rs:27:25 | @@ -52,6 +36,22 @@ note: in edition 2024, the requirement `!: opaque::Trait` will fail | LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } | ^^^^^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: this function depends on never type fallback being `()` + --> $DIR/unsupported.rs:20:9 + | +LL | fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: opaque::Trait` will fail + --> $DIR/unsupported.rs:20:28 + | +LL | fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^ error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` --> $DIR/unsupported.rs:30:24 diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr index d8a85c8838d4d..7dfd79c728641 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr @@ -18,14 +18,6 @@ LL | type Bop = impl std::fmt::Debug; = help: add `#![feature(impl_trait_in_assoc_type)]` 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: unconstrained opaque type - --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:6:16 - | -LL | type Bar = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: `Bar` must be used in combination with a concrete type within the same impl - error[E0658]: inherent associated types are unstable --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:14:5 | @@ -36,6 +28,14 @@ LL | type Bop = impl std::fmt::Debug; = help: add `#![feature(inherent_associated_types)]` 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: unconstrained opaque type + --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:6:16 + | +LL | type Bar = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `Bar` must be used in combination with a concrete type within the same impl + error: unconstrained opaque type --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:14:16 | diff --git a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr index 50a9f3ebeabb8..91550f0e284c1 100644 --- a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr +++ b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr @@ -19,7 +19,10 @@ error[E0720]: cannot resolve opaque type --> $DIR/impl-fn-predefined-lifetimes.rs:4:35 | LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { - | ^^^^^^^^^^^^^^^ cannot resolve opaque type + | ^^^^^^^^^^^^^^^ recursive opaque type +... +LL | |x| x + | ----- returning here with type `{closure@$DIR/impl-fn-predefined-lifetimes.rs:7:5: 7:8}` error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/impl-trait/issues/issue-78722-2.stderr b/tests/ui/impl-trait/issues/issue-78722-2.stderr index dc5579c1c829e..2cf6b94dd9d9b 100644 --- a/tests/ui/impl-trait/issues/issue-78722-2.stderr +++ b/tests/ui/impl-trait/issues/issue-78722-2.stderr @@ -1,9 +1,3 @@ -error[E0271]: expected `{async block@$DIR/issue-78722-2.rs:13:13: 13:18}` to be a future that resolves to `u8`, but it resolves to `()` - --> $DIR/issue-78722-2.rs:11:30 - | -LL | fn concrete_use() -> F { - | ^ expected `()`, found `u8` - error[E0308]: mismatched types --> $DIR/issue-78722-2.rs:16:20 | @@ -18,6 +12,12 @@ LL | let f: F = async { 1 }; = note: expected opaque type `F` found `async` block `{async block@$DIR/issue-78722-2.rs:16:20: 16:25}` +error[E0271]: expected `{async block@$DIR/issue-78722-2.rs:13:13: 13:18}` to be a future that resolves to `u8`, but it resolves to `()` + --> $DIR/issue-78722-2.rs:11:30 + | +LL | fn concrete_use() -> F { + | ^ expected `()`, found `u8` + error: aborting due to 2 previous errors Some errors have detailed explanations: E0271, E0308. diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr index 3692cc77b0fb4..6485aa207103c 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr @@ -1,24 +1,3 @@ -error: item does not constrain `a::Foo::{opaque#0}`, but has it in its signature - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:10:12 - | -LL | fn eq(&self, _other: &(Foo, i32)) -> bool { - | ^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16 - | -LL | type Foo = impl PartialEq<(Foo, i32)>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: unconstrained opaque type - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16 - | -LL | type Foo = impl PartialEq<(Foo, i32)>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `Foo` must be used in combination with a concrete type within the same module - error[E0053]: method `eq` has an incompatible type for trait --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:10:30 | @@ -35,8 +14,21 @@ help: change the parameter type to match the trait LL | fn eq(&self, _other: &(a::Bar, i32)) -> bool { | ~~~~~~~~~~~~~~ +error: item does not constrain `a::Foo::{opaque#0}`, but has it in its signature + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:10:12 + | +LL | fn eq(&self, _other: &(Foo, i32)) -> bool { + | ^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16 + | +LL | type Foo = impl PartialEq<(Foo, i32)>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: unconstrained opaque type - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:19:16 + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16 | LL | type Foo = impl PartialEq<(Foo, i32)>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,6 +56,14 @@ help: change the parameter type to match the trait LL | fn eq(&self, _other: &(b::Foo, i32)) -> bool { | ~~~~~~~~~~~~~~ +error: unconstrained opaque type + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:19:16 + | +LL | type Foo = impl PartialEq<(Foo, i32)>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Foo` must be used in combination with a concrete type within the same module + error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index 2770a6cc40e59..13f50fcea7b3f 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -342,6 +342,47 @@ LL | let _in_return_in_local_variable = || -> impl Fn() { || {} }; | = note: `impl Trait` is only allowed in arguments and return types of functions and methods +error[E0053]: method `in_trait_impl_return` has an incompatible type for trait + --> $DIR/where-allowed.rs:128:34 + | +LL | type Out = impl Debug; + | ---------- the expected opaque type +... +LL | fn in_trait_impl_return() -> impl Debug { () } + | ^^^^^^^^^^ expected opaque type, found a different opaque type + | +note: type in trait + --> $DIR/where-allowed.rs:118:34 + | +LL | fn in_trait_impl_return() -> Self::Out; + | ^^^^^^^^^ + = note: expected signature `fn() -> <() as DummyTrait>::Out` + found signature `fn() -> impl Debug` + = note: distinct uses of `impl Trait` result in different opaque types +help: change the output type to match the trait + | +LL | fn in_trait_impl_return() -> <() as DummyTrait>::Out { () } + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/where-allowed.rs:245:36 + | +LL | fn in_method_generic_param_default(_: T) {} + | ^^^^^^^^^^^^^^ + | + = 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 #36887 + = note: `#[deny(invalid_type_param_default)]` on by default + +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/where-allowed.rs:238:7 + | +LL | impl T {} + | ^^^^^^^^^^^^^^ + | + = 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 #36887 + error[E0283]: type annotations needed --> $DIR/where-allowed.rs:46:57 | @@ -362,16 +403,6 @@ LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { pani - impl Fn for Box where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; -error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:238:7 - | -LL | impl T {} - | ^^^^^^^^^^^^^^ - | - = 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 #36887 - = note: `#[deny(invalid_type_param_default)]` on by default - error[E0118]: no nominal type found for inherent implementation --> $DIR/where-allowed.rs:238:1 | @@ -380,28 +411,6 @@ LL | impl T {} | = note: either implement a trait on it or create a newtype to wrap it instead -error[E0053]: method `in_trait_impl_return` has an incompatible type for trait - --> $DIR/where-allowed.rs:128:34 - | -LL | type Out = impl Debug; - | ---------- the expected opaque type -... -LL | fn in_trait_impl_return() -> impl Debug { () } - | ^^^^^^^^^^ expected opaque type, found a different opaque type - | -note: type in trait - --> $DIR/where-allowed.rs:118:34 - | -LL | fn in_trait_impl_return() -> Self::Out; - | ^^^^^^^^^ - = note: expected signature `fn() -> <() as DummyTrait>::Out` - found signature `fn() -> impl Debug` - = note: distinct uses of `impl Trait` result in different opaque types -help: change the output type to match the trait - | -LL | fn in_trait_impl_return() -> <() as DummyTrait>::Out { () } - | ~~~~~~~~~~~~~~~~~~~~~~~ - error: unconstrained opaque type --> $DIR/where-allowed.rs:121:16 | @@ -410,25 +419,16 @@ LL | type Out = impl Debug; | = note: `Out` must be used in combination with a concrete type within the same impl -error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:245:36 - | -LL | fn in_method_generic_param_default(_: T) {} - | ^^^^^^^^^^^^^^ - | - = 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 #36887 - error: aborting due to 49 previous errors Some errors have detailed explanations: E0053, E0118, E0283, E0562, E0658, E0666. For more information about an error, try `rustc --explain E0053`. Future incompatibility report: Future breakage diagnostic: error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:238:7 + --> $DIR/where-allowed.rs:245:36 | -LL | impl T {} - | ^^^^^^^^^^^^^^ +LL | fn in_method_generic_param_default(_: T) {} + | ^^^^^^^^^^^^^^ | = 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 #36887 @@ -436,10 +436,10 @@ LL | impl T {} Future breakage diagnostic: error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:245:36 + --> $DIR/where-allowed.rs:238:7 | -LL | fn in_method_generic_param_default(_: T) {} - | ^^^^^^^^^^^^^^ +LL | impl T {} + | ^^^^^^^^^^^^^^ | = 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 #36887 diff --git a/tests/ui/privacy/private-type-in-interface.rs b/tests/ui/privacy/private-type-in-interface.rs index 2e8be28d76ebf..9eadd09867d41 100644 --- a/tests/ui/privacy/private-type-in-interface.rs +++ b/tests/ui/privacy/private-type-in-interface.rs @@ -26,7 +26,5 @@ type A = ::X; //~ ERROR type `Priv` is private trait Tr2 {} impl Tr2 for u8 {} fn g() -> impl Tr2 { 0 } //~ ERROR type `Priv` is private - //~| ERROR type `Priv` is private fn g_ext() -> impl Tr2 { 0 } //~ ERROR type `ext::Priv` is private - //~| ERROR type `ext::Priv` is private fn main() {} diff --git a/tests/ui/privacy/private-type-in-interface.stderr b/tests/ui/privacy/private-type-in-interface.stderr index 091cae42dea3c..03225d84fdb34 100644 --- a/tests/ui/privacy/private-type-in-interface.stderr +++ b/tests/ui/privacy/private-type-in-interface.stderr @@ -46,23 +46,11 @@ error: type `Priv` is private LL | fn g() -> impl Tr2 { 0 } | ^^^^^^^^^^^^^^^^^^ private type -error: type `Priv` is private - --> $DIR/private-type-in-interface.rs:28:11 - | -LL | fn g() -> impl Tr2 { 0 } - | ^^^^^^^^^^^^^^^^^^ private type - -error: type `ext::Priv` is private - --> $DIR/private-type-in-interface.rs:30:15 - | -LL | fn g_ext() -> impl Tr2 { 0 } - | ^^^^^^^^^^^^^^^^^^^^ private type - error: type `ext::Priv` is private - --> $DIR/private-type-in-interface.rs:30:15 + --> $DIR/private-type-in-interface.rs:29:15 | LL | fn g_ext() -> impl Tr2 { 0 } | ^^^^^^^^^^^^^^^^^^^^ private type -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr index b59c6d1eed8da..1040af7541c08 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr @@ -68,76 +68,76 @@ LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; | ^^^^^^^^ error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:25:29 + --> $DIR/const-impl-trait.rs:29:29 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { | ^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:25:48 + --> $DIR/const-impl-trait.rs:29:48 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { | ^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:25:29 + --> $DIR/const-impl-trait.rs:29:29 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { | ^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:25:48 + --> $DIR/const-impl-trait.rs:29:48 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { | ^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:29:29 + --> $DIR/const-impl-trait.rs:50:41 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { - | ^^^^^^^^^ +LL | const fn apit(_: impl ~const T + ~const Destruct) {} + | ^^^^^^^^ error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:29:48 + --> $DIR/const-impl-trait.rs:54:73 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { - | ^^^^^^^^ +LL | const fn apit_assoc_bound(_: impl IntoIterator + ~const Destruct) {} + | ^^^^^^^^ error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:29:29 + --> $DIR/const-impl-trait.rs:25:29 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; | ^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:29:48 + --> $DIR/const-impl-trait.rs:25:48 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; | ^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:50:41 + --> $DIR/const-impl-trait.rs:25:29 | -LL | const fn apit(_: impl ~const T + ~const Destruct) {} - | ^^^^^^^^ +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:54:73 + --> $DIR/const-impl-trait.rs:25:48 | -LL | const fn apit_assoc_bound(_: impl IntoIterator + ~const Destruct) {} - | ^^^^^^^^ +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits --> $DIR/const-impl-trait.rs:25:29 diff --git a/tests/ui/self/arbitrary-self-opaque.stderr b/tests/ui/self/arbitrary-self-opaque.stderr index 5ccc076bfaf6c..5634b3d6e64a3 100644 --- a/tests/ui/self/arbitrary-self-opaque.stderr +++ b/tests/ui/self/arbitrary-self-opaque.stderr @@ -1,3 +1,12 @@ +error[E0307]: invalid `self` parameter type: `Bar` + --> $DIR/arbitrary-self-opaque.rs:8:18 + | +LL | fn foo(self: Bar) {} + | ^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + error: item does not constrain `Bar::{opaque#0}`, but has it in its signature --> $DIR/arbitrary-self-opaque.rs:8:8 | @@ -19,15 +28,6 @@ LL | type Bar = impl Sized; | = note: `Bar` must be used in combination with a concrete type within the same module -error[E0307]: invalid `self` parameter type: `Bar` - --> $DIR/arbitrary-self-opaque.rs:8:18 - | -LL | fn foo(self: Bar) {} - | ^^^ - | - = note: type of `self` must be `Self` or a type that dereferences to it - = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) - error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr index b00260fa0efa4..f3393830eebb3 100644 --- a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr +++ b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr @@ -9,8 +9,8 @@ LL | async fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn a<'a>(self: Pin<&'a Foo>, f: &'a Foo) -> &Foo { f } - | ++++ ++ ++ +LL | async fn a<'a>(self: Pin<&Foo>, f: &'a Foo) -> &'a Foo { f } + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:11:75 @@ -23,8 +23,8 @@ LL | async fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { ( | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn c<'a>(self: Pin<&'a Self>, f: &'a Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } - | ++++ ++ ++ +LL | async fn c<'a>(self: Pin<&Self>, f: &'a Foo, g: &Foo) -> (Pin<&'a Foo>, &'a Foo) { (self, f) } + | ++++ ++ ++ ++ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:17:64 @@ -37,8 +37,8 @@ LL | async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } | help: consider reusing a named lifetime parameter and update trait if needed | -LL | async fn bar<'a>(self: Alias<&'a Self>, arg: &'a ()) -> &() { arg } - | ++ +LL | async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &'a () { arg } + | ++ error: aborting due to 3 previous errors diff --git a/tests/ui/self/elision/lt-ref-self-async.fixed b/tests/ui/self/elision/lt-ref-self-async.fixed index 914511641b894..aae94f7a6ccce 100644 --- a/tests/ui/self/elision/lt-ref-self-async.fixed +++ b/tests/ui/self/elision/lt-ref-self-async.fixed @@ -11,34 +11,34 @@ struct Struct<'a> { impl<'a> Struct<'a> { // Test using `&self` sugar: - async fn ref_self<'b>(&'b self, f: &'b u32) -> &u32 { + async fn ref_self<'b>(&self, f: &'b u32) -> &'b u32 { f //~^ ERROR lifetime may not live long enough } // Test using `&Self` explicitly: - async fn ref_Self<'b>(self: &'b Self, f: &'b u32) -> &u32 { + async fn ref_Self<'b>(self: &Self, f: &'b u32) -> &'b u32 { f //~^ ERROR lifetime may not live long enough } - async fn box_ref_Self<'b>(self: Box<&'b Self>, f: &'b u32) -> &u32 { + async fn box_ref_Self<'b>(self: Box<&Self>, f: &'b u32) -> &'b u32 { f //~^ ERROR lifetime may not live long enough } - async fn pin_ref_Self<'b>(self: Pin<&'b Self>, f: &'b u32) -> &u32 { + async fn pin_ref_Self<'b>(self: Pin<&Self>, f: &'b u32) -> &'b u32 { f //~^ ERROR lifetime may not live long enough } - async fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &u32 { + async fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { f //~^ ERROR lifetime may not live long enough } - async fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &u32 { + async fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { f //~^ ERROR lifetime may not live long enough } diff --git a/tests/ui/self/elision/lt-ref-self-async.stderr b/tests/ui/self/elision/lt-ref-self-async.stderr index b84044f754887..c43ff49d5080c 100644 --- a/tests/ui/self/elision/lt-ref-self-async.stderr +++ b/tests/ui/self/elision/lt-ref-self-async.stderr @@ -10,8 +10,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_self<'b>(&'b self, f: &'b u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_self<'b>(&self, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:22:9 @@ -25,8 +25,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_Self<'b>(self: &'b Self, f: &'b u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_Self<'b>(self: &Self, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:27:9 @@ -40,8 +40,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_ref_Self<'b>(self: Box<&'b Self>, f: &'b u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_ref_Self<'b>(self: Box<&Self>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:32:9 @@ -55,8 +55,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn pin_ref_Self<'b>(self: Pin<&'b Self>, f: &'b u32) -> &u32 { - | ++++ ++ ++ +LL | async fn pin_ref_Self<'b>(self: Pin<&Self>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:37:9 @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/lt-ref-self-async.rs:42:9 @@ -85,8 +85,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: aborting due to 6 previous errors diff --git a/tests/ui/self/elision/ref-assoc-async.stderr b/tests/ui/self/elision/ref-assoc-async.stderr index cf54a86b45f06..9f2768d5e695d 100644 --- a/tests/ui/self/elision/ref-assoc-async.stderr +++ b/tests/ui/self/elision/ref-assoc-async.stderr @@ -10,8 +10,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_AssocType<'a>(self: &'a ::AssocType, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_AssocType<'a>(self: &::AssocType, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-assoc-async.rs:24:9 @@ -25,8 +25,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_ref_AssocType<'a>(self: Box<&'a ::AssocType>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_ref_AssocType<'a>(self: Box<&::AssocType>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-assoc-async.rs:29:9 @@ -40,8 +40,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn pin_ref_AssocType<'a>(self: Pin<&'a ::AssocType>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn pin_ref_AssocType<'a>(self: Pin<&::AssocType>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-assoc-async.rs:34:9 @@ -55,8 +55,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_box_ref_AssocType<'a>(self: Box::AssocType>>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_box_ref_AssocType<'a>(self: Box::AssocType>>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-assoc-async.rs:39:9 @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_pin_ref_AssocType<'a>(self: Box::AssocType>>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_pin_ref_AssocType<'a>(self: Box::AssocType>>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: aborting due to 5 previous errors diff --git a/tests/ui/self/elision/ref-mut-self-async.stderr b/tests/ui/self/elision/ref-mut-self-async.stderr index 62543ba533955..945fb5e028216 100644 --- a/tests/ui/self/elision/ref-mut-self-async.stderr +++ b/tests/ui/self/elision/ref-mut-self-async.stderr @@ -10,8 +10,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_self<'a>(&'a mut self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_self<'a>(&mut self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:20:9 @@ -25,8 +25,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_Self<'a>(self: &'a mut Self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_Self<'a>(self: &mut Self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:25:9 @@ -40,8 +40,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_ref_Self<'a>(self: Box<&'a mut Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_ref_Self<'a>(self: Box<&mut Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:30:9 @@ -55,8 +55,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn pin_ref_Self<'a>(self: Pin<&'a mut Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn pin_ref_Self<'a>(self: Pin<&mut Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:35:9 @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-self-async.rs:40:9 @@ -85,8 +85,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: aborting due to 6 previous errors diff --git a/tests/ui/self/elision/ref-mut-struct-async.stderr b/tests/ui/self/elision/ref-mut-struct-async.stderr index f8fb2e4a1383b..149ab01045c1f 100644 --- a/tests/ui/self/elision/ref-mut-struct-async.stderr +++ b/tests/ui/self/elision/ref-mut-struct-async.stderr @@ -10,8 +10,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_Struct<'a>(self: &'a mut Struct, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_Struct<'a>(self: &mut Struct, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:18:9 @@ -25,8 +25,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_ref_Struct<'a>(self: Box<&'a mut Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_ref_Struct<'a>(self: Box<&mut Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:23:9 @@ -40,8 +40,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn pin_ref_Struct<'a>(self: Pin<&'a mut Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn pin_ref_Struct<'a>(self: Pin<&mut Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:28:9 @@ -55,8 +55,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-mut-struct-async.rs:33:9 @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_pin_ref_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_pin_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: aborting due to 5 previous errors diff --git a/tests/ui/self/elision/ref-self-async.stderr b/tests/ui/self/elision/ref-self-async.stderr index 010d281b00224..a75ece5f2c768 100644 --- a/tests/ui/self/elision/ref-self-async.stderr +++ b/tests/ui/self/elision/ref-self-async.stderr @@ -10,8 +10,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_self<'a>(&self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-self-async.rs:30:9 @@ -25,8 +25,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_Self<'a>(self: &Self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-self-async.rs:35:9 @@ -40,8 +40,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_ref_Self<'a>(self: Box<&Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-self-async.rs:40:9 @@ -55,8 +55,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn pin_ref_Self<'a>(self: Pin<&Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-self-async.rs:45:9 @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-self-async.rs:50:9 @@ -85,8 +85,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-self-async.rs:55:9 @@ -100,8 +100,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn wrap_ref_Self_Self<'a>(self: Wrap<&'a Self, Self>, f: &'a u8) -> &u8 { - | ++++ ++ ++ +LL | async fn wrap_ref_Self_Self<'a>(self: Wrap<&Self, Self>, f: &'a u8) -> &'a u8 { + | ++++ ++ ++ error: aborting due to 7 previous errors diff --git a/tests/ui/self/elision/ref-struct-async.stderr b/tests/ui/self/elision/ref-struct-async.stderr index c9376d58f9096..6bdc145223a68 100644 --- a/tests/ui/self/elision/ref-struct-async.stderr +++ b/tests/ui/self/elision/ref-struct-async.stderr @@ -10,8 +10,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_Struct<'a>(self: &'a Struct, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn ref_Struct<'a>(self: &Struct, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:18:9 @@ -25,8 +25,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_ref_Struct<'a>(self: Box<&'a Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_ref_Struct<'a>(self: Box<&Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:23:9 @@ -40,8 +40,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn pin_ref_Struct<'a>(self: Pin<&'a Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn pin_ref_Struct<'a>(self: Pin<&Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:28:9 @@ -55,8 +55,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough --> $DIR/ref-struct-async.rs:33:9 @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_pin_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | async fn box_pin_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: aborting due to 5 previous errors diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr index b526ab49d8d4a..ec8a51b08188a 100644 --- a/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr +++ b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr @@ -1,3 +1,11 @@ +error: `Bar` is forbidden as the type of a const generic parameter + --> $DIR/const_generic_type.rs:8:24 + | +LL | async fn test() { + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool`, and `char` + error: item does not constrain `Bar::{opaque#0}`, but has it in its signature --> $DIR/const_generic_type.rs:8:10 | @@ -39,13 +47,5 @@ LL | type Bar = impl std::fmt::Display; | = note: `Bar` must be used in combination with a concrete type within the same module -error: `Bar` is forbidden as the type of a const generic parameter - --> $DIR/const_generic_type.rs:8:24 - | -LL | async fn test() { - | ^^^^^^^^^^ - | - = note: the only supported types are integers, `bool`, and `char` - error: aborting due to 4 previous errors diff --git a/tests/ui/type-alias-impl-trait/constrain_inputs.stderr b/tests/ui/type-alias-impl-trait/constrain_inputs.stderr index 2468fb7480b07..436326e66c386 100644 --- a/tests/ui/type-alias-impl-trait/constrain_inputs.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_inputs.stderr @@ -7,19 +7,6 @@ LL | fn execute(ty: Ty<'_>) -> &str { todo!() } = note: lifetimes appearing in an associated or opaque type are not considered constrained = note: consider introducing a named lifetime parameter -error: item does not constrain `lifetime_params::Ty::{opaque#0}`, but has it in its signature - --> $DIR/constrain_inputs.rs:6:8 - | -LL | fn execute(ty: Ty<'_>) -> &str { todo!() } - | ^^^^^^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/constrain_inputs.rs:4:19 - | -LL | type Ty<'a> = impl Sized; - | ^^^^^^^^^^ - error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types --> $DIR/constrain_inputs.rs:10:35 | @@ -38,6 +25,19 @@ LL | type BadTraitRef = dyn Fn(Ty<'_>) -> &str; = note: lifetimes appearing in an associated or opaque type are not considered constrained = note: consider introducing a named lifetime parameter +error: item does not constrain `lifetime_params::Ty::{opaque#0}`, but has it in its signature + --> $DIR/constrain_inputs.rs:6:8 + | +LL | fn execute(ty: Ty<'_>) -> &str { todo!() } + | ^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/constrain_inputs.rs:4:19 + | +LL | type Ty<'a> = impl Sized; + | ^^^^^^^^^^ + error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types --> $DIR/constrain_inputs.rs:19:31 | diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr index be9b07823ae0a..88529b370f133 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr @@ -1,13 +1,8 @@ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:9:51 + --> $DIR/generic_underconstrained.rs:9:31 | -LL | fn underconstrain(_: T) -> Underconstrained { - | ___________________________________________________^ -LL | | -LL | | -LL | | unimplemented!() -LL | | } - | |_^ the trait `Trait` is not implemented for `T` +LL | fn underconstrain(_: T) -> Underconstrained { + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained.rs:6:26 @@ -20,10 +15,15 @@ LL | fn underconstrain(_: T) -> Underconstrained { | +++++++ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:9:31 + --> $DIR/generic_underconstrained.rs:9:51 | -LL | fn underconstrain(_: T) -> Underconstrained { - | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` +LL | fn underconstrain(_: T) -> Underconstrained { + | ___________________________________________________^ +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_^ the trait `Trait` is not implemented for `T` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained.rs:6:26 diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr index 15d96191ba9e2..b3b9cbca96854 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -1,13 +1,8 @@ error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:8:53 + --> $DIR/generic_underconstrained2.rs:8:33 | -LL | fn underconstrained(_: U) -> Underconstrained { - | _____________________________________________________^ -LL | | -LL | | -LL | | 5u32 -LL | | } - | |_^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained(_: U) -> Underconstrained { + | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained2.rs:5:26 @@ -20,15 +15,10 @@ LL | fn underconstrained(_: U) -> Underconstrained { | +++++++++++++++++ error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:17:64 + --> $DIR/generic_underconstrained2.rs:17:43 | -LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { - | ________________________________________________________________^ -LL | | -LL | | -LL | | 5u32 -LL | | } - | |_^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { + | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained2` --> $DIR/generic_underconstrained2.rs:14:27 @@ -41,10 +31,15 @@ LL | fn underconstrained2(_: U, _: V) -> Underconstrained | +++++++++++++++++ error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:8:33 + --> $DIR/generic_underconstrained2.rs:8:53 | -LL | fn underconstrained(_: U) -> Underconstrained { - | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained(_: U) -> Underconstrained { + | _____________________________________________________^ +LL | | +LL | | +LL | | 5u32 +LL | | } + | |_^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained2.rs:5:26 @@ -57,10 +52,15 @@ LL | fn underconstrained(_: U) -> Underconstrained { | +++++++++++++++++ error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:17:43 + --> $DIR/generic_underconstrained2.rs:17:64 | -LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { - | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { + | ________________________________________________________________^ +LL | | +LL | | +LL | | 5u32 +LL | | } + | |_^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained2` --> $DIR/generic_underconstrained2.rs:14:27 diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr index 22c776e171c2f..eace96317dc1d 100644 --- a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr @@ -1,3 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:41:6 + | +LL | impl>>, U> MyIndex> for Scope { + | ^ unconstrained type parameter + error: item does not constrain `DummyT::{opaque#0}`, but has it in its signature --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:28:8 | @@ -24,12 +30,6 @@ note: this opaque type is in the signature LL | type DummyT = impl F; | ^^^^^^ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:41:6 - | -LL | impl>>, U> MyIndex> for Scope { - | ^ unconstrained type parameter - error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/type-alias-impl-trait/in-where-clause.stderr b/tests/ui/type-alias-impl-trait/in-where-clause.stderr index f1361b47c56e6..5ac09e20b02c2 100644 --- a/tests/ui/type-alias-impl-trait/in-where-clause.stderr +++ b/tests/ui/type-alias-impl-trait/in-where-clause.stderr @@ -1,14 +1,25 @@ -error[E0391]: cycle detected when computing type of `Bar::{opaque#0}` - --> $DIR/in-where-clause.rs:5:12 +error[E0283]: type annotations needed: cannot satisfy `Bar: Send` + --> $DIR/in-where-clause.rs:12:9 | -LL | type Bar = impl Sized; - | ^^^^^^^^^^ +LL | [0; 1 + 2] + | ^^^^^ | -note: ...which requires computing type of opaque `Bar::{opaque#0}`... + = note: cannot satisfy `Bar: Send` +note: required by a bound in `foo` + --> $DIR/in-where-clause.rs:10:10 + | +LL | fn foo() -> Bar + | --- required by a bound in this function +LL | where +LL | Bar: Send, + | ^^^^ required by this bound in `foo` + +error[E0391]: cycle detected when computing type of opaque `Bar::{opaque#0}` --> $DIR/in-where-clause.rs:5:12 | LL | type Bar = impl Sized; | ^^^^^^^^^^ + | note: ...which requires type-checking `foo`... --> $DIR/in-where-clause.rs:8:1 | @@ -17,30 +28,15 @@ LL | | where LL | | Bar: Send, | |______________^ = note: ...which requires revealing opaque types in `[Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] }]`... - = note: ...which again requires computing type of `Bar::{opaque#0}`, completing the cycle -note: cycle used when checking that `Bar::{opaque#0}` is well-formed +note: ...which requires computing type of `Bar::{opaque#0}`... --> $DIR/in-where-clause.rs:5:12 | LL | type Bar = impl Sized; | ^^^^^^^^^^ + = note: ...which again requires computing type of opaque `Bar::{opaque#0}`, completing the cycle + = note: cycle used when evaluating trait selection obligation `Bar: core::marker::Send` = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0283]: type annotations needed: cannot satisfy `Bar: Send` - --> $DIR/in-where-clause.rs:12:9 - | -LL | [0; 1 + 2] - | ^^^^^ - | - = note: cannot satisfy `Bar: Send` -note: required by a bound in `foo` - --> $DIR/in-where-clause.rs:10:10 - | -LL | fn foo() -> Bar - | --- required by a bound in this function -LL | where -LL | Bar: Send, - | ^^^^ required by this bound in `foo` - error: aborting due to 2 previous errors Some errors have detailed explanations: E0283, E0391. diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.rs b/tests/ui/type-alias-impl-trait/issue-53092-2.rs index 2adfad4fc5b80..2383008d042f7 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.rs +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.rs @@ -1,15 +1,14 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -type Bug = impl Fn(T) -> U + Copy; //~ ERROR cycle detected +type Bug = impl Fn(T) -> U + Copy; const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; -//~^ ERROR: non-defining opaque type use -//~| ERROR: item does not constrain -//~| ERROR: item does not constrain +//~^ ERROR cycle detected +//~| ERROR: non-defining opaque type use fn make_bug>() -> Bug { - |x| x.into() //~ ERROR the trait bound `U: From` is not satisfied + |x| x.into() } fn main() { diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr index 121f765e66726..ac580866704d7 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr @@ -10,75 +10,33 @@ note: for this opaque type LL | type Bug = impl Fn(T) -> U + Copy; | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0391]: cycle detected when computing type of `Bug::{opaque#0}` - --> $DIR/issue-53092-2.rs:4:18 - | -LL | type Bug = impl Fn(T) -> U + Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires computing type of opaque `Bug::{opaque#0}`... - --> $DIR/issue-53092-2.rs:4:18 - | -LL | type Bug = impl Fn(T) -> U + Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `CONST_BUG`... +error[E0391]: cycle detected when type-checking `CONST_BUG` --> $DIR/issue-53092-2.rs:6:1 | LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | = note: ...which requires computing layout of `Bug`... = note: ...which requires normalizing `Bug`... - = note: ...which again requires computing type of `Bug::{opaque#0}`, completing the cycle -note: cycle used when checking that `Bug::{opaque#0}` is well-formed +note: ...which requires computing type of `Bug::{opaque#0}`... --> $DIR/issue-53092-2.rs:4:18 | LL | type Bug = impl Fn(T) -> U + Copy; | ^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: item does not constrain `Bug::{opaque#0}`, but has it in its signature - --> $DIR/issue-53092-2.rs:6:7 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature +note: ...which requires computing type of opaque `Bug::{opaque#0}`... --> $DIR/issue-53092-2.rs:4:18 | LL | type Bug = impl Fn(T) -> U + Copy; | ^^^^^^^^^^^^^^^^^^^^^^ - -error: item does not constrain `Bug::{opaque#0}`, but has it in its signature - --> $DIR/issue-53092-2.rs:6:61 + = note: ...which again requires type-checking `CONST_BUG`, completing the cycle +note: cycle used when checking that `CONST_BUG` is well-formed + --> $DIR/issue-53092-2.rs:6:1 | LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/issue-53092-2.rs:4:18 - | -LL | type Bug = impl Fn(T) -> U + Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `U: From` is not satisfied - --> $DIR/issue-53092-2.rs:12:5 - | -LL | |x| x.into() - | ^^^^^^^^^^^^ the trait `From` is not implemented for `U` - | -note: required by a bound in `make_bug` - --> $DIR/issue-53092-2.rs:11:19 - | -LL | fn make_bug>() -> Bug { - | ^^^^^^^ required by this bound in `make_bug` -help: consider restricting type parameter `U` - | -LL | type Bug> = impl Fn(T) -> U + Copy; - | +++++++++++++++++++++++ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0277, E0391, E0792. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0391, E0792. +For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr index 2b064dcfc31a5..ec7b9e0e12b3f 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr @@ -1,3 +1,12 @@ +error[E0119]: conflicting implementations of trait `Trait` + --> $DIR/issue-84660-unsoundness.rs:29:1 + | +LL | impl Trait for Out { + | ------------------------------------ first implementation here +... +LL | impl Trait<(), In> for Out { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + error: item does not constrain `Bar::{opaque#0}`, but has it in its signature --> $DIR/issue-84660-unsoundness.rs:22:8 | @@ -11,15 +20,6 @@ note: this opaque type is in the signature LL | type Bar = impl Foo; | ^^^^^^^^ -error[E0119]: conflicting implementations of trait `Trait` - --> $DIR/issue-84660-unsoundness.rs:29:1 - | -LL | impl Trait for Out { - | ------------------------------------ first implementation here -... -LL | impl Trait<(), In> for Out { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation - error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr index 5a728a00138c8..e33102f687c53 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr @@ -1,3 +1,12 @@ +error[E0119]: conflicting implementations of trait `Trait` + --> $DIR/issue-84660-unsoundness.rs:29:1 + | +LL | impl Trait for Out { + | ------------------------------------ first implementation here +... +LL | impl Trait<(), In> for Out { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + error[E0284]: type annotations needed: cannot satisfy `Bar == _` --> $DIR/issue-84660-unsoundness.rs:22:37 | @@ -9,15 +18,6 @@ LL | | unreachable!(); LL | | } | |_____^ cannot satisfy `Bar == _` -error[E0119]: conflicting implementations of trait `Trait` - --> $DIR/issue-84660-unsoundness.rs:29:1 - | -LL | impl Trait for Out { - | ------------------------------------ first implementation here -... -LL | impl Trait<(), In> for Out { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation - error: aborting due to 2 previous errors Some errors have detailed explanations: E0119, E0284. diff --git a/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr index d0fe920b35f4a..aa0c1076117cc 100644 --- a/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr +++ b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr @@ -1,11 +1,3 @@ -error: unconstrained opaque type - --> $DIR/nested-in-anon-const.rs:13:33 - | -LL | type B = impl Sized; - | ^^^^^^^^^^ - | - = note: `B` must be used in combination with a concrete type within the same item - error[E0308]: mismatched types --> $DIR/nested-in-anon-const.rs:12:17 | @@ -15,6 +7,14 @@ LL | | LL | | }, | |_________________^ expected `usize`, found `()` +error: unconstrained opaque type + --> $DIR/nested-in-anon-const.rs:13:33 + | +LL | type B = impl Sized; + | ^^^^^^^^^^ + | + = note: `B` must be used in combination with a concrete type within the same item + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.stderr index aedb78bf5e7a1..99646aa4d1b2c 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.stderr +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-4.stderr @@ -10,18 +10,6 @@ note: required by a bound on the type alias `Ty` LL | Ty: Id, | ^^^^^^^^^^^^^^ required by this bound -error[E0275]: overflow evaluating the requirement `Ty: Id` - --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:15:19 - | -LL | fn define() -> Ty {} - | ^^ - | -note: required by a bound on the type alias `Ty` - --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:13:9 - | -LL | Ty: Id, - | ^^^^^^^^^^^^^^ required by this bound - error[E0275]: overflow evaluating the requirement `Ty: Id` --> $DIR/type-alias-impl-trait-with-cycle-error-4.rs:15:16 | @@ -34,6 +22,6 @@ note: required by a bound on the type alias `Ty` LL | Ty: Id, | ^^^^^^^^^^^^^^ required by this bound -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index 29a21a1f45f56..437a1aed40307 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -160,7 +160,7 @@ impl BadTrait<_> for BadStruct<_> {} //~^ ERROR the placeholder `_` is not allowed within types on item signatures for implementations fn impl_trait() -> impl BadTrait<_> { -//~^ ERROR the placeholder `_` is not allowed within types on item signatures for opaque types +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions unimplemented!() } @@ -180,7 +180,7 @@ struct Struct; trait Trait {} impl Trait for Struct {} type Y = impl Trait<_>; -//~^ ERROR the placeholder `_` is not allowed within types on item signatures for opaque types +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for type aliases fn foo() -> Y { Struct } diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index 2c064fbb19f71..8a765c21624e6 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -484,11 +484,16 @@ help: use type parameters instead LL | impl BadTrait for BadStruct {} | +++ ~ ~ -error[E0121]: the placeholder `_` is not allowed within types on item signatures for opaque types +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:162:34 | LL | fn impl_trait() -> impl BadTrait<_> { | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn impl_trait() -> impl BadTrait { + | +++ ~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/typeck_type_placeholder_item.rs:167:25 @@ -518,33 +523,17 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures LL | type X = Box<_>; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for opaque types +error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases --> $DIR/typeck_type_placeholder_item.rs:182:21 | LL | type Y = impl Trait<_>; | ^ not allowed in type signatures -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/typeck_type_placeholder_item.rs:44:27 - | -LL | fn test10(&self, _x : _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn test10(&self, _x : T) { } - | +++ ~ - -error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/typeck_type_placeholder_item.rs:110:34 - | -LL | fn fn_test10(&self, _x : _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants + --> $DIR/typeck_type_placeholder_item.rs:206:14 | -LL | fn fn_test10(&self, _x : T) { } - | +++ ~ +LL | const C: _; + | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants --> $DIR/typeck_type_placeholder_item.rs:194:14 @@ -555,6 +544,21 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace with the correct type: `i32` +error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants + --> $DIR/typeck_type_placeholder_item.rs:209:14 + | +LL | const D: _ = 42; + | ^ not allowed in type signatures + +error[E0046]: not all trait items implemented, missing: `F` + --> $DIR/typeck_type_placeholder_item.rs:200:1 + | +LL | type F: std::ops::Fn(_); + | ----------------------- `F` from trait +... +LL | impl Qux for Struct { + | ^^^^^^^^^^^^^^^^^^^ missing `F` in implementation + error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:217:31 | @@ -573,27 +577,6 @@ LL | const _: Option<_> = map(value); | not allowed in type signatures | help: replace with the correct type: `Option` -error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:206:14 - | -LL | const C: _; - | ^ not allowed in type signatures - -error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:209:14 - | -LL | const D: _ = 42; - | ^ not allowed in type signatures - -error[E0046]: not all trait items implemented, missing: `F` - --> $DIR/typeck_type_placeholder_item.rs:200:1 - | -LL | type F: std::ops::Fn(_); - | ----------------------- `F` from trait -... -LL | impl Qux for Struct { - | ^^^^^^^^^^^^^^^^^^^ missing `F` in implementation - error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:225:31 | @@ -624,6 +607,17 @@ LL | fn test9(&self) -> _ { () } | not allowed in type signatures | help: replace with the correct return type: `()` +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/typeck_type_placeholder_item.rs:44:27 + | +LL | fn test10(&self, _x : _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test10(&self, _x : T) { } + | +++ ~ + error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:107:31 | @@ -633,6 +627,17 @@ LL | fn fn_test9(&self) -> _ { () } | not allowed in type signatures | help: replace with the correct return type: `()` +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/typeck_type_placeholder_item.rs:110:34 + | +LL | fn fn_test10(&self, _x : _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test10(&self, _x : T) { } + | +++ ~ + error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types --> $DIR/typeck_type_placeholder_item.rs:202:14 | From c7cb45a791b0a3191b68a3cfaf5883e1958466ec Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 22 Aug 2024 00:55:23 +0000 Subject: [PATCH 1233/1301] Remove stray fixmes. --- compiler/rustc_hir_analysis/src/collect.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 02ad5355d252b..640907c3e4a50 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -260,7 +260,6 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( | hir::ItemKind::Trait(_, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Struct(_, generics) => (generics, true), - // FIXME: how to handle opaque types since no longer items hir::ItemKind::TyAlias(_, generics) => (generics, false), // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. _ => return, @@ -744,8 +743,6 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } - // FIXME: ok to ignore opaque tys in collection? - // hir::ItemKind::TyAlias(..) => { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); From 6ec58a44e20fa3c0a5c957ab3c31f2d039e9a24a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 18 Aug 2024 16:10:54 +0000 Subject: [PATCH 1234/1301] Simplify bound var resolution. --- .../src/collect/resolve_bound_vars.rs | 56 +++++++++---------- .../src/middle/resolve_bound_vars.rs | 10 ++-- compiler/rustc_middle/src/query/mod.rs | 6 +- compiler/rustc_middle/src/ty/context.rs | 11 ++-- 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 513d4e9b4a555..c8852a3a3695a 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -11,10 +11,13 @@ use std::fmt; use rustc_ast::visit::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::sorted_map::SortedMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirId, HirIdMap, LifetimeName, Node}; +use rustc_hir::{ + GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, LifetimeName, Node, +}; use rustc_macros::extension; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::*; @@ -74,7 +77,7 @@ impl ResolvedArg { struct NamedVarMap { // maps from every use of a named (not anonymous) bound var to a // `ResolvedArg` describing how that variable is bound - defs: HirIdMap, + defs: ItemLocalMap, // Maps relevant hir items to the bound vars on them. These include: // - function defs @@ -82,7 +85,7 @@ struct NamedVarMap { // - closures // - trait refs // - bound types (like `T` in `for<'a> T<'a>: Foo`) - late_bound_vars: HirIdMap>, + late_bound_vars: ItemLocalMap>, } struct BoundVarContext<'a, 'tcx> { @@ -225,10 +228,10 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { resolve_bound_vars, - named_variable_map: |tcx, id| tcx.resolve_bound_vars(id).defs.get(&id), + named_variable_map: |tcx, id| &tcx.resolve_bound_vars(id).defs, is_late_bound_map, object_lifetime_default, - late_bound_vars_map: |tcx, id| tcx.resolve_bound_vars(id).late_bound_vars.get(&id), + late_bound_vars_map: |tcx, id| &tcx.resolve_bound_vars(id).late_bound_vars, ..*providers }; @@ -265,16 +268,12 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou hir::OwnerNode::Synthetic => unreachable!(), } - let mut rl = ResolveBoundVars::default(); - - for (hir_id, v) in named_variable_map.defs { - let map = rl.defs.entry(hir_id.owner).or_default(); - map.insert(hir_id.local_id, v); - } - for (hir_id, v) in named_variable_map.late_bound_vars { - let map = rl.late_bound_vars.entry(hir_id.owner).or_default(); - map.insert(hir_id.local_id, v); - } + let defs = named_variable_map.defs.into_sorted_stable_ord(); + let late_bound_vars = named_variable_map.late_bound_vars.into_sorted_stable_ord(); + let rl = ResolveBoundVars { + defs: SortedMap::from_presorted_elements(defs), + late_bound_vars: SortedMap::from_presorted_elements(late_bound_vars), + }; debug!(?rl.defs); debug!(?rl.late_bound_vars); @@ -340,7 +339,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Binder { hir_id, .. } => { // Nested poly trait refs have the binders concatenated let mut full_binders = - self.map.late_bound_vars.entry(*hir_id).or_default().clone(); + self.map.late_bound_vars.entry(hir_id.local_id).or_default().clone(); full_binders.extend(supertrait_bound_vars); break (full_binders, BinderScopeType::Concatenating); } @@ -677,7 +676,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { hir::TyKind::Ref(lifetime_ref, ref mt) => { self.visit_lifetime(lifetime_ref); let scope = Scope::ObjectLifetimeDefault { - lifetime: self.map.defs.get(&lifetime_ref.hir_id).cloned(), + lifetime: self.map.defs.get(&lifetime_ref.hir_id.local_id).cloned(), s: self.scope, }; self.with(scope, |this| this.visit_ty(mt.ty)); @@ -704,7 +703,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // and ban them. Type variables instantiated inside binders aren't // well-supported at the moment, so this doesn't work. // In the future, this should be fixed and this error should be removed. - let def = self.map.defs.get(&lifetime.hir_id).copied(); + let def = self.map.defs.get(&lifetime.hir_id.local_id).copied(); let Some(ResolvedArg::LateBound(_, _, lifetime_def_id)) = def else { continue }; let lifetime_hir_id = self.tcx.local_def_id_to_hir_id(lifetime_def_id); @@ -841,7 +840,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let bound_vars: Vec<_> = self.tcx.fn_sig(sig_id).skip_binder().bound_vars().iter().collect(); let hir_id = self.tcx.local_def_id_to_hir_id(def_id); - self.map.late_bound_vars.insert(hir_id, bound_vars); + self.map.late_bound_vars.insert(hir_id.local_id, bound_vars); } self.visit_fn_like_elision(fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); intravisit::walk_fn_kind(self, fk); @@ -1019,10 +1018,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec) { - if let Some(old) = self.map.late_bound_vars.insert(hir_id, binder) { + if let Some(old) = self.map.late_bound_vars.insert(hir_id.local_id, binder) { bug!( "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}", - self.map.late_bound_vars[&hir_id] + self.map.late_bound_vars[&hir_id.local_id] ) } } @@ -1381,9 +1380,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { kind.descr(param_def_id.to_def_id()) ), }; - self.map.defs.insert(hir_id, ResolvedArg::Error(guar)); + self.map.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); } else { - self.map.defs.insert(hir_id, def); + self.map.defs.insert(hir_id.local_id, def); } return; } @@ -1416,7 +1415,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { bug!("unexpected def-kind: {}", kind.descr(param_def_id.to_def_id())) } }); - self.map.defs.insert(hir_id, ResolvedArg::Error(guar)); + self.map.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); return; } Scope::Root { .. } => break, @@ -1526,7 +1525,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // This index can be used with `generic_args` since `parent_count == 0`. let index = generics.param_def_id_to_index[¶m_def_id] as usize; generic_args.args.get(index).and_then(|arg| match arg { - GenericArg::Lifetime(lt) => map.defs.get(<.hir_id).copied(), + GenericArg::Lifetime(lt) => map.defs.get(<.hir_id.local_id).copied(), _ => None, }) } @@ -1816,7 +1815,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: ResolvedArg) { debug!(span = ?lifetime_ref.ident.span); - self.map.defs.insert(lifetime_ref.hir_id, def); + self.map.defs.insert(lifetime_ref.hir_id.local_id, def); } /// Sometimes we resolve a lifetime, but later find that it is an @@ -1827,8 +1826,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { lifetime_ref: &'tcx hir::Lifetime, bad_def: ResolvedArg, ) { - // FIXME(#120456) - is `swap_remove` correct? - let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id); + let old_value = self.map.defs.remove(&lifetime_ref.hir_id.local_id); assert_eq!(old_value, Some(bad_def)); } @@ -1998,7 +1996,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // See where these vars are used in `HirTyLowerer::lower_ty_maybe_return_type_notation`. // And this is exercised in: // `tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs`. - let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id).unwrap(); + let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id.local_id).unwrap(); let existing_bound_vars_saved = existing_bound_vars.clone(); existing_bound_vars.extend(bound_vars); self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved); diff --git a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs index 32e2f3b4b1685..13e35cd090983 100644 --- a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs +++ b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs @@ -1,9 +1,9 @@ //! Name resolution for lifetimes and late-bound type and const variables: type declarations. -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir::ItemLocalId; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{ItemLocalId, OwnerId}; use rustc_macros::{Decodable, Encodable, HashStable, TyDecodable, TyEncodable}; use crate::ty; @@ -47,11 +47,11 @@ pub enum ObjectLifetimeDefault { /// Maps the id of each lifetime reference to the lifetime decl /// that it corresponds to. -#[derive(Default, HashStable, Debug)] +#[derive(HashStable, Debug)] pub struct ResolveBoundVars { /// Maps from every use of a named (not anonymous) lifetime to a /// `Region` describing how that region is bound - pub defs: FxIndexMap>, + pub defs: SortedMap, - pub late_bound_vars: FxIndexMap>>, + pub late_bound_vars: SortedMap>, } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index bd6a7578a6864..f0be70e00dfca 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -16,6 +16,7 @@ use rustc_ast::expand::StrippedCfgItem; use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; @@ -1742,8 +1743,7 @@ rustc_queries! { arena_cache desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) } } - query named_variable_map(owner_id: hir::OwnerId) -> - Option<&'tcx FxIndexMap> { + query named_variable_map(owner_id: hir::OwnerId) -> &'tcx SortedMap { desc { |tcx| "looking up a named region inside `{}`", tcx.def_path_str(owner_id) } } query is_late_bound_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexSet> { @@ -1759,7 +1759,7 @@ rustc_queries! { separate_provide_extern } query late_bound_vars_map(owner_id: hir::OwnerId) - -> Option<&'tcx FxIndexMap>> { + -> &'tcx SortedMap> { desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d6547b51186b6..27c1b88f93f74 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2996,7 +2996,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn named_bound_var(self, id: HirId) -> Option { debug!(?id, "named_region"); - self.named_variable_map(id.owner).and_then(|map| map.get(&id.local_id).cloned()) + self.named_variable_map(id.owner).get(&id.local_id).cloned() } pub fn is_late_bound(self, id: HirId) -> bool { @@ -3005,12 +3005,9 @@ impl<'tcx> TyCtxt<'tcx> { pub fn late_bound_vars(self, id: HirId) -> &'tcx List { self.mk_bound_variable_kinds( - &self - .late_bound_vars_map(id.owner) - .and_then(|map| map.get(&id.local_id).cloned()) - .unwrap_or_else(|| { - bug!("No bound vars found for {}", self.hir().node_to_string(id)) - }), + &self.late_bound_vars_map(id.owner).get(&id.local_id).cloned().unwrap_or_else(|| { + bug!("No bound vars found for {}", self.hir().node_to_string(id)) + }), ) } From 6278e0f50745bafb8563c5765ef6a1622e3718c4 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 18 Aug 2024 16:35:03 +0000 Subject: [PATCH 1235/1301] Promote crash tests to ui. --- tests/crashes/119716-2.rs | 4 --- tests/crashes/119716.rs | 4 --- tests/crashes/121422.rs | 8 ----- tests/crashes/125843.rs | 4 --- tests/crashes/129099.rs | 15 ---------- .../bound-lifetime-through-dyn-trait.rs | 18 +++++++++++ .../bound-lifetime-through-dyn-trait.stderr | 28 +++++++++++++++++ .../non-lifetime-binder-in-constraint.rs | 13 ++++++++ .../non-lifetime-binder-in-constraint.stderr | 30 +++++++++++++++++++ .../non-lifetime-binder.rs | 10 +++++++ .../non-lifetime-binder.stderr | 25 ++++++++++++++++ 11 files changed, 124 insertions(+), 35 deletions(-) delete mode 100644 tests/crashes/119716-2.rs delete mode 100644 tests/crashes/119716.rs delete mode 100644 tests/crashes/121422.rs delete mode 100644 tests/crashes/125843.rs delete mode 100644 tests/crashes/129099.rs create mode 100644 tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs create mode 100644 tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr create mode 100644 tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs create mode 100644 tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr create mode 100644 tests/ui/type-alias-impl-trait/non-lifetime-binder.rs create mode 100644 tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr diff --git a/tests/crashes/119716-2.rs b/tests/crashes/119716-2.rs deleted file mode 100644 index 47bffb5c1de0d..0000000000000 --- a/tests/crashes/119716-2.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #119716 -#![feature(non_lifetime_binders)] -trait Trait {} -fn f() -> impl for Trait> {} diff --git a/tests/crashes/119716.rs b/tests/crashes/119716.rs deleted file mode 100644 index d7cba0f51c4e0..0000000000000 --- a/tests/crashes/119716.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #119716 -#![feature(non_lifetime_binders)] -trait v0 {} -fn kind :(v3main impl for v0<'_, v2 = impl v0 + '_>) {} diff --git a/tests/crashes/121422.rs b/tests/crashes/121422.rs deleted file mode 100644 index 5d7ef6e8ce991..0000000000000 --- a/tests/crashes/121422.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #121422 -#![feature(non_lifetime_binders)] - -trait Trait {} - -fn produce() -> impl for Trait<(), Assoc = impl Trait> { - 16 -} diff --git a/tests/crashes/125843.rs b/tests/crashes/125843.rs deleted file mode 100644 index 8b9a3913c7e62..0000000000000 --- a/tests/crashes/125843.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: rust-lang/rust#125843 -#![feature(non_lifetime_binders)] -trait v0<> {} -fn kind :(v3main impl for v0<'_, v2 = impl v0 + '_>) {} diff --git a/tests/crashes/129099.rs b/tests/crashes/129099.rs deleted file mode 100644 index 9aaab756b5b8d..0000000000000 --- a/tests/crashes/129099.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: rust-lang/rust#129099 - -#![feature(type_alias_impl_trait)] - -fn dyn_hoops() -> dyn for<'a> Iterator> { - loop {} -} - -pub fn main() { - type Opaque = impl Sized; - fn define() -> Opaque { - let x: Opaque = dyn_hoops::<()>(0); - x - } -} diff --git a/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs new file mode 100644 index 0000000000000..df589473a8460 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs @@ -0,0 +1,18 @@ +#![feature(type_alias_impl_trait)] + +trait Captures<'a> {} +impl Captures<'_> for T {} + +fn dyn_hoops() -> dyn for<'a> Iterator> { + //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type + loop {} +} + +pub fn main() { + //~^ ERROR item does not constrain `Opaque::{opaque#0}`, but has it in its signature + type Opaque = impl Sized; + fn define() -> Opaque { + let x: Opaque = dyn_hoops::<()>(); + x + } +} diff --git a/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr new file mode 100644 index 0000000000000..59d9ff86c6e09 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr @@ -0,0 +1,28 @@ +error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type + --> $DIR/bound-lifetime-through-dyn-trait.rs:6:71 + | +LL | fn dyn_hoops() -> dyn for<'a> Iterator> { + | ^^ + | +note: lifetime declared here + --> $DIR/bound-lifetime-through-dyn-trait.rs:6:37 + | +LL | fn dyn_hoops() -> dyn for<'a> Iterator> { + | ^^ + +error: item does not constrain `Opaque::{opaque#0}`, but has it in its signature + --> $DIR/bound-lifetime-through-dyn-trait.rs:11:8 + | +LL | pub fn main() { + | ^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/bound-lifetime-through-dyn-trait.rs:13:19 + | +LL | type Opaque = impl Sized; + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0657`. diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs b/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs new file mode 100644 index 0000000000000..dda42580e0f3a --- /dev/null +++ b/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs @@ -0,0 +1,13 @@ +#![allow(incomplete_features)] +#![feature(non_lifetime_binders)] + +trait Trait {} + +fn produce() -> impl for Trait<(), Assoc = impl Trait> { + //~^ ERROR associated type `Assoc` not found for `Trait` + //~| ERROR associated type `Assoc` not found for `Trait` + //~| the trait bound `{integer}: Trait<()>` is not satisfied + 16 +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr b/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr new file mode 100644 index 0000000000000..fa3306ff11fa0 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr @@ -0,0 +1,30 @@ +error[E0220]: associated type `Assoc` not found for `Trait` + --> $DIR/non-lifetime-binder-in-constraint.rs:6:39 + | +LL | fn produce() -> impl for Trait<(), Assoc = impl Trait> { + | ^^^^^ associated type `Assoc` not found + +error[E0220]: associated type `Assoc` not found for `Trait` + --> $DIR/non-lifetime-binder-in-constraint.rs:6:39 + | +LL | fn produce() -> impl for Trait<(), Assoc = impl Trait> { + | ^^^^^ associated type `Assoc` not found + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `{integer}: Trait<()>` is not satisfied + --> $DIR/non-lifetime-binder-in-constraint.rs:6:17 + | +LL | fn produce() -> impl for Trait<(), Assoc = impl Trait> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<()>` is not implemented for `{integer}` + | +help: this trait has no implementations, consider adding one + --> $DIR/non-lifetime-binder-in-constraint.rs:4:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0220, E0277. +For more information about an error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder.rs b/tests/ui/type-alias-impl-trait/non-lifetime-binder.rs new file mode 100644 index 0000000000000..23951c3427006 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/non-lifetime-binder.rs @@ -0,0 +1,10 @@ +#![allow(incomplete_features)] +#![feature(non_lifetime_binders)] + +trait Trait {} + +fn f() -> impl for Trait> {} +//~^ ERROR nested `impl Trait` is not allowed +//~| ERROR the trait bound `(): Trait>` is not satisfied + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr b/tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr new file mode 100644 index 0000000000000..5859d952b75c7 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr @@ -0,0 +1,25 @@ +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/non-lifetime-binder.rs:6:29 + | +LL | fn f() -> impl for Trait> {} + | ------------------^^^^^^^^^^^^^- + | | | + | | nested `impl Trait` here + | outer `impl Trait` + +error[E0277]: the trait bound `(): Trait>` is not satisfied + --> $DIR/non-lifetime-binder.rs:6:11 + | +LL | fn f() -> impl for Trait> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/non-lifetime-binder.rs:4:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0666. +For more information about an error, try `rustc --explain E0277`. From 98941f76ff5cd166d9c88177cae1e06db08bbe5f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 18 Aug 2024 16:54:02 +0000 Subject: [PATCH 1236/1301] Bless incremental tests. --- tests/incremental/hashes/function_interfaces.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/incremental/hashes/function_interfaces.rs b/tests/incremental/hashes/function_interfaces.rs index ab4d578458d40..016a1813babd7 100644 --- a/tests/incremental/hashes/function_interfaces.rs +++ b/tests/incremental/hashes/function_interfaces.rs @@ -318,9 +318,9 @@ pub fn change_return_impl_trait() -> impl Clone { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(cfg = "cfail2")] +#[rustc_clean(cfg = "cfail2", except = "opt_hir_owner_nodes")] #[rustc_clean(cfg = "cfail3")] -#[rustc_clean(cfg = "cfail5", except = "typeck")] +#[rustc_clean(cfg = "cfail5", except = "opt_hir_owner_nodes, typeck")] #[rustc_clean(cfg = "cfail6")] pub fn change_return_impl_trait() -> impl Copy { 0u32 From ef17eb79bbc5c7f5b303e203c1095e2eb216f84b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 18 Aug 2024 16:43:39 +0000 Subject: [PATCH 1237/1301] Adapt clippy. --- .../clippy_lints/src/extra_unused_type_parameters.rs | 8 +------- .../clippy/clippy_lints/src/implied_bounds_in_impls.rs | 9 +++------ src/tools/clippy/clippy_lints/src/len_zero.rs | 6 +----- src/tools/clippy/clippy_lints/src/lifetimes.rs | 8 +++----- src/tools/clippy/clippy_lints/src/manual_async_fn.rs | 6 ++---- src/tools/clippy/clippy_lints/src/missing_doc.rs | 3 +-- src/tools/clippy/clippy_lints/src/missing_inline.rs | 1 - src/tools/clippy/clippy_lints/src/use_self.rs | 10 ++-------- 8 files changed, 13 insertions(+), 38 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 796af851bac0a..6ad879b9fe7ae 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty}; use rustc_hir::{ BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, - PredicateOrigin, Ty, TyKind, WherePredicate, + PredicateOrigin, Ty, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; @@ -199,12 +199,6 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> { fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { if let Some((def_id, _)) = t.peel_refs().as_generic_param() { self.ty_params.remove(&def_id); - } else if let TyKind::OpaqueDef(id, _) = t.kind { - // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls - // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're - // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. - let item = self.nested_visit_map().item(id); - walk_item(self, item); } else { walk_ty(self, t); } diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 6794c6cabfeef..5f349d780537b 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def_id::DefId; use rustc_hir::{ - AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, + AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, TyKind, WherePredicate, }; use rustc_hir_analysis::lower_ty; @@ -342,11 +342,8 @@ impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &rustc_hir::Ty<'_>) { - if let TyKind::OpaqueDef(item_id, ..) = ty.kind - && let item = cx.tcx.hir().item(item_id) - && let ItemKind::OpaqueTy(opaque_ty) = item.kind - { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &rustc_hir::Ty<'tcx>) { + if let TyKind::OpaqueDef(opaque_ty, ..) = ty.kind { check(cx, opaque_ty.bounds); } } diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 45346cd18a61b..311bbce14bd7e 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -308,11 +308,7 @@ enum LenOutput { fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { if let ty::Alias(_, alias_ty) = ty.kind() - && let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) - && let Item { - kind: ItemKind::OpaqueTy(opaque), - .. - } = item + && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir().get_if_local(alias_ty.def_id) && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds && let Some(segment) = trait_ref.trait_ref.path.segments.last() diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 5c37e73544577..7c3ef98fd74f5 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate, }; use rustc_hir::{ @@ -420,11 +420,9 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::OpaqueDef(item, bounds) => { - let map = self.cx.tcx.hir(); - let item = map.item(item); + TyKind::OpaqueDef(opaque, bounds) => { let len = self.lts.len(); - walk_item(self, item); + self.visit_opaque_ty(opaque); self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { GenericArg::Lifetime(&l) => Some(l), diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 7097c85156c34..81115cffdca88 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, - FnRetTy, GenericArg, GenericBound, ImplItem, Item, ItemKind, LifetimeName, Node, TraitRef, Ty, TyKind, + FnRetTy, GenericArg, GenericBound, ImplItem, Item, LifetimeName, Node, TraitRef, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -105,9 +105,7 @@ fn future_trait_ref<'tcx>( cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>, ) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { - if let TyKind::OpaqueDef(item_id, bounds) = ty.kind - && let item = cx.tcx.hir().item(item_id) - && let ItemKind::OpaqueTy(opaque) = &item.kind + if let TyKind::OpaqueDef(opaque, bounds) = ty.kind && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { if let GenericBound::Trait(poly, _) = bound { Some(&poly.trait_ref) diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 64fc1a8a1a580..007bcebdff650 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -193,8 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::OpaqueTy(..) => {}, + | hir::ItemKind::Union(..) => {} hir::ItemKind::ExternCrate(..) | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::GlobalAsm(..) diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index d342be4545cea..f95a0f63fab7a 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -130,7 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::TyAlias(..) | hir::ItemKind::Union(..) - | hir::ItemKind::OpaqueTy(..) | hir::ItemKind::ExternCrate(..) | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::Impl { .. } diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 08449de79b364..f5cf4a586fd29 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -85,10 +85,6 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - if matches!(item.kind, ItemKind::OpaqueTy(_)) { - // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>` - return; - } // We push the self types of `impl`s on a stack here. Only the top type on the stack is // relevant for linting, since this is the self type of the `impl` we're currently in. To // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that @@ -130,10 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { self.stack.push(stack_item); } - fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { - if !matches!(item.kind, ItemKind::OpaqueTy(_)) { - self.stack.pop(); - } + fn check_item_post(&mut self, _: &LateContext<'_>, _: &Item<'_>) { + self.stack.pop(); } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { From 6b67c46a253d453674401fad70c515d8fd439e35 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 24 Aug 2024 18:19:38 +0000 Subject: [PATCH 1238/1301] Compute array length from type for unconditional panic. --- .../rustc_mir_transform/src/known_panics_lint.rs | 16 +++++++++------- tests/ui/lint/unconditional_panic_promoted.rs | 8 ++++++++ .../ui/lint/unconditional_panic_promoted.stderr | 10 ++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 tests/ui/lint/unconditional_panic_promoted.rs create mode 100644 tests/ui/lint/unconditional_panic_promoted.stderr diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index ccc029b1e281e..8f490094d6030 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -600,13 +600,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } Len(place) => { - let len = match self.get_const(place)? { - Value::Immediate(src) => src.len(&self.ecx).discard_err()?, - Value::Aggregate { fields, .. } => fields.len() as u64, - Value::Uninit => match place.ty(self.local_decls(), self.tcx).ty.kind() { - ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, - _ => return None, - }, + let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind() + { + n.try_eval_target_usize(self.tcx, self.param_env)? + } else { + match self.get_const(place)? { + Value::Immediate(src) => src.len(&self.ecx).discard_err()?, + Value::Aggregate { fields, .. } => fields.len() as u64, + Value::Uninit => return None, + } }; ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into() } diff --git a/tests/ui/lint/unconditional_panic_promoted.rs b/tests/ui/lint/unconditional_panic_promoted.rs new file mode 100644 index 0000000000000..37bcf04651352 --- /dev/null +++ b/tests/ui/lint/unconditional_panic_promoted.rs @@ -0,0 +1,8 @@ +//@ build-fail + +fn main() { + // MIR encodes this as a reborrow from a promoted constant. + // But the array lenth can still be gotten from the type. + let slice = &[0, 1]; + let _ = slice[2]; //~ ERROR: this operation will panic at runtime [unconditional_panic] +} diff --git a/tests/ui/lint/unconditional_panic_promoted.stderr b/tests/ui/lint/unconditional_panic_promoted.stderr new file mode 100644 index 0000000000000..647a84a55fda5 --- /dev/null +++ b/tests/ui/lint/unconditional_panic_promoted.stderr @@ -0,0 +1,10 @@ +error: this operation will panic at runtime + --> $DIR/unconditional_panic_promoted.rs:7:13 + | +LL | let _ = slice[2]; + | ^^^^^^^^ index out of bounds: the length is 2 but the index is 2 + | + = note: `#[deny(unconditional_panic)]` on by default + +error: aborting due to 1 previous error + From 479779d6a90cc228fc45b5126c558ca52539b2c2 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 25 Aug 2024 12:13:15 +0000 Subject: [PATCH 1239/1301] Bless clippy. --- .../clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed | 1 + src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs | 1 + .../clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr | 2 +- src/tools/clippy/tests/ui/get_unwrap.fixed | 1 + src/tools/clippy/tests/ui/get_unwrap.rs | 1 + src/tools/clippy/tests/ui/get_unwrap.stderr | 8 ++++---- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed index baf939af24efb..cdb8fa0454cd4 100644 --- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed +++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed @@ -86,6 +86,7 @@ mod issue9612 { util(); } + #[allow(unconditional_panic)] fn util() { let _a: u8 = 4.try_into().unwrap(); let _a: u8 = 5.try_into().expect(""); diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs index e300ba18c3309..e53d53db5f7a4 100644 --- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -86,6 +86,7 @@ mod issue9612 { util(); } + #[allow(unconditional_panic)] fn util() { let _a: u8 = 4.try_into().unwrap(); let _a: u8 = 5.try_into().expect(""); diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr index 320578bfabce0..b58ce9b8af3e4 100644 --- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -274,7 +274,7 @@ LL | let _ = &boxed_slice[1]; | ~~~~~~~~~~~~~~~ error: called `.get().unwrap()` on a slice - --> tests/ui-toml/unwrap_used/unwrap_used.rs:93:17 + --> tests/ui-toml/unwrap_used/unwrap_used.rs:94:17 | LL | let _ = Box::new([0]).get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/get_unwrap.fixed b/src/tools/clippy/tests/ui/get_unwrap.fixed index 62beb195939c6..2dd3c30a4e29b 100644 --- a/src/tools/clippy/tests/ui/get_unwrap.fixed +++ b/src/tools/clippy/tests/ui/get_unwrap.fixed @@ -70,6 +70,7 @@ fn main() { mod issue9909 { #![allow(clippy::identity_op, clippy::unwrap_used, dead_code)] + #[allow(unconditional_panic)] fn reduced() { let f = &[1, 2, 3]; diff --git a/src/tools/clippy/tests/ui/get_unwrap.rs b/src/tools/clippy/tests/ui/get_unwrap.rs index 1e09ff5c67e51..94226564cacb5 100644 --- a/src/tools/clippy/tests/ui/get_unwrap.rs +++ b/src/tools/clippy/tests/ui/get_unwrap.rs @@ -70,6 +70,7 @@ fn main() { mod issue9909 { #![allow(clippy::identity_op, clippy::unwrap_used, dead_code)] + #[allow(unconditional_panic)] fn reduced() { let f = &[1, 2, 3]; diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr index 0f8b279da1e7a..8eacb249c60c6 100644 --- a/src/tools/clippy/tests/ui/get_unwrap.stderr +++ b/src/tools/clippy/tests/ui/get_unwrap.stderr @@ -266,7 +266,7 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice - --> tests/ui/get_unwrap.rs:77:24 + --> tests/ui/get_unwrap.rs:78:24 | LL | let _x: &i32 = f.get(1 + 2).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL | let _x: &i32 = &f[1 + 2]; | ~~~~~~~~~ error: called `.get().unwrap()` on a slice - --> tests/ui/get_unwrap.rs:80:18 + --> tests/ui/get_unwrap.rs:81:18 | LL | let _x = f.get(1 + 2).unwrap().to_string(); | ^^^^^^^^^^^^^^^^^^^^^ @@ -288,7 +288,7 @@ LL | let _x = f[1 + 2].to_string(); | ~~~~~~~~ error: called `.get().unwrap()` on a slice - --> tests/ui/get_unwrap.rs:83:18 + --> tests/ui/get_unwrap.rs:84:18 | LL | let _x = f.get(1 + 2).unwrap().abs(); | ^^^^^^^^^^^^^^^^^^^^^ @@ -299,7 +299,7 @@ LL | let _x = f[1 + 2].abs(); | ~~~~~~~~ error: called `.get_mut().unwrap()` on a slice - --> tests/ui/get_unwrap.rs:100:33 + --> tests/ui/get_unwrap.rs:101:33 | LL | let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 00ed47b8496bacb9da0196127508e07ba5d14bb4 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 28 Aug 2024 00:00:08 +0200 Subject: [PATCH 1240/1301] Make deprecated_cfg_attr_crate_type_name a hard error --- compiler/rustc_expand/messages.ftl | 6 +++ compiler/rustc_expand/src/config.rs | 19 +++------ compiler/rustc_expand/src/errors.rs | 14 +++++++ compiler/rustc_lint/messages.ftl | 6 --- .../rustc_lint/src/context/diagnostics.rs | 6 --- compiler/rustc_lint/src/lib.rs | 5 +++ compiler/rustc_lint/src/lints.rs | 8 ---- compiler/rustc_lint_defs/src/builtin.rs | 37 ----------------- compiler/rustc_lint_defs/src/lib.rs | 2 - ....rs => crate-attributes-using-cfg_attr.rs} | 4 -- .../crate-attributes-using-cfg_attr.stderr | 30 ++++++++++++++ ...pat-crate-attributes-using-cfg_attr.stderr | 41 ------------------- 12 files changed, 60 insertions(+), 118 deletions(-) rename tests/ui/cfg/{future-compat-crate-attributes-using-cfg_attr.rs => crate-attributes-using-cfg_attr.rs} (63%) create mode 100644 tests/ui/cfg/crate-attributes-using-cfg_attr.stderr delete mode 100644 tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 766d96e268f04..57bac9d09d5ec 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -27,6 +27,12 @@ expand_collapse_debuginfo_illegal = expand_count_repetition_misplaced = `count` can not be placed inside the inner-most repetition +expand_crate_name_in_cfg_attr = + `crate_name` within an `#![cfg_attr]` attribute is forbidden + +expand_crate_type_in_cfg_attr = + `crate_type` within an `#![cfg_attr]` attribute is forbidden + expand_custom_attribute_panicked = custom attribute panicked .help = message: {$message} diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 3208837427736..7bd4372d18645 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -21,8 +21,9 @@ use thin_vec::ThinVec; use tracing::instrument; use crate::errors::{ - FeatureNotAllowed, FeatureRemoved, FeatureRemovedReason, InvalidCfg, MalformedFeatureAttribute, - MalformedFeatureAttributeHelp, RemoveExprNotSupported, + CrateNameInCfgAttr, CrateTypeInCfgAttr, FeatureNotAllowed, FeatureRemoved, + FeatureRemovedReason, InvalidCfg, MalformedFeatureAttribute, MalformedFeatureAttributeHelp, + RemoveExprNotSupported, }; /// A folder that strips out items that do not belong in the current configuration. @@ -358,20 +359,10 @@ impl<'a> StripUnconfigured<'a> { item_span, ); if attr.has_name(sym::crate_type) { - self.sess.psess.buffer_lint( - rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, - attr.span, - ast::CRATE_NODE_ID, - BuiltinLintDiag::CrateTypeInCfgAttr, - ); + self.sess.dcx().emit_err(CrateTypeInCfgAttr { span: attr.span }); } if attr.has_name(sym::crate_name) { - self.sess.psess.buffer_lint( - rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, - attr.span, - ast::CRATE_NODE_ID, - BuiltinLintDiag::CrateNameInCfgAttr, - ); + self.sess.dcx().emit_err(CrateNameInCfgAttr { span: attr.span }); } attr } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 0fdccb0891899..5682c574552b4 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -467,6 +467,20 @@ pub(crate) struct GlobDelegationOutsideImpls { pub span: Span, } +#[derive(Diagnostic)] +#[diag(expand_crate_name_in_cfg_attr)] +pub(crate) struct CrateNameInCfgAttr { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(expand_crate_type_in_cfg_attr)] +pub(crate) struct CrateTypeInCfgAttr { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(expand_glob_delegation_traitless_qpath)] pub(crate) struct GlobDelegationTraitlessQpath { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 375cfccbe9f71..3b360bdef2fa9 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -203,12 +203,6 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i .current_use = this identifier can be confused with `{$existing_sym}` .other_use = other identifier used here -lint_crate_name_in_cfg_attr_deprecated = - `crate_name` within an `#![cfg_attr]` attribute is deprecated - -lint_crate_type_in_cfg_attr_deprecated = - `crate_type` within an `#![cfg_attr]` attribute is deprecated - lint_cstring_ptr = getting the inner pointer of a temporary `CString` .as_ptr_label = this pointer will be invalid .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 168e3a8e92c5f..b5ab56912cb2d 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -400,12 +400,6 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & BuiltinLintDiag::CfgAttrNoAttributes => { lints::CfgAttrNoAttributes.decorate_lint(diag); } - BuiltinLintDiag::CrateTypeInCfgAttr => { - lints::CrateTypeInCfgAttr.decorate_lint(diag); - } - BuiltinLintDiag::CrateNameInCfgAttr => { - lints::CrateNameInCfgAttr.decorate_lint(diag); - } BuiltinLintDiag::MissingFragmentSpecifier => { lints::MissingFragmentSpecifier.decorate_lint(diag); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c74cb866f21fa..38a9fb8abfc4c 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -570,6 +570,11 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see RFC #3535 \ for more information", ); + store.register_removed( + "deprecated_cfg_attr_crate_type_name", + "converted into hard error, see issue #91632 \ + for more information", + ); store.register_removed( "pointer_structural_match", "converted into hard error, see RFC #3535 \ diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0045cfcaa56ad..5187a13bd41cf 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2441,14 +2441,6 @@ pub(crate) struct DuplicateMacroAttribute; #[diag(lint_cfg_attr_no_attributes)] pub(crate) struct CfgAttrNoAttributes; -#[derive(LintDiagnostic)] -#[diag(lint_crate_type_in_cfg_attr_deprecated)] -pub(crate) struct CrateTypeInCfgAttr; - -#[derive(LintDiagnostic)] -#[diag(lint_crate_name_in_cfg_attr_deprecated)] -pub(crate) struct CrateNameInCfgAttr; - #[derive(LintDiagnostic)] #[diag(lint_missing_fragment_specifier)] pub(crate) struct MissingFragmentSpecifier; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 549dc64a562b2..3cc9cb901a6af 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -34,7 +34,6 @@ declare_lint_pass! { DEAD_CODE, DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, DEPRECATED, - DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DEPRECATED_IN_FUTURE, DEPRECATED_SAFE_2024, DEPRECATED_WHERE_CLAUSE_LOCATION, @@ -3143,42 +3142,6 @@ declare_lint! { "detects large moves or copies", } -declare_lint! { - /// The `deprecated_cfg_attr_crate_type_name` lint detects uses of the - /// `#![cfg_attr(..., crate_type = "...")]` and - /// `#![cfg_attr(..., crate_name = "...")]` attributes to conditionally - /// specify the crate type and name in the source code. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![cfg_attr(debug_assertions, crate_type = "lib")] - /// ``` - /// - /// {{produces}} - /// - /// - /// ### Explanation - /// - /// The `#![crate_type]` and `#![crate_name]` attributes require a hack in - /// the compiler to be able to change the used crate type and crate name - /// after macros have been expanded. Neither attribute works in combination - /// with Cargo as it explicitly passes `--crate-type` and `--crate-name` on - /// the commandline. These values must match the value used in the source - /// code to prevent an error. - /// - /// To fix the warning use `--crate-type` on the commandline when running - /// rustc instead of `#![cfg_attr(..., crate_type = "...")]` and - /// `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]`. - pub DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, - Deny, - "detects usage of `#![cfg_attr(..., crate_type/crate_name = \"...\")]`", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, - reference: "issue #91632 ", - }; -} - declare_lint! { /// The `unexpected_cfgs` lint detects unexpected conditional compilation conditions. /// diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 734e7069c9026..386918a5c4157 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -720,8 +720,6 @@ pub enum BuiltinLintDiag { UnnameableTestItems, DuplicateMacroAttribute, CfgAttrNoAttributes, - CrateTypeInCfgAttr, - CrateNameInCfgAttr, MissingFragmentSpecifier, MetaVariableStillRepeating(MacroRulesNormalizedIdent), MetaVariableWrongOperator, diff --git a/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.rs b/tests/ui/cfg/crate-attributes-using-cfg_attr.rs similarity index 63% rename from tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.rs rename to tests/ui/cfg/crate-attributes-using-cfg_attr.rs index 3ced3a630e334..f99fad881f284 100644 --- a/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.rs +++ b/tests/ui/cfg/crate-attributes-using-cfg_attr.rs @@ -3,13 +3,9 @@ #![cfg_attr(foo, crate_type="bin")] //~^ERROR `crate_type` within -//~| WARN this was previously accepted //~|ERROR `crate_type` within -//~| WARN this was previously accepted #![cfg_attr(foo, crate_name="bar")] //~^ERROR `crate_name` within -//~| WARN this was previously accepted //~|ERROR `crate_name` within -//~| WARN this was previously accepted fn main() {} diff --git a/tests/ui/cfg/crate-attributes-using-cfg_attr.stderr b/tests/ui/cfg/crate-attributes-using-cfg_attr.stderr new file mode 100644 index 0000000000000..1dfca2b88d0e5 --- /dev/null +++ b/tests/ui/cfg/crate-attributes-using-cfg_attr.stderr @@ -0,0 +1,30 @@ +error: `crate_type` within an `#![cfg_attr]` attribute is forbidden + --> $DIR/crate-attributes-using-cfg_attr.rs:4:18 + | +LL | #![cfg_attr(foo, crate_type="bin")] + | ^^^^^^^^^^^^^^^^ + +error: `crate_name` within an `#![cfg_attr]` attribute is forbidden + --> $DIR/crate-attributes-using-cfg_attr.rs:7:18 + | +LL | #![cfg_attr(foo, crate_name="bar")] + | ^^^^^^^^^^^^^^^^ + +error: `crate_type` within an `#![cfg_attr]` attribute is forbidden + --> $DIR/crate-attributes-using-cfg_attr.rs:4:18 + | +LL | #![cfg_attr(foo, crate_type="bin")] + | ^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `crate_name` within an `#![cfg_attr]` attribute is forbidden + --> $DIR/crate-attributes-using-cfg_attr.rs:7:18 + | +LL | #![cfg_attr(foo, crate_name="bar")] + | ^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr b/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr deleted file mode 100644 index 82b2d7d1b1da6..0000000000000 --- a/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr +++ /dev/null @@ -1,41 +0,0 @@ -error: `crate_type` within an `#![cfg_attr]` attribute is deprecated - --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:4:18 - | -LL | #![cfg_attr(foo, crate_type="bin")] - | ^^^^^^^^^^^^^^^^ - | - = 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 #91632 - = note: `#[deny(deprecated_cfg_attr_crate_type_name)]` on by default - -error: `crate_name` within an `#![cfg_attr]` attribute is deprecated - --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:9:18 - | -LL | #![cfg_attr(foo, crate_name="bar")] - | ^^^^^^^^^^^^^^^^ - | - = 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 #91632 - -error: `crate_type` within an `#![cfg_attr]` attribute is deprecated - --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:4:18 - | -LL | #![cfg_attr(foo, crate_type="bin")] - | ^^^^^^^^^^^^^^^^ - | - = 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 #91632 - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: `crate_name` within an `#![cfg_attr]` attribute is deprecated - --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:9:18 - | -LL | #![cfg_attr(foo, crate_name="bar")] - | ^^^^^^^^^^^^^^^^ - | - = 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 #91632 - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors - From 05f7b092a484c1bd0132ab645800e53be3709aef Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sat, 5 Oct 2024 08:07:26 +0300 Subject: [PATCH 1241/1301] remove outdated contribution direction Signed-off-by: onur-ozkan --- src/bootstrap/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 0ac58645d2dfc..f036603ee707b 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -182,10 +182,8 @@ Some general areas that you may be interested in modifying are: `Config` struct. * Adding a sanity check? Take a look at `bootstrap/src/core/sanity.rs`. -If you make a major change on bootstrap configuration, please remember to: - -+ Update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`. -* Update `change-id = {pull-request-id}` in `config.example.toml`. +If you make a major change on bootstrap configuration, please add a new entry to +`CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`. A 'major change' includes From 2feed6279655d6febce04a0efd167c2aea56facf Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Sat, 5 Oct 2024 10:01:27 +0300 Subject: [PATCH 1242/1301] Handle `rustc_interface` cases of `rustc::potential_query_instability` lint --- compiler/rustc_interface/src/passes.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 204ae437a3e9a..fd850d2f39a5f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -470,7 +470,6 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P }) } - #[allow(rustc::potential_query_instability)] let extra_tracked_files = hash_iter_files( file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str()))), checksum_hash_algo, From d0e67586774530fa8fd70e563768067c0b2ca99d Mon Sep 17 00:00:00 2001 From: onestacked Date: Sun, 29 Sep 2024 23:51:18 +0200 Subject: [PATCH 1243/1301] Stabilize `const_slice_split_at_mut` and `const_slice_first_last_chunk` --- library/core/src/lib.rs | 1 - library/core/src/slice/mod.rs | 21 ++++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b1dbcc744ac60..380140601a50a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -146,7 +146,6 @@ #![feature(const_size_of_val)] #![feature(const_size_of_val_raw)] #![feature(const_slice_from_ref)] -#![feature(const_slice_split_at_mut)] #![feature(const_strict_overflow_ops)] #![feature(const_swap)] #![feature(const_try)] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 754a736b15b3c..90ddc9c1d85d6 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -356,7 +356,8 @@ impl [T] { /// ``` #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] + #[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn first_chunk_mut(&mut self) -> Option<&mut [T; N]> { if self.len() < N { None @@ -421,7 +422,8 @@ impl [T] { /// ``` #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] + #[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn split_first_chunk_mut( &mut self, ) -> Option<(&mut [T; N], &mut [T])> { @@ -491,7 +493,8 @@ impl [T] { /// ``` #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] + #[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn split_last_chunk_mut( &mut self, ) -> Option<(&mut [T], &mut [T; N])> { @@ -560,7 +563,8 @@ impl [T] { /// ``` #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")] + #[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn last_chunk_mut(&mut self) -> Option<&mut [T; N]> { if self.len() < N { None @@ -1903,7 +1907,8 @@ impl [T] { #[inline] #[track_caller] #[must_use] - #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] + #[rustc_const_stable(feature = "const_slice_split_at_mut", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { match self.split_at_mut_checked(mid) { Some(pair) => pair, @@ -2005,7 +2010,8 @@ impl [T] { /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); /// ``` #[stable(feature = "slice_split_at_unchecked", since = "1.79.0")] - #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] + #[rustc_const_stable(feature = "const_slice_split_at_mut", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] #[inline] #[must_use] pub const unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut [T], &mut [T]) { @@ -2105,7 +2111,8 @@ impl [T] { /// assert_eq!(None, v.split_at_mut_checked(7)); /// ``` #[stable(feature = "split_at_checked", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] + #[rustc_const_stable(feature = "const_slice_split_at_mut", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] #[inline] #[must_use] pub const fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut [T], &mut [T])> { From 0cd0f7ceef442f2767024df261d3898db23402d8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 4 Oct 2024 17:43:27 +0200 Subject: [PATCH 1244/1301] move f16/f128 const fn under f16/f128 feature gate --- library/core/src/lib.rs | 2 -- library/core/src/num/f128.rs | 16 ++++++++-------- library/core/src/num/f16.rs | 16 ++++++++-------- .../clippy/tests/ui/transmute_float_to_int.fixed | 4 ++-- .../clippy/tests/ui/transmute_float_to_int.rs | 4 ++-- tests/ui/float/classify-runtime-const.rs | 4 ++-- tests/ui/float/conv-bits-runtime-const.rs | 2 -- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b1dbcc744ac60..577ce124251af 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -158,8 +158,6 @@ #![feature(coverage_attribute)] #![feature(do_not_recommend)] #![feature(duration_consts_float)] -#![feature(f128_const)] -#![feature(f16_const)] #![feature(internal_impls_macro)] #![feature(ip)] #![feature(is_ascii_octdigit)] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 133d6e3fc9ae2..764df4fe4b058 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -910,7 +910,7 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_bits(self) -> u128 { // SAFETY: `u128` is a plain old datatype so we can always transmute to it. @@ -959,7 +959,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn from_bits(v: u128) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u128` is a plain old datatype so we can always transmute from it. @@ -986,7 +986,7 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 16] { self.to_bits().to_be_bytes() @@ -1012,7 +1012,7 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 16] { self.to_bits().to_le_bytes() @@ -1049,7 +1049,7 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_ne_bytes(self) -> [u8; 16] { self.to_bits().to_ne_bytes() @@ -1077,7 +1077,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn from_be_bytes(bytes: [u8; 16]) -> Self { Self::from_bits(u128::from_be_bytes(bytes)) } @@ -1104,7 +1104,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn from_le_bytes(bytes: [u8; 16]) -> Self { Self::from_bits(u128::from_le_bytes(bytes)) } @@ -1141,7 +1141,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128_const", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn from_ne_bytes(bytes: [u8; 16]) -> Self { Self::from_bits(u128::from_ne_bytes(bytes)) } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e50f5e7e8fbd5..897fc8c105d46 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -896,7 +896,7 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_bits(self) -> u16 { // SAFETY: `u16` is a plain old datatype so we can always transmute to it. @@ -944,7 +944,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn from_bits(v: u16) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u16` is a plain old datatype so we can always transmute from it. @@ -970,7 +970,7 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 2] { self.to_bits().to_be_bytes() @@ -995,7 +995,7 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 2] { self.to_bits().to_le_bytes() @@ -1033,7 +1033,7 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_ne_bytes(self) -> [u8; 2] { self.to_bits().to_ne_bytes() @@ -1057,7 +1057,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn from_be_bytes(bytes: [u8; 2]) -> Self { Self::from_bits(u16::from_be_bytes(bytes)) } @@ -1080,7 +1080,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn from_le_bytes(bytes: [u8; 2]) -> Self { Self::from_bits(u16::from_le_bytes(bytes)) } @@ -1114,7 +1114,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16_const", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn from_ne_bytes(bytes: [u8; 2]) -> Self { Self::from_bits(u16::from_ne_bytes(bytes)) } diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed index 83814ca43b96d..075a198918a0d 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed @@ -1,7 +1,7 @@ #![warn(clippy::transmute_float_to_int)] #![allow(clippy::missing_transmute_annotations)] -#![feature(f128, f128_const)] -#![feature(f16, f16_const)] +#![feature(f128)] +#![feature(f16)] fn float_to_int() { let _: u32 = unsafe { 1f32.to_bits() }; diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs index 64d6e9172039d..12541b2f7cf32 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -1,7 +1,7 @@ #![warn(clippy::transmute_float_to_int)] #![allow(clippy::missing_transmute_annotations)] -#![feature(f128, f128_const)] -#![feature(f16, f16_const)] +#![feature(f128)] +#![feature(f16)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; diff --git a/tests/ui/float/classify-runtime-const.rs b/tests/ui/float/classify-runtime-const.rs index 2a24e51cabbc4..ca852ea2468bc 100644 --- a/tests/ui/float/classify-runtime-const.rs +++ b/tests/ui/float/classify-runtime-const.rs @@ -6,8 +6,8 @@ // This tests the float classification functions, for regular runtime code and for const evaluation. -#![feature(f16_const)] -#![feature(f128_const)] +#![feature(f16)] +#![feature(f128)] use std::num::FpCategory::*; diff --git a/tests/ui/float/conv-bits-runtime-const.rs b/tests/ui/float/conv-bits-runtime-const.rs index 60c45cc4cc194..3046728fe66ff 100644 --- a/tests/ui/float/conv-bits-runtime-const.rs +++ b/tests/ui/float/conv-bits-runtime-const.rs @@ -5,8 +5,6 @@ #![feature(f16)] #![feature(f128)] -#![feature(f16_const)] -#![feature(f128_const)] #![allow(unused_macro_rules)] use std::hint::black_box; From 155380523c1fe0381255556d2b0588f9a0fdcacd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Oct 2024 10:40:38 +0200 Subject: [PATCH 1245/1301] pthread mutex: better error in reentrant-locking-UB, also test PTHREAD_MUTEX_INITIALIZER --- src/tools/miri/src/shims/unix/sync.rs | 4 +++- ...ck.rs => libc_pthread_mutex_NULL_reentrant.rs} | 4 ++-- ...r => libc_pthread_mutex_NULL_reentrant.stderr} | 8 ++++---- ...rs => libc_pthread_mutex_default_reentrant.rs} | 9 +++++++-- ...> libc_pthread_mutex_default_reentrant.stderr} | 8 ++++---- ....rs => libc_pthread_mutex_normal_reentrant.rs} | 2 ++ ...=> libc_pthread_mutex_normal_reentrant.stderr} | 4 ++-- .../libc_pthread_mutex_staticinit_reentrant.rs | 12 ++++++++++++ ...libc_pthread_mutex_staticinit_reentrant.stderr | 15 +++++++++++++++ 9 files changed, 51 insertions(+), 15 deletions(-) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_NULL_deadlock.rs => libc_pthread_mutex_NULL_reentrant.rs} (72%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_NULL_deadlock.stderr => libc_pthread_mutex_NULL_reentrant.stderr} (68%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_default_deadlock.rs => libc_pthread_mutex_default_reentrant.rs} (52%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_default_deadlock.stderr => libc_pthread_mutex_default_reentrant.stderr} (68%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_normal_deadlock.rs => libc_pthread_mutex_normal_reentrant.rs} (81%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_normal_deadlock.stderr => libc_pthread_mutex_normal_reentrant.stderr} (79%) create mode 100644 src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs create mode 100644 src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 017291f81a22e..b05f340861e78 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -483,7 +483,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Trying to acquire the same mutex again. match kind { MutexKind::Default => - throw_ub_format!("trying to acquire already locked default mutex"), + throw_ub_format!( + "trying to acquire default mutex already locked by the current thread" + ), MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock), MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"), MutexKind::Recursive => { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs similarity index 72% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs index a79abe65328e0..f2df8bdca12bd 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs @@ -1,12 +1,12 @@ //@ignore-target: windows # No pthreads on Windows // -// Check that if we pass NULL attribute, then we get the default mutex type. +// Check that if we pass NULL attribute, then reentrant locking is UB. fn main() { unsafe { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, std::ptr::null() as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: Undefined Behavior: trying to acquire already locked default mutex + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: already locked by the current thread } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.stderr similarity index 68% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.stderr index e9961ed413d0c..9455e70437629 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: trying to acquire already locked default mutex - --> tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs:LL:CC +error: Undefined Behavior: trying to acquire default mutex already locked by the current thread + --> tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire already locked default mutex + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire default mutex already locked by the current thread | = 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 = note: BACKTRACE: - = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs:LL:CC + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs similarity index 52% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs index d9293f938b6aa..d2d0ffff07a92 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs @@ -1,6 +1,11 @@ //@ignore-target: windows # No pthreads on Windows // -// Check that if we do not set the mutex type, it is the default. +// Check that if we do not set the mutex type, it is UB to do reentrant locking. glibc apparently +// actually exploits this, see +// : +// one must actively call pthread_mutexattr_settype to disable lock elision. This means a call to +// pthread_mutexattr_settype(PTHREAD_MUTEX_NORMAL) makes a difference even if +// PTHREAD_MUTEX_NORMAL == PTHREAD_MUTEX_DEFAULT! fn main() { unsafe { @@ -9,6 +14,6 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: Undefined Behavior: trying to acquire already locked default mutex + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: already locked by the current thread } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.stderr similarity index 68% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.stderr index a57d10753d90d..a9ffbde1b65cf 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: trying to acquire already locked default mutex - --> tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs:LL:CC +error: Undefined Behavior: trying to acquire default mutex already locked by the current thread + --> tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire already locked default mutex + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire default mutex already locked by the current thread | = 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 = note: BACKTRACE: - = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs:LL:CC + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs similarity index 81% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs index b38582482b88b..9a88639edf795 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs @@ -10,6 +10,8 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); + // A "normal" mutex properly tries to acquire the lock even if its is already held + // by the current thread -- and then we deadlock. libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: deadlock: the evaluated program deadlocked } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr similarity index 79% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr index 4337475963e4a..f20b26297e274 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr @@ -1,11 +1,11 @@ error: deadlock: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs:LL:CC + --> tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked | = note: BACKTRACE: - = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs:LL:CC + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs new file mode 100644 index 0000000000000..bd8aef787e6fb --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs @@ -0,0 +1,12 @@ +//@ignore-target: windows # No pthreads on Windows +// +// Check that if we use PTHREAD_MUTEX_INITIALIZER, then reentrant locking is UB. +// glibc apparently actually exploits this so we better catch it! + +fn main() { + unsafe { + let mut mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER; + assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: already locked by the current thread + } +} diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr new file mode 100644 index 0000000000000..984bb07b72895 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: trying to acquire default mutex already locked by the current thread + --> tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs:LL:CC + | +LL | libc::pthread_mutex_lock(&mut mutex as *mut _); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire default mutex already locked by the current thread + | + = 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 + = note: BACKTRACE: + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 98aa3d96e2d93eefa50374f1ea6057377bcec17b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Oct 2024 11:08:33 +0200 Subject: [PATCH 1246/1301] make Cell unstably const --- library/core/src/cell.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 690d439513712..95cf55a923e2a 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -494,8 +494,9 @@ impl Cell { /// ``` #[inline] #[stable(feature = "move_cell", since = "1.17.0")] + #[rustc_const_unstable(feature = "const_cell", issue = "131283")] #[rustc_confusables("swap")] - pub fn replace(&self, val: T) -> T { + pub const fn replace(&self, val: T) -> T { // SAFETY: This can cause data races if called from a separate thread, // but `Cell` is `!Sync` so this won't happen. mem::replace(unsafe { &mut *self.value.get() }, val) @@ -535,7 +536,8 @@ impl Cell { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self) -> T { + #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + pub const fn get(&self) -> T { // SAFETY: This can cause data races if called from a separate thread, // but `Cell` is `!Sync` so this won't happen. unsafe { *self.value.get() } @@ -613,7 +615,8 @@ impl Cell { /// ``` #[inline] #[stable(feature = "cell_get_mut", since = "1.11.0")] - pub fn get_mut(&mut self) -> &mut T { + #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + pub const fn get_mut(&mut self) -> &mut T { self.value.get_mut() } @@ -632,7 +635,8 @@ impl Cell { /// ``` #[inline] #[stable(feature = "as_cell", since = "1.37.0")] - pub fn from_mut(t: &mut T) -> &Cell { + #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + pub const fn from_mut(t: &mut T) -> &Cell { // SAFETY: `&mut` ensures unique access. unsafe { &*(t as *mut T as *const Cell) } } @@ -686,7 +690,8 @@ impl Cell<[T]> { /// assert_eq!(slice_cell.len(), 3); /// ``` #[stable(feature = "as_cell", since = "1.37.0")] - pub fn as_slice_of_cells(&self) -> &[Cell] { + #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + pub const fn as_slice_of_cells(&self) -> &[Cell] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T]> as *const [Cell]) } } @@ -706,7 +711,8 @@ impl Cell<[T; N]> { /// let array_cell: &[Cell; 3] = cell_array.as_array_of_cells(); /// ``` #[unstable(feature = "as_array_of_cells", issue = "88248")] - pub fn as_array_of_cells(&self) -> &[Cell; N] { + #[rustc_const_unstable(feature = "as_array_of_cells", issue = "88248")] + pub const fn as_array_of_cells(&self) -> &[Cell; N] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T; N]> as *const [Cell; N]) } } From b22b348e0d0334c42521aed90955cb39bd0d278c Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Thu, 19 Sep 2024 13:14:52 +0300 Subject: [PATCH 1247/1301] Add targets: riscv32{e|em|emc} - Based on riscv32{i|im|imc} - Set data_layout stack alignment: S32 (bits) - Set llvm_abiname = ilp32e --- compiler/rustc_target/src/spec/mod.rs | 4 +++ .../spec/targets/riscv32e_unknown_none_elf.rs | 25 +++++++++++++++++++ .../targets/riscv32em_unknown_none_elf.rs | 25 +++++++++++++++++++ .../targets/riscv32emc_unknown_none_elf.rs | 25 +++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs create mode 100644 compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs create mode 100644 compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 18ec8ee9476b4..82e11a3afce32 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1841,6 +1841,10 @@ supported_targets! { ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), ("riscv32imafc-esp-espidf", riscv32imafc_esp_espidf), + ("riscv32e-unknown-none-elf", riscv32e_unknown_none_elf), + ("riscv32em-unknown-none-elf", riscv32em_unknown_none_elf), + ("riscv32emc-unknown-none-elf", riscv32emc_unknown_none_elf), + ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), ("riscv32imafc-unknown-none-elf", riscv32imafc_unknown_none_elf), ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf), diff --git a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs new file mode 100644 index 0000000000000..71a5607f927a4 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs @@ -0,0 +1,25 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + llvm_abiname: "ilp32e".into(), + max_atomic_width: Some(32), + atomic_cas: false, + features: "+e,+forced-atomics".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs new file mode 100644 index 0000000000000..ed7a006963d0d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs @@ -0,0 +1,25 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + llvm_abiname: "ilp32e".into(), + max_atomic_width: Some(32), + atomic_cas: false, + features: "+e,+m,+forced-atomics".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs new file mode 100644 index 0000000000000..cba19c28be3b7 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs @@ -0,0 +1,25 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + llvm_abiname: "ilp32e".into(), + max_atomic_width: Some(32), + atomic_cas: false, + features: "+e,+m,+c,+forced-atomics".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} From 7a0bac49c8bbdd06869aad64d2ea25e41b24cb9d Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Fri, 4 Oct 2024 19:18:25 +0300 Subject: [PATCH 1248/1301] Add comment: data_layout --- .../rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs | 3 +++ .../src/spec/targets/riscv32em_unknown_none_elf.rs | 3 +++ .../src/spec/targets/riscv32emc_unknown_none_elf.rs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs index 71a5607f927a4..9aeb591cb5790 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs @@ -2,6 +2,8 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { + // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also + // `options.llvm_abiname`. data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), llvm_target: "riscv32".into(), pointer_width: 32, @@ -11,6 +13,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), + // The ilp32e ABI specifies the `data_layout` llvm_abiname: "ilp32e".into(), max_atomic_width: Some(32), atomic_cas: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs index ed7a006963d0d..07b1a0228a2ad 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs @@ -2,6 +2,8 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { + // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also + // `options.llvm_abiname`. data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), llvm_target: "riscv32".into(), pointer_width: 32, @@ -11,6 +13,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), + // The ilp32e ABI specifies the `data_layout` llvm_abiname: "ilp32e".into(), max_atomic_width: Some(32), atomic_cas: false, diff --git a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs index cba19c28be3b7..18da14a00d439 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs @@ -2,6 +2,8 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { + // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also + // `options.llvm_abiname`. data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), llvm_target: "riscv32".into(), pointer_width: 32, @@ -11,6 +13,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), + // The ilp32e ABI specifies the `data_layout` llvm_abiname: "ilp32e".into(), max_atomic_width: Some(32), atomic_cas: false, From 04099b663c1ee252be058f27147d2e6cfec9ec5f Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Thu, 19 Sep 2024 13:24:21 +0300 Subject: [PATCH 1249/1301] Update target fns to latest main --- .../src/spec/targets/riscv32e_unknown_none_elf.rs | 8 +++++++- .../src/spec/targets/riscv32em_unknown_none_elf.rs | 8 +++++++- .../src/spec/targets/riscv32emc_unknown_none_elf.rs | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs index 9aeb591cb5790..b1f52973c107f 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs @@ -1,11 +1,17 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> Target { +pub(crate) fn target() -> Target { Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Bare RISC-V (RV32E ISA)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs index 07b1a0228a2ad..feeaa48778d43 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs @@ -1,11 +1,17 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> Target { +pub(crate) fn target() -> Target { Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Bare RISC-V (RV32EM ISA)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs index 18da14a00d439..45d73c1323371 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs @@ -1,11 +1,17 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> Target { +pub(crate) fn target() -> Target { Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. data_layout: "e-m:e-p:32:32-i64:64-n32-S32".into(), llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Bare RISC-V (RV32EMC ISA)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, pointer_width: 32, arch: "riscv32".into(), From f3037823de584ea94cf2b1c275d4c1dd8a769507 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Fri, 27 Sep 2024 22:01:44 +0300 Subject: [PATCH 1250/1301] doc: platform-support.md: Document port --- src/doc/rustc/src/platform-support.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 9d2efc0f6dee9..0ef95ba64a1f5 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -413,5 +413,8 @@ target | std | host | notes [`riscv32imafc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 32bit with NuttX [`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 64bit with NuttX [`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 64bit with NuttX +[`riscv32e-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32E ISA) +[`riscv32em-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32EM ISA) +[`riscv32emc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32EMC ISA) [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets From 66b3d0b36ad48400e83ae2692799b42a252ae023 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Thu, 19 Sep 2024 13:54:11 +0300 Subject: [PATCH 1251/1301] Work around the stage0 sanity check Add rv32e-targets to 'stage0 missing targets'. This prevents the error "no such target exists in the target list". --- src/bootstrap/src/core/sanity.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 888ba8e2a3f75..11260f87d00f0 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -37,6 +37,9 @@ pub struct Finder { const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined "armv7-rtems-eabihf", + "riscv32e-unknown-none-elf", + "riscv32em-unknown-none-elf", + "riscv32emc-unknown-none-elf", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM From fe658e1fe4d44e2eeeffc079acd7481b2d67b7d9 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Thu, 19 Sep 2024 16:43:33 +0300 Subject: [PATCH 1252/1301] Add assembly tests to satisfy 'tidy' --- tests/assembly/targets/targets-elf.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index e08e6a8b17458..f26d06a0ecb87 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -375,6 +375,15 @@ //@ revisions: riscv32_wrs_vxworks //@ [riscv32_wrs_vxworks] compile-flags: --target riscv32-wrs-vxworks //@ [riscv32_wrs_vxworks] needs-llvm-components: riscv +//@ revisions: riscv32e_unknown_none_elf +//@ [riscv32e_unknown_none_elf] compile-flags: --target riscv32e-unknown-none-elf +//@ [riscv32e_unknown_none_elf] needs-llvm-components: riscv +//@ revisions: riscv32em_unknown_none_elf +//@ [riscv32em_unknown_none_elf] compile-flags: --target riscv32em-unknown-none-elf +//@ [riscv32em_unknown_none_elf] needs-llvm-components: riscv +//@ revisions: riscv32emc_unknown_none_elf +//@ [riscv32emc_unknown_none_elf] compile-flags: --target riscv32emc-unknown-none-elf +//@ [riscv32emc_unknown_none_elf] needs-llvm-components: riscv //@ revisions: riscv32gc_unknown_linux_gnu //@ [riscv32gc_unknown_linux_gnu] compile-flags: --target riscv32gc-unknown-linux-gnu //@ [riscv32gc_unknown_linux_gnu] needs-llvm-components: riscv From 346afc7017fc9b4475fbbe8984f41d7467c6065a Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Fri, 4 Oct 2024 18:20:53 +0300 Subject: [PATCH 1253/1301] Add UI test to verify invalid loads are not generated --- .../ui/abi/riscv32e-registers.riscv32e.stderr | 194 ++++++++++++++++++ .../abi/riscv32e-registers.riscv32em.stderr | 194 ++++++++++++++++++ .../abi/riscv32e-registers.riscv32emc.stderr | 194 ++++++++++++++++++ tests/ui/abi/riscv32e-registers.rs | 91 ++++++++ 4 files changed, 673 insertions(+) create mode 100644 tests/ui/abi/riscv32e-registers.riscv32e.stderr create mode 100644 tests/ui/abi/riscv32e-registers.riscv32em.stderr create mode 100644 tests/ui/abi/riscv32e-registers.riscv32emc.stderr create mode 100644 tests/ui/abi/riscv32e-registers.rs diff --git a/tests/ui/abi/riscv32e-registers.riscv32e.stderr b/tests/ui/abi/riscv32e-registers.riscv32e.stderr new file mode 100644 index 0000000000000..e3894431eb44d --- /dev/null +++ b/tests/ui/abi/riscv32e-registers.riscv32e.stderr @@ -0,0 +1,194 @@ +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:43:11 + | +LL | asm!("li x16, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x16, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:46:11 + | +LL | asm!("li x17, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x17, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:49:11 + | +LL | asm!("li x18, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x18, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:52:11 + | +LL | asm!("li x19, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x19, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:55:11 + | +LL | asm!("li x20, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x20, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:58:11 + | +LL | asm!("li x21, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x21, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:61:11 + | +LL | asm!("li x22, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x22, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:64:11 + | +LL | asm!("li x23, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x23, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:67:11 + | +LL | asm!("li x24, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x24, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:70:11 + | +LL | asm!("li x25, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x25, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:73:11 + | +LL | asm!("li x26, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x26, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:76:11 + | +LL | asm!("li x27, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x27, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:79:11 + | +LL | asm!("li x28, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x28, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:82:11 + | +LL | asm!("li x29, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x29, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:85:11 + | +LL | asm!("li x30, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x30, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:88:11 + | +LL | asm!("li x31, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x31, 0 + | ^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/abi/riscv32e-registers.riscv32em.stderr b/tests/ui/abi/riscv32e-registers.riscv32em.stderr new file mode 100644 index 0000000000000..e3894431eb44d --- /dev/null +++ b/tests/ui/abi/riscv32e-registers.riscv32em.stderr @@ -0,0 +1,194 @@ +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:43:11 + | +LL | asm!("li x16, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x16, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:46:11 + | +LL | asm!("li x17, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x17, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:49:11 + | +LL | asm!("li x18, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x18, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:52:11 + | +LL | asm!("li x19, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x19, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:55:11 + | +LL | asm!("li x20, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x20, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:58:11 + | +LL | asm!("li x21, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x21, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:61:11 + | +LL | asm!("li x22, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x22, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:64:11 + | +LL | asm!("li x23, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x23, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:67:11 + | +LL | asm!("li x24, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x24, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:70:11 + | +LL | asm!("li x25, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x25, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:73:11 + | +LL | asm!("li x26, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x26, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:76:11 + | +LL | asm!("li x27, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x27, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:79:11 + | +LL | asm!("li x28, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x28, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:82:11 + | +LL | asm!("li x29, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x29, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:85:11 + | +LL | asm!("li x30, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x30, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:88:11 + | +LL | asm!("li x31, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x31, 0 + | ^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/abi/riscv32e-registers.riscv32emc.stderr b/tests/ui/abi/riscv32e-registers.riscv32emc.stderr new file mode 100644 index 0000000000000..e3894431eb44d --- /dev/null +++ b/tests/ui/abi/riscv32e-registers.riscv32emc.stderr @@ -0,0 +1,194 @@ +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:43:11 + | +LL | asm!("li x16, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x16, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:46:11 + | +LL | asm!("li x17, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x17, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:49:11 + | +LL | asm!("li x18, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x18, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:52:11 + | +LL | asm!("li x19, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x19, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:55:11 + | +LL | asm!("li x20, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x20, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:58:11 + | +LL | asm!("li x21, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x21, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:61:11 + | +LL | asm!("li x22, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x22, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:64:11 + | +LL | asm!("li x23, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x23, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:67:11 + | +LL | asm!("li x24, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x24, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:70:11 + | +LL | asm!("li x25, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x25, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:73:11 + | +LL | asm!("li x26, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x26, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:76:11 + | +LL | asm!("li x27, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x27, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:79:11 + | +LL | asm!("li x28, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x28, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:82:11 + | +LL | asm!("li x29, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x29, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:85:11 + | +LL | asm!("li x30, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x30, 0 + | ^ + +error: invalid operand for instruction + --> $DIR/riscv32e-registers.rs:88:11 + | +LL | asm!("li x31, 0"); + | ^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x31, 0 + | ^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/abi/riscv32e-registers.rs b/tests/ui/abi/riscv32e-registers.rs new file mode 100644 index 0000000000000..714b0ee4633c9 --- /dev/null +++ b/tests/ui/abi/riscv32e-registers.rs @@ -0,0 +1,91 @@ +// Test that loads into registers x16..=x31 are never generated for riscv32{e,em,emc} targets +// +//@ build-fail +//@ revisions: riscv32e riscv32em riscv32emc +// +//@ compile-flags: --crate-type=rlib +//@ [riscv32e] needs-llvm-components: riscv +//@ [riscv32e] compile-flags: --target=riscv32e-unknown-none-elf +//@ [riscv32em] needs-llvm-components: riscv +//@ [riscv32em] compile-flags: --target=riscv32em-unknown-none-elf +//@ [riscv32emc] needs-llvm-components: riscv +//@ [riscv32emc] compile-flags: --target=riscv32emc-unknown-none-elf + +#![no_core] +#![feature(no_core, lang_items, rustc_attrs)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +#[lang = "sized"] +trait Sized {} + +// Verify registers x1..=x15 are addressable on riscv32e, but registers x16..=x31 are not +#[no_mangle] +pub unsafe fn registers() { + asm!("li x1, 0"); + asm!("li x2, 0"); + asm!("li x3, 0"); + asm!("li x4, 0"); + asm!("li x5, 0"); + asm!("li x6, 0"); + asm!("li x7, 0"); + asm!("li x8, 0"); + asm!("li x9, 0"); + asm!("li x10, 0"); + asm!("li x11, 0"); + asm!("li x12, 0"); + asm!("li x13, 0"); + asm!("li x14, 0"); + asm!("li x15, 0"); + asm!("li x16, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x17, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x18, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x19, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x20, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x21, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x22, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x23, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x24, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x25, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x26, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x27, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x28, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x29, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x30, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here + asm!("li x31, 0"); + //~^ ERROR invalid operand for instruction + //~| NOTE instantiated into assembly here +} From 6edd0b356fac7585b27b14b4b9e6cd2b29537726 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Fri, 4 Oct 2024 20:58:39 +0300 Subject: [PATCH 1254/1301] Add platform support doc for rv32e --- .../riscv32-unknown-none-elf.md | 2 +- .../riscv32e-unknown-none-elf.md | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md index 9a27a568b5703..38742143c4ba2 100644 --- a/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md @@ -35,4 +35,4 @@ Rust test-suite on this target. ## Cross-compilation toolchains and C code This target supports C code. If interlinking with C or C++, you may need to use -`riscv64-unknown-elf-gcc` as a linker instead of `rust-lld`. +`riscv32-unknown-elf-gcc` as a linker instead of `rust-lld`. diff --git a/src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md new file mode 100644 index 0000000000000..69f08774f8381 --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md @@ -0,0 +1,30 @@ +# `riscv32{e,em,emc}-unknown-none-elf` + +**Tier: 3** + +Bare-metal target for RISC-V CPUs with the RV32E, RV32EM and RV32EMC ISAs. + +## Target maintainers + +* Henri Lunnikivi, , [@hegza](https://github.com/hegza) + +## Requirements + +The target is cross-compiled, and uses static linking. No external toolchain is +required and the default `rust-lld` linker works, but you must specify a linker +script. + +## Building the target + +This target is included in Rust and can be installed via `rustup`. + +## Testing + +This is a cross-compiled `no-std` target, which must be run either in a +simulator or by programming them onto suitable hardware. It is not possible to +run the Rust test-suite on this target. + +## Cross-compilation toolchains and C code + +This target supports C code. If interlinking with C or C++, you may need to use +`riscv32-unknown-elf-gcc` as a linker instead of `rust-lld`. From f0ddc7b472a6ea84f2224364e2b71c688b7b285e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Oct 2024 12:19:14 +0200 Subject: [PATCH 1255/1301] clarify semantics of ConstantIndex MIR projection --- compiler/rustc_middle/src/mir/syntax.rs | 6 ++++-- compiler/stable_mir/src/mir/body.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index ae75f2d4187ba..1722a7a1f358f 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1135,8 +1135,10 @@ pub enum ProjectionElem { ConstantIndex { /// index or -index (in Python terms), depending on from_end offset: u64, - /// The thing being indexed must be at least this long. For arrays this - /// is always the exact length. + /// The thing being indexed must be at least this long -- otherwise, the + /// projection is UB. + /// + /// For arrays this is always the exact length. min_length: u64, /// Counting backwards from end? This is always false when indexing an /// array. diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 9f4db7e483335..742469a1c9325 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -768,8 +768,10 @@ pub enum ProjectionElem { ConstantIndex { /// index or -index (in Python terms), depending on from_end offset: u64, - /// The thing being indexed must be at least this long. For arrays this - /// is always the exact length. + /// The thing being indexed must be at least this long -- otherwise, the + /// projection is UB. + /// + /// For arrays this is always the exact length. min_length: u64, /// Counting backwards from end? This is always false when indexing an /// array. From ab8673501ce13573c06b5989b179f5cfed85c771 Mon Sep 17 00:00:00 2001 From: Veera Date: Sun, 22 Sep 2024 19:32:56 -0400 Subject: [PATCH 1256/1301] Add a Lint for Pointer to Integer Transmutes in Consts --- compiler/rustc_lint_defs/src/builtin.rs | 35 +++++++++ compiler/rustc_mir_transform/messages.ftl | 5 ++ .../src/check_undefined_transmutes.rs | 77 +++++++++++++++++++ compiler/rustc_mir_transform/src/errors.rs | 7 ++ compiler/rustc_mir_transform/src/lib.rs | 2 + library/core/src/ptr/mod.rs | 1 + .../transmutes_expressible_as_ptr_casts.fixed | 9 ++- .../ui/transmutes_expressible_as_ptr_casts.rs | 9 ++- ...-to-int-transmute-in-consts-issue-87525.rs | 22 +++--- ...int-transmute-in-consts-issue-87525.stderr | 43 ++++++++++- 10 files changed, 192 insertions(+), 18 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/check_undefined_transmutes.rs diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 25d33126754af..a618bd773011f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -82,6 +82,7 @@ declare_lint_pass! { PRIVATE_INTERFACES, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PTR_CAST_ADD_AUTO_TO_OBJECT, + PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, REDUNDANT_LIFETIMES, @@ -5095,3 +5096,37 @@ declare_lint! { reference: "issue #124535 ", }; } + +declare_lint! { + /// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer + /// transmute in const functions and associated constants. + /// + /// ### Example + /// + /// ```rust + /// const fn foo(ptr: *const u8) -> usize { + /// unsafe { + /// std::mem::transmute::<*const u8, usize>(ptr) + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Transmuting pointers to integers in a `const` context is undefined behavior. + /// Any attempt to use the resulting integer will abort const-evaluation. + /// + /// But sometimes the compiler might not emit an error for pointer to integer transmutes + /// inside const functions and associated consts because they are evaluated only when referenced. + /// Therefore, this lint serves as an extra layer of defense to prevent any undefined behavior + /// from compiling without any warnings or errors. + /// + /// See [std::mem::transmute] in the reference for more details. + /// + /// [std::mem::transmute]: https://doc.rust-lang.org/std/mem/fn.transmute.html + pub PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, + Warn, + "detects pointer to integer transmutes in const functions and associated constants", +} diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index f9b79d72b0504..b81c090673476 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -27,3 +27,8 @@ mir_transform_unaligned_packed_ref = reference to packed field is unaligned .note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses .note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) .help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +mir_transform_undefined_transmute = pointers cannot be transmuted to integers during const eval + .note = at compile-time, pointers do not have an integer value + .note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior + .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html diff --git a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs new file mode 100644 index 0000000000000..8ba14a1158ef9 --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs @@ -0,0 +1,77 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; +use rustc_middle::ty::{AssocItem, AssocKind, TyCtxt}; +use rustc_session::lint::builtin::PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS; +use rustc_span::sym; + +use crate::errors; + +/// Check for transmutes that exhibit undefined behavior. +/// For example, transmuting pointers to integers in a const context. +pub(super) struct CheckUndefinedTransmutes; + +impl<'tcx> crate::MirLint<'tcx> for CheckUndefinedTransmutes { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let mut checker = UndefinedTransmutesChecker { body, tcx }; + checker.visit_body(body); + } +} + +struct UndefinedTransmutesChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> UndefinedTransmutesChecker<'a, 'tcx> { + // This functions checks two things: + // 1. `function` takes a raw pointer as input and returns an integer as output. + // 2. `function` is called from a const function or an associated constant. + // + // Why do we consider const functions and associated constants only? + // + // Generally, undefined behavior in const items are handled by the evaluator. + // But, const functions and associated constants are evaluated only when referenced. + // This can result in undefined behavior in a library going unnoticed until + // the function or constant is actually used. + // + // Therefore, we only consider const functions and associated constants here and leave + // other const items to be handled by the evaluator. + fn is_ptr_to_int_in_const(&self, function: &Operand<'tcx>) -> bool { + let def_id = self.body.source.def_id(); + + if self.tcx.is_const_fn(def_id) + || matches!( + self.tcx.opt_associated_item(def_id), + Some(AssocItem { kind: AssocKind::Const, .. }) + ) + { + let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); + if let [input] = fn_sig.inputs() { + return input.is_unsafe_ptr() && fn_sig.output().is_integral(); + } + } + false + } +} + +impl<'tcx> Visitor<'tcx> for UndefinedTransmutesChecker<'_, 'tcx> { + // Check each block's terminator for calls to pointer to integer transmutes + // in const functions or associated constants and emit a lint. + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + if let TerminatorKind::Call { func, .. } = &terminator.kind + && let Some((func_def_id, _)) = func.const_fn_def() + && self.tcx.is_intrinsic(func_def_id, sym::transmute) + && self.is_ptr_to_int_in_const(func) + && let Some(call_id) = self.body.source.def_id().as_local() + { + let hir_id = self.tcx.local_def_id_to_hir_id(call_id); + let span = self.body.source_info(location).span; + self.tcx.emit_node_span_lint( + PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, + hir_id, + span, + errors::UndefinedTransmute, + ); + } + } +} diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 9a93c3a72b1c9..e480537402da1 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -121,3 +121,10 @@ pub(crate) struct MustNotSuspendReason { pub span: Span, pub reason: String, } + +#[derive(LintDiagnostic)] +#[diag(mir_transform_undefined_transmute)] +#[note] +#[note(mir_transform_note2)] +#[help] +pub(crate) struct UndefinedTransmute; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 84d07d3833071..626b4584fb27f 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -51,6 +51,7 @@ mod add_subtyping_projections; mod check_alignment; mod check_const_item_mutation; mod check_packed_ref; +mod check_undefined_transmutes; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck; mod copy_prop; @@ -293,6 +294,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &Lint(check_packed_ref::CheckPackedRef), &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), + &Lint(check_undefined_transmutes::CheckUndefinedTransmutes), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, &Lint(sanity_check::SanityCheck), diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 08d06cad55d06..8d6560111dad0 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1916,6 +1916,7 @@ pub unsafe fn write_volatile(dst: *mut T, src: T) { /// than trying to adapt this to accommodate that change. /// /// Any questions go to @nagisa. +#[cfg_attr(not(bootstrap), allow(ptr_to_integer_transmute_in_consts))] #[lang = "align_offset"] pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usize { // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed index e95054a7ccb0e..617d32d1fa793 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -84,8 +84,11 @@ fn issue_10449() { } // Pointers cannot be cast to integers in const contexts +#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")] const fn issue_12402

(ptr: *const P) { - unsafe { transmute::<*const i32, usize>(&42i32) }; - unsafe { transmute::(issue_12402) }; - let _ = unsafe { transmute::<_, usize>(ptr) }; + // This test exists even though the compiler lints against it + // to test that clippy's transmute lints do not trigger on this. + unsafe { std::mem::transmute::<*const i32, usize>(&42i32) }; + unsafe { std::mem::transmute::(issue_12402) }; + let _ = unsafe { std::mem::transmute::<_, usize>(ptr) }; } diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs index e5fcdef7a1c39..d68db3c2deb9f 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -84,8 +84,11 @@ fn issue_10449() { } // Pointers cannot be cast to integers in const contexts +#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")] const fn issue_12402

(ptr: *const P) { - unsafe { transmute::<*const i32, usize>(&42i32) }; - unsafe { transmute::(issue_12402) }; - let _ = unsafe { transmute::<_, usize>(ptr) }; + // This test exists even though the compiler lints against it + // to test that clippy's transmute lints do not trigger on this. + unsafe { std::mem::transmute::<*const i32, usize>(&42i32) }; + unsafe { std::mem::transmute::(issue_12402) }; + let _ = unsafe { std::mem::transmute::<_, usize>(ptr) }; } diff --git a/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.rs b/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.rs index f8e97904e9e08..19c78f019aab7 100644 --- a/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.rs +++ b/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.rs @@ -1,21 +1,21 @@ const fn foo(ptr: *const u8) -> usize { unsafe { std::mem::transmute(ptr) - //~^ ERROR pointers cannot be transmuted to integers + //~^ WARN pointers cannot be transmuted to integers } } trait Human { - const ID: u64 = { + const ID: usize = { let value = 10; - let ptr: *const i32 = &value; + let ptr: *const usize = &value; unsafe { std::mem::transmute(ptr) - //~^ ERROR pointers cannot be transmuted to integers + //~^ WARN pointers cannot be transmuted to integers } }; - fn id_plus_one() -> u64 { + fn id_plus_one() -> usize { Self::ID + 1 } } @@ -23,16 +23,16 @@ trait Human { struct Type(T); impl Type { - const ID: u64 = { + const ID: usize = { let value = 10; - let ptr: *const i32 = &value; + let ptr: *const usize = &value; unsafe { std::mem::transmute(ptr) - //~^ ERROR pointers cannot be transmuted to integers + //~^ WARN pointers cannot be transmuted to integers } }; - fn id_plus_one() -> u64 { + fn id_plus_one() -> usize { Self::ID + 1 } } @@ -59,10 +59,10 @@ impl ControlStruct { const fn zoom(ptr: *const u8) -> usize { unsafe { std::mem::transmute(ptr) - //~^ ERROR pointers cannot be transmuted to integers + //~^ WARN pointers cannot be transmuted to integers } } - + fn main() { const a: u8 = 10; const value: usize = zoom(&a); diff --git a/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.stderr b/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.stderr index a57bc8e1e70ec..ca6ad9408ab91 100644 --- a/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.stderr +++ b/tests/ui/consts/const-eval/ptr-to-int-transmute-in-consts-issue-87525.stderr @@ -1,3 +1,14 @@ +warning: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:61:9 + | +LL | std::mem::transmute(ptr) + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: at compile-time, pointers do not have an integer value + = note: avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior + = help: for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html + = note: `#[warn(ptr_to_integer_transmute_in_consts)]` on by default + error[E0080]: evaluation of constant value failed --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:68:26 | @@ -7,6 +18,36 @@ LL | const value: usize = zoom(&a); = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error: aborting due to 1 previous error +warning: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:3:9 + | +LL | std::mem::transmute(ptr) + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: at compile-time, pointers do not have an integer value + = note: avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior + = help: for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html + +warning: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:13:13 + | +LL | std::mem::transmute(ptr) + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: at compile-time, pointers do not have an integer value + = note: avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior + = help: for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html + +warning: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:30:13 + | +LL | std::mem::transmute(ptr) + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: at compile-time, pointers do not have an integer value + = note: avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior + = help: for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html + +error: aborting due to 1 previous error; 4 warnings emitted For more information about this error, try `rustc --explain E0080`. From 70260dbeca3303e4d62d2ff4c136501ddac7491d Mon Sep 17 00:00:00 2001 From: DianQK Date: Sat, 5 Oct 2024 21:10:04 +0800 Subject: [PATCH 1257/1301] Update to LLVM 19.1.1 --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 56997739365e8..dd46457da7825 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 56997739365e8132cc817e00899480746c09d7d9 +Subproject commit dd46457da782554454106d48ecd4f6b4c2f9af73 From 8faf3722ac963a637265c3fb087fce08f9ceb163 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Oct 2024 17:12:46 +0200 Subject: [PATCH 1258/1301] fix typo in 'lang item with track_caller' message --- compiler/rustc_passes/messages.ftl | 2 +- tests/ui/panic-handler/panic-handler-with-track-caller.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 5369f54afb908..f2682acf8aa0b 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -391,7 +391,7 @@ passes_lang_item_fn_with_target_feature = passes_lang_item_fn_with_track_caller = {passes_lang_item_fn} is not allowed to have `#[track_caller]` - .label = {passes_lang_item_fn} is not allowed to have `#[target_feature]` + .label = {passes_lang_item_fn} is not allowed to have `#[track_caller]` passes_lang_item_on_incorrect_target = `{$name}` lang item must be applied to a {$expected_target} diff --git a/tests/ui/panic-handler/panic-handler-with-track-caller.stderr b/tests/ui/panic-handler/panic-handler-with-track-caller.stderr index 9ed387fc8d1f4..605567acdb58b 100644 --- a/tests/ui/panic-handler/panic-handler-with-track-caller.stderr +++ b/tests/ui/panic-handler/panic-handler-with-track-caller.stderr @@ -5,7 +5,7 @@ LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | LL | fn panic(info: &PanicInfo) -> ! { - | ------------------------------- `#[panic_handler]` function is not allowed to have `#[target_feature]` + | ------------------------------- `#[panic_handler]` function is not allowed to have `#[track_caller]` error: aborting due to 1 previous error From 7d63efdb8c2d3b9ea25fa2bbb8703168d21483c8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Oct 2024 17:05:47 +0200 Subject: [PATCH 1259/1301] fix GVN trying to transmute pointers to integers --- compiler/rustc_mir_transform/src/gvn.rs | 30 +++++++++++++++++----- tests/coverage/closure_macro.cov-map | 8 +++--- tests/coverage/closure_macro_async.cov-map | 8 +++--- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 50c9702cb9b02..7707d0d51f1bc 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -103,7 +103,7 @@ use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; -use rustc_target::abi::{self, Abi, FIRST_VARIANT, FieldIdx, Size, VariantIdx}; +use rustc_target::abi::{self, Abi, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use smallvec::SmallVec; use tracing::{debug, instrument, trace}; @@ -568,13 +568,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { CastKind::Transmute => { let value = self.evaluated[value].as_ref()?; let to = self.ecx.layout_of(to).ok()?; - // `offset` for immediates only supports scalar/scalar-pair ABIs, - // so bail out if the target is not one. + // `offset` for immediates generally only supports projections that match the + // type of the immediate. However, as a HACK, we exploit that it can also do + // limited transmutes: it only works between types with the same layout, and + // cannot transmute pointers to integers. if value.as_mplace_or_imm().is_right() { - match (value.layout.abi, to.abi) { - (Abi::Scalar(..), Abi::Scalar(..)) => {} - (Abi::ScalarPair(..), Abi::ScalarPair(..)) => {} - _ => return None, + let can_transmute = match (value.layout.abi, to.abi) { + (Abi::Scalar(s1), Abi::Scalar(s2)) => { + s1.size(&self.ecx) == s2.size(&self.ecx) + && !matches!(s1.primitive(), Primitive::Pointer(..)) + } + (Abi::ScalarPair(a1, b1), Abi::ScalarPair(a2, b2)) => { + a1.size(&self.ecx) == a2.size(&self.ecx) && + b1.size(&self.ecx) == b2.size(&self.ecx) && + // The alignment of the second component determines its offset, so that also needs to match. + b1.align(&self.ecx) == b2.align(&self.ecx) && + // None of the inputs may be a pointer. + !matches!(a1.primitive(), Primitive::Pointer(..)) + && !matches!(b1.primitive(), Primitive::Pointer(..)) + } + _ => false, + }; + if !can_transmute { + return None; } } value.offset(Size::ZERO, to, &self.ecx).discard_err()? diff --git a/tests/coverage/closure_macro.cov-map b/tests/coverage/closure_macro.cov-map index eb5f94d108068..fb4a137d4c8ec 100644 --- a/tests/coverage/closure_macro.cov-map +++ b/tests/coverage/closure_macro.cov-map @@ -23,19 +23,19 @@ Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro::main::{closure#0} -Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] +Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add) -- expression 2 operands: lhs = Counter(2), rhs = Zero +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 16, 28) to (start + 3, 33) - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39) - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22) = (c0 - c1) -- Code(Zero) at (prev + 0, 23) to (start + 0, 30) +- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30) - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c2 + Zero)) + = (c1 + (c2 + c3)) diff --git a/tests/coverage/closure_macro_async.cov-map b/tests/coverage/closure_macro_async.cov-map index 4d0597f58bf1d..091e87909f900 100644 --- a/tests/coverage/closure_macro_async.cov-map +++ b/tests/coverage/closure_macro_async.cov-map @@ -31,19 +31,19 @@ Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro_async::test::{closure#0}::{closure#0} -Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 15, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] +Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 15, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add) -- expression 2 operands: lhs = Counter(2), rhs = Zero +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 21, 28) to (start + 3, 33) - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39) - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22) = (c0 - c1) -- Code(Zero) at (prev + 0, 23) to (start + 0, 30) +- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30) - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c2 + Zero)) + = (c1 + (c2 + c3)) From 2223328d166a16cde864a17a5cb96de0853c9981 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Sat, 5 Oct 2024 21:29:40 +0300 Subject: [PATCH 1260/1301] Android: Debug assertion after setting thread name --- library/std/src/sys/pal/unix/thread.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 2f2d6e6add396..040246618360f 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -117,13 +117,15 @@ impl Thread { pub fn set_name(name: &CStr) { const PR_SET_NAME: libc::c_int = 15; unsafe { - libc::prctl( + let res = libc::prctl( PR_SET_NAME, name.as_ptr(), 0 as libc::c_ulong, 0 as libc::c_ulong, 0 as libc::c_ulong, ); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); } } From 4f50efd1329f430998231f018c2cf61bfe0db449 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sat, 5 Oct 2024 22:48:55 +0300 Subject: [PATCH 1261/1301] update "build/host" symlink comment Signed-off-by: onur-ozkan --- src/bootstrap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 75659f46431af..bcd6b07f2a25e 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -470,7 +470,7 @@ impl Build { crate::core::metadata::build(&mut build); } - // Make a symbolic link so we can use a consistent directory in the documentation. + // Create symbolic link to use host sysroot from a consistent path (e.g., in the rust-analyzer config file). let build_triple = build.out.join(build.build); t!(fs::create_dir_all(&build_triple)); let host = build.out.join("host"); From 9d2495db60312472506939fdde49bb852b75b414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Sun, 29 Sep 2024 22:28:08 +0200 Subject: [PATCH 1262/1301] enable f16 and f128 on windows-gnullvm targets --- library/std/build.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std/build.rs b/library/std/build.rs index 7d37d4e9d7d83..032326556bd5b 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -7,6 +7,7 @@ fn main() { let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set"); let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set"); + let target_abi = env::var("CARGO_CFG_TARGET_ABI").expect("CARGO_CFG_TARGET_ABI was not set"); let target_pointer_width: u32 = env::var("CARGO_CFG_TARGET_POINTER_WIDTH") .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set") .parse() @@ -101,7 +102,7 @@ fn main() { // Unsupported ("arm64ec", _) => false, // MinGW ABI bugs - ("x86_64", "windows") if target_env == "gnu" => false, + ("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false, // Infinite recursion ("csky", _) => false, ("hexagon", _) => false, @@ -129,7 +130,7 @@ fn main() { // ABI unsupported ("sparc", _) => false, // MinGW ABI bugs - ("x86_64", "windows") if target_env == "gnu" => false, + ("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false, // 64-bit Linux is about the only platform to have f128 symbols by default (_, "linux") if target_pointer_width == 64 => true, // Almost all OSs are missing symbol. compiler-builtins will have to add them. From 6371ef6e964a72ca3b4c7557312d8808e98f4ff3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:02:26 -0400 Subject: [PATCH 1263/1301] Evaluating place expr that is never read from does not diverge --- compiler/rustc_hir_typeck/src/expr.rs | 67 ++++++++++++++++++- compiler/rustc_hir_typeck/src/pat.rs | 7 ++ ...ocess_never.SimplifyLocals-final.after.mir | 5 +- tests/mir-opt/uninhabited_enum.rs | 2 - tests/ui/never_type/diverging-place-match.rs | 13 ++++ .../never_type/diverging-place-match.stderr | 20 ++++++ .../raw-ref-op/never-place-isnt-diverging.rs | 13 ++++ .../never-place-isnt-diverging.stderr | 20 ++++++ tests/ui/reachable/expr_assign.stderr | 9 +-- .../reachable/unwarned-match-on-never.stderr | 2 +- 10 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 tests/ui/never_type/diverging-place-match.rs create mode 100644 tests/ui/never_type/diverging-place-match.stderr create mode 100644 tests/ui/raw-ref-op/never-place-isnt-diverging.rs create mode 100644 tests/ui/raw-ref-op/never-place-isnt-diverging.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e3c7dded0ca6e..870a260c9fe09 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -238,8 +238,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), } - // Any expression that produces a value of type `!` must have diverged - if ty.is_never() { + // Any expression that produces a value of type `!` must have diverged, + // unless it's a place expression that isn't being read from, in which case + // diverging would be unsound since we may never actually read the `!`. + // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. + if ty.is_never() && self.expr_constitutes_read(expr) { self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); } @@ -257,6 +260,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + pub(super) fn expr_constitutes_read(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { + // We only care about place exprs. Anything else returns an immediate + // which would constitute a read. We don't care about distinguishing + // "syntactic" place exprs since if the base of a field projection is + // not a place then it would've been UB to read from it anyways since + // that constitutes a read. + if !expr.is_syntactic_place_expr() { + return true; + } + + // If this expression has any adjustments applied after the place expression, + // they may constitute reads. + if !self.typeck_results.borrow().expr_adjustments(expr).is_empty() { + return true; + } + + fn pat_does_read(pat: &hir::Pat<'_>) -> bool { + let mut does_read = false; + pat.walk(|pat| { + if matches!( + pat.kind, + hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) + ) { + true + } else { + does_read = true; + // No need to continue. + false + } + }); + does_read + } + + match self.tcx.parent_hir_node(expr.hir_id) { + // Addr-of, field projections, and LHS of assignment don't constitute reads. + // Assignment does call `drop_in_place`, though, but its safety + // requirements are not the same. + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. }) => false, + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Assign(target, _, _) | hir::ExprKind::Field(target, _), + .. + }) if expr.hir_id == target.hir_id => false, + + // If we have a subpattern that performs a read, we want to consider this + // to diverge for compatibility to support something like `let x: () = *never_ptr;`. + hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Let(hir::LetExpr { init: target, pat, .. }), + .. + }) if expr.hir_id == target.hir_id && !pat_does_read(*pat) => false, + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Match(target, arms, _), .. }) + if expr.hir_id == target.hir_id + && !arms.iter().any(|arm| pat_does_read(arm.pat)) => + { + false + } + _ => true, + } + } + #[instrument(skip(self, expr), level = "debug")] fn check_expr_kind( &self, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 49c5a7d8a652d..824918d1fa222 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -27,6 +27,7 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; +use crate::diverges::Diverges; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, LoweredTy, errors}; @@ -276,6 +277,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + // All other patterns constitute a read, which causes us to diverge + // if the type is never. + if ty.is_never() && !matches!(pat.kind, PatKind::Wild | PatKind::Never | PatKind::Or(_)) { + self.diverges.set(self.diverges.get() | Diverges::always(pat.span)); + } + self.write_ty(pat.hir_id, ty); // (note_1): In most of the cases where (note_1) is referenced diff --git a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir index 240f409817d77..64fc81e298946 100644 --- a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir @@ -3,12 +3,11 @@ fn process_never(_1: *const !) -> () { debug input => _1; let mut _0: (); - let _2: &!; scope 1 { - debug _input => _2; + debug _input => _1; } bb0: { - unreachable; + return; } } diff --git a/tests/mir-opt/uninhabited_enum.rs b/tests/mir-opt/uninhabited_enum.rs index 859535852cf0d..9d845e34fbda8 100644 --- a/tests/mir-opt/uninhabited_enum.rs +++ b/tests/mir-opt/uninhabited_enum.rs @@ -13,8 +13,6 @@ pub fn process_never(input: *const !) { #[no_mangle] pub fn process_void(input: *const Void) { let _input = unsafe { &*input }; - // In the future, this should end with `unreachable`, but we currently only do - // unreachability analysis for `!`. } fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs new file mode 100644 index 0000000000000..7bc00773c0ba5 --- /dev/null +++ b/tests/ui/never_type/diverging-place-match.rs @@ -0,0 +1,13 @@ +#![feature(never_type)] + +fn make_up_a_value() -> T { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let _: ! = *x; + // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this + // is unsound since we act as if it diverges but it doesn't. + } +} + +fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.stderr b/tests/ui/never_type/diverging-place-match.stderr new file mode 100644 index 0000000000000..e86c634d59186 --- /dev/null +++ b/tests/ui/never_type/diverging-place-match.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:4:5 + | +LL | fn make_up_a_value() -> T { + | - expected this type parameter +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let _: ! = *x; +LL | | // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this +LL | | // is unsound since we act as if it diverges but it doesn't. +LL | | } + | |_____^ expected type parameter `T`, found `()` + | + = note: expected type parameter `T` + found unit type `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs new file mode 100644 index 0000000000000..52f4158dbc988 --- /dev/null +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs @@ -0,0 +1,13 @@ +#![feature(never_type)] + +fn make_up_a_value() -> T { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + &raw const *x; + // Since `*x` is `!`, HIR typeck used to think that it diverges + // and allowed the block to coerce to any value, leading to UB. + } +} + +fn main() {} diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr new file mode 100644 index 0000000000000..9eba57dde8fa7 --- /dev/null +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/never-place-isnt-diverging.rs:4:5 + | +LL | fn make_up_a_value() -> T { + | - expected this type parameter +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | &raw const *x; +LL | | // Since `*x` is `!`, HIR typeck used to think that it diverges +LL | | // and allowed the block to coerce to any value, leading to UB. +LL | | } + | |_____^ expected type parameter `T`, found `()` + | + = note: expected type parameter `T` + found unit type `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/reachable/expr_assign.stderr b/tests/ui/reachable/expr_assign.stderr index c51156b3f40cf..cfbbe04db769d 100644 --- a/tests/ui/reachable/expr_assign.stderr +++ b/tests/ui/reachable/expr_assign.stderr @@ -14,12 +14,13 @@ LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ error: unreachable expression - --> $DIR/expr_assign.rs:20:14 + --> $DIR/expr_assign.rs:20:9 | LL | *p = return; - | -- ^^^^^^ unreachable expression - | | - | any code following this expression is unreachable + | ^^^^^------ + | | | + | | any code following this expression is unreachable + | unreachable expression error: unreachable expression --> $DIR/expr_assign.rs:26:15 diff --git a/tests/ui/reachable/unwarned-match-on-never.stderr b/tests/ui/reachable/unwarned-match-on-never.stderr index a296d2a055e09..c1ad3511b4e08 100644 --- a/tests/ui/reachable/unwarned-match-on-never.stderr +++ b/tests/ui/reachable/unwarned-match-on-never.stderr @@ -2,7 +2,7 @@ error: unreachable expression --> $DIR/unwarned-match-on-never.rs:10:5 | LL | match x {} - | - any code following this expression is unreachable + | ---------- any code following this expression is unreachable LL | // But matches in unreachable code are warned. LL | match x {} | ^^^^^^^^^^ unreachable expression From 5193c211ea594ad9918372868bdb5d4dfafaea87 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:21:52 -0400 Subject: [PATCH 1264/1301] Do not coerce places if they do not constitute reads --- compiler/rustc_hir_typeck/src/coercion.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index fb462eec1b9aa..6a1018883c98a 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -82,6 +82,7 @@ struct Coerce<'a, 'tcx> { /// See #47489 and #48598 /// See docs on the "AllowTwoPhase" type for a more detailed discussion allow_two_phase: AllowTwoPhase, + coerce_never: bool, } impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { @@ -125,8 +126,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fcx: &'f FnCtxt<'f, 'tcx>, cause: ObligationCause<'tcx>, allow_two_phase: AllowTwoPhase, + coerce_never: bool, ) -> Self { - Coerce { fcx, cause, allow_two_phase, use_lub: false } + Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never } } fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { @@ -177,7 +179,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Coercing from `!` to any type is allowed: if a.is_never() { - return success(simple(Adjust::NeverToAny)(b), b, vec![]); + if self.coerce_never { + return success(simple(Adjust::NeverToAny)(b), b, vec![]); + } else { + return self.unify_and(a, b, identity); + } } // Coercing *from* an unresolved inference variable means that @@ -1038,7 +1044,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// The expressions *must not* have any preexisting adjustments. pub(crate) fn coerce( &self, - expr: &hir::Expr<'_>, + expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, mut target: Ty<'tcx>, allow_two_phase: AllowTwoPhase, @@ -1055,7 +1061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable)); - let coerce = Coerce::new(self, cause, allow_two_phase); + let coerce = Coerce::new(self, cause, allow_two_phase, self.expr_constitutes_read(expr)); let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?; let (adjustments, _) = self.register_infer_ok_obligations(ok); @@ -1078,7 +1084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); // We don't ever need two-phase here since we throw out the result of the coercion - let coerce = Coerce::new(self, cause, AllowTwoPhase::No); + let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); self.probe(|_| { let Ok(ok) = coerce.coerce(source, target) else { return false; @@ -1095,7 +1101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option { let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); // We don't ever need two-phase here since we throw out the result of the coercion - let coerce = Coerce::new(self, cause, AllowTwoPhase::No); + let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); coerce .autoderef(DUMMY_SP, expr_ty) .find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps)) @@ -1252,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // probably aren't processing function arguments here and even if we were, // they're going to get autorefed again anyway and we can apply 2-phase borrows // at that time. - let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No); + let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true); coerce.use_lub = true; // First try to coerce the new expression to the type of the previous ones, From 73d49f8c697da34ee1372b51540bfb0f1ae0c712 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:49:38 -0400 Subject: [PATCH 1265/1301] Fix up tests --- compiler/rustc_hir_typeck/src/coercion.rs | 17 ++++- .../src/fn_ctxt/suggestions.rs | 6 +- .../miri/tests/pass/underscore_pattern.rs | 51 ++++++++++++- ...t_read.main.SimplifyLocals-final.after.mir | 49 ++++++++++++ tests/mir-opt/uninhabited_not_read.rs | 27 +++++++ tests/ui/never_type/diverging-place-match.rs | 36 ++++++++- .../never_type/diverging-place-match.stderr | 75 +++++++++++++++++-- 7 files changed, 245 insertions(+), 16 deletions(-) create mode 100644 tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir create mode 100644 tests/mir-opt/uninhabited_not_read.rs diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 6a1018883c98a..1705421c2f40e 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -82,6 +82,10 @@ struct Coerce<'a, 'tcx> { /// See #47489 and #48598 /// See docs on the "AllowTwoPhase" type for a more detailed discussion allow_two_phase: AllowTwoPhase, + /// Whether we allow `NeverToAny` coercions. This is unsound if we're + /// coercing a place expression without it counting as a read in the MIR. + /// This is a side-effect of HIR not really having a great distinction + /// between places and values. coerce_never: bool, } @@ -1083,7 +1087,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("coercion::can_with_predicates({:?} -> {:?})", source, target); let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); - // We don't ever need two-phase here since we throw out the result of the coercion + // We don't ever need two-phase here since we throw out the result of the coercion. + // We also just always set `coerce_never` to true, since this is a heuristic. let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); self.probe(|_| { let Ok(ok) = coerce.coerce(source, target) else { @@ -1096,11 +1101,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Given a type and a target type, this function will calculate and return - /// how many dereference steps needed to achieve `expr_ty <: target`. If + /// how many dereference steps needed to coerce `expr_ty` to `target`. If /// it's not possible, return `None`. - pub(crate) fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option { + pub(crate) fn deref_steps_for_suggestion( + &self, + expr_ty: Ty<'tcx>, + target: Ty<'tcx>, + ) -> Option { let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); - // We don't ever need two-phase here since we throw out the result of the coercion + // We don't ever need two-phase here since we throw out the result of the coercion. let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); coerce .autoderef(DUMMY_SP, expr_ty) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 435b7d0f39a37..1df4d32f3cbac 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2608,7 +2608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind - && let Some(1) = self.deref_steps(expected, checked_ty) + && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty) { // We have `*&T`, check if what was expected was `&T`. // If so, we may want to suggest removing a `*`. @@ -2738,7 +2738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => { - if let Some(steps) = self.deref_steps(ty_a, ty_b) + if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b) // Only suggest valid if dereferencing needed. && steps > 0 // The pointer type implements `Copy` trait so the suggestion is always valid. @@ -2782,7 +2782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } _ if sp == expr.span => { - if let Some(mut steps) = self.deref_steps(checked_ty, expected) { + if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) { let mut expr = expr.peel_blocks(); let mut prefix_span = expr.span.shrink_to_lo(); let mut remove = String::new(); diff --git a/src/tools/miri/tests/pass/underscore_pattern.rs b/src/tools/miri/tests/pass/underscore_pattern.rs index f0afe5589546e..f59bb9f5c82d5 100644 --- a/src/tools/miri/tests/pass/underscore_pattern.rs +++ b/src/tools/miri/tests/pass/underscore_pattern.rs @@ -1,5 +1,7 @@ // Various tests ensuring that underscore patterns really just construct the place, but don't check its contents. #![feature(strict_provenance)] +#![feature(never_type)] + use std::ptr; fn main() { @@ -9,6 +11,7 @@ fn main() { invalid_let(); dangling_let_type_annotation(); invalid_let_type_annotation(); + never(); } fn dangling_match() { @@ -34,6 +37,13 @@ fn invalid_match() { _ => {} } } + + unsafe { + let x: Uninit = Uninit { uninit: () }; + match x.value { + _ => {} + } + } } fn dangling_let() { @@ -41,6 +51,11 @@ fn dangling_let() { let ptr = ptr::without_provenance::(0x40); let _ = *ptr; } + + unsafe { + let ptr = ptr::without_provenance::(0x40); + let _ = *ptr; + } } fn invalid_let() { @@ -49,6 +64,12 @@ fn invalid_let() { let ptr = ptr::addr_of!(val).cast::(); let _ = *ptr; } + + unsafe { + let val = 3u8; + let ptr = ptr::addr_of!(val).cast::(); + let _ = *ptr; + } } // Adding a type annotation used to change how MIR is generated, make sure we cover both cases. @@ -57,6 +78,11 @@ fn dangling_let_type_annotation() { let ptr = ptr::without_provenance::(0x40); let _: bool = *ptr; } + + unsafe { + let ptr = ptr::without_provenance::(0x40); + let _: ! = *ptr; + } } fn invalid_let_type_annotation() { @@ -65,7 +91,28 @@ fn invalid_let_type_annotation() { let ptr = ptr::addr_of!(val).cast::(); let _: bool = *ptr; } + + unsafe { + let val = 3u8; + let ptr = ptr::addr_of!(val).cast::(); + let _: ! = *ptr; + } } -// FIXME: we should also test `!`, not just `bool` -- but that s currently buggy: -// https://github.com/rust-lang/rust/issues/117288 +// Regression test from . +fn never() { + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _: ! = *x; + } + + // Without a type annotation, make sure we don't implicitly coerce `!` to `()` + // when we do the noop `*x` (as that would require a `!` *value*, creating + // which is UB). + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _ = *x; + } +} diff --git a/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir new file mode 100644 index 0000000000000..6bf4be652bef3 --- /dev/null +++ b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir @@ -0,0 +1,49 @@ +// MIR for `main` after SimplifyLocals-final + +fn main() -> () { + let mut _0: (); + let _1: u8; + let mut _2: *const !; + let mut _3: *const u8; + let _4: u8; + let mut _5: *const !; + let mut _6: *const u8; + scope 1 { + debug x => _1; + scope 2 { + debug x => _2; + scope 3 { + } + } + } + scope 4 { + debug x => _4; + scope 5 { + debug x => _5; + scope 6 { + } + } + } + + bb0: { + StorageLive(_1); + _1 = const 3_u8; + StorageLive(_2); + StorageLive(_3); + _3 = &raw const _1; + _2 = move _3 as *const ! (PtrToPtr); + StorageDead(_3); + StorageDead(_2); + StorageDead(_1); + StorageLive(_4); + _4 = const 3_u8; + StorageLive(_5); + StorageLive(_6); + _6 = &raw const _4; + _5 = move _6 as *const ! (PtrToPtr); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + return; + } +} diff --git a/tests/mir-opt/uninhabited_not_read.rs b/tests/mir-opt/uninhabited_not_read.rs new file mode 100644 index 0000000000000..39ffdbdbb3359 --- /dev/null +++ b/tests/mir-opt/uninhabited_not_read.rs @@ -0,0 +1,27 @@ +// skip-filecheck + +//@ edition: 2021 +// In ed 2021 and below, we don't fallback `!` to `()`. +// This would introduce a `! -> ()` coercion which would +// be UB if we didn't disallow this explicitly. + +#![feature(never_type)] + +// EMIT_MIR uninhabited_not_read.main.SimplifyLocals-final.after.mir +fn main() { + // With a type annotation + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _: ! = *x; + } + + // Without a type annotation, make sure we don't implicitly coerce `!` to `()` + // when we do the noop `*x`. + unsafe { + let x = 3u8; + let x: *const ! = &x as *const u8 as *const _; + let _ = *x; + } +} + diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs index 7bc00773c0ba5..185e17c05a773 100644 --- a/tests/ui/never_type/diverging-place-match.rs +++ b/tests/ui/never_type/diverging-place-match.rs @@ -1,6 +1,6 @@ #![feature(never_type)] -fn make_up_a_value() -> T { +fn not_a_read() -> ! { unsafe { //~^ ERROR mismatched types let x: *const ! = 0 as _; @@ -10,4 +10,38 @@ fn make_up_a_value() -> T { } } +fn not_a_read_implicit() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let _ = *x; + } +} + +fn not_a_read_guide_coercion() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let _: () = *x; + //~^ ERROR mismatched types + } +} + +fn empty_match() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + match *x { _ => {} }; + } +} + +fn field_projection() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const (!, ()) = 0 as _; + let _ = (*x).0; + // ^ I think this is still UB, but because of the inbounds projection. + } +} + fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.stderr b/tests/ui/never_type/diverging-place-match.stderr index e86c634d59186..14b14f4270131 100644 --- a/tests/ui/never_type/diverging-place-match.stderr +++ b/tests/ui/never_type/diverging-place-match.stderr @@ -1,8 +1,6 @@ error[E0308]: mismatched types --> $DIR/diverging-place-match.rs:4:5 | -LL | fn make_up_a_value() -> T { - | - expected this type parameter LL | / unsafe { LL | | LL | | let x: *const ! = 0 as _; @@ -10,11 +8,76 @@ LL | | let _: ! = *x; LL | | // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this LL | | // is unsound since we act as if it diverges but it doesn't. LL | | } - | |_____^ expected type parameter `T`, found `()` + | |_____^ expected `!`, found `()` | - = note: expected type parameter `T` - found unit type `()` + = note: expected type `!` + found unit type `()` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:14:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let _ = *x; +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:25:21 + | +LL | let _: () = *x; + | -- ^^ expected `()`, found `!` + | | + | expected due to this + | + = note: expected unit type `()` + found type `!` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:22:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let _: () = *x; +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:31:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | match *x { _ => {} }; +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:39:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const (!, ()) = 0 as _; +LL | | let _ = (*x).0; +LL | | // ^ I think this is still UB, but because of the inbounds projection. +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0308`. From 515b93297ff6b96e6368070f3eb3f3a833112bc0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 07:53:49 -0400 Subject: [PATCH 1266/1301] Document things a bit more carefully, also account for coercion in check_expr_has_type_or_error --- compiler/rustc_hir_typeck/src/coercion.rs | 1 + compiler/rustc_hir_typeck/src/expr.rs | 16 +++++++++++++++- tests/mir-opt/uninhabited_not_read.rs | 1 - .../match/pattern-matching-should-fail.stderr | 6 ++---- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 1705421c2f40e..96bff04dbc7cf 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -186,6 +186,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if self.coerce_never { return success(simple(Adjust::NeverToAny)(b), b, vec![]); } else { + // Otherwise the only coercion we can do is unification. return self.unify_and(a, b, identity); } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 870a260c9fe09..604bec95701d5 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -62,7 +62,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. - if ty.is_never() { + if ty.is_never() && self.expr_constitutes_read(expr) { if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { let reported = self.dcx().span_delayed_bug( expr.span, @@ -260,6 +260,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + /// Whether this expression constitutes a read of value of the type that + /// it evaluates to. + /// + /// This is used to determine if we should consider the block to diverge + /// if the expression evaluates to `!`, and if we should insert a `NeverToAny` + /// coercion for values of type `!`. + /// + /// This function generally returns `false` if the expression is a place + /// expression and the *parent* expression is the scrutinee of a match or + /// the pointee of an `&` addr-of expression, since both of those parent + /// expressions take a *place* and not a value. + /// + /// This function is unfortunately a bit heuristical, though it is certainly + /// far sounder than the prior status quo: . pub(super) fn expr_constitutes_read(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { // We only care about place exprs. Anything else returns an immediate // which would constitute a read. We don't care about distinguishing diff --git a/tests/mir-opt/uninhabited_not_read.rs b/tests/mir-opt/uninhabited_not_read.rs index 39ffdbdbb3359..15769cdd75b3f 100644 --- a/tests/mir-opt/uninhabited_not_read.rs +++ b/tests/mir-opt/uninhabited_not_read.rs @@ -24,4 +24,3 @@ fn main() { let _ = *x; } } - diff --git a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr index f8ed792e3c675..e0d900a1eb564 100644 --- a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr @@ -7,14 +7,12 @@ LL | let c1 = || match x { }; | ^ `x` used here but it isn't initialized error[E0381]: used binding `x` isn't initialized - --> $DIR/pattern-matching-should-fail.rs:15:14 + --> $DIR/pattern-matching-should-fail.rs:15:23 | LL | let x: !; | - binding declared here but left uninitialized LL | let c2 = || match x { _ => () }; - | ^^ - borrow occurs due to use in closure - | | - | `x` used here but it isn't initialized + | ^ `x` used here but it isn't initialized error[E0381]: used binding `variant` isn't initialized --> $DIR/pattern-matching-should-fail.rs:27:13 From d2bd018dadc98442a15f52d962aaf3c5d102f034 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 5 Sep 2024 09:42:44 -0400 Subject: [PATCH 1267/1301] Be more thorough in expr_constitutes_read --- compiler/rustc_hir_typeck/src/expr.rs | 181 ++++++++++++++++++++------ compiler/rustc_hir_typeck/src/pat.rs | 2 +- 2 files changed, 141 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 604bec95701d5..cef00cf8ce865 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1,3 +1,6 @@ +// ignore-tidy-filelength +// FIXME: we should move the field error reporting code somewhere else. + //! Type checking expressions. //! //! See [`rustc_hir_analysis::check`] for more context on type checking in general. @@ -284,54 +287,150 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } - // If this expression has any adjustments applied after the place expression, - // they may constitute reads. - if !self.typeck_results.borrow().expr_adjustments(expr).is_empty() { - return true; - } + let parent_node = self.tcx.parent_hir_node(expr.hir_id); + match parent_node { + hir::Node::Expr(parent_expr) => { + match parent_expr.kind { + // Addr-of, field projections, and LHS of assignment don't constitute reads. + // Assignment does call `drop_in_place`, though, but its safety + // requirements are not the same. + ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false, + ExprKind::Assign(lhs, _, _) => { + // Only the LHS does not constitute a read + expr.hir_id != lhs.hir_id + } - fn pat_does_read(pat: &hir::Pat<'_>) -> bool { - let mut does_read = false; - pat.walk(|pat| { - if matches!( - pat.kind, - hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) - ) { - true - } else { - does_read = true; - // No need to continue. - false - } - }); - does_read - } + // If we have a subpattern that performs a read, we want to consider this + // to diverge for compatibility to support something like `let x: () = *never_ptr;`. + ExprKind::Match(scrutinee, arms, _) => { + assert_eq!(scrutinee.hir_id, expr.hir_id); + arms.iter().any(|arm| self.pat_constitutes_read(arm.pat)) + } + ExprKind::Let(hir::LetExpr { init, pat, .. }) => { + assert_eq!(init.hir_id, expr.hir_id); + self.pat_constitutes_read(*pat) + } - match self.tcx.parent_hir_node(expr.hir_id) { - // Addr-of, field projections, and LHS of assignment don't constitute reads. - // Assignment does call `drop_in_place`, though, but its safety - // requirements are not the same. - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. }) => false, - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Assign(target, _, _) | hir::ExprKind::Field(target, _), - .. - }) if expr.hir_id == target.hir_id => false, + // Any expression child of these expressions constitute reads. + ExprKind::Array(_) + | ExprKind::Call(_, _) + | ExprKind::MethodCall(_, _, _, _) + | ExprKind::Tup(_) + | ExprKind::Binary(_, _, _) + | ExprKind::Unary(_, _) + | ExprKind::Cast(_, _) + | ExprKind::Type(_, _) + | ExprKind::DropTemps(_) + | ExprKind::If(_, _, _) + | ExprKind::Closure(_) + | ExprKind::Block(_, _) + | ExprKind::AssignOp(_, _, _) + | ExprKind::Index(_, _, _) + | ExprKind::Break(_, _) + | ExprKind::Ret(_) + | ExprKind::Become(_) + | ExprKind::InlineAsm(_) + | ExprKind::Struct(_, _, _) + | ExprKind::Repeat(_, _) + | ExprKind::Yield(_, _) => true, + + // These expressions have no (direct) sub-exprs. + ExprKind::ConstBlock(_) + | ExprKind::Loop(_, _, _, _) + | ExprKind::Lit(_) + | ExprKind::Path(_) + | ExprKind::Continue(_) + | ExprKind::OffsetOf(_, _) + | ExprKind::Err(_) => unreachable!("no sub-expr expected for {:?}", expr.kind), + } + } // If we have a subpattern that performs a read, we want to consider this // to diverge for compatibility to support something like `let x: () = *never_ptr;`. - hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Let(hir::LetExpr { init: target, pat, .. }), + hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) => { + assert_eq!(target.hir_id, expr.hir_id); + self.pat_constitutes_read(*pat) + } + + // These nodes (if they have a sub-expr) do constitute a read. + hir::Node::Block(_) + | hir::Node::Arm(_) + | hir::Node::ExprField(_) + | hir::Node::AnonConst(_) + | hir::Node::ConstBlock(_) + | hir::Node::ConstArg(_) + | hir::Node::Stmt(_) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..), .. - }) if expr.hir_id == target.hir_id && !pat_does_read(*pat) => false, - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Match(target, arms, _), .. }) - if expr.hir_id == target.hir_id - && !arms.iter().any(|arm| pat_does_read(arm.pat)) => - { - false + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..), .. + }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true, + + // These nodes do not have direct sub-exprs. + hir::Node::Param(_) + | hir::Node::Item(_) + | hir::Node::ForeignItem(_) + | hir::Node::TraitItem(_) + | hir::Node::ImplItem(_) + | hir::Node::Variant(_) + | hir::Node::Field(_) + | hir::Node::PathSegment(_) + | hir::Node::Ty(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Pat(_) + | hir::Node::PatField(_) + | hir::Node::LetStmt(_) + | hir::Node::Synthetic + | hir::Node::Err(_) + | hir::Node::Ctor(_) + | hir::Node::Lifetime(_) + | hir::Node::GenericParam(_) + | hir::Node::Crate(_) + | hir::Node::Infer(_) + | hir::Node::WhereBoundPredicate(_) + | hir::Node::ArrayLenInfer(_) + | hir::Node::PreciseCapturingNonLifetimeArg(_) + | hir::Node::OpaqueTy(_) => { + unreachable!("no sub-expr expected for {parent_node:?}") + } + } + } + + /// Whether this pattern constitutes a read of value of the scrutinee that + /// it is matching against. + /// + /// See above for the nuances of what happens when this returns true. + pub(super) fn pat_constitutes_read(&self, pat: &hir::Pat<'_>) -> bool { + let mut does_read = false; + pat.walk(|pat| { + match pat.kind { + hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) => { + // Recurse + true + } + hir::PatKind::Binding(_, _, _, _) + | hir::PatKind::Struct(_, _, _) + | hir::PatKind::TupleStruct(_, _, _) + | hir::PatKind::Path(_) + | hir::PatKind::Tuple(_, _) + | hir::PatKind::Box(_) + | hir::PatKind::Ref(_, _) + | hir::PatKind::Deref(_) + | hir::PatKind::Lit(_) + | hir::PatKind::Range(_, _, _) + | hir::PatKind::Slice(_, _, _) + | hir::PatKind::Err(_) => { + does_read = true; + // No need to continue. + false + } } - _ => true, - } + }); + does_read } #[instrument(skip(self, expr), level = "debug")] diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 824918d1fa222..c2e25d259c582 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -279,7 +279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All other patterns constitute a read, which causes us to diverge // if the type is never. - if ty.is_never() && !matches!(pat.kind, PatKind::Wild | PatKind::Never | PatKind::Or(_)) { + if ty.is_never() && self.pat_constitutes_read(pat) { self.diverges.set(self.diverges.get() | Diverges::always(pat.span)); } From e8d5eb2a2b6cdddd6725501e1b54a33c278d9697 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 6 Sep 2024 10:19:07 -0400 Subject: [PATCH 1268/1301] Be far more strict about what we consider to be a read of never --- compiler/rustc_hir_typeck/src/coercion.rs | 10 ++- compiler/rustc_hir_typeck/src/expr.rs | 87 ++++++++++--------- compiler/rustc_hir_typeck/src/pat.rs | 7 -- ...ocess_never.SimplifyLocals-final.after.mir | 4 +- ...rocess_void.SimplifyLocals-final.after.mir | 2 +- tests/mir-opt/uninhabited_enum.rs | 7 +- tests/ui/never_type/diverging-place-match.rs | 27 ++++++ .../never_type/diverging-place-match.stderr | 61 ++++++++++++- .../raw-ref-op/never-place-isnt-diverging.rs | 9 ++ .../never-place-isnt-diverging.stderr | 16 +++- .../reachable/unwarned-match-on-never.stderr | 2 +- 11 files changed, 177 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 96bff04dbc7cf..642db3f36b586 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1066,7 +1066,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable)); - let coerce = Coerce::new(self, cause, allow_two_phase, self.expr_constitutes_read(expr)); + let coerce = Coerce::new( + self, + cause, + allow_two_phase, + self.expr_guaranteed_to_constitute_read_for_never(expr), + ); let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?; let (adjustments, _) = self.register_infer_ok_obligations(ok); @@ -1268,6 +1273,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // probably aren't processing function arguments here and even if we were, // they're going to get autorefed again anyway and we can apply 2-phase borrows // at that time. + // + // NOTE: we set `coerce_never` to `true` here because coercion LUBs only + // operate on values and not places, so a never coercion is valid. let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true); coerce.use_lub = true; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index cef00cf8ce865..2d71829537496 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. - if ty.is_never() && self.expr_constitutes_read(expr) { + if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) { if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { let reported = self.dcx().span_delayed_bug( expr.span, @@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // unless it's a place expression that isn't being read from, in which case // diverging would be unsound since we may never actually read the `!`. // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. - if ty.is_never() && self.expr_constitutes_read(expr) { + if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) { self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); } @@ -274,10 +274,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// expression and the *parent* expression is the scrutinee of a match or /// the pointee of an `&` addr-of expression, since both of those parent /// expressions take a *place* and not a value. - /// - /// This function is unfortunately a bit heuristical, though it is certainly - /// far sounder than the prior status quo: . - pub(super) fn expr_constitutes_read(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { + pub(super) fn expr_guaranteed_to_constitute_read_for_never( + &self, + expr: &'tcx hir::Expr<'tcx>, + ) -> bool { // We only care about place exprs. Anything else returns an immediate // which would constitute a read. We don't care about distinguishing // "syntactic" place exprs since if the base of a field projection is @@ -300,15 +300,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.hir_id != lhs.hir_id } - // If we have a subpattern that performs a read, we want to consider this - // to diverge for compatibility to support something like `let x: () = *never_ptr;`. + // See note on `PatKind::Or` below for why this is `all`. ExprKind::Match(scrutinee, arms, _) => { assert_eq!(scrutinee.hir_id, expr.hir_id); - arms.iter().any(|arm| self.pat_constitutes_read(arm.pat)) + arms.iter() + .all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat)) } ExprKind::Let(hir::LetExpr { init, pat, .. }) => { assert_eq!(init.hir_id, expr.hir_id); - self.pat_constitutes_read(*pat) + self.pat_guaranteed_to_constitute_read_for_never(*pat) } // Any expression child of these expressions constitute reads. @@ -349,7 +349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to diverge for compatibility to support something like `let x: () = *never_ptr;`. hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) => { assert_eq!(target.hir_id, expr.hir_id); - self.pat_constitutes_read(*pat) + self.pat_guaranteed_to_constitute_read_for_never(*pat) } // These nodes (if they have a sub-expr) do constitute a read. @@ -401,36 +401,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Whether this pattern constitutes a read of value of the scrutinee that - /// it is matching against. + /// it is matching against. This is used to determine whether we should + /// perform `NeverToAny` coercions. /// /// See above for the nuances of what happens when this returns true. - pub(super) fn pat_constitutes_read(&self, pat: &hir::Pat<'_>) -> bool { - let mut does_read = false; - pat.walk(|pat| { - match pat.kind { - hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) => { - // Recurse - true - } - hir::PatKind::Binding(_, _, _, _) - | hir::PatKind::Struct(_, _, _) - | hir::PatKind::TupleStruct(_, _, _) - | hir::PatKind::Path(_) - | hir::PatKind::Tuple(_, _) - | hir::PatKind::Box(_) - | hir::PatKind::Ref(_, _) - | hir::PatKind::Deref(_) - | hir::PatKind::Lit(_) - | hir::PatKind::Range(_, _, _) - | hir::PatKind::Slice(_, _, _) - | hir::PatKind::Err(_) => { - does_read = true; - // No need to continue. - false - } - } - }); - does_read + pub(super) fn pat_guaranteed_to_constitute_read_for_never(&self, pat: &hir::Pat<'_>) -> bool { + match pat.kind { + // Does not constitute a read. + hir::PatKind::Wild => false, + + // This is unnecessarily restrictive when the pattern that doesn't + // constitute a read is unreachable. + // + // For example `match *never_ptr { value => {}, _ => {} }` or + // `match *never_ptr { _ if false => {}, value => {} }`. + // + // It is however fine to be restrictive here; only returning `true` + // can lead to unsoundness. + hir::PatKind::Or(subpats) => { + subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(pat)) + } + + // Does constitute a read, since it is equivalent to a discriminant read. + hir::PatKind::Never => true, + + // All of these constitute a read, or match on something that isn't `!`, + // which would require a `NeverToAny` coercion. + hir::PatKind::Binding(_, _, _, _) + | hir::PatKind::Struct(_, _, _) + | hir::PatKind::TupleStruct(_, _, _) + | hir::PatKind::Path(_) + | hir::PatKind::Tuple(_, _) + | hir::PatKind::Box(_) + | hir::PatKind::Ref(_, _) + | hir::PatKind::Deref(_) + | hir::PatKind::Lit(_) + | hir::PatKind::Range(_, _, _) + | hir::PatKind::Slice(_, _, _) + | hir::PatKind::Err(_) => true, + } } #[instrument(skip(self, expr), level = "debug")] diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index c2e25d259c582..49c5a7d8a652d 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -27,7 +27,6 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; -use crate::diverges::Diverges; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, LoweredTy, errors}; @@ -277,12 +276,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - // All other patterns constitute a read, which causes us to diverge - // if the type is never. - if ty.is_never() && self.pat_constitutes_read(pat) { - self.diverges.set(self.diverges.get() | Diverges::always(pat.span)); - } - self.write_ty(pat.hir_id, ty); // (note_1): In most of the cases where (note_1) is referenced diff --git a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir index 64fc81e298946..02e1f4be15e2e 100644 --- a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir @@ -4,10 +4,10 @@ fn process_never(_1: *const !) -> () { debug input => _1; let mut _0: (); scope 1 { - debug _input => _1; + debug _input => const (); } bb0: { - return; + unreachable; } } diff --git a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir index 51514ba5e5d90..64711755f7394 100644 --- a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir @@ -4,7 +4,7 @@ fn process_void(_1: *const Void) -> () { debug input => _1; let mut _0: (); scope 1 { - debug _input => _1; + debug _input => const ZeroSized: Void; } bb0: { diff --git a/tests/mir-opt/uninhabited_enum.rs b/tests/mir-opt/uninhabited_enum.rs index 9d845e34fbda8..90b5353f29143 100644 --- a/tests/mir-opt/uninhabited_enum.rs +++ b/tests/mir-opt/uninhabited_enum.rs @@ -1,18 +1,21 @@ // skip-filecheck #![feature(never_type)] +#[derive(Copy, Clone)] pub enum Void {} // EMIT_MIR uninhabited_enum.process_never.SimplifyLocals-final.after.mir #[no_mangle] pub fn process_never(input: *const !) { - let _input = unsafe { &*input }; + let _input = unsafe { *input }; } // EMIT_MIR uninhabited_enum.process_void.SimplifyLocals-final.after.mir #[no_mangle] pub fn process_void(input: *const Void) { - let _input = unsafe { &*input }; + let _input = unsafe { *input }; + // In the future, this should end with `unreachable`, but we currently only do + // unreachability analysis for `!`. } fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs index 185e17c05a773..b9bc29a218c4a 100644 --- a/tests/ui/never_type/diverging-place-match.rs +++ b/tests/ui/never_type/diverging-place-match.rs @@ -44,4 +44,31 @@ fn field_projection() -> ! { } } +fn covered_arm() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let (_ | 1i32) = *x; + //~^ ERROR mismatched types + } +} + +// FIXME: This *could* be considered a read of `!`, but we're not that sophisticated.. +fn uncovered_arm() -> ! { + unsafe { + //~^ ERROR mismatched types + let x: *const ! = 0 as _; + let (1i32 | _) = *x; + //~^ ERROR mismatched types + } +} + +fn coerce_ref_binding() -> ! { + unsafe { + let x: *const ! = 0 as _; + let ref _x: () = *x; + //~^ ERROR mismatched types + } +} + fn main() {} diff --git a/tests/ui/never_type/diverging-place-match.stderr b/tests/ui/never_type/diverging-place-match.stderr index 14b14f4270131..74e1bfa0a6b64 100644 --- a/tests/ui/never_type/diverging-place-match.stderr +++ b/tests/ui/never_type/diverging-place-match.stderr @@ -78,6 +78,65 @@ LL | | } = note: expected type `!` found unit type `()` -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:51:18 + | +LL | let (_ | 1i32) = *x; + | ^^^^ -- this expression has type `!` + | | + | expected `!`, found `i32` + | + = note: expected type `!` + found type `i32` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:48:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let (_ | 1i32) = *x; +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:61:14 + | +LL | let (1i32 | _) = *x; + | ^^^^ -- this expression has type `!` + | | + | expected `!`, found `i32` + | + = note: expected type `!` + found type `i32` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:58:5 + | +LL | / unsafe { +LL | | +LL | | let x: *const ! = 0 as _; +LL | | let (1i32 | _) = *x; +LL | | +LL | | } + | |_____^ expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/diverging-place-match.rs:69:26 + | +LL | let ref _x: () = *x; + | ^^ expected `()`, found `!` + | + = note: expected unit type `()` + found type `!` + +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs index 52f4158dbc988..80d441729f746 100644 --- a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs @@ -10,4 +10,13 @@ fn make_up_a_value() -> T { } } + +fn make_up_a_pointer() -> *const T { + unsafe { + let x: *const ! = 0 as _; + &raw const *x + //~^ ERROR mismatched types + } +} + fn main() {} diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr index 9eba57dde8fa7..af9e788982179 100644 --- a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr +++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr @@ -15,6 +15,20 @@ LL | | } = note: expected type parameter `T` found unit type `()` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/never-place-isnt-diverging.rs:17:9 + | +LL | fn make_up_a_pointer() -> *const T { + | - -------- expected `*const T` because of return type + | | + | expected this type parameter +... +LL | &raw const *x + | ^^^^^^^^^^^^^ expected `*const T`, found `*const !` + | + = note: expected raw pointer `*const T` + found raw pointer `*const !` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/reachable/unwarned-match-on-never.stderr b/tests/ui/reachable/unwarned-match-on-never.stderr index c1ad3511b4e08..a296d2a055e09 100644 --- a/tests/ui/reachable/unwarned-match-on-never.stderr +++ b/tests/ui/reachable/unwarned-match-on-never.stderr @@ -2,7 +2,7 @@ error: unreachable expression --> $DIR/unwarned-match-on-never.rs:10:5 | LL | match x {} - | ---------- any code following this expression is unreachable + | - any code following this expression is unreachable LL | // But matches in unreachable code are warned. LL | match x {} | ^^^^^^^^^^ unreachable expression From 7f5548fa8b22bccdadb3cbe7cf4d566a970eeb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 6 Oct 2024 01:44:59 +0000 Subject: [PATCH 1269/1301] On function and method calls in patterns, link to the book ``` error: expected a pattern, found an expression --> f889.rs:3:13 | 3 | let (x, y.drop()) = (1, 2); //~ ERROR | ^^^^^^^^ not a pattern | = note: arbitrary expressions are not allowed in patterns: error[E0532]: expected a pattern, found a function call --> f889.rs:2:13 | 2 | let (x, drop(y)) = (1, 2); //~ ERROR | ^^^^ not a tuple struct or tuple variant | = note: function calls are not allowed in patterns: ``` Fix #97200. --- compiler/rustc_parse/messages.ftl | 3 +- compiler/rustc_parse/src/errors.rs | 1 + .../rustc_resolve/src/late/diagnostics.rs | 13 +++ .../range_pat_interactions1.stderr | 3 +- .../range_pat_interactions2.stderr | 3 +- tests/ui/parser/bad-name.stderr | 4 +- tests/ui/parser/issues/issue-24197.stderr | 4 +- tests/ui/parser/issues/issue-24375.stderr | 3 +- tests/ui/parser/pat-lt-bracket-5.stderr | 4 +- tests/ui/parser/pat-lt-bracket-6.stderr | 4 +- tests/ui/parser/pat-ranges-3.stderr | 8 +- .../parser/recover/recover-pat-exprs.stderr | 103 ++++++++++++------ .../parser/recover/recover-pat-issues.stderr | 18 ++- .../ui/parser/recover/recover-pat-lets.stderr | 18 ++- .../parser/recover/recover-pat-ranges.stderr | 18 ++- .../recover/recover-pat-wildcards.stderr | 3 +- tests/ui/resolve/issue-10200.rs | 2 +- tests/ui/resolve/issue-10200.stderr | 4 +- ...s-in-pattern-with-ty-err-doesnt-ice.stderr | 4 +- 19 files changed, 156 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 5d1c300b45357..948199fd55c65 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -812,7 +812,8 @@ parse_unexpected_expr_in_pat = *[false] a pattern }, found an expression - .label = arbitrary expressions are not allowed in patterns + .label = not a pattern + .note = arbitrary expressions are not allowed in patterns: parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 40502158469f0..dade39127515f 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2608,6 +2608,7 @@ pub(crate) struct ExpectedCommaAfterPatternField { #[derive(Diagnostic)] #[diag(parse_unexpected_expr_in_pat)] +#[note] pub(crate) struct UnexpectedExpressionInPattern { /// The unexpected expr's span. #[primary_span] diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 35d166e8b4ab3..fce5ec36c661b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -443,6 +443,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { self.suggest_bare_struct_literal(&mut err); self.suggest_changing_type_to_const_param(&mut err, res, source, span); + self.explain_functions_in_pattern(&mut err, res, source); if self.suggest_pattern_match_with_let(&mut err, source, span) { // Fallback label. @@ -1166,6 +1167,18 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } + fn explain_functions_in_pattern( + &mut self, + err: &mut Diag<'_>, + res: Option, + source: PathSource<'_>, + ) { + let PathSource::TupleStruct(_, _) = source else { return }; + let Some(Res::Def(DefKind::Fn, _)) = res else { return }; + err.primary_message("expected a pattern, found a function call"); + err.note("function calls are not allowed in patterns: "); + } + fn suggest_changing_type_to_const_param( &mut self, err: &mut Diag<'_>, diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr index 9831348de7569..e2916725fbd90 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr @@ -2,8 +2,9 @@ error: expected a pattern range bound, found an expression --> $DIR/range_pat_interactions1.rs:17:16 | LL | 0..5+1 => errors_only.push(x), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 5+1; diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr index 1b5e875cccb68..f54e07c3a6370 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr @@ -14,8 +14,9 @@ error: expected a pattern range bound, found an expression --> $DIR/range_pat_interactions2.rs:10:18 | LL | 0..=(5+1) => errors_only.push(x), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 5+1; diff --git a/tests/ui/parser/bad-name.stderr b/tests/ui/parser/bad-name.stderr index 3fc416dd5311f..5ca248380ee5b 100644 --- a/tests/ui/parser/bad-name.stderr +++ b/tests/ui/parser/bad-name.stderr @@ -8,7 +8,9 @@ error: expected a pattern, found an expression --> $DIR/bad-name.rs:2:7 | LL | let x.y::.z foo; - | ^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected one of `(`, `.`, `::`, `:`, `;`, `=`, `?`, `|`, or an operator, found `foo` --> $DIR/bad-name.rs:2:22 diff --git a/tests/ui/parser/issues/issue-24197.stderr b/tests/ui/parser/issues/issue-24197.stderr index 7ebbf4ac370d7..c92e165b23b65 100644 --- a/tests/ui/parser/issues/issue-24197.stderr +++ b/tests/ui/parser/issues/issue-24197.stderr @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/issue-24197.rs:2:9 | LL | let buf[0] = 0; - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr index a25c277d78a5b..fef3fcde7b799 100644 --- a/tests/ui/parser/issues/issue-24375.stderr +++ b/tests/ui/parser/issues/issue-24375.stderr @@ -2,8 +2,9 @@ error: expected a pattern, found an expression --> $DIR/issue-24375.rs:6:9 | LL | tmp[0] => {} - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == tmp[0] => {} diff --git a/tests/ui/parser/pat-lt-bracket-5.stderr b/tests/ui/parser/pat-lt-bracket-5.stderr index 18cf2df028216..a2a972652d188 100644 --- a/tests/ui/parser/pat-lt-bracket-5.stderr +++ b/tests/ui/parser/pat-lt-bracket-5.stderr @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/pat-lt-bracket-5.rs:2:9 | LL | let v[0] = v[1]; - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error[E0425]: cannot find value `v` in this scope --> $DIR/pat-lt-bracket-5.rs:2:16 diff --git a/tests/ui/parser/pat-lt-bracket-6.stderr b/tests/ui/parser/pat-lt-bracket-6.stderr index 892883c4aedca..14ae602fedfa1 100644 --- a/tests/ui/parser/pat-lt-bracket-6.stderr +++ b/tests/ui/parser/pat-lt-bracket-6.stderr @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/pat-lt-bracket-6.rs:5:14 | LL | let Test(&desc[..]) = x; - | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error[E0308]: mismatched types --> $DIR/pat-lt-bracket-6.rs:10:30 diff --git a/tests/ui/parser/pat-ranges-3.stderr b/tests/ui/parser/pat-ranges-3.stderr index 5e1f35d1b6f9a..ef080368e19ca 100644 --- a/tests/ui/parser/pat-ranges-3.stderr +++ b/tests/ui/parser/pat-ranges-3.stderr @@ -2,13 +2,17 @@ error: expected a pattern range bound, found an expression --> $DIR/pat-ranges-3.rs:4:16 | LL | let 10 ..= 10 + 3 = 12; - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern range bound, found an expression --> $DIR/pat-ranges-3.rs:7:9 | LL | let 10 - 3 ..= 10 = 8; - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: aborting due to 2 previous errors diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index 63956f35c079f..6cb3753de8d17 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -2,8 +2,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:5:9 | LL | x.y => (), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.y => (), @@ -24,8 +25,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:6:9 | LL | x.0 => (), - | ^^^ arbitrary expressions are not allowed in patterns + | ^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.0 => (), @@ -47,8 +49,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:7:9 | LL | x._0 => (), - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x._0 => (), @@ -71,8 +74,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:8:9 | LL | x.0.1 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.0.1 => (), @@ -95,8 +99,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:9:9 | LL | x.4.y.17.__z => (), - | ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.4.y.17.__z => (), @@ -149,8 +154,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:23:9 | LL | x[0] => (), - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x[0] => (), @@ -170,8 +176,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:24:9 | LL | x[..] => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x[..] => (), @@ -219,8 +226,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:37:9 | LL | x.f() => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.f() => (), @@ -240,8 +248,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:38:9 | LL | x._f() => (), - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x._f() => (), @@ -262,8 +271,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:39:9 | LL | x? => (), - | ^^ arbitrary expressions are not allowed in patterns + | ^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x? => (), @@ -285,8 +295,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:40:9 | LL | ().f() => (), - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == ().f() => (), @@ -309,8 +320,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:41:9 | LL | (0, x)?.f() => (), - | ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (0, x)?.f() => (), @@ -333,8 +345,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:42:9 | LL | x.f().g() => (), - | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.f().g() => (), @@ -357,8 +370,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:43:9 | LL | 0.f()?.g()?? => (), - | ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == 0.f()?.g()?? => (), @@ -381,8 +395,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:50:9 | LL | x as usize => (), - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x as usize => (), @@ -402,8 +417,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:51:9 | LL | 0 as usize => (), - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == 0 as usize => (), @@ -424,8 +440,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:52:9 | LL | x.f().0.4 as f32 => (), - | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == x.f().0.4 as f32 => (), @@ -447,8 +464,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:59:9 | LL | 1 + 1 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == 1 + 1 => (), @@ -468,8 +486,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:60:9 | LL | (1 + 2) * 3 => (), - | ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (1 + 2) * 3 => (), @@ -490,8 +509,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:63:9 | LL | x.0 > 2 => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (x.0 > 2) => (), @@ -514,8 +534,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:64:9 | LL | x.0 == 2 => (), - | ^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == (x.0 == 2) => (), @@ -538,8 +559,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:69:13 | LL | (x, y.0 > 2) if x != 0 => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to the match arm guard | LL | (x, val) if x != 0 && val == (y.0 > 2) => (), @@ -559,8 +581,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:70:13 | LL | (x, y.0 > 2) if x != 0 || x != 1 => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to the match arm guard | LL | (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (), @@ -598,8 +621,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:81:9 | LL | u8::MAX.abs() => (), - | ^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == u8::MAX.abs() => (), @@ -619,8 +643,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:86:17 | LL | z @ w @ v.u() => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | z @ w @ val if val == v.u() => (), @@ -643,8 +668,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:88:9 | LL | y.ilog(3) => (), - | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == y.ilog(3) => (), @@ -667,8 +693,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:90:9 | LL | n + 1 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == n + 1 => (), @@ -691,8 +718,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:92:10 | LL | ("".f() + 14 * 8) => (), - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | (val) if val == "".f() + 14 * 8 => (), @@ -715,8 +743,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:95:9 | LL | f?() => (), - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | val if val == f?() => (), @@ -739,7 +768,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:101:9 | LL | let 1 + 1 = 2; - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected one of `)`, `,`, `@`, or `|`, found `*` --> $DIR/recover-pat-exprs.rs:104:28 @@ -754,19 +785,25 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:60:10 | LL | (1 + 2) * 3 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:75:5 | LL | 1 + 2 * PI.cos() => 2, - | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:83:9 | LL | x.sqrt() @ .. => (), - | ^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: aborting due to 45 previous errors diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr index 596bff2139525..17cb7b4aead80 100644 --- a/tests/ui/parser/recover/recover-pat-issues.stderr +++ b/tests/ui/parser/recover/recover-pat-issues.stderr @@ -2,8 +2,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:6:13 | LL | Foo("hi".to_owned()) => true, - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | Foo(val) if val == "hi".to_owned() => true, @@ -23,8 +24,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:14:20 | LL | Bar { baz: "hi".to_owned() } => true, - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | Bar { baz } if baz == "hi".to_owned() => true, @@ -44,8 +46,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:25:11 | LL | &["foo".to_string()] => {} - | ^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | LL | &[val] if val == "foo".to_string() => {} @@ -65,8 +68,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:36:17 | LL | if let Some(MAGIC.0 as usize) = None:: {} - | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = MAGIC.0 as usize; @@ -81,8 +85,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:41:13 | LL | if let (-1.some(4)) = (0, Some(4)) {} - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = -1.some(4); @@ -97,8 +102,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:44:13 | LL | if let (-1.Some(4)) = (0, Some(4)) {} - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = -1.Some(4); diff --git a/tests/ui/parser/recover/recover-pat-lets.stderr b/tests/ui/parser/recover/recover-pat-lets.stderr index e54586b092483..b481813b24621 100644 --- a/tests/ui/parser/recover/recover-pat-lets.stderr +++ b/tests/ui/parser/recover/recover-pat-lets.stderr @@ -2,26 +2,33 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:4:9 | LL | let x.expect("foo"); - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:7:9 | LL | let x.unwrap(): u32; - | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:10:9 | LL | let x[0] = 1; - | ^^^^ arbitrary expressions are not allowed in patterns + | ^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:13:14 | LL | let Some(1 + 1) = x else { - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 1; @@ -36,8 +43,9 @@ error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:17:17 | LL | if let Some(1 + 1) = x { - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 1; diff --git a/tests/ui/parser/recover/recover-pat-ranges.stderr b/tests/ui/parser/recover/recover-pat-ranges.stderr index 088f83b0ccbac..0a9b54474689a 100644 --- a/tests/ui/parser/recover/recover-pat-ranges.stderr +++ b/tests/ui/parser/recover/recover-pat-ranges.stderr @@ -86,8 +86,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:11:12 | LL | ..=1 + 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 2; @@ -106,8 +107,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:15:10 | LL | (-4 + 0).. => (), - | ^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = -4 + 0; @@ -126,8 +128,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:18:10 | LL | (1 + 4)...1 * 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 4; @@ -146,8 +149,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:18:19 | LL | (1 + 4)...1 * 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 * 2; @@ -166,8 +170,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:24:9 | LL | 0.x()..="y".z() => (), - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 0.x(); @@ -186,8 +191,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:24:17 | LL | 0.x()..="y".z() => (), - | ^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "y".z(); diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index 30307726a973b..8d4212ed389dd 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -75,8 +75,9 @@ error: expected a pattern range bound, found an expression --> $DIR/recover-pat-wildcards.rs:55:14 | LL | 4..=(2 + _) => () - | ^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^ not a pattern | + = note: arbitrary expressions are not allowed in patterns: help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 2 + _; diff --git a/tests/ui/resolve/issue-10200.rs b/tests/ui/resolve/issue-10200.rs index fe36a7e00bff3..d529536b95256 100644 --- a/tests/ui/resolve/issue-10200.rs +++ b/tests/ui/resolve/issue-10200.rs @@ -3,7 +3,7 @@ fn foo(_: usize) -> Foo { Foo(false) } fn main() { match Foo(true) { - foo(x) //~ ERROR expected tuple struct or tuple variant, found function `foo` + foo(x) //~ ERROR expected a pattern, found a function call => () } } diff --git a/tests/ui/resolve/issue-10200.stderr b/tests/ui/resolve/issue-10200.stderr index 7b218694b269e..172d016c6e6c4 100644 --- a/tests/ui/resolve/issue-10200.stderr +++ b/tests/ui/resolve/issue-10200.stderr @@ -1,4 +1,4 @@ -error[E0532]: expected tuple struct or tuple variant, found function `foo` +error[E0532]: expected a pattern, found a function call --> $DIR/issue-10200.rs:6:9 | LL | struct Foo(bool); @@ -6,6 +6,8 @@ LL | struct Foo(bool); ... LL | foo(x) | ^^^ help: a tuple struct with a similar name exists (notice the capitalization): `Foo` + | + = note: function calls are not allowed in patterns: error: aborting due to 1 previous error diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr index fc431eb14127f..d40e98224355b 100644 --- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr @@ -2,7 +2,9 @@ error: expected a pattern, found an expression --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:31 | LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; - | ^^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^^^^^^^^^^ not a pattern + | + = note: arbitrary expressions are not allowed in patterns: error[E0412]: cannot find type `T` in this scope --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:55 From 7c0c51193327fcf7f09d6450bd4dcc744adf6fe3 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 5 Oct 2024 21:34:51 -0500 Subject: [PATCH 1270/1301] Update `compiler-builtins` to 0.1.133 This includes [1], which should help resolve an infinite recusion issue on WASM and SPARC (possibly other platforms). See [2] and [3] for further details. [1]: https://github.com/rust-lang/compiler-builtins/pull/708 [2]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/sparc-unknown-none-elf.20regresssion.20between.20compiler-built.2E.2E.2E [3]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/.5Bwasm32.5D.20Infinite.20recursion.20.60compiler-builtins.60.20.60__multi3.60 --- library/Cargo.lock | 4 ++-- library/alloc/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 66ded59ae3661..59b76d8d4427d 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.132" +version = "0.1.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88f58330b127df19326d19e9debbb722578589cb1b9c061b5cdc4036d038b54b" +checksum = "ab10bf45b2ed1b4f4c25401527a61684142c042b3c86ace7da7ea6881e26741b" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index fa18c7aec743a..259a3ed2bebde 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.132", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.133", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 6bf70e1d82abe..f2669326065d5 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.132" } +compiler_builtins = { version = "0.1.133" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ From 8b968764f15ea84b389b2af98297b9d30fb9952e Mon Sep 17 00:00:00 2001 From: Shriram Balaji Date: Thu, 12 Sep 2024 00:56:52 +0530 Subject: [PATCH 1271/1301] bootstrap: add `std_features` config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bootstrap: add std_features to config.example fix: use BTreeSet for std-features; add unit tests * fix formatting of string in front of std_features * rename `std_features` to `std-features` in config.toml fix: remove space before std-features in config.toml fix: remove explicit .into_iter conversion bootstrap: add details for rust.std-features in config.example.toml Co-authored-by: Onur Özkan fix: remove `Option` from `rust_std_features` fix: move default rust_std_features to config fix: make std_features CI rustc incompatible --- config.example.toml | 10 +++++++++ src/bootstrap/src/core/config/config.rs | 12 ++++++++++- src/bootstrap/src/core/config/tests.rs | 22 ++++++++++++++++++++ src/bootstrap/src/lib.rs | 27 ++++++++++++++----------- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/config.example.toml b/config.example.toml index 47ebb20d8fa82..4b591b949b36d 100644 --- a/config.example.toml +++ b/config.example.toml @@ -759,6 +759,16 @@ # Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std` #validate-mir-opts = 3 +# Configure `std` features used during bootstrap. +# Default features will be expanded in the following cases: +# - If `rust.llvm-libunwind` or `target.llvm-libunwind` is enabled: +# - "llvm-libunwind" will be added for in-tree LLVM builds. +# - "system-llvm-libunwind" will be added for system LLVM builds. +# - If `rust.backtrace` is enabled, "backtrace" will be added. +# - If `rust.profiler` or `target.profiler` is enabled, "profiler" will be added. +# - If building for a zkvm target, "compiler-builtins-mem" will be added. +#std-features = ["panic_unwind"] + # ============================================================================= # Options for specific targets # diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 07460b81412ac..7dc3b7b081aea 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -4,7 +4,7 @@ //! how the build runs. use std::cell::{Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::{self, Display}; use std::io::IsTerminal; use std::path::{Path, PathBuf, absolute}; @@ -287,6 +287,7 @@ pub struct Config { pub rust_profile_generate: Option, pub rust_lto: RustcLto, pub rust_validate_mir_opts: Option, + pub rust_std_features: BTreeSet, pub llvm_profile_use: Option, pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option, @@ -1152,6 +1153,7 @@ define_config! { download_rustc: Option = "download-rustc", lto: Option = "lto", validate_mir_opts: Option = "validate-mir-opts", + std_features: Option> = "std-features", } } @@ -1645,6 +1647,7 @@ impl Config { let mut optimize = None; let mut omit_git_hash = None; let mut lld_enabled = None; + let mut std_features = None; let mut is_user_configured_rust_channel = false; @@ -1703,6 +1706,7 @@ impl Config { stack_protector, strip, lld_mode, + std_features: std_features_toml, } = rust; is_user_configured_rust_channel = channel.is_some(); @@ -1722,6 +1726,7 @@ impl Config { debuginfo_level_tools = debuginfo_level_tools_toml; debuginfo_level_tests = debuginfo_level_tests_toml; lld_enabled = lld_enabled_toml; + std_features = std_features_toml; optimize = optimize_toml; omit_git_hash = omit_git_hash_toml; @@ -2118,6 +2123,9 @@ impl Config { ); } + let default_std_features = BTreeSet::from([String::from("panic-unwind")]); + config.rust_std_features = std_features.unwrap_or(default_std_features); + let default = debug == Some(true); config.rust_debug_assertions = debug_assertions.unwrap_or(default); config.rust_debug_assertions_std = @@ -3016,6 +3024,7 @@ fn check_incompatible_options_for_ci_rustc( description, incremental, default_linker, + std_features, // Rest of the options can simply be ignored. debug: _, @@ -3077,6 +3086,7 @@ fn check_incompatible_options_for_ci_rustc( err!(current_rust_config.default_linker, default_linker); err!(current_rust_config.stack_protector, stack_protector); err!(current_rust_config.lto, lto); + err!(current_rust_config.std_features, std_features); warn!(current_rust_config.channel, channel); warn!(current_rust_config.description, description); diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index e38d4eac051d8..278becdcbc77d 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::env; use std::fs::{File, remove_file}; use std::io::Write; @@ -326,3 +327,24 @@ fn verbose_tests_default_value() { let config = Config::parse(Flags::parse(&["build".into(), "compiler".into(), "-v".into()])); assert_eq!(config.verbose_tests, true); } + +#[test] +fn parse_rust_std_features() { + let config = parse("rust.std-features = [\"panic-unwind\", \"backtrace\"]"); + let expected_features: BTreeSet = + ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect(); + assert_eq!(config.rust_std_features, expected_features); +} + +#[test] +fn parse_rust_std_features_empty() { + let config = parse("rust.std-features = []"); + let expected_features: BTreeSet = BTreeSet::new(); + assert_eq!(config.rust_std_features, expected_features); +} + +#[test] +#[should_panic] +fn parse_rust_std_features_invalid() { + parse("rust.std-features = \"backtrace\""); +} diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 75659f46431af..313b49346bc56 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -17,7 +17,7 @@ //! also check out the `src/bootstrap/README.md` file for more information. use std::cell::{Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; use std::fs::{self, File}; use std::path::{Path, PathBuf}; @@ -645,28 +645,31 @@ impl Build { &self.config.rust_info } - /// Gets the space-separated set of activated features for the standard - /// library. + /// Gets the space-separated set of activated features for the standard library. + /// This can be configured with the `std-features` key in config.toml. fn std_features(&self, target: TargetSelection) -> String { - let mut features = " panic-unwind".to_string(); + let mut features: BTreeSet<&str> = + self.config.rust_std_features.iter().map(|s| s.as_str()).collect(); match self.config.llvm_libunwind(target) { - LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"), - LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"), - LlvmLibunwind::No => {} - } + LlvmLibunwind::InTree => features.insert("llvm-libunwind"), + LlvmLibunwind::System => features.insert("system-llvm-libunwind"), + LlvmLibunwind::No => false, + }; + if self.config.backtrace { - features.push_str(" backtrace"); + features.insert("backtrace"); } if self.config.profiler_enabled(target) { - features.push_str(" profiler"); + features.insert("profiler"); } // Generate memcpy, etc. FIXME: Remove this once compiler-builtins // automatically detects this target. if target.contains("zkvm") { - features.push_str(" compiler-builtins-mem"); + features.insert("compiler-builtins-mem"); } - features + + features.into_iter().collect::>().join(" ") } /// Gets the space-separated set of activated features for the compiler. From 002afd1ae9c646d4120cc8e078bb329de8b3ecac Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sat, 5 Oct 2024 22:01:02 -0700 Subject: [PATCH 1272/1301] Fix typo in primitive_docs.rs --- library/core/src/primitive_docs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 962da6643ddb1..c25501f12001a 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1251,7 +1251,7 @@ mod prim_f16 {} /// - **Unchanged NaN propagation**: The quiet bit and payload are copied from any input operand /// that is a NaN. If the inputs and outputs do not have the same size (i.e., for `as` casts), the /// same rules as for "quieting NaN propagation" apply, with one caveat: if the output is smaller -/// than the input, droppig the low-order bits may result in a payload of 0; a payload of 0 is not +/// than the input, dropping the low-order bits may result in a payload of 0; a payload of 0 is not /// possible with a signaling NaN (the all-0 significand encodes an infinity) so unchanged NaN /// propagation cannot occur with some inputs. /// - **Target-specific NaN**: The quiet bit is set and the payload is picked from a target-specific From e0a20b484d0b88183ddd46b82aa47d7bbfe48468 Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Sun, 6 Oct 2024 10:39:03 +0300 Subject: [PATCH 1273/1301] Handle `librustdoc` cases of `rustc::potential_query_instability` lint --- compiler/rustc_resolve/src/rustdoc.rs | 6 +-- src/librustdoc/clean/types.rs | 16 ++++---- src/librustdoc/config.rs | 4 +- src/librustdoc/core.rs | 4 +- src/librustdoc/doctest.rs | 15 ++++---- src/librustdoc/doctest/runner.rs | 6 +-- src/librustdoc/formats/cache.rs | 16 ++++---- src/librustdoc/html/highlight.rs | 4 +- src/librustdoc/html/highlight/tests.rs | 4 +- src/librustdoc/html/layout.rs | 4 +- src/librustdoc/html/markdown.rs | 8 ++-- src/librustdoc/html/render/context.rs | 14 +++---- src/librustdoc/html/render/mod.rs | 38 +++++++++---------- src/librustdoc/html/render/print_item.rs | 4 +- src/librustdoc/html/render/search_index.rs | 10 ++--- src/librustdoc/html/render/span_map.rs | 4 +- src/librustdoc/html/render/write_shared.rs | 10 ++--- src/librustdoc/html/sources.rs | 8 ++-- src/librustdoc/lib.rs | 3 +- .../passes/collect_intra_doc_links.rs | 6 +-- src/librustdoc/passes/collect_trait_impls.rs | 2 +- src/librustdoc/scrape_examples.rs | 10 ++--- src/librustdoc/theme.rs | 33 ++++++++-------- 23 files changed, 114 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index d1c1b6c882eaa..02e255d7726d1 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -6,7 +6,7 @@ use pulldown_cmark::{ }; use rustc_ast as ast; use rustc_ast::util::comments::beautify_doc_string; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::symbol::{Symbol, kw, sym}; @@ -235,8 +235,8 @@ fn span_for_value(attr: &ast::Attribute) -> Span { /// early and late doc link resolution regardless of their position. pub fn prepare_to_doc_link_resolution( doc_fragments: &[DocFragment], -) -> FxHashMap, String> { - let mut res = FxHashMap::default(); +) -> FxIndexMap, String> { + let mut res = FxIndexMap::default(); for fragment in doc_fragments { let out_str = res.entry(fragment.item_id).or_default(); add_doc_fragment(out_str, fragment); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ec31a51a81e2d..ea4388b305b36 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -9,7 +9,7 @@ use rustc_ast::NestedMetaItem; use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, Stability, StableSince}; use rustc_const_eval::const_eval::is_unstable_const_fn; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::lang_items::LangItem; @@ -114,7 +114,7 @@ impl From for ItemId { pub(crate) struct Crate { pub(crate) module: Item, /// Only here so that they can be filtered through the rustdoc passes. - pub(crate) external_traits: Box>, + pub(crate) external_traits: Box>, } impl Crate { @@ -1223,7 +1223,7 @@ impl Attributes { } pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> { - let mut aliases = FxHashSet::default(); + let mut aliases = FxIndexSet::default(); for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) { if let Some(values) = attr.meta_item_list() { @@ -1759,7 +1759,7 @@ pub(crate) enum PrimitiveType { Never, } -type SimplifiedTypes = FxHashMap>; +type SimplifiedTypes = FxIndexMap>; impl PrimitiveType { pub(crate) fn from_hir(prim: hir::PrimTy) -> PrimitiveType { use ast::{FloatTy, IntTy, UintTy}; @@ -1927,10 +1927,10 @@ impl PrimitiveType { /// In particular, if a crate depends on both `std` and another crate that also defines /// `rustc_doc_primitive`, then it's entirely random whether `std` or the other crate is picked. /// (no_std crates are usually fine unless multiple dependencies define a primitive.) - pub(crate) fn primitive_locations(tcx: TyCtxt<'_>) -> &FxHashMap { - static PRIMITIVE_LOCATIONS: OnceCell> = OnceCell::new(); + pub(crate) fn primitive_locations(tcx: TyCtxt<'_>) -> &FxIndexMap { + static PRIMITIVE_LOCATIONS: OnceCell> = OnceCell::new(); PRIMITIVE_LOCATIONS.get_or_init(|| { - let mut primitive_locations = FxHashMap::default(); + let mut primitive_locations = FxIndexMap::default(); // NOTE: technically this misses crates that are only passed with `--extern` and not loaded when checking the crate. // This is a degenerate case that I don't plan to support. for &crate_num in tcx.crates(()) { @@ -2460,7 +2460,7 @@ pub(crate) struct Impl { } impl Impl { - pub(crate) fn provided_trait_methods(&self, tcx: TyCtxt<'_>) -> FxHashSet { + pub(crate) fn provided_trait_methods(&self, tcx: TyCtxt<'_>) -> FxIndexSet { self.trait_ .as_ref() .map(|t| t.def_id()) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 7a37f5c70a592..2cd69474b1cf0 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; use rustc_session::config::{ self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns, @@ -249,7 +249,7 @@ pub(crate) struct RenderOptions { pub(crate) extern_html_root_takes_precedence: bool, /// A map of the default settings (values are as for DOM storage API). Keys should lack the /// `rustdoc-` prefix. - pub(crate) default_settings: FxHashMap, + pub(crate) default_settings: FxIndexMap, /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages. pub(crate) resource_suffix: String, /// Whether to create an index page in the root of the output directory. If this is true but diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 4c48c075a944d..aaf4c80f99763 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -2,7 +2,7 @@ use std::sync::atomic::AtomicBool; use std::sync::{Arc, LazyLock}; use std::{io, mem}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; @@ -39,7 +39,7 @@ pub(crate) struct DocContext<'tcx> { /// Most of this logic is copied from rustc_lint::late. pub(crate) param_env: ParamEnv<'tcx>, /// Later on moved through `clean::Crate` into `cache` - pub(crate) external_traits: FxHashMap, + pub(crate) external_traits: FxIndexMap, /// Used while populating `external_traits` to ensure we don't process the same trait twice at /// the same time. pub(crate) active_extern_traits: DefIdSet, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 4e7571a480380..d5bc2a93fa807 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -14,7 +14,7 @@ use std::{panic, str}; pub(crate) use make::DocTestBuilder; pub(crate) use markdown::test as test_markdown; use rustc_ast as ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_errors::{ColorConfig, DiagCtxtHandle, ErrorGuaranteed, FatalError}; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LOCAL_CRATE; @@ -213,12 +213,13 @@ pub(crate) fn run( let unused_extern_reports: Vec<_> = std::mem::take(&mut unused_extern_reports.lock().unwrap()); if unused_extern_reports.len() == compiling_test_count { - let extern_names = externs.iter().map(|(name, _)| name).collect::>(); + let extern_names = + externs.iter().map(|(name, _)| name).collect::>(); let mut unused_extern_names = unused_extern_reports .iter() - .map(|uexts| uexts.unused_extern_names.iter().collect::>()) + .map(|uexts| uexts.unused_extern_names.iter().collect::>()) .fold(extern_names, |uextsa, uextsb| { - uextsa.intersection(&uextsb).copied().collect::>() + uextsa.intersection(&uextsb).copied().collect::>() }) .iter() .map(|v| (*v).clone()) @@ -253,7 +254,7 @@ pub(crate) fn run_tests( rustdoc_options: &Arc, unused_extern_reports: &Arc>>, mut standalone_tests: Vec, - mergeable_tests: FxHashMap>, + mergeable_tests: FxIndexMap>, ) { let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1); test_args.insert(0, "rustdoctest".to_string()); @@ -775,7 +776,7 @@ pub(crate) trait DocTestVisitor { struct CreateRunnableDocTests { standalone_tests: Vec, - mergeable_tests: FxHashMap>, + mergeable_tests: FxIndexMap>, rustdoc_options: Arc, opts: GlobalTestOptions, @@ -790,7 +791,7 @@ impl CreateRunnableDocTests { let can_merge_doctests = rustdoc_options.edition >= Edition::Edition2024; CreateRunnableDocTests { standalone_tests: Vec::new(), - mergeable_tests: FxHashMap::default(), + mergeable_tests: FxIndexMap::default(), rustdoc_options: Arc::new(rustdoc_options), opts, visited_tests: FxHashMap::default(), diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 326ca4ee1e68f..942ec8d9936cf 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -1,6 +1,6 @@ use std::fmt::Write; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_span::edition::Edition; use crate::doctest::{ @@ -11,7 +11,7 @@ use crate::html::markdown::{Ignore, LangString}; /// Convenient type to merge compatible doctests into one. pub(crate) struct DocTestRunner { - crate_attrs: FxHashSet, + crate_attrs: FxIndexSet, ids: String, output: String, supports_color: bool, @@ -21,7 +21,7 @@ pub(crate) struct DocTestRunner { impl DocTestRunner { pub(crate) fn new() -> Self { Self { - crate_attrs: FxHashSet::default(), + crate_attrs: FxIndexSet::default(), ids: String::new(), output: String::new(), supports_color: true, diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index db1a0bd0af938..ff0d537b19f75 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,6 +1,6 @@ use std::mem; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; @@ -42,7 +42,7 @@ pub(crate) struct Cache { /// URLs when a type is being linked to. External paths are not located in /// this map because the `External` type itself has all the information /// necessary. - pub(crate) paths: FxHashMap, ItemType)>, + pub(crate) paths: FxIndexMap, ItemType)>, /// Similar to `paths`, but only holds external paths. This is only used for /// generating explicit hyperlinks to other crates. @@ -64,18 +64,18 @@ pub(crate) struct Cache { /// Implementations of a crate should inherit the documentation of the /// parent trait if no extra documentation is specified, and default methods /// should show up in documentation about trait implementations. - pub(crate) traits: FxHashMap, + pub(crate) traits: FxIndexMap, /// When rendering traits, it's often useful to be able to list all /// implementors of the trait, and this mapping is exactly, that: a mapping /// of trait ids to the list of known implementors of the trait - pub(crate) implementors: FxHashMap>, + pub(crate) implementors: FxIndexMap>, /// Cache of where external crate documentation can be found. - pub(crate) extern_locations: FxHashMap, + pub(crate) extern_locations: FxIndexMap, /// Cache of where documentation for primitives can be found. - pub(crate) primitive_locations: FxHashMap, + pub(crate) primitive_locations: FxIndexMap, // Note that external items for which `doc(hidden)` applies to are shown as // non-reachable while local items aren't. This is because we're reusing @@ -118,7 +118,7 @@ pub(crate) struct Cache { // crawl. In order to prevent crashes when looking for notable traits or // when gathering trait documentation on a type, hold impls here while // folding and add them to the cache later on if we find the trait. - orphan_trait_impls: Vec<(DefId, FxHashSet, Impl)>, + orphan_trait_impls: Vec<(DefId, FxIndexSet, Impl)>, /// All intra-doc links resolved so far. /// @@ -376,7 +376,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // Figure out the id of this impl. This may map to a // primitive rather than always to a struct/enum. // Note: matching twice to restrict the lifetime of the `i` borrow. - let mut dids = FxHashSet::default(); + let mut dids = FxIndexSet::default(); match i.for_ { clean::Type::Path { ref path } | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } => { diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 69b3421f8881f..e7ddd4b73b47b 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -8,7 +8,7 @@ use std::collections::VecDeque; use std::fmt::{Display, Write}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_lexer::{Cursor, LiteralKind, TokenKind}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; @@ -34,7 +34,7 @@ pub(crate) struct HrefContext<'a, 'tcx> { /// Decorations are represented as a map from CSS class to vector of character ranges. /// Each range will be wrapped in a span with that class. #[derive(Default)] -pub(crate) struct DecorationInfo(pub(crate) FxHashMap<&'static str, Vec<(u32, u32)>>); +pub(crate) struct DecorationInfo(pub(crate) FxIndexMap<&'static str, Vec<(u32, u32)>>); #[derive(Eq, PartialEq, Clone, Copy)] pub(crate) enum Tooltip { diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 75328e724fea8..fd5275189d661 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -1,5 +1,5 @@ use expect_test::expect_file; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_span::create_default_session_globals_then; use super::{DecorationInfo, write_code}; @@ -73,7 +73,7 @@ fn test_decorations() { let y = 2; let z = 3; let a = 4;"; - let mut decorations = FxHashMap::default(); + let mut decorations = FxIndexMap::default(); decorations.insert("example", vec![(0, 10), (11, 21)]); decorations.insert("example2", vec![(22, 32)]); diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 3684dc42ac73c..31ccfeb38738c 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use rinja::Template; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use super::static_files::{STATIC_FILES, StaticFiles}; use crate::externalfiles::ExternalHtml; @@ -13,7 +13,7 @@ pub(crate) struct Layout { pub(crate) logo: String, pub(crate) favicon: String, pub(crate) external_html: ExternalHtml, - pub(crate) default_settings: FxHashMap, + pub(crate) default_settings: FxIndexMap, pub(crate) krate: String, pub(crate) krate_version: String, /// The given user css file which allow to customize the generated diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 8ae5484feda7a..6c5c58754a88f 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -38,7 +38,7 @@ use pulldown_cmark::{ BrokenLink, BrokenLinkCallback, CodeBlockKind, CowStr, Event, LinkType, OffsetIter, Options, Parser, Tag, TagEnd, html, }; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Diag, DiagMessage}; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; @@ -651,12 +651,12 @@ impl<'a, I: Iterator>> Iterator for SummaryLine<'a, I> { /// references. struct Footnotes<'a, I> { inner: I, - footnotes: FxHashMap>, u16)>, + footnotes: FxIndexMap>, u16)>, } impl<'a, I> Footnotes<'a, I> { fn new(iter: I) -> Self { - Footnotes { inner: iter, footnotes: FxHashMap::default() } + Footnotes { inner: iter, footnotes: FxIndexMap::default() } } fn get_entry(&mut self, key: &str) -> &mut (Vec>, u16) { @@ -694,7 +694,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { Some(e) => return Some(e), None => { if !self.footnotes.is_empty() { - let mut v: Vec<_> = self.footnotes.drain().map(|(_, x)| x).collect(); + let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect(); v.sort_by(|a, b| a.1.cmp(&b.1)); let mut ret = String::from("


    "); for (mut content, id) in v { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index bce3f21890857..dc4d45e592eb7 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use std::sync::mpsc::{Receiver, channel}; use rinja::Template; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -69,16 +69,16 @@ pub(crate) struct Context<'tcx> { /// `true`. pub(crate) include_sources: bool, /// Collection of all types with notable traits referenced in the current module. - pub(crate) types_with_notable_traits: FxHashSet, + pub(crate) types_with_notable_traits: FxIndexSet, /// Field used during rendering, to know if we're inside an inlined item. pub(crate) is_inside_inlined_module: bool, } // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. #[cfg(all(not(windows), target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 160); +rustc_data_structures::static_assert_size!(Context<'_>, 184); #[cfg(all(windows, target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 168); +rustc_data_structures::static_assert_size!(Context<'_>, 192); /// Shared mutable state used in [`Context`] and elsewhere. pub(crate) struct SharedContext<'tcx> { @@ -90,7 +90,7 @@ pub(crate) struct SharedContext<'tcx> { /// creation of the context (contains info like the favicon and added html). pub(crate) layout: layout::Layout, /// The local file sources we've emitted and their respective url-paths. - pub(crate) local_sources: FxHashMap, + pub(crate) local_sources: FxIndexMap, /// Show the memory layout of types in the docs. pub(super) show_type_layout: bool, /// The base-URL of the issue tracker for when an item has been tagged with @@ -567,7 +567,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { deref_id_map: Default::default(), shared: Rc::new(scx), include_sources, - types_with_notable_traits: FxHashSet::default(), + types_with_notable_traits: FxIndexSet::default(), is_inside_inlined_module: false, }; @@ -591,7 +591,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { id_map: IdMap::new(), shared: Rc::clone(&self.shared), include_sources: self.include_sources, - types_with_notable_traits: FxHashSet::default(), + types_with_notable_traits: FxIndexSet::default(), is_inside_inlined_module: self.is_inside_inlined_module, } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 227df0c5f3930..399730a01c82f 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -47,7 +47,7 @@ use std::{fs, str}; use rinja::Template; use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince}; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::ty::print::PrintTraitRefExt; @@ -328,24 +328,24 @@ impl Ord for ItemEntry { #[derive(Debug)] struct AllTypes { - structs: FxHashSet, - enums: FxHashSet, - unions: FxHashSet, - primitives: FxHashSet, - traits: FxHashSet, - macros: FxHashSet, - functions: FxHashSet, - type_aliases: FxHashSet, - statics: FxHashSet, - constants: FxHashSet, - attribute_macros: FxHashSet, - derive_macros: FxHashSet, - trait_aliases: FxHashSet, + structs: FxIndexSet, + enums: FxIndexSet, + unions: FxIndexSet, + primitives: FxIndexSet, + traits: FxIndexSet, + macros: FxIndexSet, + functions: FxIndexSet, + type_aliases: FxIndexSet, + statics: FxIndexSet, + constants: FxIndexSet, + attribute_macros: FxIndexSet, + derive_macros: FxIndexSet, + trait_aliases: FxIndexSet, } impl AllTypes { fn new() -> AllTypes { - let new_set = |cap| FxHashSet::with_capacity_and_hasher(cap, Default::default()); + let new_set = |cap| FxIndexSet::with_capacity_and_hasher(cap, Default::default()); AllTypes { structs: new_set(100), enums: new_set(100), @@ -437,7 +437,7 @@ impl AllTypes { } fn print(self, f: &mut Buffer) { - fn print_entries(f: &mut Buffer, e: &FxHashSet, kind: ItemSection) { + fn print_entries(f: &mut Buffer, e: &FxIndexSet, kind: ItemSection) { if !e.is_empty() { let mut e: Vec<&ItemEntry> = e.iter().collect(); e.sort(); @@ -1151,7 +1151,7 @@ fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Con #[derive(Copy, Clone)] enum AssocItemLink<'a> { Anchor(Option<&'a str>), - GotoSource(ItemId, &'a FxHashSet), + GotoSource(ItemId, &'a FxIndexSet), } impl<'a> AssocItemLink<'a> { @@ -1494,7 +1494,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { for it in &impl_.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { out.push_str("
    "); - let empty_set = FxHashSet::default(); + let empty_set = FxIndexSet::default(); let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); assoc_type( &mut out, @@ -2526,7 +2526,7 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c })() .unwrap_or(DUMMY_SP); - let mut decoration_info = FxHashMap::default(); + let mut decoration_info = FxIndexMap::default(); decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); decoration_info.insert("highlight", byte_ranges); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 38276e4d20c26..3c96f8736814e 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use itertools::Itertools; use rinja::Template; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; @@ -932,7 +932,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let cloned_shared = Rc::clone(&cx.shared); let cache = &cloned_shared.cache; - let mut extern_crates = FxHashSet::default(); + let mut extern_crates = FxIndexSet::default(); if !t.is_object_safe(cx.tcx()) { write_section_heading( diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 660ca3b2594cc..c958458b662c2 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -774,7 +774,7 @@ pub(crate) fn get_function_type_for_search<'tcx>( fn get_index_type( clean_type: &clean::Type, generics: Vec, - rgen: &mut FxHashMap)>, + rgen: &mut FxIndexMap)>, ) -> RenderType { RenderType { id: get_index_type_id(clean_type, rgen), @@ -785,7 +785,7 @@ fn get_index_type( fn get_index_type_id( clean_type: &clean::Type, - rgen: &mut FxHashMap)>, + rgen: &mut FxIndexMap)>, ) -> Option { use rustc_hir::def::{DefKind, Res}; match *clean_type { @@ -854,7 +854,7 @@ fn simplify_fn_type<'a, 'tcx>( tcx: TyCtxt<'tcx>, recurse: usize, res: &mut Vec, - rgen: &mut FxHashMap)>, + rgen: &mut FxIndexMap)>, is_return: bool, cache: &Cache, ) { @@ -1198,7 +1198,7 @@ fn simplify_fn_constraint<'a, 'tcx>( tcx: TyCtxt<'tcx>, recurse: usize, res: &mut Vec<(RenderTypeId, Vec)>, - rgen: &mut FxHashMap)>, + rgen: &mut FxIndexMap)>, is_return: bool, cache: &Cache, ) { @@ -1285,7 +1285,7 @@ fn get_fn_inputs_and_outputs<'tcx>( ) -> (Vec, Vec, Vec>) { let decl = &func.decl; - let mut rgen: FxHashMap)> = Default::default(); + let mut rgen: FxIndexMap)> = Default::default(); let combined_generics; let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_or_trait_generics { diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 7e1ea5cde8320..b314b060368aa 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; @@ -44,7 +44,7 @@ pub(crate) fn collect_spans_and_sources( src_root: &Path, include_sources: bool, generate_link_to_definition: bool, -) -> (FxHashMap, FxHashMap) { +) -> (FxIndexMap, FxHashMap) { let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() }; if include_sources { diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index af94b1042c05c..12b6346005697 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -28,7 +28,7 @@ use indexmap::IndexMap; use itertools::Itertools; use regex::Regex; use rustc_data_structures::flock; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_span::Symbol; @@ -505,8 +505,8 @@ createSrcSidebar();", struct Hierarchy { parent: Weak, elem: OsString, - children: RefCell>>, - elems: RefCell>, + children: RefCell>>, + elems: RefCell>, } impl Hierarchy { @@ -961,8 +961,8 @@ impl Serialize for AliasSerializableImpl { fn get_path_parts( dst: &Path, crates_info: &[CrateInfo], -) -> FxHashMap> { - let mut templates: FxHashMap> = FxHashMap::default(); +) -> FxIndexMap> { + let mut templates: FxIndexMap> = FxIndexMap::default(); crates_info .iter() .map(|crate_info| T::from_crate_info(crate_info).parts.iter()) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 7be9cc0b8858e..f4a0ef01c253b 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use std::{fmt, fs}; use rinja::Template; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -39,15 +39,15 @@ pub(crate) fn collect_local_sources<'tcx>( tcx: TyCtxt<'tcx>, src_root: &Path, krate: &clean::Crate, -) -> FxHashMap { - let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root }; +) -> FxIndexMap { + let mut lsc = LocalSourcesCollector { tcx, local_sources: FxIndexMap::default(), src_root }; lsc.visit_crate(krate); lsc.local_sources } struct LocalSourcesCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, - local_sources: FxHashMap, + local_sources: FxIndexMap, src_root: &'a Path, } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 2be415e2e0ef3..10fc0ea999015 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -20,7 +20,6 @@ #![warn(rustc::internal)] #![allow(clippy::collapsible_if, clippy::collapsible_else_if)] #![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] extern crate thin_vec; @@ -99,7 +98,7 @@ use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL; /// Commas between elements are required (even if the expression is a block). macro_rules! map { ($( $key: expr => $val: expr ),* $(,)*) => {{ - let mut map = ::rustc_data_structures::fx::FxHashMap::default(); + let mut map = ::rustc_data_structures::fx::FxIndexMap::default(); $( map.insert($key, $val); )* map }} diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index def9ac3ce4b5a..2eb0e32b83173 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -9,7 +9,7 @@ use std::ops::Range; use pulldown_cmark::LinkType; use rustc_ast::util::comments::may_have_doc_links; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_errors::{Applicability, Diag, DiagMessage}; use rustc_hir::def::Namespace::*; @@ -778,9 +778,9 @@ fn trait_impls_for<'a>( cx: &mut DocContext<'a>, ty: Ty<'a>, module: DefId, -) -> FxHashSet<(DefId, DefId)> { +) -> FxIndexSet<(DefId, DefId)> { let tcx = cx.tcx; - let mut impls = FxHashSet::default(); + let mut impls = FxIndexSet::default(); for &trait_ in tcx.doc_link_traits_in_scope(module) { tcx.for_each_relevant_impl(trait_, ty, |impl_| { diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index b307e84e42ec2..d1a1f0df3e7ed 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -219,7 +219,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> panic!("collect-trait-impls can't run"); }; - krate.external_traits.extend(cx.external_traits.drain()); + krate.external_traits.extend(cx.external_traits.drain(..)); krate } diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 9d4d203562cb4..a59c43bfbf904 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -3,7 +3,7 @@ use std::fs; use std::path::PathBuf; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir}; @@ -102,8 +102,8 @@ pub(crate) struct CallData { pub(crate) is_bin: bool, } -pub(crate) type FnCallLocations = FxHashMap; -pub(crate) type AllCallLocations = FxHashMap; +pub(crate) type FnCallLocations = FxIndexMap; +pub(crate) type AllCallLocations = FxIndexMap; /// Visitor for traversing a crate and finding instances of function calls. struct FindCalls<'a, 'tcx> { @@ -293,7 +293,7 @@ pub(crate) fn run( debug!("Scrape examples target_crates: {target_crates:?}"); // Run call-finder on all items - let mut calls = FxHashMap::default(); + let mut calls = FxIndexMap::default(); let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates, bin_crate }; tcx.hir().visit_all_item_likes_in_crate(&mut finder); @@ -332,7 +332,7 @@ pub(crate) fn load_call_locations( with_examples: Vec, dcx: DiagCtxtHandle<'_>, ) -> AllCallLocations { - let mut all_calls: AllCallLocations = FxHashMap::default(); + let mut all_calls: AllCallLocations = FxIndexMap::default(); for path in with_examples { let bytes = match fs::read(&path) { Ok(bytes) => bytes, diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 409728a8e867c..2c00bb7e13295 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -1,10 +1,9 @@ -use std::collections::hash_map::Entry; use std::fs; use std::iter::Peekable; use std::path::Path; use std::str::Chars; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_errors::DiagCtxtHandle; #[cfg(test)] @@ -12,8 +11,8 @@ mod tests; #[derive(Debug)] pub(crate) struct CssPath { - pub(crate) rules: FxHashMap, - pub(crate) children: FxHashMap, + pub(crate) rules: FxIndexMap, + pub(crate) children: FxIndexMap, } /// When encountering a `"` or a `'`, returns the whole string, including the quote characters. @@ -120,10 +119,10 @@ fn parse_rules( content: &str, selector: String, iter: &mut Peekable>, - paths: &mut FxHashMap, + paths: &mut FxIndexMap, ) -> Result<(), String> { - let mut rules = FxHashMap::default(); - let mut children = FxHashMap::default(); + let mut rules = FxIndexMap::default(); + let mut children = FxIndexMap::default(); loop { // If the parent isn't a "normal" CSS selector, we only expect sub-selectors and not CSS @@ -146,10 +145,10 @@ fn parse_rules( return Err(format!("Found empty value for rule `{rule}` in selector `{selector}`")); } match rules.entry(rule) { - Entry::Occupied(mut o) => { + IndexEntry::Occupied(mut o) => { *o.get_mut() = value; } - Entry::Vacant(v) => { + IndexEntry::Vacant(v) => { v.insert(value); } } @@ -159,7 +158,7 @@ fn parse_rules( } match paths.entry(selector) { - Entry::Occupied(mut o) => { + IndexEntry::Occupied(mut o) => { let v = o.get_mut(); for (key, value) in rules.into_iter() { v.rules.insert(key, value); @@ -168,7 +167,7 @@ fn parse_rules( v.children.insert(sel, child); } } - Entry::Vacant(v) => { + IndexEntry::Vacant(v) => { v.insert(CssPath { rules, children }); } } @@ -178,7 +177,7 @@ fn parse_rules( pub(crate) fn parse_selectors( content: &str, iter: &mut Peekable>, - paths: &mut FxHashMap, + paths: &mut FxIndexMap, ) -> Result<(), String> { let mut selector = String::new(); @@ -202,17 +201,17 @@ pub(crate) fn parse_selectors( /// The entry point to parse the CSS rules. Every time we encounter a `{`, we then parse the rules /// inside it. -pub(crate) fn load_css_paths(content: &str) -> Result, String> { +pub(crate) fn load_css_paths(content: &str) -> Result, String> { let mut iter = content.chars().peekable(); - let mut paths = FxHashMap::default(); + let mut paths = FxIndexMap::default(); parse_selectors(content, &mut iter, &mut paths)?; Ok(paths) } pub(crate) fn get_differences( - origin: &FxHashMap, - against: &FxHashMap, + origin: &FxIndexMap, + against: &FxIndexMap, v: &mut Vec, ) { for (selector, entry) in origin.iter() { @@ -235,7 +234,7 @@ pub(crate) fn get_differences( pub(crate) fn test_theme_against>( f: &P, - origin: &FxHashMap, + origin: &FxIndexMap, dcx: DiagCtxtHandle<'_>, ) -> (bool, Vec) { let against = match fs::read_to_string(f) From aa75b5f686945b0d2c4927ae3f277cd2216a1350 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sun, 6 Oct 2024 18:08:28 +0800 Subject: [PATCH 1274/1301] Update out-dated link --- compiler/rustc_driver_impl/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_driver_impl/README.md b/compiler/rustc_driver_impl/README.md index 6d7fba36fb3d0..9fa4242de3c82 100644 --- a/compiler/rustc_driver_impl/README.md +++ b/compiler/rustc_driver_impl/README.md @@ -7,4 +7,4 @@ options). For more information about how the driver works, see the [rustc dev guide]. -[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/rustc-driver.html +[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/rustc-driver/intro.html From 3f90bb15ed1672984c826b38d93e6d771c320701 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 6 Oct 2024 20:59:24 +1100 Subject: [PATCH 1275/1301] coverage: Make `BcbCounter` module-private --- compiler/rustc_mir_transform/src/coverage/counters.rs | 8 ++++---- compiler/rustc_mir_transform/src/coverage/mod.rs | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index ef4031c5c034f..a954ca359857d 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -13,13 +13,13 @@ use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverage /// The coverage counter or counter expression associated with a particular /// BCB node or BCB edge. #[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub(super) enum BcbCounter { +enum BcbCounter { Counter { id: CounterId }, Expression { id: ExpressionId }, } impl BcbCounter { - pub(super) fn as_term(&self) -> CovTerm { + fn as_term(&self) -> CovTerm { match *self { BcbCounter::Counter { id, .. } => CovTerm::Counter(id), BcbCounter::Expression { id, .. } => CovTerm::Expression(id), @@ -218,8 +218,8 @@ impl CoverageCounters { } } - pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option { - self.bcb_counters[bcb] + pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option { + self.bcb_counters[bcb].map(|counter| counter.as_term()) } /// Returns an iterator over all the nodes/edges in the coverage graph that diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 104f340c8d63f..97684d796b726 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -153,12 +153,8 @@ fn create_mappings<'tcx>( &source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(), ); - let term_for_bcb = |bcb| { - coverage_counters - .bcb_counter(bcb) - .expect("all BCBs with spans were given counters") - .as_term() - }; + let term_for_bcb = + |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); let region_for_span = |span: Span| make_source_region(source_map, file_name, span, body_span); // Fully destructure the mappings struct to make sure we don't miss any kinds. From c6e4fcd4faaa57fa059165a5c24b119d1e8f0f7f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 5 Oct 2024 17:08:07 +1000 Subject: [PATCH 1276/1301] coverage: Have MakeBcbCounters own its CoverageCounters --- .../src/coverage/counters.rs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index a954ca359857d..95c4d9f88fbe9 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -80,19 +80,20 @@ impl CoverageCounters { basic_coverage_blocks: &CoverageGraph, bcb_needs_counter: impl Fn(BasicCoverageBlock) -> bool, ) -> Self { - let num_bcbs = basic_coverage_blocks.num_nodes(); + let mut counters = MakeBcbCounters::new(basic_coverage_blocks); + counters.make_bcb_counters(bcb_needs_counter); - let mut this = Self { + counters.coverage_counters + } + + fn with_num_bcbs(num_bcbs: usize) -> Self { + Self { counter_increment_sites: IndexVec::new(), bcb_counters: IndexVec::from_elem_n(None, num_bcbs), bcb_edge_counters: FxHashMap::default(), expressions: IndexVec::new(), expressions_memo: FxHashMap::default(), - }; - - MakeBcbCounters::new(&mut this, basic_coverage_blocks).make_bcb_counters(bcb_needs_counter); - - this + } } /// Shared helper used by [`Self::make_phys_node_counter`] and @@ -265,16 +266,16 @@ impl CoverageCounters { /// Helper struct that allows counter creation to inspect the BCB graph. struct MakeBcbCounters<'a> { - coverage_counters: &'a mut CoverageCounters, + coverage_counters: CoverageCounters, basic_coverage_blocks: &'a CoverageGraph, } impl<'a> MakeBcbCounters<'a> { - fn new( - coverage_counters: &'a mut CoverageCounters, - basic_coverage_blocks: &'a CoverageGraph, - ) -> Self { - Self { coverage_counters, basic_coverage_blocks } + fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self { + Self { + coverage_counters: CoverageCounters::with_num_bcbs(basic_coverage_blocks.num_nodes()), + basic_coverage_blocks, + } } fn make_bcb_counters(&mut self, bcb_needs_counter: impl Fn(BasicCoverageBlock) -> bool) { From ab154e69993f15c4f37842dd2c2dfc6750af6a8a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 6 Oct 2024 22:44:14 +1100 Subject: [PATCH 1277/1301] coverage: Store `bcb_needs_counter` in a field as a bitset This makes it possible for other parts of counter-assignment to check whether a node is guaranteed to end up with some kind of counter. Switching from `impl Fn` to a concrete `&BitSet` just avoids the hassle of trying to store a closure in a struct field, and currently there's no foreseeable need for this information to not be a bitset. --- .../src/coverage/counters.rs | 19 +++++++++++++------ .../rustc_mir_transform/src/coverage/mod.rs | 3 +-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 95c4d9f88fbe9..9408815675692 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -4,6 +4,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; +use rustc_index::bit_set::BitSet; use rustc_middle::bug; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; use tracing::{debug, debug_span, instrument}; @@ -78,10 +79,10 @@ impl CoverageCounters { /// counters or counter expressions for nodes and edges as required. pub(super) fn make_bcb_counters( basic_coverage_blocks: &CoverageGraph, - bcb_needs_counter: impl Fn(BasicCoverageBlock) -> bool, + bcb_needs_counter: &BitSet, ) -> Self { - let mut counters = MakeBcbCounters::new(basic_coverage_blocks); - counters.make_bcb_counters(bcb_needs_counter); + let mut counters = MakeBcbCounters::new(basic_coverage_blocks, bcb_needs_counter); + counters.make_bcb_counters(); counters.coverage_counters } @@ -268,17 +269,23 @@ impl CoverageCounters { struct MakeBcbCounters<'a> { coverage_counters: CoverageCounters, basic_coverage_blocks: &'a CoverageGraph, + bcb_needs_counter: &'a BitSet, } impl<'a> MakeBcbCounters<'a> { - fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self { + fn new( + basic_coverage_blocks: &'a CoverageGraph, + bcb_needs_counter: &'a BitSet, + ) -> Self { + assert_eq!(basic_coverage_blocks.num_nodes(), bcb_needs_counter.domain_size()); Self { coverage_counters: CoverageCounters::with_num_bcbs(basic_coverage_blocks.num_nodes()), basic_coverage_blocks, + bcb_needs_counter, } } - fn make_bcb_counters(&mut self, bcb_needs_counter: impl Fn(BasicCoverageBlock) -> bool) { + fn make_bcb_counters(&mut self) { debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); // Traverse the coverage graph, ensuring that every node that needs a @@ -291,7 +298,7 @@ impl<'a> MakeBcbCounters<'a> { let mut traversal = TraverseCoverageGraphWithLoops::new(self.basic_coverage_blocks); while let Some(bcb) = traversal.next() { let _span = debug_span!("traversal", ?bcb).entered(); - if bcb_needs_counter(bcb) { + if self.bcb_needs_counter.contains(bcb) { self.make_node_counter_and_out_edge_counters(&traversal, bcb); } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 97684d796b726..79482ba39198e 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -94,9 +94,8 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: return; } - let bcb_has_counter_mappings = |bcb| bcbs_with_counter_mappings.contains(bcb); let coverage_counters = - CoverageCounters::make_bcb_counters(&basic_coverage_blocks, bcb_has_counter_mappings); + CoverageCounters::make_bcb_counters(&basic_coverage_blocks, &bcbs_with_counter_mappings); let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &coverage_counters); if mappings.is_empty() { From 0df736c2a9a7b5ceddd90bfdd6aa991801145a54 Mon Sep 17 00:00:00 2001 From: "Naveen R. Iyer" Date: Sat, 28 Sep 2024 20:27:51 -0500 Subject: [PATCH 1278/1301] add clarity for custom path installation install.sysconfdir is another value, in addition to install.prefix, that could be set for custom path installation. Signed-off-by: Naveen R. Iyer --- INSTALL.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index ded0b59fc6cd3..74fcc58348b43 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -79,9 +79,23 @@ See [the rustc-dev-guide for more info][sysllvm]. ./configure ``` - If you plan to use `x.py install` to create an installation, it is - recommended that you set the `prefix` value in the `[install]` section to a - directory: `./configure --set install.prefix=` + If you plan to use `x.py install` to create an installation, you can either + set `DESTDIR` environment variable to your custom directory path: + + ```bash + export DESTDIR= + ``` + + or set `prefix` and `sysconfdir` in the `[install]` section to your custom + directory path: + + ```sh + ./configure --set install.prefix= --set install.sysconfdir= + ``` + + When the `DESTDIR` environment variable is present, the `prefix` and + `sysconfdir` values are combined with the path from the `DESTDIR` + environment variable. 3. Build and install: From aa5bbf05f424ae7bfb2bd859141c7e8553120d87 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 4 Aug 2024 16:42:37 +0200 Subject: [PATCH 1279/1301] implement `naked_asm` macro --- compiler/rustc_builtin_macros/messages.ftl | 24 +-- compiler/rustc_builtin_macros/src/asm.rs | 159 ++++++++++++++------ compiler/rustc_builtin_macros/src/errors.rs | 27 ++-- library/core/src/arch.rs | 14 ++ src/tools/rustfmt/src/parse/macros/asm.rs | 4 +- 5 files changed, 160 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 9695df9c87e8a..b25892242b54b 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -12,9 +12,9 @@ builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}` builtin_macros_asm_expected_comma = expected token: `,` .label = expected `,` -builtin_macros_asm_expected_other = expected operand, {$is_global_asm -> - [true] options - *[false] clobber_abi, options +builtin_macros_asm_expected_other = expected operand, {$is_inline_asm -> + [false] options + *[true] clobber_abi, options }, or additional template string builtin_macros_asm_expected_string_literal = expected string literal @@ -51,6 +51,15 @@ builtin_macros_asm_sym_no_path = expected a path for argument to `sym` builtin_macros_asm_underscore_input = _ cannot be used for input operands +builtin_macros_asm_unsupported_clobber_abi = `clobber_abi` cannot be used with `{$macro_name}!` + +builtin_macros_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!` + .label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it + +builtin_macros_asm_unsupported_option = the `{$symbol}` option cannot be used with `{$macro_name}!` + .label = the `{$symbol}` option is not meaningful for global-scoped inline assembly + .suggestion = remove this option + builtin_macros_assert_missing_comma = unexpected string literal .suggestion = try adding a comma @@ -194,15 +203,6 @@ builtin_macros_format_unused_args = multiple unused formatting arguments builtin_macros_format_use_positional = consider using a positional formatting argument instead -builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!` - -builtin_macros_global_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `global_asm!` - .label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it - -builtin_macros_global_asm_unsupported_option = the `{$symbol}` option cannot be used with `global_asm!` - .label = the `{$symbol}` option is not meaningful for global-scoped inline assembly - .suggestion = remove this option - builtin_macros_invalid_crate_attribute = invalid crate attribute builtin_macros_multiple_default_attrs = multiple `#[default]` attributes diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 75dbb3d8e6abb..2019c42e296f5 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -37,29 +37,66 @@ pub struct AsmArgs { /// - `Ok(true)` if the current token matches the keyword, and was expected /// - `Ok(false)` if the current token does not match the keyword /// - `Err(_)` if the current token matches the keyword, but was not expected -fn eat_operand_keyword<'a>(p: &mut Parser<'a>, symbol: Symbol, expect: bool) -> PResult<'a, bool> { - if expect { +fn eat_operand_keyword<'a>( + p: &mut Parser<'a>, + symbol: Symbol, + asm_macro: AsmMacro, +) -> PResult<'a, bool> { + if matches!(asm_macro, AsmMacro::Asm) { Ok(p.eat_keyword(symbol)) } else { let span = p.token.span; if p.eat_keyword_noexpect(symbol) { // in gets printed as `r#in` otherwise let symbol = if symbol == kw::In { "in" } else { symbol.as_str() }; - Err(p.dcx().create_err(errors::GlobalAsmUnsupportedOperand { span, symbol })) + Err(p.dcx().create_err(errors::AsmUnsupportedOperand { + span, + symbol, + macro_name: asm_macro.macro_name(), + })) } else { Ok(false) } } } +// Public for rustfmt consumption. +#[derive(Copy, Clone)] +pub enum AsmMacro { + /// The `asm!` macro + Asm, + /// The `global_asm!` macro + GlobalAsm, + /// The `naked_asm!` macro + NakedAsm, +} + +impl AsmMacro { + const fn macro_name(&self) -> &'static str { + match self { + AsmMacro::Asm => "asm", + AsmMacro::GlobalAsm => "global_asm", + AsmMacro::NakedAsm => "naked_asm", + } + } + + const fn is_supported_option(&self, option: ast::InlineAsmOptions) -> bool { + match self { + AsmMacro::Asm => true, + AsmMacro::GlobalAsm => ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option), + AsmMacro::NakedAsm => ast::InlineAsmOptions::NAKED_OPTIONS.contains(option), + } + } +} + fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream, - is_global_asm: bool, + asm_macro: AsmMacro, ) -> PResult<'a, AsmArgs> { let mut p = ecx.new_parser_from_tts(tts); - parse_asm_args(&mut p, sp, is_global_asm) + parse_asm_args(&mut p, sp, asm_macro) } // Primarily public for rustfmt consumption. @@ -67,7 +104,7 @@ fn parse_args<'a>( pub fn parse_asm_args<'a>( p: &mut Parser<'a>, sp: Span, - is_global_asm: bool, + asm_macro: AsmMacro, ) -> PResult<'a, AsmArgs> { let dcx = p.dcx(); @@ -110,7 +147,7 @@ pub fn parse_asm_args<'a>( // Parse options if p.eat_keyword(sym::options) { - parse_options(p, &mut args, is_global_asm)?; + parse_options(p, &mut args, asm_macro)?; allow_templates = false; continue; } @@ -129,7 +166,7 @@ pub fn parse_asm_args<'a>( }; let mut explicit_reg = false; - let op = if eat_operand_keyword(p, kw::In, !is_global_asm)? { + let op = if eat_operand_keyword(p, kw::In, asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); @@ -137,15 +174,15 @@ pub fn parse_asm_args<'a>( } let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, sym::out, !is_global_asm)? { + } else if eat_operand_keyword(p, sym::out, asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, sym::lateout, !is_global_asm)? { + } else if eat_operand_keyword(p, sym::lateout, asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, sym::inout, !is_global_asm)? { + } else if eat_operand_keyword(p, sym::inout, asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); @@ -159,7 +196,7 @@ pub fn parse_asm_args<'a>( } else { ast::InlineAsmOperand::InOut { reg, expr, late: false } } - } else if eat_operand_keyword(p, sym::inlateout, !is_global_asm)? { + } else if eat_operand_keyword(p, sym::inlateout, asm_macro)? { let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); @@ -173,7 +210,7 @@ pub fn parse_asm_args<'a>( } else { ast::InlineAsmOperand::InOut { reg, expr, late: true } } - } else if eat_operand_keyword(p, sym::label, !is_global_asm)? { + } else if eat_operand_keyword(p, sym::label, asm_macro)? { let block = p.parse_block()?; ast::InlineAsmOperand::Label { block } } else if p.eat_keyword(kw::Const) { @@ -205,7 +242,7 @@ pub fn parse_asm_args<'a>( _ => { let err = dcx.create_err(errors::AsmExpectedOther { span: template.span, - is_global_asm, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), }); return Err(err); } @@ -301,20 +338,25 @@ pub fn parse_asm_args<'a>( dcx.emit_err(errors::AsmMayUnwind { labels_sp }); } - if args.clobber_abis.len() > 0 { - if is_global_asm { - let err = dcx.create_err(errors::GlobalAsmClobberAbi { - spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(), - }); + if !args.clobber_abis.is_empty() { + match asm_macro { + AsmMacro::GlobalAsm | AsmMacro::NakedAsm => { + let err = dcx.create_err(errors::AsmUnsupportedClobberAbi { + spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + macro_name: asm_macro.macro_name(), + }); - // Bail out now since this is likely to confuse later stages - return Err(err); - } - if !regclass_outputs.is_empty() { - dcx.emit_err(errors::AsmClobberNoReg { - spans: regclass_outputs, - clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(), - }); + // Bail out now since this is likely to confuse later stages + return Err(err); + } + AsmMacro::Asm => { + if !regclass_outputs.is_empty() { + dcx.emit_err(errors::AsmClobberNoReg { + spans: regclass_outputs, + clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + }); + } + } } } @@ -335,10 +377,15 @@ fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { /// /// This function must be called immediately after the option token is parsed. /// Otherwise, the suggestion will be incorrect. -fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) { +fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) { // Tool-only output let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::GlobalAsmUnsupportedOption { span, symbol, full_span }); + p.dcx().emit_err(errors::AsmUnsupportedOption { + span, + symbol, + full_span, + macro_name: asm_macro.macro_name(), + }); } /// Try to set the provided option in the provided `AsmArgs`. @@ -349,12 +396,12 @@ fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) { fn try_set_option<'a>( p: &Parser<'a>, args: &mut AsmArgs, - is_global_asm: bool, + asm_macro: AsmMacro, symbol: Symbol, option: ast::InlineAsmOptions, ) { - if is_global_asm && !ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) { - err_unsupported_option(p, symbol, p.prev_token.span); + if !asm_macro.is_supported_option(option) { + err_unsupported_option(p, asm_macro, symbol, p.prev_token.span); } else if args.options.contains(option) { err_duplicate_option(p, symbol, p.prev_token.span); } else { @@ -365,7 +412,7 @@ fn try_set_option<'a>( fn parse_options<'a>( p: &mut Parser<'a>, args: &mut AsmArgs, - is_global_asm: bool, + asm_macro: AsmMacro, ) -> PResult<'a, ()> { let span_start = p.prev_token.span; @@ -386,15 +433,14 @@ fn parse_options<'a>( 'blk: { for (symbol, option) in OPTIONS { - let kw_matched = - if !is_global_asm || ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) { - p.eat_keyword(symbol) - } else { - p.eat_keyword_noexpect(symbol) - }; + let kw_matched = if asm_macro.is_supported_option(option) { + p.eat_keyword(symbol) + } else { + p.eat_keyword_noexpect(symbol) + }; if kw_matched { - try_set_option(p, args, is_global_asm, symbol, option); + try_set_option(p, args, asm_macro, symbol, option); break 'blk; } } @@ -797,7 +843,7 @@ pub(super) fn expand_asm<'cx>( sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(match parse_args(ecx, sp, tts, false) { + ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) { Ok(args) => { let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else { return ExpandResult::Retry(()); @@ -865,7 +911,7 @@ pub(super) fn expand_global_asm<'cx>( sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(match parse_args(ecx, sp, tts, true) { + ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) { Ok(args) => { let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args) else { @@ -894,3 +940,32 @@ pub(super) fn expand_global_asm<'cx>( } }) } + +pub(super) fn expand_naked_asm<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) { + Ok(args) => { + let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else { + return ExpandResult::Retry(()); + }; + let expr = match mac { + Ok(inline_asm) => P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::InlineAsm(P(inline_asm)), + span: sp, + attrs: ast::AttrVec::new(), + tokens: None, + }), + Err(guar) => DummyResult::raw_expr(sp, Some(guar)), + }; + MacEager::expr(expr) + } + Err(err) => { + let guar = err.emit(); + DummyResult::any(sp, guar) + } + }) +} diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 4fffffb91c8b4..f13ca224a45d4 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -751,7 +751,7 @@ pub(crate) struct AsmExpectedOther { #[primary_span] #[label(builtin_macros_asm_expected_other)] pub(crate) span: Span, - pub(crate) is_global_asm: bool, + pub(crate) is_inline_asm: bool, } #[derive(Diagnostic)] @@ -799,13 +799,6 @@ pub(crate) struct AsmMayUnwind { pub(crate) labels_sp: Vec, } -#[derive(Diagnostic)] -#[diag(builtin_macros_global_asm_clobber_abi)] -pub(crate) struct GlobalAsmClobberAbi { - #[primary_span] - pub(crate) spans: Vec, -} - pub(crate) struct AsmClobberNoReg { pub(crate) spans: Vec, pub(crate) clobbers: Vec, @@ -841,23 +834,33 @@ pub(crate) struct AsmOptAlreadyprovided { } #[derive(Diagnostic)] -#[diag(builtin_macros_global_asm_unsupported_option)] -pub(crate) struct GlobalAsmUnsupportedOption { +#[diag(builtin_macros_asm_unsupported_option)] +pub(crate) struct AsmUnsupportedOption { #[primary_span] #[label] pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] pub(crate) full_span: Span, + pub(crate) macro_name: &'static str, } #[derive(Diagnostic)] -#[diag(builtin_macros_global_asm_unsupported_operand)] -pub(crate) struct GlobalAsmUnsupportedOperand<'a> { +#[diag(builtin_macros_asm_unsupported_operand)] +pub(crate) struct AsmUnsupportedOperand<'a> { #[primary_span] #[label] pub(crate) span: Span, pub(crate) symbol: &'a str, + pub(crate) macro_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_asm_unsupported_clobber_abi)] +pub(crate) struct AsmUnsupportedClobberAbi { + #[primary_span] + pub(crate) spans: Vec, + pub(crate) macro_name: &'static str, } #[derive(Diagnostic)] diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 4945c045bc635..f1a0739128cdb 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -77,3 +77,17 @@ pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } + +/// Inline assembly used in combination with `#[naked]` functions. +/// +/// Refer to [Rust By Example] for a usage guide and the [reference] for +/// detailed information about the syntax and available options. +/// +/// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html +/// [reference]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html +#[unstable(feature = "naked_functions", issue = "90957")] +#[rustc_builtin_macro] +#[cfg(not(bootstrap))] +pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ +} diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 5ee083da5399d..5d30171e29543 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,5 +1,5 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args}; +use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs, AsmMacro}; use crate::rewrite::RewriteContext; @@ -7,5 +7,5 @@ use crate::rewrite::RewriteContext; pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { let ts = mac.args.tokens.clone(); let mut parser = super::build_parser(context, ts); - parse_asm_args(&mut parser, mac.span(), false).ok() + parse_asm_args(&mut parser, mac.span(), AsmMacro::Asm).ok() } From 0aec55504cf188d99b2047e49aad9027604df89e Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 4 Aug 2024 16:43:12 +0200 Subject: [PATCH 1280/1301] use `naked_asm!` in `tests/ui/asm/naked-functions.rs` --- tests/ui/asm/naked-functions.rs | 72 +++++------ tests/ui/asm/naked-functions.stderr | 184 ++++++++++++---------------- 2 files changed, 117 insertions(+), 139 deletions(-) diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index 116a84506c570..86c89ea1262fd 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -6,7 +6,7 @@ #![feature(asm_unwind, linkage)] #![crate_type = "lib"] -use std::arch::asm; +use std::arch::naked_asm; #[repr(C)] pub struct P { @@ -25,7 +25,7 @@ pub unsafe extern "C" fn patterns( P { x, y }: P, //~^ ERROR patterns not allowed in naked function parameters ) { - asm!("", options(noreturn)) + naked_asm!("", options(noreturn)) } #[naked] @@ -38,9 +38,8 @@ pub unsafe extern "C" fn inc(a: u32) -> u32 { #[naked] #[allow(asm_sub_register)] pub unsafe extern "C" fn inc_asm(a: u32) -> u32 { - asm!("/* {0} */", in(reg) a, options(noreturn)); - //~^ ERROR referencing function parameters is not allowed in naked functions - //~| ERROR only `const` and `sym` operands are supported in naked functions + naked_asm!("/* {0} */", in(reg) a, options(noreturn)) + //~^ ERROR the `in` operand cannot be used with `naked_asm!` } #[naked] @@ -59,10 +58,9 @@ pub unsafe extern "C" fn unsupported_operands() { let mut e = 0usize; const F: usize = 0usize; static G: usize = 0usize; - asm!("/* {0} {1} {2} {3} {4} {5} {6} */", - //~^ ERROR asm in naked functions must use `noreturn` option + naked_asm!("/* {0} {1} {2} {3} {4} {5} {6} */", in(reg) a, - //~^ ERROR only `const` and `sym` operands are supported in naked functions + //~^ ERROR the `in` operand cannot be used with `naked_asm!` inlateout(reg) b, inout(reg) c, lateout(reg) d, @@ -81,13 +79,13 @@ pub extern "C" fn missing_assembly() { pub extern "C" fn too_many_asm_blocks() { //~^ ERROR naked functions must contain a single asm block unsafe { - asm!(""); + naked_asm!(""); //~^ ERROR asm in naked functions must use `noreturn` option - asm!(""); + naked_asm!(""); //~^ ERROR asm in naked functions must use `noreturn` option - asm!(""); + naked_asm!(""); //~^ ERROR asm in naked functions must use `noreturn` option - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); } } @@ -103,40 +101,42 @@ pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { #[naked] unsafe extern "C" fn invalid_options() { - asm!("", options(nomem, preserves_flags, noreturn)); - //~^ ERROR asm options unsupported in naked functions: `nomem`, `preserves_flags` + naked_asm!("", options(nomem, preserves_flags, noreturn)); + //~^ ERROR the `nomem` option cannot be used with `naked_asm!` + //~| ERROR the `preserves_flags` option cannot be used with `naked_asm!` } #[naked] unsafe extern "C" fn invalid_options_continued() { - asm!("", options(readonly, nostack), options(pure)); - //~^ ERROR asm with the `pure` option must have at least one output - //~| ERROR asm options unsupported in naked functions: `pure`, `readonly`, `nostack` + naked_asm!("", options(readonly, nostack), options(pure)); + //~^ ERROR the `readonly` option cannot be used with `naked_asm!` + //~| ERROR the `nostack` option cannot be used with `naked_asm!` + //~| ERROR the `pure` option cannot be used with `naked_asm!` //~| ERROR asm in naked functions must use `noreturn` option } #[naked] unsafe extern "C" fn invalid_may_unwind() { - asm!("", options(noreturn, may_unwind)); - //~^ ERROR asm options unsupported in naked functions: `may_unwind` + naked_asm!("", options(noreturn, may_unwind)); + //~^ ERROR the `may_unwind` option cannot be used with `naked_asm!` } #[naked] pub unsafe fn default_abi() { //~^ WARN Rust ABI is unsupported in naked functions - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); } #[naked] pub unsafe fn rust_abi() { //~^ WARN Rust ABI is unsupported in naked functions - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); } #[naked] pub extern "C" fn valid_a() -> T { unsafe { - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); } } @@ -145,7 +145,7 @@ pub extern "C" fn valid_b() { unsafe { { { - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); }; }; } @@ -153,13 +153,13 @@ pub extern "C" fn valid_b() { #[naked] pub unsafe extern "C" fn valid_c() { - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); } #[cfg(target_arch = "x86_64")] #[naked] pub unsafe extern "C" fn valid_att_syntax() { - asm!("", options(noreturn, att_syntax)); + naked_asm!("", options(noreturn, att_syntax)); } #[naked] @@ -173,12 +173,12 @@ pub unsafe extern "C" fn allow_compile_error(a: u32) -> u32 { pub unsafe extern "C" fn allow_compile_error_and_asm(a: u32) -> u32 { compile_error!("this is a user specified error"); //~^ ERROR this is a user specified error - asm!("", options(noreturn)) + naked_asm!("", options(noreturn)) } #[naked] pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 { - asm!(invalid_syntax) + naked_asm!(invalid_syntax) //~^ ERROR asm template must be a string literal } @@ -186,7 +186,7 @@ pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 { #[cfg_attr(target_pointer_width = "64", no_mangle)] #[naked] pub unsafe extern "C" fn compatible_cfg_attributes() { - asm!("", options(noreturn, att_syntax)); + naked_asm!("", options(noreturn, att_syntax)); } #[allow(dead_code)] @@ -195,20 +195,20 @@ pub unsafe extern "C" fn compatible_cfg_attributes() { #[forbid(dead_code)] #[naked] pub unsafe extern "C" fn compatible_diagnostic_attributes() { - asm!("", options(noreturn, raw)); + naked_asm!("", options(noreturn, raw)); } #[deprecated = "test"] #[naked] pub unsafe extern "C" fn compatible_deprecated_attributes() { - asm!("", options(noreturn, raw)); + naked_asm!("", options(noreturn, raw)); } #[cfg(target_arch = "x86_64")] #[must_use] #[naked] pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 { - asm!( + naked_asm!( " mov rax, 42 ret @@ -222,20 +222,20 @@ pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 { #[no_mangle] #[naked] pub unsafe extern "C" fn compatible_ffi_attributes_1() { - asm!("", options(noreturn, raw)); + naked_asm!("", options(noreturn, raw)); } #[cold] #[naked] pub unsafe extern "C" fn compatible_codegen_attributes() { - asm!("", options(noreturn, raw)); + naked_asm!("", options(noreturn, raw)); } #[cfg(target_arch = "x86_64")] #[target_feature(enable = "sse2")] #[naked] pub unsafe extern "C" fn compatible_target_feature() { - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); } #[doc = "foo bar baz"] @@ -244,11 +244,11 @@ pub unsafe extern "C" fn compatible_target_feature() { #[doc(alias = "ADocAlias")] #[naked] pub unsafe extern "C" fn compatible_doc_attributes() { - asm!("", options(noreturn, raw)); + naked_asm!("", options(noreturn, raw)); } #[linkage = "external"] #[naked] pub unsafe extern "C" fn compatible_linkage() { - asm!("", options(noreturn, raw)); + naked_asm!("", options(noreturn, raw)); } diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index 93c02e2fbef82..22c1946a960c1 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -1,8 +1,50 @@ -error: asm with the `pure` option must have at least one output - --> $DIR/naked-functions.rs:112:14 +error: the `in` operand cannot be used with `naked_asm!` + --> $DIR/naked-functions.rs:41:29 | -LL | asm!("", options(readonly, nostack), options(pure)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ +LL | naked_asm!("/* {0} */", in(reg) a, options(noreturn)) + | ^^ 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:62:10 + | +LL | in(reg) a, + | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it + +error: the `nomem` option cannot be used with `naked_asm!` + --> $DIR/naked-functions.rs:104:28 + | +LL | naked_asm!("", options(nomem, preserves_flags, noreturn)); + | ^^^^^ 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:104:35 + | +LL | naked_asm!("", options(nomem, preserves_flags, noreturn)); + | ^^^^^^^^^^^^^^^ 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:111: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:111: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:111: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:120:38 + | +LL | naked_asm!("", options(noreturn, 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:168:5 @@ -17,10 +59,10 @@ LL | compile_error!("this is a user specified error"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/naked-functions.rs:181:10 + --> $DIR/naked-functions.rs:181:16 | -LL | asm!(invalid_syntax) - | ^^^^^^^^^^^^^^ +LL | naked_asm!(invalid_syntax) + | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters --> $DIR/naked-functions.rs:19:5 @@ -63,22 +105,8 @@ LL | LL | a + 1 | ----- non-asm is unsupported in naked functions -error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:41:31 - | -LL | asm!("/* {0} */", in(reg) a, options(noreturn)); - | ^ - | - = help: follow the calling convention in asm block to use parameters - -error[E0787]: only `const` and `sym` operands are supported in naked functions - --> $DIR/naked-functions.rs:41:23 - | -LL | asm!("/* {0} */", in(reg) a, options(noreturn)); - | ^^^^^^^^^ - error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:47:1 + --> $DIR/naked-functions.rs:46:1 | LL | pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -86,40 +114,8 @@ LL | LL | (|| a + 1)() | ------------ non-asm is unsupported in naked functions -error[E0787]: only `const` and `sym` operands are supported in naked functions - --> $DIR/naked-functions.rs:64:10 - | -LL | in(reg) a, - | ^^^^^^^^^ -LL | -LL | inlateout(reg) b, - | ^^^^^^^^^^^^^^^^ -LL | inout(reg) c, - | ^^^^^^^^^^^^ -LL | lateout(reg) d, - | ^^^^^^^^^^^^^^ -LL | out(reg) e, - | ^^^^^^^^^^ - -error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:62:5 - | -LL | / asm!("/* {0} {1} {2} {3} {4} {5} {6} */", -LL | | -LL | | in(reg) a, -LL | | -... | -LL | | sym G, -LL | | ); - | |_____^ - | -help: consider specifying that the asm block is responsible for returning from the function - | -LL | sym G, options(noreturn), - | +++++++++++++++++++ - error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:53:1 + --> $DIR/naked-functions.rs:52:1 | LL | pub unsafe extern "C" fn unsupported_operands() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,61 +132,61 @@ LL | let mut e = 0usize; | ------------------- non-asm is unsupported in naked functions error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:76:1 + --> $DIR/naked-functions.rs:74:1 | LL | pub extern "C" fn missing_assembly() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:84:9 + --> $DIR/naked-functions.rs:82:9 | -LL | asm!(""); - | ^^^^^^^^ +LL | naked_asm!(""); + | ^^^^^^^^^^^^^^ | help: consider specifying that the asm block is responsible for returning from the function | -LL | asm!("", options(noreturn)); - | +++++++++++++++++++ +LL | naked_asm!("", options(noreturn)); + | +++++++++++++++++++ error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:86:9 + --> $DIR/naked-functions.rs:84:9 | -LL | asm!(""); - | ^^^^^^^^ +LL | naked_asm!(""); + | ^^^^^^^^^^^^^^ | help: consider specifying that the asm block is responsible for returning from the function | -LL | asm!("", options(noreturn)); - | +++++++++++++++++++ +LL | naked_asm!("", options(noreturn)); + | +++++++++++++++++++ error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:88:9 + --> $DIR/naked-functions.rs:86:9 | -LL | asm!(""); - | ^^^^^^^^ +LL | naked_asm!(""); + | ^^^^^^^^^^^^^^ | help: consider specifying that the asm block is responsible for returning from the function | -LL | asm!("", options(noreturn)); - | +++++++++++++++++++ +LL | naked_asm!("", options(noreturn)); + | +++++++++++++++++++ error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:81:1 + --> $DIR/naked-functions.rs:79:1 | LL | pub extern "C" fn too_many_asm_blocks() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... -LL | asm!(""); - | -------- multiple asm blocks are unsupported in naked functions +LL | naked_asm!(""); + | -------------- multiple asm blocks are unsupported in naked functions LL | -LL | asm!(""); - | -------- multiple asm blocks are unsupported in naked functions +LL | naked_asm!(""); + | -------------- multiple asm blocks are unsupported in naked functions LL | -LL | asm!("", options(noreturn)); - | --------------------------- multiple asm blocks are unsupported in naked functions +LL | naked_asm!("", options(noreturn)); + | --------------------------------- multiple asm blocks are unsupported in naked functions error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:98:11 + --> $DIR/naked-functions.rs:96:11 | LL | *&y | ^ @@ -198,7 +194,7 @@ LL | *&y = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:96:5 + --> $DIR/naked-functions.rs:94:5 | LL | pub extern "C" fn inner(y: usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,34 +202,16 @@ LL | LL | *&y | --- non-asm is unsupported in naked functions -error[E0787]: asm options unsupported in naked functions: `nomem`, `preserves_flags` - --> $DIR/naked-functions.rs:106:5 - | -LL | asm!("", options(nomem, preserves_flags, noreturn)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0787]: asm options unsupported in naked functions: `pure`, `readonly`, `nostack` - --> $DIR/naked-functions.rs:112:5 - | -LL | asm!("", options(readonly, nostack), options(pure)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:112:5 + --> $DIR/naked-functions.rs:111:5 | -LL | asm!("", options(readonly, nostack), options(pure)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | naked_asm!("", options(readonly, nostack), options(pure)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider specifying that the asm block is responsible for returning from the function | -LL | asm!("", options(noreturn), options(readonly, nostack), options(pure)); - | +++++++++++++++++++ - -error[E0787]: asm options unsupported in naked functions: `may_unwind` - --> $DIR/naked-functions.rs:120:5 - | -LL | asm!("", options(noreturn, may_unwind)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | naked_asm!("", options(noreturn), options(readonly, nostack), options(pure)); + | +++++++++++++++++++ warning: Rust ABI is unsupported in naked functions --> $DIR/naked-functions.rs:125:1 From 47b42bef3226bb71506988572c73ec1b6f99a730 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 4 Aug 2024 16:45:48 +0200 Subject: [PATCH 1281/1301] use `naked_asm!` in naked-function tests --- tests/codegen/naked-fn/naked-functions.rs | 6 +-- tests/ui/asm/naked-functions-ffi.rs | 4 +- tests/ui/asm/naked-functions-inline.rs | 2 +- .../ui/asm/naked-functions-instruction-set.rs | 6 +-- tests/ui/asm/naked-functions-testattrs.rs | 10 ++--- .../asm/naked-functions-unused.aarch64.stderr | 16 +++---- tests/ui/asm/naked-functions-unused.rs | 42 ++++++++++++++----- .../asm/naked-functions-unused.x86_64.stderr | 16 +++---- .../feature-gate-naked_functions.rs | 4 +- 9 files changed, 63 insertions(+), 43 deletions(-) diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 307745a921cf6..2b2625429f8e0 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -4,7 +4,7 @@ #![crate_type = "lib"] #![feature(naked_functions)] -use std::arch::asm; +use std::arch::naked_asm; // CHECK: Function Attrs: naked // CHECK-NEXT: define{{.*}}void @naked_empty() @@ -14,7 +14,7 @@ pub unsafe extern "C" fn naked_empty() { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - asm!("ret", options(noreturn)); + naked_asm!("ret", options(noreturn)); } // CHECK: Function Attrs: naked @@ -25,5 +25,5 @@ pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize // CHECK-NEXT: {{.+}}: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); + naked_asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); } diff --git a/tests/ui/asm/naked-functions-ffi.rs b/tests/ui/asm/naked-functions-ffi.rs index 93d23885b1353..6b32290905d49 100644 --- a/tests/ui/asm/naked-functions-ffi.rs +++ b/tests/ui/asm/naked-functions-ffi.rs @@ -3,13 +3,13 @@ #![feature(naked_functions)] #![crate_type = "lib"] -use std::arch::asm; +use std::arch::naked_asm; #[naked] pub extern "C" fn naked(p: char) -> u128 { //~^ WARN uses type `char` //~| WARN uses type `u128` unsafe { - asm!("", options(noreturn)); + naked_asm!("", options(noreturn)); } } diff --git a/tests/ui/asm/naked-functions-inline.rs b/tests/ui/asm/naked-functions-inline.rs index 74049e8ecbc7c..a4df38e3347c6 100644 --- a/tests/ui/asm/naked-functions-inline.rs +++ b/tests/ui/asm/naked-functions-inline.rs @@ -19,7 +19,7 @@ pub unsafe extern "C" fn inline_hint() { #[naked] #[inline(always)] //~^ ERROR [E0736] -pub unsafe extern "C" fn inline_always() { +<<<<<<< HEAD naked_asm!(""); } diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions-instruction-set.rs index b81b65cff74fc..6c6dcdf008779 100644 --- a/tests/ui/asm/naked-functions-instruction-set.rs +++ b/tests/ui/asm/naked-functions-instruction-set.rs @@ -8,7 +8,7 @@ #![no_core] #[rustc_builtin_macro] -macro_rules! asm { +macro_rules! naked_asm { () => {}; } @@ -19,12 +19,12 @@ trait Sized {} #[naked] #[instruction_set(arm::t32)] unsafe extern "C" fn test_thumb() { - asm!("bx lr", options(noreturn)); + naked_asm!("bx lr", options(noreturn)); } #[no_mangle] #[naked] #[instruction_set(arm::t32)] unsafe extern "C" fn test_arm() { - asm!("bx lr", options(noreturn)); + naked_asm!("bx lr", options(noreturn)); } diff --git a/tests/ui/asm/naked-functions-testattrs.rs b/tests/ui/asm/naked-functions-testattrs.rs index 12943ac0378b7..2532e6e86d63a 100644 --- a/tests/ui/asm/naked-functions-testattrs.rs +++ b/tests/ui/asm/naked-functions-testattrs.rs @@ -6,13 +6,13 @@ #![feature(test)] #![crate_type = "lib"] -use std::arch::asm; +use std::arch::naked_asm; #[test] #[naked] //~^ ERROR [E0736] fn test_naked() { - unsafe { asm!("", options(noreturn)) }; + unsafe { naked_asm!("", options(noreturn)) }; } #[should_panic] @@ -20,7 +20,7 @@ fn test_naked() { #[naked] //~^ ERROR [E0736] fn test_naked_should_panic() { - unsafe { asm!("", options(noreturn)) }; + unsafe { naked_asm!("", options(noreturn)) }; } #[ignore] @@ -28,12 +28,12 @@ fn test_naked_should_panic() { #[naked] //~^ ERROR [E0736] fn test_naked_ignore() { - unsafe { asm!("", options(noreturn)) }; + unsafe { naked_asm!("", options(noreturn)) }; } #[bench] #[naked] //~^ ERROR [E0736] fn bench_naked() { - unsafe { asm!("", options(noreturn)) }; + unsafe { naked_asm!("", options(noreturn)) }; } diff --git a/tests/ui/asm/naked-functions-unused.aarch64.stderr b/tests/ui/asm/naked-functions-unused.aarch64.stderr index 8d3c300e0586c..ea63ced1aab04 100644 --- a/tests/ui/asm/naked-functions-unused.aarch64.stderr +++ b/tests/ui/asm/naked-functions-unused.aarch64.stderr @@ -18,49 +18,49 @@ LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:26:38 + --> $DIR/naked-functions-unused.rs:28:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:26:48 + --> $DIR/naked-functions-unused.rs:28:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:32:41 + --> $DIR/naked-functions-unused.rs:36:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:32:51 + --> $DIR/naked-functions-unused.rs:36:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:40:40 + --> $DIR/naked-functions-unused.rs:46:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:40:50 + --> $DIR/naked-functions-unused.rs:46:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:46:43 + --> $DIR/naked-functions-unused.rs:54:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:46:53 + --> $DIR/naked-functions-unused.rs:54:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/asm/naked-functions-unused.rs b/tests/ui/asm/naked-functions-unused.rs index 745d30e6a84a9..6d3cc0afed836 100644 --- a/tests/ui/asm/naked-functions-unused.rs +++ b/tests/ui/asm/naked-functions-unused.rs @@ -17,7 +17,9 @@ pub mod normal { pub extern "C" fn function(a: usize, b: usize) -> usize { //~^ ERROR unused variable: `a` //~| ERROR unused variable: `b` - unsafe { asm!("", options(noreturn)); } + unsafe { + asm!("", options(noreturn)); + } } pub struct Normal; @@ -26,13 +28,17 @@ pub mod normal { pub extern "C" fn associated(a: usize, b: usize) -> usize { //~^ ERROR unused variable: `a` //~| ERROR unused variable: `b` - unsafe { asm!("", options(noreturn)); } + unsafe { + asm!("", options(noreturn)); + } } pub extern "C" fn method(&self, a: usize, b: usize) -> usize { //~^ ERROR unused variable: `a` //~| ERROR unused variable: `b` - unsafe { asm!("", options(noreturn)); } + unsafe { + asm!("", options(noreturn)); + } } } @@ -40,23 +46,29 @@ pub mod normal { extern "C" fn trait_associated(a: usize, b: usize) -> usize { //~^ ERROR unused variable: `a` //~| ERROR unused variable: `b` - unsafe { asm!("", options(noreturn)); } + unsafe { + asm!("", options(noreturn)); + } } extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { //~^ ERROR unused variable: `a` //~| ERROR unused variable: `b` - unsafe { asm!("", options(noreturn)); } + unsafe { + asm!("", options(noreturn)); + } } } } pub mod naked { - use std::arch::asm; + use std::arch::naked_asm; #[naked] pub extern "C" fn function(a: usize, b: usize) -> usize { - unsafe { asm!("", options(noreturn)); } + unsafe { + naked_asm!("", options(noreturn)); + } } pub struct Naked; @@ -64,24 +76,32 @@ pub mod naked { impl Naked { #[naked] pub extern "C" fn associated(a: usize, b: usize) -> usize { - unsafe { asm!("", options(noreturn)); } + unsafe { + naked_asm!("", options(noreturn)); + } } #[naked] pub extern "C" fn method(&self, a: usize, b: usize) -> usize { - unsafe { asm!("", options(noreturn)); } + unsafe { + naked_asm!("", options(noreturn)); + } } } impl super::Trait for Naked { #[naked] extern "C" fn trait_associated(a: usize, b: usize) -> usize { - unsafe { asm!("", options(noreturn)); } + unsafe { + naked_asm!("", options(noreturn)); + } } #[naked] extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { - unsafe { asm!("", options(noreturn)); } + unsafe { + naked_asm!("", options(noreturn)); + } } } } diff --git a/tests/ui/asm/naked-functions-unused.x86_64.stderr b/tests/ui/asm/naked-functions-unused.x86_64.stderr index 8d3c300e0586c..ea63ced1aab04 100644 --- a/tests/ui/asm/naked-functions-unused.x86_64.stderr +++ b/tests/ui/asm/naked-functions-unused.x86_64.stderr @@ -18,49 +18,49 @@ LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:26:38 + --> $DIR/naked-functions-unused.rs:28:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:26:48 + --> $DIR/naked-functions-unused.rs:28:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:32:41 + --> $DIR/naked-functions-unused.rs:36:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:32:51 + --> $DIR/naked-functions-unused.rs:36:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:40:40 + --> $DIR/naked-functions-unused.rs:46:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:40:50 + --> $DIR/naked-functions-unused.rs:46:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:46:43 + --> $DIR/naked-functions-unused.rs:54:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:46:53 + --> $DIR/naked-functions-unused.rs:54:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.rs b/tests/ui/feature-gates/feature-gate-naked_functions.rs index 36980fd74c264..298b687e4f2fd 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions.rs +++ b/tests/ui/feature-gates/feature-gate-naked_functions.rs @@ -5,14 +5,14 @@ use std::arch::asm; #[naked] //~^ the `#[naked]` attribute is an experimental feature extern "C" fn naked() { - asm!("", options(noreturn)) + naked_asm!("", options(noreturn)) //~^ ERROR: requires unsafe } #[naked] //~^ the `#[naked]` attribute is an experimental feature extern "C" fn naked_2() -> isize { - asm!("", options(noreturn)) + naked_asm!("", options(noreturn)) //~^ ERROR: requires unsafe } From 1a9c1cbf367fb2b6f04a0a81d7fae56485e29e7f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 4 Aug 2024 17:19:08 +0200 Subject: [PATCH 1282/1301] use `naked_asm!` in feature-gate-naked_functions test --- .../feature-gate-naked_functions.rs | 9 ++-- .../feature-gate-naked_functions.stderr | 48 +++++++++++++++---- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.rs b/tests/ui/feature-gates/feature-gate-naked_functions.rs index 298b687e4f2fd..c8802ea4c31f2 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions.rs +++ b/tests/ui/feature-gates/feature-gate-naked_functions.rs @@ -1,19 +1,22 @@ //@ needs-asm-support -use std::arch::asm; +use std::arch::naked_asm; +//~^ ERROR use of unstable library feature 'naked_functions' #[naked] //~^ the `#[naked]` attribute is an experimental feature extern "C" fn naked() { naked_asm!("", options(noreturn)) - //~^ ERROR: requires unsafe + //~^ ERROR use of unstable library feature 'naked_functions' + //~| ERROR: requires unsafe } #[naked] //~^ the `#[naked]` attribute is an experimental feature extern "C" fn naked_2() -> isize { naked_asm!("", options(noreturn)) - //~^ ERROR: requires unsafe + //~^ ERROR use of unstable library feature 'naked_functions' + //~| ERROR: requires unsafe } fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.stderr b/tests/ui/feature-gates/feature-gate-naked_functions.stderr index ffdf31e147aed..5fecc218a1622 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions.stderr +++ b/tests/ui/feature-gates/feature-gate-naked_functions.stderr @@ -1,5 +1,25 @@ +error[E0658]: use of unstable library feature 'naked_functions' + --> $DIR/feature-gate-naked_functions.rs:9:5 + | +LL | naked_asm!("", options(noreturn)) + | ^^^^^^^^^ + | + = note: see issue #90957 for more information + = help: add `#![feature(naked_functions)]` 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 'naked_functions' + --> $DIR/feature-gate-naked_functions.rs:17:5 + | +LL | naked_asm!("", options(noreturn)) + | ^^^^^^^^^ + | + = note: see issue #90957 for more information + = help: add `#![feature(naked_functions)]` 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]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:5:1 + --> $DIR/feature-gate-naked_functions.rs:6:1 | LL | #[naked] | ^^^^^^^^ @@ -9,7 +29,7 @@ LL | #[naked] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:12:1 + --> $DIR/feature-gate-naked_functions.rs:14:1 | LL | #[naked] | ^^^^^^^^ @@ -18,23 +38,33 @@ LL | #[naked] = help: add `#![feature(naked_functions)]` 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 'naked_functions' + --> $DIR/feature-gate-naked_functions.rs:3:5 + | +LL | use std::arch::naked_asm; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #90957 for more information + = help: add `#![feature(naked_functions)]` 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[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/feature-gate-naked_functions.rs:8:5 + --> $DIR/feature-gate-naked_functions.rs:9:5 | -LL | asm!("", options(noreturn)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of inline assembly +LL | naked_asm!("", options(noreturn)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of inline assembly | = note: inline assembly is entirely unchecked and can cause undefined behavior error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/feature-gate-naked_functions.rs:15:5 + --> $DIR/feature-gate-naked_functions.rs:17:5 | -LL | asm!("", options(noreturn)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of inline assembly +LL | naked_asm!("", options(noreturn)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of inline assembly | = note: inline assembly is entirely unchecked and can cause undefined behavior -error: aborting due to 4 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0133, E0658. For more information about an error, try `rustc --explain E0133`. From 562ec5a6fb469a360b72aa8ba0ce149598cc7975 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 5 Sep 2024 13:45:26 +0200 Subject: [PATCH 1283/1301] disallow `asm!` in `#[naked]` functions also disallow the `noreturn` option, and infer `naked_asm!` as `!` --- compiler/rustc_ast/src/ast.rs | 20 ++- compiler/rustc_builtin_macros/src/asm.rs | 62 +------ .../src/error_codes/E0787.md | 5 +- compiler/rustc_hir_typeck/src/expr.rs | 6 +- compiler/rustc_passes/messages.ftl | 12 +- compiler/rustc_passes/src/errors.rs | 8 +- compiler/rustc_passes/src/naked_functions.rs | 47 +++--- library/core/src/arch.rs | 14 -- src/tools/rustfmt/src/parse/macros/asm.rs | 4 +- tests/codegen/naked-fn/aligned.rs | 4 +- tests/codegen/naked-fn/naked-functions.rs | 4 +- tests/codegen/naked-fn/naked-nocoverage.rs | 4 +- tests/codegen/naked-fn/naked-noinline.rs | 4 +- tests/ui/asm/naked-functions-ffi.rs | 2 +- tests/ui/asm/naked-functions-inline.rs | 2 +- .../ui/asm/naked-functions-instruction-set.rs | 4 +- tests/ui/asm/naked-functions-testattrs.rs | 8 +- tests/ui/asm/naked-functions-unused.rs | 10 +- tests/ui/asm/naked-functions.rs | 68 ++++---- tests/ui/asm/naked-functions.stderr | 156 +++++++----------- tests/ui/asm/naked-invalid-attr.rs | 13 +- tests/ui/asm/naked-invalid-attr.stderr | 6 +- tests/ui/asm/naked-with-invalid-repr-attr.rs | 12 +- .../asm/naked-with-invalid-repr-attr.stderr | 12 +- tests/ui/asm/named-asm-labels.rs | 10 +- tests/ui/asm/named-asm-labels.stderr | 18 +- .../feature-gate-naked_functions.rs | 4 +- 27 files changed, 222 insertions(+), 297 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 37f429cce44d7..cb71521317637 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2278,7 +2278,7 @@ impl InlineAsmOptions { pub const COUNT: usize = Self::all().bits().count_ones() as usize; pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW); - pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN); + pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW); pub fn human_readable_names(&self) -> Vec<&'static str> { let mut options = vec![]; @@ -2434,6 +2434,24 @@ pub enum AsmMacro { NakedAsm, } +impl AsmMacro { + pub const fn macro_name(&self) -> &'static str { + match self { + AsmMacro::Asm => "asm", + AsmMacro::GlobalAsm => "global_asm", + AsmMacro::NakedAsm => "naked_asm", + } + } + + pub const fn is_supported_option(&self, option: InlineAsmOptions) -> bool { + match self { + AsmMacro::Asm => true, + AsmMacro::GlobalAsm => InlineAsmOptions::GLOBAL_OPTIONS.contains(option), + AsmMacro::NakedAsm => InlineAsmOptions::NAKED_OPTIONS.contains(option), + } + } +} + /// Inline assembly. /// /// E.g., `asm!("NOP");`. diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 2019c42e296f5..515ac17f70a14 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -60,35 +60,6 @@ fn eat_operand_keyword<'a>( } } -// Public for rustfmt consumption. -#[derive(Copy, Clone)] -pub enum AsmMacro { - /// The `asm!` macro - Asm, - /// The `global_asm!` macro - GlobalAsm, - /// The `naked_asm!` macro - NakedAsm, -} - -impl AsmMacro { - const fn macro_name(&self) -> &'static str { - match self { - AsmMacro::Asm => "asm", - AsmMacro::GlobalAsm => "global_asm", - AsmMacro::NakedAsm => "naked_asm", - } - } - - const fn is_supported_option(&self, option: ast::InlineAsmOptions) -> bool { - match self { - AsmMacro::Asm => true, - AsmMacro::GlobalAsm => ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option), - AsmMacro::NakedAsm => ast::InlineAsmOptions::NAKED_OPTIONS.contains(option), - } - } -} - fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -529,7 +500,7 @@ fn parse_reg<'a>( fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, - asm_macro: ast::AsmMacro, + asm_macro: AsmMacro, args: AsmArgs, ) -> ExpandResult, ()> { let mut template = vec![]; @@ -872,7 +843,7 @@ pub(super) fn expand_naked_asm<'cx>( sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(match parse_args(ecx, sp, tts, false) { + ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) { Ok(args) => { let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args) else { @@ -940,32 +911,3 @@ pub(super) fn expand_global_asm<'cx>( } }) } - -pub(super) fn expand_naked_asm<'cx>( - ecx: &'cx mut ExtCtxt<'_>, - sp: Span, - tts: TokenStream, -) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) { - Ok(args) => { - let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else { - return ExpandResult::Retry(()); - }; - let expr = match mac { - Ok(inline_asm) => P(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::InlineAsm(P(inline_asm)), - span: sp, - attrs: ast::AttrVec::new(), - tokens: None, - }), - Err(guar) => DummyResult::raw_expr(sp, Some(guar)), - }; - MacEager::expr(expr) - } - Err(err) => { - let guar = err.emit(); - DummyResult::any(sp, guar) - } - }) -} diff --git a/compiler/rustc_error_codes/src/error_codes/E0787.md b/compiler/rustc_error_codes/src/error_codes/E0787.md index cee5082927026..f5c5faa066b6b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0787.md +++ b/compiler/rustc_error_codes/src/error_codes/E0787.md @@ -11,11 +11,10 @@ pub extern "C" fn f() -> u32 { } ``` -The naked functions must be defined using a single inline assembly -block. +The naked function must be defined using a single `naked_asm!` assembly block. The execution must never fall through past the end of the assembly -code so the block must use `noreturn` option. The asm block can also +code, so it must either return or diverge. The asm block can also use `att_syntax` and `raw` options, but others options are not allowed. The asm block must not contain any operands other than `const` and diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2d71829537496..5c2f39f879c4c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3507,7 +3507,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { - let mut diverge = asm.options.contains(ast::InlineAsmOptions::NORETURN); + let mut diverge = match asm.asm_macro { + rustc_ast::AsmMacro::Asm => asm.options.contains(ast::InlineAsmOptions::NORETURN), + rustc_ast::AsmMacro::GlobalAsm => true, + rustc_ast::AsmMacro::NakedAsm => true, + }; for (op, _op_sp) in asm.operands { match op { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index f2682acf8aa0b..1b1d1b1fb72ed 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -488,9 +488,9 @@ passes_naked_asm_outside_naked_fn = the `naked_asm!` macro can only be used in functions marked with `#[naked]` passes_naked_functions_asm_block = - naked functions must contain a single asm block - .label_multiple_asm = multiple asm blocks are unsupported in naked functions - .label_non_asm = non-asm is unsupported in naked functions + naked functions must contain a single `naked_asm!` invocation + .label_multiple_asm = multiple `naked_asm!` invocations are not allowed in naked functions + .label_non_asm = not allowed in naked functions passes_naked_functions_asm_options = asm options unsupported in naked functions: {$unsupported_options} @@ -500,9 +500,9 @@ passes_naked_functions_incompatible_attribute = .label = the `{$attr}` attribute is incompatible with `#[naked]` .naked_attribute = function marked with `#[naked]` here -passes_naked_functions_must_use_noreturn = - asm in naked functions must use `noreturn` option - .suggestion = consider specifying that the asm block is responsible for returning from the function +passes_naked_functions_must_naked_asm = + the `asm!` macro is not allowed in naked functions + .suggestion = consider using the `naked_asm!` macro instead passes_naked_functions_operands = only `const` and `sym` operands are supported in naked functions diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 29a087bf75975..c46768ace53ab 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1202,12 +1202,12 @@ pub(crate) struct NakedFunctionsAsmOptions { } #[derive(Diagnostic)] -#[diag(passes_naked_functions_must_use_noreturn, code = E0787)] -pub(crate) struct NakedFunctionsMustUseNoreturn { +#[diag(passes_naked_functions_must_naked_asm, code = E0787)] +pub(crate) struct NakedFunctionsMustNakedAsm { #[primary_span] pub span: Span, - #[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")] - pub last_span: Span, + #[suggestion(code = "naked_asm!", applicability = "machine-applicable")] + pub macro_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 8d3f7e0231f78..6046c7284304e 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -10,13 +10,13 @@ use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{BytePos, Span}; use rustc_target::spec::abi::Abi; use crate::errors::{ NakedAsmOutsideNakedFn, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, - NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, + NakedFunctionsMustNakedAsm, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, UndefinedNakedFunctionAbi, }; @@ -121,21 +121,29 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) { let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; this.visit_body(body); - if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] { + if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] { // Ok. } else { let mut must_show_error = false; - let mut has_asm = false; + let mut has_naked_asm = false; let mut has_err = false; let mut multiple_asms = vec![]; let mut non_asms = vec![]; for &(kind, span) in &this.items { match kind { - ItemKind::Asm if has_asm => { + ItemKind::NakedAsm if has_naked_asm => { must_show_error = true; multiple_asms.push(span); } - ItemKind::Asm => has_asm = true, + ItemKind::NakedAsm => has_naked_asm = true, + ItemKind::InlineAsm => { + has_err = true; + + // the span that contains the `asm!` call, + // so tooling can replace it with `naked_asm!` + let macro_span = span.with_hi(span.lo() + BytePos("asm!".len() as u32)); + tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span, macro_span }); + } ItemKind::NonAsm => { must_show_error = true; non_asms.push(span); @@ -164,7 +172,8 @@ struct CheckInlineAssembly<'tcx> { #[derive(Copy, Clone)] enum ItemKind { - Asm, + NakedAsm, + InlineAsm, NonAsm, Err, } @@ -205,8 +214,18 @@ impl<'tcx> CheckInlineAssembly<'tcx> { } ExprKind::InlineAsm(asm) => { - self.items.push((ItemKind::Asm, span)); - self.check_inline_asm(asm, span); + match asm.asm_macro { + rustc_ast::AsmMacro::Asm => { + self.items.push((ItemKind::InlineAsm, span)); + } + rustc_ast::AsmMacro::NakedAsm => { + self.items.push((ItemKind::NakedAsm, span)); + self.check_inline_asm(asm, span); + } + rustc_ast::AsmMacro::GlobalAsm => { + // not allowed in this position + } + } } ExprKind::DropTemps(..) | ExprKind::Block(..) => { @@ -250,16 +269,6 @@ impl<'tcx> CheckInlineAssembly<'tcx> { .join(", "), }); } - - if !asm.options.contains(InlineAsmOptions::NORETURN) { - let last_span = asm - .operands - .last() - .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1) - .shrink_to_hi(); - - self.tcx.dcx().emit_err(NakedFunctionsMustUseNoreturn { span, last_span }); - } } } diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index f1a0739128cdb..4945c045bc635 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -77,17 +77,3 @@ pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } - -/// Inline assembly used in combination with `#[naked]` functions. -/// -/// Refer to [Rust By Example] for a usage guide and the [reference] for -/// detailed information about the syntax and available options. -/// -/// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html -/// [reference]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html -#[unstable(feature = "naked_functions", issue = "90957")] -#[rustc_builtin_macro] -#[cfg(not(bootstrap))] -pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) { - /* compiler built-in */ -} diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 5d30171e29543..0c37d12490e2f 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,5 +1,5 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs, AsmMacro}; +use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; use crate::rewrite::RewriteContext; @@ -7,5 +7,5 @@ use crate::rewrite::RewriteContext; pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { let ts = mac.args.tokens.clone(); let mut parser = super::build_parser(context, ts); - parse_asm_args(&mut parser, mac.span(), AsmMacro::Asm).ok() + parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() } diff --git a/tests/codegen/naked-fn/aligned.rs b/tests/codegen/naked-fn/aligned.rs index d5faac44836fe..3bbd67981e5bf 100644 --- a/tests/codegen/naked-fn/aligned.rs +++ b/tests/codegen/naked-fn/aligned.rs @@ -4,7 +4,7 @@ #![crate_type = "lib"] #![feature(naked_functions, fn_align)] -use std::arch::asm; +use std::arch::naked_asm; // CHECK: Function Attrs: naked // CHECK-NEXT: define{{.*}}void @naked_empty() @@ -16,5 +16,5 @@ pub unsafe extern "C" fn naked_empty() { // CHECK-NEXT: start: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - asm!("ret", options(noreturn)); + naked_asm!("ret"); } diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 2b2625429f8e0..3f7447af599ab 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -14,7 +14,7 @@ pub unsafe extern "C" fn naked_empty() { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - naked_asm!("ret", options(noreturn)); + naked_asm!("ret"); } // CHECK: Function Attrs: naked @@ -25,5 +25,5 @@ pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize // CHECK-NEXT: {{.+}}: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - naked_asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); + naked_asm!("lea rax, [rdi + rsi]", "ret"); } diff --git a/tests/codegen/naked-fn/naked-nocoverage.rs b/tests/codegen/naked-fn/naked-nocoverage.rs index d73c5b7fd26d1..f63661bcd3a7a 100644 --- a/tests/codegen/naked-fn/naked-nocoverage.rs +++ b/tests/codegen/naked-fn/naked-nocoverage.rs @@ -6,7 +6,7 @@ //@ compile-flags: -Cinstrument-coverage #![crate_type = "lib"] #![feature(naked_functions)] -use std::arch::asm; +use std::arch::naked_asm; #[naked] #[no_mangle] @@ -15,5 +15,5 @@ pub unsafe extern "C" fn f() { // CHECK-NEXT: start: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - asm!("", options(noreturn)); + naked_asm!(""); } diff --git a/tests/codegen/naked-fn/naked-noinline.rs b/tests/codegen/naked-fn/naked-noinline.rs index c1e8f368249fd..6ea36d9678315 100644 --- a/tests/codegen/naked-fn/naked-noinline.rs +++ b/tests/codegen/naked-fn/naked-noinline.rs @@ -5,7 +5,7 @@ #![crate_type = "lib"] #![feature(naked_functions)] -use std::arch::asm; +use std::arch::naked_asm; #[naked] #[no_mangle] @@ -15,7 +15,7 @@ pub unsafe extern "C" fn f() { // CHECK: define {{(dso_local )?}}void @f() unnamed_addr [[ATTR:#[0-9]+]] // CHECK-NEXT: start: // CHECK-NEXT: call void asm - asm!("", options(noreturn)); + naked_asm!(""); } #[no_mangle] diff --git a/tests/ui/asm/naked-functions-ffi.rs b/tests/ui/asm/naked-functions-ffi.rs index 6b32290905d49..b78d1e6a0d1c6 100644 --- a/tests/ui/asm/naked-functions-ffi.rs +++ b/tests/ui/asm/naked-functions-ffi.rs @@ -10,6 +10,6 @@ pub extern "C" fn naked(p: char) -> u128 { //~^ WARN uses type `char` //~| WARN uses type `u128` unsafe { - naked_asm!("", options(noreturn)); + naked_asm!(""); } } diff --git a/tests/ui/asm/naked-functions-inline.rs b/tests/ui/asm/naked-functions-inline.rs index a4df38e3347c6..74049e8ecbc7c 100644 --- a/tests/ui/asm/naked-functions-inline.rs +++ b/tests/ui/asm/naked-functions-inline.rs @@ -19,7 +19,7 @@ pub unsafe extern "C" fn inline_hint() { #[naked] #[inline(always)] //~^ ERROR [E0736] -<<<<<<< HEAD +pub unsafe extern "C" fn inline_always() { naked_asm!(""); } diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions-instruction-set.rs index 6c6dcdf008779..37c7b52c191cd 100644 --- a/tests/ui/asm/naked-functions-instruction-set.rs +++ b/tests/ui/asm/naked-functions-instruction-set.rs @@ -19,12 +19,12 @@ trait Sized {} #[naked] #[instruction_set(arm::t32)] unsafe extern "C" fn test_thumb() { - naked_asm!("bx lr", options(noreturn)); + naked_asm!("bx lr"); } #[no_mangle] #[naked] #[instruction_set(arm::t32)] unsafe extern "C" fn test_arm() { - naked_asm!("bx lr", options(noreturn)); + naked_asm!("bx lr"); } diff --git a/tests/ui/asm/naked-functions-testattrs.rs b/tests/ui/asm/naked-functions-testattrs.rs index 2532e6e86d63a..7e373270e9fc6 100644 --- a/tests/ui/asm/naked-functions-testattrs.rs +++ b/tests/ui/asm/naked-functions-testattrs.rs @@ -12,7 +12,7 @@ use std::arch::naked_asm; #[naked] //~^ ERROR [E0736] fn test_naked() { - unsafe { naked_asm!("", options(noreturn)) }; + unsafe { naked_asm!("") }; } #[should_panic] @@ -20,7 +20,7 @@ fn test_naked() { #[naked] //~^ ERROR [E0736] fn test_naked_should_panic() { - unsafe { naked_asm!("", options(noreturn)) }; + unsafe { naked_asm!("") }; } #[ignore] @@ -28,12 +28,12 @@ fn test_naked_should_panic() { #[naked] //~^ ERROR [E0736] fn test_naked_ignore() { - unsafe { naked_asm!("", options(noreturn)) }; + unsafe { naked_asm!("") }; } #[bench] #[naked] //~^ ERROR [E0736] fn bench_naked() { - unsafe { naked_asm!("", options(noreturn)) }; + unsafe { naked_asm!("") }; } diff --git a/tests/ui/asm/naked-functions-unused.rs b/tests/ui/asm/naked-functions-unused.rs index 6d3cc0afed836..c27037819a44f 100644 --- a/tests/ui/asm/naked-functions-unused.rs +++ b/tests/ui/asm/naked-functions-unused.rs @@ -67,7 +67,7 @@ pub mod naked { #[naked] pub extern "C" fn function(a: usize, b: usize) -> usize { unsafe { - naked_asm!("", options(noreturn)); + naked_asm!(""); } } @@ -77,14 +77,14 @@ pub mod naked { #[naked] pub extern "C" fn associated(a: usize, b: usize) -> usize { unsafe { - naked_asm!("", options(noreturn)); + naked_asm!(""); } } #[naked] pub extern "C" fn method(&self, a: usize, b: usize) -> usize { unsafe { - naked_asm!("", options(noreturn)); + naked_asm!(""); } } } @@ -93,14 +93,14 @@ pub mod naked { #[naked] extern "C" fn trait_associated(a: usize, b: usize) -> usize { unsafe { - naked_asm!("", options(noreturn)); + naked_asm!(""); } } #[naked] extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { unsafe { - naked_asm!("", options(noreturn)); + naked_asm!(""); } } } diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index 86c89ea1262fd..5c58f1498cc97 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -6,7 +6,13 @@ #![feature(asm_unwind, linkage)] #![crate_type = "lib"] -use std::arch::naked_asm; +use std::arch::{asm, naked_asm}; + +#[naked] +pub unsafe extern "C" fn inline_asm_macro() { + asm!("", options(raw)); + //~^ERROR the `asm!` macro is not allowed in naked functions +} #[repr(C)] pub struct P { @@ -25,12 +31,12 @@ pub unsafe extern "C" fn patterns( P { x, y }: P, //~^ ERROR patterns not allowed in naked function parameters ) { - naked_asm!("", options(noreturn)) + naked_asm!("") } #[naked] pub unsafe extern "C" fn inc(a: u32) -> u32 { - //~^ ERROR naked functions must contain a single asm block + //~^ ERROR naked functions must contain a single `naked_asm!` invocation a + 1 //~^ ERROR referencing function parameters is not allowed in naked functions } @@ -38,19 +44,19 @@ pub unsafe extern "C" fn inc(a: u32) -> u32 { #[naked] #[allow(asm_sub_register)] pub unsafe extern "C" fn inc_asm(a: u32) -> u32 { - naked_asm!("/* {0} */", in(reg) a, options(noreturn)) + naked_asm!("/* {0} */", in(reg) a) //~^ ERROR the `in` operand cannot be used with `naked_asm!` } #[naked] pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { - //~^ ERROR naked functions must contain a single asm block + //~^ ERROR naked functions must contain a single `naked_asm!` invocation (|| a + 1)() } #[naked] pub unsafe extern "C" fn unsupported_operands() { - //~^ ERROR naked functions must contain a single asm block + //~^ ERROR naked functions must contain a single `naked_asm!` invocation let mut a = 0usize; let mut b = 0usize; let mut c = 0usize; @@ -72,27 +78,23 @@ pub unsafe extern "C" fn unsupported_operands() { #[naked] pub extern "C" fn missing_assembly() { - //~^ ERROR naked functions must contain a single asm block + //~^ ERROR naked functions must contain a single `naked_asm!` invocation } #[naked] pub extern "C" fn too_many_asm_blocks() { - //~^ ERROR naked functions must contain a single asm block + //~^ ERROR naked functions must contain a single `naked_asm!` invocation unsafe { - naked_asm!(""); - //~^ ERROR asm in naked functions must use `noreturn` option - naked_asm!(""); - //~^ ERROR asm in naked functions must use `noreturn` option - naked_asm!(""); - //~^ ERROR asm in naked functions must use `noreturn` option naked_asm!("", options(noreturn)); + //~^ ERROR the `noreturn` option cannot be used with `naked_asm!` + naked_asm!(""); } } pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { #[naked] pub extern "C" fn inner(y: usize) -> usize { - //~^ ERROR naked functions must contain a single asm block + //~^ ERROR naked functions must contain a single `naked_asm!` invocation *&y //~^ ERROR referencing function parameters is not allowed in naked functions } @@ -101,7 +103,7 @@ pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { #[naked] unsafe extern "C" fn invalid_options() { - naked_asm!("", options(nomem, preserves_flags, noreturn)); + naked_asm!("", options(nomem, preserves_flags)); //~^ ERROR the `nomem` option cannot be used with `naked_asm!` //~| ERROR the `preserves_flags` option cannot be used with `naked_asm!` } @@ -112,31 +114,30 @@ unsafe extern "C" fn invalid_options_continued() { //~^ ERROR the `readonly` option cannot be used with `naked_asm!` //~| ERROR the `nostack` option cannot be used with `naked_asm!` //~| ERROR the `pure` option cannot be used with `naked_asm!` - //~| ERROR asm in naked functions must use `noreturn` option } #[naked] unsafe extern "C" fn invalid_may_unwind() { - naked_asm!("", options(noreturn, may_unwind)); + naked_asm!("", options(may_unwind)); //~^ ERROR the `may_unwind` option cannot be used with `naked_asm!` } #[naked] pub unsafe fn default_abi() { //~^ WARN Rust ABI is unsupported in naked functions - naked_asm!("", options(noreturn)); + naked_asm!(""); } #[naked] pub unsafe fn rust_abi() { //~^ WARN Rust ABI is unsupported in naked functions - naked_asm!("", options(noreturn)); + naked_asm!(""); } #[naked] pub extern "C" fn valid_a() -> T { unsafe { - naked_asm!("", options(noreturn)); + naked_asm!(""); } } @@ -145,7 +146,7 @@ pub extern "C" fn valid_b() { unsafe { { { - naked_asm!("", options(noreturn)); + naked_asm!(""); }; }; } @@ -153,13 +154,13 @@ pub extern "C" fn valid_b() { #[naked] pub unsafe extern "C" fn valid_c() { - naked_asm!("", options(noreturn)); + naked_asm!(""); } #[cfg(target_arch = "x86_64")] #[naked] pub unsafe extern "C" fn valid_att_syntax() { - naked_asm!("", options(noreturn, att_syntax)); + naked_asm!("", options(att_syntax)); } #[naked] @@ -173,7 +174,7 @@ pub unsafe extern "C" fn allow_compile_error(a: u32) -> u32 { pub unsafe extern "C" fn allow_compile_error_and_asm(a: u32) -> u32 { compile_error!("this is a user specified error"); //~^ ERROR this is a user specified error - naked_asm!("", options(noreturn)) + naked_asm!("") } #[naked] @@ -186,7 +187,7 @@ pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 { #[cfg_attr(target_pointer_width = "64", no_mangle)] #[naked] pub unsafe extern "C" fn compatible_cfg_attributes() { - naked_asm!("", options(noreturn, att_syntax)); + naked_asm!("", options(att_syntax)); } #[allow(dead_code)] @@ -195,13 +196,13 @@ pub unsafe extern "C" fn compatible_cfg_attributes() { #[forbid(dead_code)] #[naked] pub unsafe extern "C" fn compatible_diagnostic_attributes() { - naked_asm!("", options(noreturn, raw)); + naked_asm!("", options(raw)); } #[deprecated = "test"] #[naked] pub unsafe extern "C" fn compatible_deprecated_attributes() { - naked_asm!("", options(noreturn, raw)); + naked_asm!("", options(raw)); } #[cfg(target_arch = "x86_64")] @@ -213,7 +214,6 @@ pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 { mov rax, 42 ret ", - options(noreturn) ) } @@ -222,20 +222,20 @@ pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 { #[no_mangle] #[naked] pub unsafe extern "C" fn compatible_ffi_attributes_1() { - naked_asm!("", options(noreturn, raw)); + naked_asm!("", options(raw)); } #[cold] #[naked] pub unsafe extern "C" fn compatible_codegen_attributes() { - naked_asm!("", options(noreturn, raw)); + naked_asm!("", options(raw)); } #[cfg(target_arch = "x86_64")] #[target_feature(enable = "sse2")] #[naked] pub unsafe extern "C" fn compatible_target_feature() { - naked_asm!("", options(noreturn)); + naked_asm!(""); } #[doc = "foo bar baz"] @@ -244,11 +244,11 @@ pub unsafe extern "C" fn compatible_target_feature() { #[doc(alias = "ADocAlias")] #[naked] pub unsafe extern "C" fn compatible_doc_attributes() { - naked_asm!("", options(noreturn, raw)); + naked_asm!("", options(raw)); } #[linkage = "external"] #[naked] pub unsafe extern "C" fn compatible_linkage() { - naked_asm!("", options(noreturn, raw)); + naked_asm!("", options(raw)); } diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index 22c1946a960c1..1ceeafffd9091 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -1,220 +1,184 @@ error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:41:29 + --> $DIR/naked-functions.rs:47:29 | -LL | naked_asm!("/* {0} */", in(reg) a, options(noreturn)) +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:62: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:88:32 + | +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:104:28 + --> $DIR/naked-functions.rs:106:28 | -LL | naked_asm!("", options(nomem, preserves_flags, noreturn)); +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:104:35 + --> $DIR/naked-functions.rs:106:35 | -LL | naked_asm!("", options(nomem, preserves_flags, noreturn)); +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:111:28 + --> $DIR/naked-functions.rs:113: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:111:38 + --> $DIR/naked-functions.rs:113: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:111:56 + --> $DIR/naked-functions.rs:113: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:120:38 + --> $DIR/naked-functions.rs:121:28 | -LL | naked_asm!("", options(noreturn, may_unwind)); - | ^^^^^^^^^^ the `may_unwind` option is not meaningful for global-scoped inline assembly +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:168:5 + --> $DIR/naked-functions.rs:169:5 | LL | compile_error!("this is a user specified error") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this is a user specified error - --> $DIR/naked-functions.rs:174:5 + --> $DIR/naked-functions.rs:175:5 | LL | compile_error!("this is a user specified error"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/naked-functions.rs:181:16 + --> $DIR/naked-functions.rs:182:16 | LL | naked_asm!(invalid_syntax) | ^^^^^^^^^^^^^^ +error[E0787]: the `asm!` macro is not allowed in naked functions + --> $DIR/naked-functions.rs:13:5 + | +LL | asm!("", options(raw)); + | ----^^^^^^^^^^^^^^^^^^ + | | + | help: consider using the `naked_asm!` macro instead: `naked_asm!` + error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:19:5 + --> $DIR/naked-functions.rs:25:5 | LL | mut a: u32, | ^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:21:5 + --> $DIR/naked-functions.rs:27:5 | LL | &b: &i32, | ^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:23:6 + --> $DIR/naked-functions.rs:29:6 | LL | (None | Some(_)): Option>, | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:25: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:34:5 + --> $DIR/naked-functions.rs:40:5 | LL | a + 1 | ^ | = help: follow the calling convention in asm block to use parameters -error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:32:1 +error[E0787]: naked functions must contain a single `naked_asm!` invocation + --> $DIR/naked-functions.rs:38:1 | LL | pub unsafe extern "C" fn inc(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | a + 1 - | ----- non-asm is unsupported in naked functions + | ----- not allowed in naked functions -error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:46:1 +error[E0787]: naked functions must contain a single `naked_asm!` invocation + --> $DIR/naked-functions.rs:52:1 | LL | pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | (|| a + 1)() - | ------------ non-asm is unsupported in naked functions + | ------------ not allowed in naked functions -error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:52:1 +error[E0787]: naked functions must contain a single `naked_asm!` invocation + --> $DIR/naked-functions.rs:58:1 | LL | pub unsafe extern "C" fn unsupported_operands() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | let mut a = 0usize; - | ------------------- non-asm is unsupported in naked functions + | ------------------- not allowed in naked functions LL | let mut b = 0usize; - | ------------------- non-asm is unsupported in naked functions + | ------------------- not allowed in naked functions LL | let mut c = 0usize; - | ------------------- non-asm is unsupported in naked functions + | ------------------- not allowed in naked functions LL | let mut d = 0usize; - | ------------------- non-asm is unsupported in naked functions + | ------------------- not allowed in naked functions LL | let mut e = 0usize; - | ------------------- non-asm is unsupported in naked functions + | ------------------- not allowed in naked functions -error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:74:1 +error[E0787]: naked functions must contain a single `naked_asm!` invocation + --> $DIR/naked-functions.rs:80:1 | LL | pub extern "C" fn missing_assembly() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:82:9 - | -LL | naked_asm!(""); - | ^^^^^^^^^^^^^^ - | -help: consider specifying that the asm block is responsible for returning from the function - | -LL | naked_asm!("", options(noreturn)); - | +++++++++++++++++++ - -error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:84:9 - | -LL | naked_asm!(""); - | ^^^^^^^^^^^^^^ - | -help: consider specifying that the asm block is responsible for returning from the function - | -LL | naked_asm!("", options(noreturn)); - | +++++++++++++++++++ - -error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:86:9 - | -LL | naked_asm!(""); - | ^^^^^^^^^^^^^^ - | -help: consider specifying that the asm block is responsible for returning from the function - | -LL | naked_asm!("", options(noreturn)); - | +++++++++++++++++++ - -error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:79:1 +error[E0787]: naked functions must contain a single `naked_asm!` invocation + --> $DIR/naked-functions.rs:85:1 | LL | pub extern "C" fn too_many_asm_blocks() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | naked_asm!(""); - | -------------- multiple asm blocks are unsupported in naked functions -LL | -LL | naked_asm!(""); - | -------------- multiple asm blocks are unsupported in naked functions -LL | -LL | naked_asm!("", options(noreturn)); - | --------------------------------- multiple asm blocks are unsupported in naked functions + | -------------- multiple `naked_asm!` invocations are not allowed in naked functions error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:96:11 + --> $DIR/naked-functions.rs:98:11 | LL | *&y | ^ | = help: follow the calling convention in asm block to use parameters -error[E0787]: naked functions must contain a single asm block - --> $DIR/naked-functions.rs:94:5 +error[E0787]: naked functions must contain a single `naked_asm!` invocation + --> $DIR/naked-functions.rs:96:5 | LL | pub extern "C" fn inner(y: usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | *&y - | --- non-asm is unsupported in naked functions - -error[E0787]: asm in naked functions must use `noreturn` option - --> $DIR/naked-functions.rs:111:5 - | -LL | naked_asm!("", options(readonly, nostack), options(pure)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider specifying that the asm block is responsible for returning from the function - | -LL | naked_asm!("", options(noreturn), options(readonly, nostack), options(pure)); - | +++++++++++++++++++ + | --- not allowed in naked functions warning: Rust ABI is unsupported in naked functions - --> $DIR/naked-functions.rs:125:1 + --> $DIR/naked-functions.rs:126:1 | LL | pub unsafe fn default_abi() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,11 +186,11 @@ LL | pub unsafe fn default_abi() { = note: `#[warn(undefined_naked_function_abi)]` on by default warning: Rust ABI is unsupported in naked functions - --> $DIR/naked-functions.rs:131:1 + --> $DIR/naked-functions.rs:132:1 | LL | pub unsafe fn rust_abi() { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors; 2 warnings emitted +error: aborting due to 25 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0787`. diff --git a/tests/ui/asm/naked-invalid-attr.rs b/tests/ui/asm/naked-invalid-attr.rs index 57edd57de998e..4053c58fb5136 100644 --- a/tests/ui/asm/naked-invalid-attr.rs +++ b/tests/ui/asm/naked-invalid-attr.rs @@ -4,7 +4,7 @@ #![feature(naked_functions)] #![naked] //~ ERROR should be applied to a function definition -use std::arch::asm; +use std::arch::naked_asm; extern "C" { #[naked] //~ ERROR should be applied to a function definition @@ -26,27 +26,28 @@ trait Invoke { impl Invoke for S { #[naked] extern "C" fn invoke(&self) { - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } } #[naked] extern "C" fn ok() { - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } impl S { #[naked] extern "C" fn g() { - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } #[naked] extern "C" fn h(&self) { - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } } fn main() { - #[naked] || {}; //~ ERROR should be applied to a function definition + #[naked] //~ ERROR should be applied to a function definition + || {}; } diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr index e8ddccc854abe..640f9d9510d15 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-invalid-attr.stderr @@ -13,8 +13,10 @@ LL | | } error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:51:5 | -LL | #[naked] || {}; - | ^^^^^^^^ ----- not a function definition +LL | #[naked] + | ^^^^^^^^ +LL | || {}; + | ----- not a function definition error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:22:5 diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.rs b/tests/ui/asm/naked-with-invalid-repr-attr.rs index 687fe1ad73db1..18b9c1014c3fa 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.rs +++ b/tests/ui/asm/naked-with-invalid-repr-attr.rs @@ -2,14 +2,14 @@ #![feature(naked_functions)] #![feature(fn_align)] #![crate_type = "lib"] -use std::arch::asm; +use std::arch::naked_asm; #[repr(C)] //~^ ERROR attribute should be applied to a struct, enum, or union [E0517] #[naked] extern "C" fn example1() { //~^ NOTE not a struct, enum, or union - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } #[repr(transparent)] @@ -17,7 +17,7 @@ extern "C" fn example1() { #[naked] extern "C" fn example2() { //~^ NOTE not a struct, enum, or union - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } #[repr(align(16), C)] @@ -25,7 +25,7 @@ extern "C" fn example2() { #[naked] extern "C" fn example3() { //~^ NOTE not a struct, enum, or union - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } // note: two errors because of packed and C @@ -36,7 +36,7 @@ extern "C" fn example3() { extern "C" fn example4() { //~^ NOTE not a struct, enum, or union //~| NOTE not a struct or union - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } #[repr(u8)] @@ -44,5 +44,5 @@ extern "C" fn example4() { #[naked] extern "C" fn example5() { //~^ NOTE not an enum - unsafe { asm!("", options(noreturn)) } + unsafe { naked_asm!("") } } diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.stderr b/tests/ui/asm/naked-with-invalid-repr-attr.stderr index 3740f17a9dc32..8248a8c165791 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.stderr +++ b/tests/ui/asm/naked-with-invalid-repr-attr.stderr @@ -6,7 +6,7 @@ LL | #[repr(C)] ... LL | / extern "C" fn example1() { LL | | -LL | | unsafe { asm!("", options(noreturn)) } +LL | | unsafe { naked_asm!("") } LL | | } | |_- not a struct, enum, or union @@ -18,7 +18,7 @@ LL | #[repr(transparent)] ... LL | / extern "C" fn example2() { LL | | -LL | | unsafe { asm!("", options(noreturn)) } +LL | | unsafe { naked_asm!("") } LL | | } | |_- not a struct, enum, or union @@ -30,7 +30,7 @@ LL | #[repr(align(16), C)] ... LL | / extern "C" fn example3() { LL | | -LL | | unsafe { asm!("", options(noreturn)) } +LL | | unsafe { naked_asm!("") } LL | | } | |_- not a struct, enum, or union @@ -43,7 +43,7 @@ LL | #[repr(C, packed)] LL | / extern "C" fn example4() { LL | | LL | | -LL | | unsafe { asm!("", options(noreturn)) } +LL | | unsafe { naked_asm!("") } LL | | } | |_- not a struct, enum, or union @@ -56,7 +56,7 @@ LL | #[repr(C, packed)] LL | / extern "C" fn example4() { LL | | LL | | -LL | | unsafe { asm!("", options(noreturn)) } +LL | | unsafe { naked_asm!("") } LL | | } | |_- not a struct or union @@ -68,7 +68,7 @@ LL | #[repr(u8)] ... LL | / extern "C" fn example5() { LL | | -LL | | unsafe { asm!("", options(noreturn)) } +LL | | unsafe { naked_asm!("") } LL | | } | |_- not an enum diff --git a/tests/ui/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs index 043aab9029d7d..77831e679ed42 100644 --- a/tests/ui/asm/named-asm-labels.rs +++ b/tests/ui/asm/named-asm-labels.rs @@ -12,7 +12,7 @@ #![feature(naked_functions)] -use std::arch::{asm, global_asm}; +use std::arch::{asm, global_asm, naked_asm}; #[no_mangle] pub static FOO: usize = 42; @@ -177,7 +177,7 @@ fn main() { // label or LTO can cause labels to break #[naked] pub extern "C" fn foo() -> i32 { - unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } + unsafe { naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) } //~^ ERROR avoid using named labels } @@ -192,7 +192,7 @@ pub extern "C" fn bar() { pub extern "C" fn aaa() { fn _local() {} - unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) } //~ ERROR avoid using named labels + unsafe { naked_asm!(".Laaa: nop; ret;") } //~ ERROR avoid using named labels } pub fn normal() { @@ -202,7 +202,7 @@ pub fn normal() { pub extern "C" fn bbb() { fn _very_local() {} - unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) } //~ ERROR avoid using named labels + unsafe { naked_asm!(".Lbbb: nop; ret;") } //~ ERROR avoid using named labels } fn _local2() {} @@ -221,7 +221,7 @@ fn closures() { || { #[naked] unsafe extern "C" fn _nested() { - asm!("ret;", options(noreturn)); + naked_asm!("ret;"); } unsafe { diff --git a/tests/ui/asm/named-asm-labels.stderr b/tests/ui/asm/named-asm-labels.stderr index e5e177fb8b83e..44ce358c62bdb 100644 --- a/tests/ui/asm/named-asm-labels.stderr +++ b/tests/ui/asm/named-asm-labels.stderr @@ -475,10 +475,10 @@ LL | #[warn(named_asm_labels)] | ^^^^^^^^^^^^^^^^ error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:180:20 + --> $DIR/named-asm-labels.rs:180:26 | -LL | unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } - | ^^^^^ +LL | unsafe { naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) } + | ^^^^^ | = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information @@ -493,19 +493,19 @@ LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noret = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:195:20 + --> $DIR/named-asm-labels.rs:195:26 | -LL | unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) } - | ^^^^^ +LL | unsafe { naked_asm!(".Laaa: nop; ret;") } + | ^^^^^ | = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:205:24 + --> $DIR/named-asm-labels.rs:205:30 | -LL | unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) } - | ^^^^^ +LL | unsafe { naked_asm!(".Lbbb: nop; ret;") } + | ^^^^^ | = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.rs b/tests/ui/feature-gates/feature-gate-naked_functions.rs index c8802ea4c31f2..5fe0bbdc77435 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions.rs +++ b/tests/ui/feature-gates/feature-gate-naked_functions.rs @@ -6,7 +6,7 @@ use std::arch::naked_asm; #[naked] //~^ the `#[naked]` attribute is an experimental feature extern "C" fn naked() { - naked_asm!("", options(noreturn)) + naked_asm!("") //~^ ERROR use of unstable library feature 'naked_functions' //~| ERROR: requires unsafe } @@ -14,7 +14,7 @@ extern "C" fn naked() { #[naked] //~^ the `#[naked]` attribute is an experimental feature extern "C" fn naked_2() -> isize { - naked_asm!("", options(noreturn)) + naked_asm!("") //~^ ERROR use of unstable library feature 'naked_functions' //~| ERROR: requires unsafe } From bc0a9543a3e8843a908d3e256e70b75441ef2743 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 5 Sep 2024 17:48:13 +0200 Subject: [PATCH 1284/1301] more `asm!` -> `naked_asm!` in tests --- tests/assembly/aarch64-naked-fn-no-bti-prolog.rs | 4 ++-- tests/assembly/x86_64-naked-fn-no-cet-prolog.rs | 4 ++-- tests/codegen/cffi/c-variadic-naked.rs | 3 +-- tests/crashes/124375.rs | 4 ++-- .../run-make/naked-symbol-visibility/a_rust_dylib.rs | 12 ++++++------ .../feature-gate-naked_functions.stderr | 12 ++++++------ .../rfcs/rfc-2091-track-caller/error-with-naked.rs | 6 +++--- 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/assembly/aarch64-naked-fn-no-bti-prolog.rs b/tests/assembly/aarch64-naked-fn-no-bti-prolog.rs index 8ee6f6792e943..46e627eaa00bd 100644 --- a/tests/assembly/aarch64-naked-fn-no-bti-prolog.rs +++ b/tests/assembly/aarch64-naked-fn-no-bti-prolog.rs @@ -5,7 +5,7 @@ #![crate_type = "lib"] #![feature(naked_functions)] -use std::arch::asm; +use std::arch::naked_asm; // The problem at hand: Rust has adopted a fairly strict meaning for "naked functions", // meaning "no prologue whatsoever, no, really, not one instruction." @@ -17,5 +17,5 @@ use std::arch::asm; pub unsafe extern "C" fn _hlt() -> ! { // CHECK-NOT: hint #34 // CHECK: hlt #0x1 - asm!("hlt #1", options(noreturn)) + naked_asm!("hlt #1") } diff --git a/tests/assembly/x86_64-naked-fn-no-cet-prolog.rs b/tests/assembly/x86_64-naked-fn-no-cet-prolog.rs index a568387418211..54e1d93c68bd6 100644 --- a/tests/assembly/x86_64-naked-fn-no-cet-prolog.rs +++ b/tests/assembly/x86_64-naked-fn-no-cet-prolog.rs @@ -5,7 +5,7 @@ #![crate_type = "lib"] #![feature(naked_functions)] -use std::arch::asm; +use std::arch::naked_asm; // The problem at hand: Rust has adopted a fairly strict meaning for "naked functions", // meaning "no prologue whatsoever, no, really, not one instruction." @@ -17,7 +17,7 @@ use std::arch::asm; pub unsafe extern "sysv64" fn will_halt() -> ! { // CHECK-NOT: endbr{{32|64}} // CHECK: hlt - asm!("hlt", options(noreturn)) + naked_asm!("hlt") } // what about aarch64? diff --git a/tests/codegen/cffi/c-variadic-naked.rs b/tests/codegen/cffi/c-variadic-naked.rs index 807873ea3689f..24b69c5f59e21 100644 --- a/tests/codegen/cffi/c-variadic-naked.rs +++ b/tests/codegen/cffi/c-variadic-naked.rs @@ -12,8 +12,7 @@ pub unsafe extern "C" fn c_variadic(_: usize, _: ...) { // CHECK-NOT: va_start // CHECK-NOT: alloca - core::arch::asm! { + core::arch::naked_asm! { "ret", - options(noreturn), } } diff --git a/tests/crashes/124375.rs b/tests/crashes/124375.rs index 7165655178d7d..1d877caeb8bc1 100644 --- a/tests/crashes/124375.rs +++ b/tests/crashes/124375.rs @@ -3,9 +3,9 @@ //@ only-x86_64 #![crate_type = "lib"] #![feature(naked_functions)] -use std::arch::asm; +use std::arch::naked_asm; #[naked] pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { - asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); + naked_asm!("lea rax, [rdi + rsi]", "ret"); } diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs index f00123f006b24..517c8da656ad0 100644 --- a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -1,7 +1,7 @@ #![feature(naked_functions, asm_const, linkage)] #![crate_type = "dylib"] -use std::arch::asm; +use std::arch::naked_asm; pub trait TraitWithConst { const COUNT: u32; @@ -28,7 +28,7 @@ extern "C" fn private_vanilla() -> u32 { #[naked] extern "C" fn private_naked() -> u32 { - unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } + unsafe { naked_asm!("mov rax, 42", "ret") } } #[no_mangle] @@ -39,7 +39,7 @@ pub extern "C" fn public_vanilla() -> u32 { #[naked] #[no_mangle] pub extern "C" fn public_naked() -> u32 { - unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } + unsafe { naked_asm!("mov rax, 42", "ret") } } pub extern "C" fn public_vanilla_generic() -> u32 { @@ -48,7 +48,7 @@ pub extern "C" fn public_vanilla_generic() -> u32 { #[naked] pub extern "C" fn public_naked_generic() -> u32 { - unsafe { asm!("mov rax, {}", "ret", const T::COUNT, options(noreturn)) } + unsafe { naked_asm!("mov rax, {}", "ret", const T::COUNT) } } #[linkage = "external"] @@ -59,7 +59,7 @@ extern "C" fn vanilla_external_linkage() -> u32 { #[naked] #[linkage = "external"] extern "C" fn naked_external_linkage() -> u32 { - unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } + unsafe { naked_asm!("mov rax, 42", "ret") } } #[cfg(not(windows))] @@ -72,7 +72,7 @@ extern "C" fn vanilla_weak_linkage() -> u32 { #[cfg(not(windows))] #[linkage = "weak"] extern "C" fn naked_weak_linkage() -> u32 { - unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } + unsafe { naked_asm!("mov rax, 42", "ret", options(noreturn)) } } // functions that are declared in an `extern "C"` block are currently not exported diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.stderr b/tests/ui/feature-gates/feature-gate-naked_functions.stderr index 5fecc218a1622..709234eb02372 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions.stderr +++ b/tests/ui/feature-gates/feature-gate-naked_functions.stderr @@ -1,7 +1,7 @@ error[E0658]: use of unstable library feature 'naked_functions' --> $DIR/feature-gate-naked_functions.rs:9:5 | -LL | naked_asm!("", options(noreturn)) +LL | naked_asm!("") | ^^^^^^^^^ | = note: see issue #90957 for more information @@ -11,7 +11,7 @@ LL | naked_asm!("", options(noreturn)) error[E0658]: use of unstable library feature 'naked_functions' --> $DIR/feature-gate-naked_functions.rs:17:5 | -LL | naked_asm!("", options(noreturn)) +LL | naked_asm!("") | ^^^^^^^^^ | = note: see issue #90957 for more information @@ -51,16 +51,16 @@ LL | use std::arch::naked_asm; error[E0133]: use of inline assembly is unsafe and requires unsafe function or block --> $DIR/feature-gate-naked_functions.rs:9:5 | -LL | naked_asm!("", options(noreturn)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of inline assembly +LL | naked_asm!("") + | ^^^^^^^^^^^^^^ use of inline assembly | = note: inline assembly is entirely unchecked and can cause undefined behavior error[E0133]: use of inline assembly is unsafe and requires unsafe function or block --> $DIR/feature-gate-naked_functions.rs:17:5 | -LL | naked_asm!("", options(noreturn)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of inline assembly +LL | naked_asm!("") + | ^^^^^^^^^^^^^^ use of inline assembly | = note: inline assembly is entirely unchecked and can cause undefined behavior diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs index 0c73b9abf351d..0e85515fd104a 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs @@ -1,14 +1,14 @@ //@ needs-asm-support #![feature(naked_functions)] -use std::arch::asm; +use std::arch::naked_asm; #[track_caller] //~ ERROR [E0736] //~^ ERROR `#[track_caller]` requires Rust ABI #[naked] extern "C" fn f() { unsafe { - asm!("", options(noreturn)); + naked_asm!(""); } } @@ -20,7 +20,7 @@ impl S { #[naked] extern "C" fn g() { unsafe { - asm!("", options(noreturn)); + naked_asm!(""); } } } From 10fa482906ab1f2fba07b32b70457ad9444e8f63 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 5 Sep 2024 18:56:38 +0200 Subject: [PATCH 1285/1301] remove checks that are now performed during macro expansion of `naked_asm!` --- compiler/rustc_passes/messages.ftl | 6 --- compiler/rustc_passes/src/errors.rs | 15 ------ compiler/rustc_passes/src/naked_functions.rs | 53 +++----------------- 3 files changed, 8 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 1b1d1b1fb72ed..414a9c6e3b964 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -492,9 +492,6 @@ passes_naked_functions_asm_block = .label_multiple_asm = multiple `naked_asm!` invocations are not allowed in naked functions .label_non_asm = not allowed in naked functions -passes_naked_functions_asm_options = - asm options unsupported in naked functions: {$unsupported_options} - passes_naked_functions_incompatible_attribute = attribute incompatible with `#[naked]` .label = the `{$attr}` attribute is incompatible with `#[naked]` @@ -504,9 +501,6 @@ passes_naked_functions_must_naked_asm = the `asm!` macro is not allowed in naked functions .suggestion = consider using the `naked_asm!` macro instead -passes_naked_functions_operands = - only `const` and `sym` operands are supported in naked functions - passes_no_link = attribute should be applied to an `extern crate` item .label = not an `extern crate` item diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c46768ace53ab..fb47137702252 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1186,21 +1186,6 @@ impl Diagnostic<'_, G> for NakedFunctionsAsmBlock { } } -#[derive(Diagnostic)] -#[diag(passes_naked_functions_operands, code = E0787)] -pub(crate) struct NakedFunctionsOperands { - #[primary_span] - pub unsupported_operands: Vec, -} - -#[derive(Diagnostic)] -#[diag(passes_naked_functions_asm_options, code = E0787)] -pub(crate) struct NakedFunctionsAsmOptions { - #[primary_span] - pub span: Span, - pub unsupported_options: String, -} - #[derive(Diagnostic)] #[diag(passes_naked_functions_must_naked_asm, code = E0787)] pub(crate) struct NakedFunctionsMustNakedAsm { diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 6046c7284304e..fa4971cd2cc7f 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -1,11 +1,10 @@ //! Checks validity of naked functions. -use rustc_ast::InlineAsmOptions; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, HirIdSet, InlineAsmOperand, StmtKind}; +use rustc_hir::{ExprKind, HirIdSet, StmtKind}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -15,9 +14,8 @@ use rustc_span::{BytePos, Span}; use rustc_target::spec::abi::Abi; use crate::errors::{ - NakedAsmOutsideNakedFn, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, - NakedFunctionsMustNakedAsm, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, - UndefinedNakedFunctionAbi, + NakedAsmOutsideNakedFn, NakedFunctionsAsmBlock, NakedFunctionsMustNakedAsm, NoPatterns, + ParamsNotAllowed, UndefinedNakedFunctionAbi, }; pub(crate) fn provide(providers: &mut Providers) { @@ -119,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { /// Checks that function body contains a single inline assembly block. fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) { - let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; + let mut this = CheckInlineAssembly { items: Vec::new() }; this.visit_body(body); if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] { // Ok. @@ -165,8 +163,7 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body< } } -struct CheckInlineAssembly<'tcx> { - tcx: TyCtxt<'tcx>, +struct CheckInlineAssembly { items: Vec<(ItemKind, Span)>, } @@ -178,8 +175,8 @@ enum ItemKind { Err, } -impl<'tcx> CheckInlineAssembly<'tcx> { - fn check_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) { +impl CheckInlineAssembly { + fn check_expr<'tcx>(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) { match expr.kind { ExprKind::ConstBlock(..) | ExprKind::Array(..) @@ -220,7 +217,6 @@ impl<'tcx> CheckInlineAssembly<'tcx> { } rustc_ast::AsmMacro::NakedAsm => { self.items.push((ItemKind::NakedAsm, span)); - self.check_inline_asm(asm, span); } rustc_ast::AsmMacro::GlobalAsm => { // not allowed in this position @@ -237,42 +233,9 @@ impl<'tcx> CheckInlineAssembly<'tcx> { } } } - - fn check_inline_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) { - let unsupported_operands: Vec = asm - .operands - .iter() - .filter_map(|&(ref op, op_sp)| match op { - InlineAsmOperand::Const { .. } - | InlineAsmOperand::SymFn { .. } - | InlineAsmOperand::SymStatic { .. } => None, - InlineAsmOperand::In { .. } - | InlineAsmOperand::Out { .. } - | InlineAsmOperand::InOut { .. } - | InlineAsmOperand::SplitInOut { .. } - | InlineAsmOperand::Label { .. } => Some(op_sp), - }) - .collect(); - if !unsupported_operands.is_empty() { - self.tcx.dcx().emit_err(NakedFunctionsOperands { unsupported_operands }); - } - - let unsupported_options = asm.options.difference(InlineAsmOptions::NAKED_OPTIONS); - if !unsupported_options.is_empty() { - self.tcx.dcx().emit_err(NakedFunctionsAsmOptions { - span, - unsupported_options: unsupported_options - .human_readable_names() - .into_iter() - .map(|name| format!("`{name}`")) - .collect::>() - .join(", "), - }); - } - } } -impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> { +impl<'tcx> Visitor<'tcx> for CheckInlineAssembly { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { match stmt.kind { StmtKind::Item(..) => {} From 5fc60d1e52ea12f53d2c8d22fee94592860739ad Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 5 Sep 2024 19:45:40 +0200 Subject: [PATCH 1286/1301] various fixes for `naked_asm!` implementation - fix for divergence - fix error message - fix another cranelift test - fix some cranelift things - don't set the NORETURN option for naked asm - fix use of naked_asm! in doc comment - fix use of naked_asm! in run-make test - use `span_bug` in unreachable branch --- compiler/rustc_ast/src/ast.rs | 12 ++++++-- compiler/rustc_borrowck/src/lib.rs | 1 + .../src/polonius/loan_invalidations.rs | 1 + compiler/rustc_builtin_macros/src/asm.rs | 23 +++++--------- .../example/mini_core.rs | 6 ++++ .../example/mini_core_hello_world.rs | 2 +- compiler/rustc_codegen_cranelift/src/base.rs | 3 ++ compiler/rustc_codegen_ssa/src/mir/block.rs | 13 ++++---- .../rustc_const_eval/src/interpret/machine.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 6 +--- compiler/rustc_lint_defs/src/builtin.rs | 6 ++-- compiler/rustc_middle/src/mir/pretty.rs | 6 ++-- compiler/rustc_middle/src/mir/syntax.rs | 24 ++++++++++++++- compiler/rustc_middle/src/mir/terminator.rs | 1 + compiler/rustc_middle/src/mir/visit.rs | 1 + compiler/rustc_middle/src/thir.rs | 3 +- compiler/rustc_middle/src/thir/visit.rs | 8 ++++- .../rustc_mir_build/src/build/expr/into.rs | 19 ++++++++---- compiler/rustc_mir_build/src/thir/cx/expr.rs | 1 + compiler/rustc_mir_build/src/thir/print.rs | 4 ++- .../src/move_paths/builder.rs | 1 + compiler/rustc_passes/messages.ftl | 2 +- compiler/rustc_passes/src/errors.rs | 3 +- compiler/rustc_passes/src/naked_functions.rs | 30 ++++++++----------- .../rustc_smir/src/rustc_smir/convert/mir.rs | 1 + src/tools/rustfmt/src/parse/macros/asm.rs | 2 +- tests/codegen/naked-asan.rs | 2 +- .../naked-symbol-visibility/a_rust_dylib.rs | 2 +- tests/ui/asm/naked-functions.stderr | 4 +-- 29 files changed, 116 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index cb71521317637..733c2d931144d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2435,7 +2435,7 @@ pub enum AsmMacro { } impl AsmMacro { - pub const fn macro_name(&self) -> &'static str { + pub const fn macro_name(self) -> &'static str { match self { AsmMacro::Asm => "asm", AsmMacro::GlobalAsm => "global_asm", @@ -2443,13 +2443,21 @@ impl AsmMacro { } } - pub const fn is_supported_option(&self, option: InlineAsmOptions) -> bool { + pub const fn is_supported_option(self, option: InlineAsmOptions) -> bool { match self { AsmMacro::Asm => true, AsmMacro::GlobalAsm => InlineAsmOptions::GLOBAL_OPTIONS.contains(option), AsmMacro::NakedAsm => InlineAsmOptions::NAKED_OPTIONS.contains(option), } } + + pub const fn diverges(self, options: InlineAsmOptions) -> bool { + match self { + AsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN), + AsmMacro::GlobalAsm => true, + AsmMacro::NakedAsm => true, + } + } } /// Inline assembly. diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 3b0b3ee1a745a..fad4d790be4a8 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -742,6 +742,7 @@ impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R> } TerminatorKind::InlineAsm { + asm_macro: _, template: _, operands, options: _, diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index afd811a0efb43..d1b65943199c4 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -169,6 +169,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { } } TerminatorKind::InlineAsm { + asm_macro: _, template: _, operands, options: _, diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 515ac17f70a14..9ae48024f445b 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -850,22 +850,13 @@ pub(super) fn expand_naked_asm<'cx>( return ExpandResult::Retry(()); }; let expr = match mac { - Ok(mut inline_asm) => { - // for future compatibility, we always set the NORETURN option. - // - // When we turn `asm!` into `naked_asm!` with this implementation, we can drop - // the `options(noreturn)`, which makes the upgrade smooth when `naked_asm!` - // starts disallowing the `noreturn` option in the future - inline_asm.options |= ast::InlineAsmOptions::NORETURN; - - P(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::InlineAsm(P(inline_asm)), - span: sp, - attrs: ast::AttrVec::new(), - tokens: None, - }) - } + Ok(inline_asm) => P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::InlineAsm(P(inline_asm)), + span: sp, + attrs: ast::AttrVec::new(), + tokens: None, + }), Err(guar) => DummyResult::raw_expr(sp, Some(guar)), }; MacEager::expr(expr) diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 5e535ff62e173..9fc0318df5dc0 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -726,6 +726,12 @@ pub macro global_asm() { /* compiler built-in */ } +#[rustc_builtin_macro] +#[rustc_macro_transparency = "semitransparent"] +pub macro naked_asm() { + /* compiler built-in */ +} + pub static A_STATIC: u8 = 42; #[lang = "panic_location"] diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index ccbd5a78485d7..e47431e0f873a 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -390,7 +390,7 @@ global_asm! { #[naked] extern "C" fn naked_test() { unsafe { - asm!("ret", options(noreturn)); + naked_asm!("ret"); } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 0968062206940..a681e6d9f3cd1 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -8,6 +8,7 @@ use rustc_ast::InlineAsmOptions; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::InlineAsmMacro; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::FnAbiOf; @@ -57,6 +58,7 @@ pub(crate) fn codegen_fn<'tcx>( match &mir.basic_blocks[START_BLOCK].terminator().kind { TerminatorKind::InlineAsm { + asm_macro: InlineAsmMacro::NakedAsm, template, operands, options, @@ -498,6 +500,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { "tail calls are not yet supported in `rustc_codegen_cranelift` backend" ), TerminatorKind::InlineAsm { + asm_macro: _, template, operands, options, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 125d3b908c733..be9a6d9a90eed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -3,7 +3,9 @@ use std::cmp; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::lang_items::LangItem; -use rustc_middle::mir::{self, AssertKind, BasicBlock, SwitchTargets, UnwindTerminateReason}; +use rustc_middle::mir::{ + self, AssertKind, BasicBlock, InlineAsmMacro, SwitchTargets, UnwindTerminateReason, +}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; @@ -1133,6 +1135,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &mut self, helper: TerminatorCodegenHelper<'tcx>, bx: &mut Bx, + asm_macro: InlineAsmMacro, terminator: &mir::Terminator<'tcx>, template: &[ast::InlineAsmTemplatePiece], operands: &[mir::InlineAsmOperand<'tcx>], @@ -1203,11 +1206,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &operands, options, line_spans, - if options.contains(InlineAsmOptions::NORETURN) { - None - } else { - targets.get(0).copied() - }, + if asm_macro.diverges(options) { None } else { targets.get(0).copied() }, unwind, instance, mergeable_succ, @@ -1381,6 +1380,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::TerminatorKind::InlineAsm { + asm_macro, template, ref operands, options, @@ -1390,6 +1390,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } => self.codegen_asm_terminator( helper, bx, + asm_macro, terminator, template, operands, diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 0f796c3122210..89d49ba046e7d 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -395,7 +395,7 @@ pub trait Machine<'tcx>: Sized { /// /// This should take care of jumping to the next block (one of `targets`) when asm goto /// is triggered, `targets[0]` when the assembly falls through, or diverge in case of - /// `InlineAsmOptions::NORETURN` being set. + /// naked_asm! or `InlineAsmOptions::NORETURN` being set. fn eval_inline_asm( _ecx: &mut InterpCx<'tcx, Self>, _template: &'tcx [InlineAsmTemplatePiece], diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5c2f39f879c4c..95ca3e6647272 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3507,11 +3507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { - let mut diverge = match asm.asm_macro { - rustc_ast::AsmMacro::Asm => asm.options.contains(ast::InlineAsmOptions::NORETURN), - rustc_ast::AsmMacro::GlobalAsm => true, - rustc_ast::AsmMacro::NakedAsm => true, - }; + let mut diverge = asm.asm_macro.diverges(asm.options); for (op, _op_sp) in asm.operands { match op { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index bd87019508be9..a439514e16f87 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2926,16 +2926,16 @@ declare_lint! { /// ```rust /// #![feature(asm_experimental_arch, naked_functions)] /// - /// use std::arch::asm; + /// use std::arch::naked_asm; /// /// #[naked] /// pub fn default_abi() -> u32 { - /// unsafe { asm!("", options(noreturn)); } + /// unsafe { naked_asm!(""); } /// } /// /// #[naked] /// pub extern "Rust" fn rust_abi() -> u32 { - /// unsafe { asm!("", options(noreturn)); } + /// unsafe { naked_asm!(""); } /// } /// ``` /// diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 4878956521831..9cafe804a8ee9 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -4,7 +4,7 @@ use std::fs; use std::io::{self, Write as _}; use std::path::{Path, PathBuf}; -use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_ast::InlineAsmTemplatePiece; use rustc_middle::mir::interpret::{ AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer, Provenance, alloc_range, read_target_uint, @@ -1024,9 +1024,9 @@ impl<'tcx> TerminatorKind<'tcx> { vec!["real".into(), "unwind".into()] } FalseUnwind { unwind: _, .. } => vec!["real".into()], - InlineAsm { options, ref targets, unwind, .. } => { + InlineAsm { asm_macro, options, ref targets, unwind, .. } => { let mut vec = Vec::with_capacity(targets.len() + 1); - if !options.contains(InlineAsmOptions::NORETURN) { + if !asm_macro.diverges(options) { vec.push("return".into()); } vec.resize(targets.len(), "label".into()); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 1722a7a1f358f..c610fac80f68f 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -605,6 +605,25 @@ impl CallSource { } } +#[derive(Clone, Copy, Debug, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] +#[derive(TypeFoldable, TypeVisitable)] +/// The macro that an inline assembly block was created by +pub enum InlineAsmMacro { + /// The `asm!` macro + Asm, + /// The `naked_asm!` macro + NakedAsm, +} + +impl InlineAsmMacro { + pub const fn diverges(self, options: InlineAsmOptions) -> bool { + match self { + InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN), + InlineAsmMacro::NakedAsm => true, + } + } +} + /////////////////////////////////////////////////////////////////////////// // Terminators @@ -859,6 +878,9 @@ pub enum TerminatorKind<'tcx> { /// Block ends with an inline assembly block. This is a terminator since /// inline assembly is allowed to diverge. InlineAsm { + /// Macro used to create this inline asm: one of `asm!` or `naked_asm!` + asm_macro: InlineAsmMacro, + /// The template for the inline assembly, with placeholders. template: &'tcx [InlineAsmTemplatePiece], @@ -874,7 +896,7 @@ pub enum TerminatorKind<'tcx> { /// Valid targets for the inline assembly. /// The first element is the fallthrough destination, unless - /// InlineAsmOptions::NORETURN is set. + /// asm_macro == InlineAsmMacro::NakedAsm or InlineAsmOptions::NORETURN is set. targets: Box<[BasicBlock]>, /// Action to be taken if the inline assembly unwinds. This is present diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 783952fb9cb8a..b919f5726db58 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -666,6 +666,7 @@ impl<'tcx> TerminatorKind<'tcx> { }, InlineAsm { + asm_macro: _, template: _, ref operands, options: _, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 64898a8495e26..9f9ee8497b60b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -576,6 +576,7 @@ macro_rules! make_mir_visitor { } TerminatorKind::InlineAsm { + asm_macro: _, template: _, operands, options: _, diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e614d41899a0e..fe865b8a51508 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -12,7 +12,7 @@ use std::cmp::Ordering; use std::fmt; use std::ops::Index; -use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; @@ -173,6 +173,7 @@ pub struct ClosureExpr<'tcx> { #[derive(Clone, Debug, HashStable)] pub struct InlineAsmExpr<'tcx> { + pub asm_macro: AsmMacro, pub template: &'tcx [InlineAsmTemplatePiece], pub operands: Box<[InlineAsmOperand<'tcx>]>, pub options: InlineAsmOptions, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 58e2ebaeaf8df..36f0e3d890cfd 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -148,7 +148,13 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( NamedConst { def_id: _, args: _, user_ty: _ } => {} ConstParam { param: _, def_id: _ } => {} StaticRef { alloc_id: _, ty: _, def_id: _ } => {} - InlineAsm(box InlineAsmExpr { ref operands, template: _, options: _, line_spans: _ }) => { + InlineAsm(box InlineAsmExpr { + asm_macro: _, + ref operands, + template: _, + options: _, + line_spans: _, + }) => { for op in &**operands { use InlineAsmOperand::*; match op { diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 86fe447f39973..dc317feb20c4c 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -2,7 +2,7 @@ use std::iter; -use rustc_ast::InlineAsmOptions; +use rustc_ast::{AsmMacro, InlineAsmOptions}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; @@ -384,6 +384,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } ExprKind::InlineAsm(box InlineAsmExpr { + asm_macro, template, ref operands, options, @@ -392,11 +393,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { use rustc_middle::{mir, thir}; let destination_block = this.cfg.start_new_block(); - let mut targets = if options.contains(InlineAsmOptions::NORETURN) { - vec![] - } else { - vec![destination_block] - }; + let mut targets = + if asm_macro.diverges(options) { vec![] } else { vec![destination_block] }; let operands = operands .into_iter() @@ -474,7 +472,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign_unit(block, source_info, destination, this.tcx); } + let asm_macro = match asm_macro { + AsmMacro::Asm => InlineAsmMacro::Asm, + AsmMacro::GlobalAsm => { + span_bug!(expr_span, "unexpected global_asm! in inline asm") + } + AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm, + }; + this.cfg.terminate(block, source_info, TerminatorKind::InlineAsm { + asm_macro, template, operands, options, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 2ffad0b4834a0..abf486af9623e 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -672,6 +672,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::InlineAsm(asm) => ExprKind::InlineAsm(Box::new(InlineAsmExpr { + asm_macro: asm.asm_macro, template: asm.template, operands: asm .operands diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 61317925d09e5..dae13df4054a4 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -818,10 +818,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { } fn print_inline_asm_expr(&mut self, expr: &InlineAsmExpr<'tcx>, depth_lvl: usize) { - let InlineAsmExpr { template, operands, options, line_spans } = expr; + let InlineAsmExpr { asm_macro, template, operands, options, line_spans } = expr; print_indented!(self, "InlineAsmExpr {", depth_lvl); + print_indented!(self, format!("asm_macro: {:?}", asm_macro), depth_lvl + 1); + print_indented!(self, "template: [", depth_lvl + 1); for template_piece in template.iter() { print_indented!(self, format!("{:?}", template_piece), depth_lvl + 2); diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 3c8be2f73e14a..162245cb95060 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -481,6 +481,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { } } TerminatorKind::InlineAsm { + asm_macro: _, template: _, ref operands, options: _, diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 414a9c6e3b964..be76d6cef2b88 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -499,7 +499,7 @@ passes_naked_functions_incompatible_attribute = passes_naked_functions_must_naked_asm = the `asm!` macro is not allowed in naked functions - .suggestion = consider using the `naked_asm!` macro instead + .label = consider using the `naked_asm!` macro instead passes_no_link = attribute should be applied to an `extern crate` item diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index fb47137702252..4f00c90fa3b0f 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1190,9 +1190,8 @@ impl Diagnostic<'_, G> for NakedFunctionsAsmBlock { #[diag(passes_naked_functions_must_naked_asm, code = E0787)] pub(crate) struct NakedFunctionsMustNakedAsm { #[primary_span] + #[label] pub span: Span, - #[suggestion(code = "naked_asm!", applicability = "machine-applicable")] - pub macro_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index fa4971cd2cc7f..b2f8d7dadff37 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -7,10 +7,11 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirIdSet, StmtKind}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::query::Providers; +use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; +use rustc_span::Span; use rustc_span::symbol::sym; -use rustc_span::{BytePos, Span}; use rustc_target::spec::abi::Abi; use crate::errors::{ @@ -137,10 +138,7 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body< ItemKind::InlineAsm => { has_err = true; - // the span that contains the `asm!` call, - // so tooling can replace it with `naked_asm!` - let macro_span = span.with_hi(span.lo() + BytePos("asm!".len() as u32)); - tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span, macro_span }); + tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span }); } ItemKind::NonAsm => { must_show_error = true; @@ -210,19 +208,17 @@ impl CheckInlineAssembly { self.items.push((ItemKind::NonAsm, span)); } - ExprKind::InlineAsm(asm) => { - match asm.asm_macro { - rustc_ast::AsmMacro::Asm => { - self.items.push((ItemKind::InlineAsm, span)); - } - rustc_ast::AsmMacro::NakedAsm => { - self.items.push((ItemKind::NakedAsm, span)); - } - rustc_ast::AsmMacro::GlobalAsm => { - // not allowed in this position - } + ExprKind::InlineAsm(asm) => match asm.asm_macro { + rustc_ast::AsmMacro::Asm => { + self.items.push((ItemKind::InlineAsm, span)); } - } + rustc_ast::AsmMacro::NakedAsm => { + self.items.push((ItemKind::NakedAsm, span)); + } + rustc_ast::AsmMacro::GlobalAsm => { + span_bug!(span, "`global_asm!` is not allowed in this position") + } + }, ExprKind::DropTemps(..) | ExprKind::Block(..) => { hir::intravisit::walk_expr(self, expr); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 0dbbc338e738b..dbae4b7e71919 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -655,6 +655,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { } } mir::TerminatorKind::InlineAsm { + asm_macro: _, template, operands, options, diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 0c37d12490e2f..58c8d21bd7a4c 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,5 +1,5 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; +use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args}; use crate::rewrite::RewriteContext; diff --git a/tests/codegen/naked-asan.rs b/tests/codegen/naked-asan.rs index ac36018eed3a1..bcaa60baeffd8 100644 --- a/tests/codegen/naked-asan.rs +++ b/tests/codegen/naked-asan.rs @@ -14,7 +14,7 @@ #[no_mangle] pub extern "x86-interrupt" fn page_fault_handler(_: u64, _: u64) { unsafe { - core::arch::asm!("ud2", options(noreturn)); + core::arch::naked_asm!("ud2"); } } diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs index 517c8da656ad0..8dd19e613bff8 100644 --- a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -72,7 +72,7 @@ extern "C" fn vanilla_weak_linkage() -> u32 { #[cfg(not(windows))] #[linkage = "weak"] extern "C" fn naked_weak_linkage() -> u32 { - unsafe { naked_asm!("mov rax, 42", "ret", options(noreturn)) } + unsafe { naked_asm!("mov rax, 42", "ret") } } // functions that are declared in an `extern "C"` block are currently not exported diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index 1ceeafffd9091..0898f3620f24f 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -74,9 +74,7 @@ error[E0787]: the `asm!` macro is not allowed in naked functions --> $DIR/naked-functions.rs:13:5 | LL | asm!("", options(raw)); - | ----^^^^^^^^^^^^^^^^^^ - | | - | help: consider using the `naked_asm!` macro instead: `naked_asm!` + | ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead error: patterns not allowed in naked function parameters --> $DIR/naked-functions.rs:25:5 From 3b2be4457da8521ab0a8a9cd7e11b1c24b0de191 Mon Sep 17 00:00:00 2001 From: dacian Date: Sun, 6 Oct 2024 20:37:10 +0300 Subject: [PATCH 1287/1301] grammar fix --- library/core/src/intrinsics/mir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index fb0aa5398a55b..a2ab39caade53 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -213,7 +213,7 @@ //! - All other locals need to be declared with `let` somewhere and then can be accessed by name. //! //! #### Places -//! - Locals implicit convert to places. +//! - Locals implicitly convert to places. //! - Field accesses, derefs, and indexing work normally. //! - Fields in variants can be accessed via the [`Variant`] and [`Field`] associated functions, //! see their documentation for details. From cfeee7bcbf182a6c22083c7dfb56c6034a65d0a0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Oct 2024 16:08:09 +1000 Subject: [PATCH 1288/1301] Remove unused features. --- compiler/rustc_infer/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 934484bf9158a..a04b2bb2b08b5 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -19,10 +19,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] -#![feature(box_patterns)] #![feature(extend_one)] -#![feature(if_let_guard)] -#![feature(iter_intersperse)] #![feature(iterator_try_collect)] #![feature(let_chains)] #![feature(rustdoc_internals)] From 7a9bbd05c42f1c6b0d920a4e741ea28452916c99 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Oct 2024 16:11:48 +1000 Subject: [PATCH 1289/1301] Rename `errors/mod.rs` as `errors.rs`. It's simpler, for this tiny module. --- compiler/rustc_infer/src/{errors/mod.rs => errors.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/rustc_infer/src/{errors/mod.rs => errors.rs} (100%) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors.rs similarity index 100% rename from compiler/rustc_infer/src/errors/mod.rs rename to compiler/rustc_infer/src/errors.rs From 5486d72e7783271f457649e4e865a821becf27a2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Oct 2024 16:18:34 +1000 Subject: [PATCH 1290/1301] Reduce visibilities. Three of the modules don't need to be `pub`, and then `warn(unreachable_pub)` identifies a bunch more things that also shouldn't be `pub`, plus a couple of things that are unused. --- compiler/rustc_infer/src/infer/mod.rs | 6 +-- .../rustc_infer/src/infer/opaque_types/mod.rs | 24 +++------- .../src/infer/opaque_types/table.rs | 2 +- .../rustc_infer/src/infer/type_variable.rs | 45 +++++++------------ 4 files changed, 25 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5fa1bf51634a3..87dae0c1e0960 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -56,17 +56,17 @@ use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligat pub mod at; pub mod canonical; mod context; -pub mod free_regions; +mod free_regions; mod freshen; mod lexical_region_resolve; -pub mod opaque_types; +mod opaque_types; pub mod outlives; mod projection; pub mod region_constraints; pub mod relate; pub mod resolve; pub(crate) mod snapshot; -pub mod type_variable; +mod type_variable; #[must_use] #[derive(Debug)] diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 55c51bc856f8e..0c5eca6ebdfd9 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -19,8 +19,8 @@ use crate::traits::{self, Obligation}; mod table; -pub type OpaqueTypeMap<'tcx> = FxIndexMap, OpaqueTypeDecl<'tcx>>; -pub use table::{OpaqueTypeStorage, OpaqueTypeTable}; +pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap, OpaqueTypeDecl<'tcx>>; +pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; use super::DefineOpaqueTypes; @@ -377,9 +377,9 @@ impl<'tcx> InferCtxt<'tcx> { /// /// We ignore any type parameters because impl trait values are assumed to /// capture all the in-scope type parameters. -pub struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { - pub tcx: TyCtxt<'tcx>, - pub op: OP, +struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { + tcx: TyCtxt<'tcx>, + op: OP, } impl<'tcx, OP> TypeVisitor> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> @@ -455,20 +455,6 @@ where } } -pub enum UseKind { - DefiningUse, - OpaqueUse, -} - -impl UseKind { - pub fn is_defining(self) -> bool { - match self { - UseKind::DefiningUse => true, - UseKind::OpaqueUse => false, - } - } -} - impl<'tcx> InferCtxt<'tcx> { #[instrument(skip(self), level = "debug")] fn register_hidden_type( diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index 4aa2ccab0e7bb..6e4cc65bec339 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -7,7 +7,7 @@ use super::{OpaqueTypeDecl, OpaqueTypeMap}; use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}; #[derive(Default, Debug, Clone)] -pub struct OpaqueTypeStorage<'tcx> { +pub(crate) struct OpaqueTypeStorage<'tcx> { /// Opaque types found in explicit return types and their /// associated fresh inference variable. Writeback resolves these /// variables to get the concrete type, which can be used to diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 7eb2c20e0d8e7..c50477b2922be 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -20,7 +20,7 @@ impl<'tcx> Rollback>>> for TypeVariabl } #[derive(Clone)] -pub struct TypeVariableStorage<'tcx> { +pub(crate) struct TypeVariableStorage<'tcx> { /// The origins of each type variable. values: IndexVec, /// Two variables are unified in `eq_relations` when we have a @@ -29,7 +29,7 @@ pub struct TypeVariableStorage<'tcx> { eq_relations: ut::UnificationTableStorage>, } -pub struct TypeVariableTable<'a, 'tcx> { +pub(crate) struct TypeVariableTable<'a, 'tcx> { storage: &'a mut TypeVariableStorage<'tcx>, undo_log: &'a mut InferCtxtUndoLogs<'tcx>, @@ -50,7 +50,7 @@ pub(crate) struct TypeVariableData { } #[derive(Copy, Clone, Debug)] -pub enum TypeVariableValue<'tcx> { +pub(crate) enum TypeVariableValue<'tcx> { Known { value: Ty<'tcx> }, Unknown { universe: ty::UniverseIndex }, } @@ -58,14 +58,14 @@ pub enum TypeVariableValue<'tcx> { impl<'tcx> TypeVariableValue<'tcx> { /// If this value is known, returns the type it is known to be. /// Otherwise, `None`. - pub fn known(&self) -> Option> { + pub(crate) fn known(&self) -> Option> { match *self { TypeVariableValue::Unknown { .. } => None, TypeVariableValue::Known { value } => Some(value), } } - pub fn is_unknown(&self) -> bool { + pub(crate) fn is_unknown(&self) -> bool { match *self { TypeVariableValue::Unknown { .. } => true, TypeVariableValue::Known { .. } => false, @@ -74,7 +74,7 @@ impl<'tcx> TypeVariableValue<'tcx> { } impl<'tcx> TypeVariableStorage<'tcx> { - pub fn new() -> TypeVariableStorage<'tcx> { + pub(crate) fn new() -> TypeVariableStorage<'tcx> { TypeVariableStorage { values: Default::default(), eq_relations: ut::UnificationTableStorage::new(), @@ -105,14 +105,14 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// /// Note that this function does not return care whether /// `vid` has been unified with something else or not. - pub fn var_origin(&self, vid: ty::TyVid) -> TypeVariableOrigin { + pub(crate) fn var_origin(&self, vid: ty::TyVid) -> TypeVariableOrigin { self.storage.values[vid].origin } /// Records that `a == b`, depending on `dir`. /// /// Precondition: neither `a` nor `b` are known. - pub fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { + pub(crate) fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); self.eq_relations().union(a, b); @@ -121,7 +121,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Instantiates `vid` with the type `ty`. /// /// Precondition: `vid` must not have been previously instantiated. - pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) { + pub(crate) fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) { let vid = self.root_var(vid); debug_assert!(!ty.is_ty_var(), "instantiating ty var with var: {vid:?} {ty:?}"); debug_assert!(self.probe(vid).is_unknown()); @@ -143,7 +143,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// - `origin`: indicates *why* the type variable was created. /// The code in this module doesn't care, but it can be useful /// for improving error messages. - pub fn new_var( + pub(crate) fn new_var( &mut self, universe: ty::UniverseIndex, origin: TypeVariableOrigin, @@ -158,7 +158,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { } /// Returns the number of type variables created thus far. - pub fn num_vars(&self) -> usize { + pub(crate) fn num_vars(&self) -> usize { self.storage.values.len() } @@ -167,42 +167,29 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// will yield the same root variable (per the union-find /// algorithm), so `root_var(a) == root_var(b)` implies that `a == /// b` (transitively). - pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + pub(crate) fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.eq_relations().find(vid).vid } /// Retrieves the type to which `vid` has been instantiated, if /// any. - pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { + pub(crate) fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { self.inlined_probe(vid) } /// An always-inlined variant of `probe`, for very hot call sites. #[inline(always)] - pub fn inlined_probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { + pub(crate) fn inlined_probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { self.eq_relations().inlined_probe_value(vid) } - /// If `t` is a type-inference variable, and it has been - /// instantiated, then return the with which it was - /// instantiated. Otherwise, returns `t`. - pub fn replace_if_possible(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { - ty::Infer(ty::TyVar(v)) => match self.probe(v) { - TypeVariableValue::Unknown { .. } => t, - TypeVariableValue::Known { value } => value, - }, - _ => t, - } - } - #[inline] fn eq_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidEqKey<'tcx>> { self.storage.eq_relations.with_log(self.undo_log) } /// Returns a range of the type variables created during the snapshot. - pub fn vars_since_snapshot( + pub(crate) fn vars_since_snapshot( &mut self, value_count: usize, ) -> (Range, Vec) { @@ -215,7 +202,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Returns indices of all variables that are not yet /// instantiated. - pub fn unresolved_variables(&mut self) -> Vec { + pub(crate) fn unresolved_variables(&mut self) -> Vec { (0..self.num_vars()) .filter_map(|i| { let vid = ty::TyVid::from_usize(i); From 5c015eed473ff648ed5febc1f8eac4d0553af845 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Oct 2024 16:36:38 +1000 Subject: [PATCH 1291/1301] Remove unused `UnitResult` type. --- compiler/rustc_infer/src/infer/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 87dae0c1e0960..c98604de83925 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -50,7 +50,6 @@ use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; -use crate::infer::relate::RelateResult; use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine}; pub mod at; @@ -76,7 +75,6 @@ pub struct InferOk<'tcx, T> { } pub type InferResult<'tcx, T> = Result, TypeError<'tcx>>; -pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult = Result; // "fixup result" pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable< From 2d2755ff97d4c1909dfcd3e2c1fce806a6550919 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Oct 2024 16:40:22 +1000 Subject: [PATCH 1292/1301] Reduce visibilities some more. It helps people reading the code understand how widely things are used. --- compiler/rustc_infer/src/infer/mod.rs | 9 +++++---- .../rustc_infer/src/infer/opaque_types/table.rs | 2 +- compiler/rustc_infer/src/infer/outlives/mod.rs | 2 +- .../rustc_infer/src/infer/outlives/verify.rs | 12 ++++++------ .../src/infer/region_constraints/mod.rs | 2 +- compiler/rustc_infer/src/infer/relate/combine.rs | 16 ++++++++-------- compiler/rustc_infer/src/infer/relate/mod.rs | 2 +- .../src/infer/relate/type_relating.rs | 4 ++-- compiler/rustc_infer/src/infer/snapshot/mod.rs | 2 +- 9 files changed, 26 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c98604de83925..8714e85411493 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -14,7 +14,8 @@ use region_constraints::{ GenericKind, RegionConstraintCollector, RegionConstraintStorage, VarInfos, VerifyBound, }; pub use relate::StructurallyRelateAliases; -pub use relate::combine::{CombineFields, PredicateEmittingRelation}; +use relate::combine::CombineFields; +pub use relate::combine::PredicateEmittingRelation; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::sync::Lrc; @@ -75,7 +76,7 @@ pub struct InferOk<'tcx, T> { } pub type InferResult<'tcx, T> = Result, TypeError<'tcx>>; -pub type FixupResult = Result; // "fixup result" +pub(crate) type FixupResult = Result; // "fixup result" pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable< ut::InPlace, &'a mut InferCtxtUndoLogs<'tcx>>, @@ -200,7 +201,7 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - pub fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'tcx> { + fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'tcx> { self.opaque_type_storage.with_log(&mut self.undo_log) } @@ -1351,7 +1352,7 @@ impl<'tcx> InferCtxt<'tcx> { } /// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method. - pub fn verify_generic_bound( + pub(crate) fn verify_generic_bound( &self, origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index 6e4cc65bec339..047d8edad3de7 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -46,7 +46,7 @@ impl<'tcx> Drop for OpaqueTypeStorage<'tcx> { } } -pub struct OpaqueTypeTable<'a, 'tcx> { +pub(crate) struct OpaqueTypeTable<'a, 'tcx> { storage: &'a mut OpaqueTypeStorage<'tcx>, undo_log: &'a mut InferCtxtUndoLogs<'tcx>, diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index f5c873b037552..a270f9322f39b 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -14,7 +14,7 @@ pub mod env; pub mod for_liveness; pub mod obligations; pub mod test_type_match; -pub mod verify; +pub(crate) mod verify; #[instrument(level = "debug", skip(param_env), ret)] pub fn explicit_outlives_bounds<'tcx>( diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 74a80a1b9aa7c..247fbc259652e 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -15,7 +15,7 @@ use crate::infer::{GenericKind, VerifyBound}; /// via a "delegate" of type `D` -- this is usually the `infcx`, which /// accrues them into the `region_obligations` code, but for NLL we /// use something else. -pub struct VerifyBoundCx<'cx, 'tcx> { +pub(crate) struct VerifyBoundCx<'cx, 'tcx> { tcx: TyCtxt<'tcx>, region_bound_pairs: &'cx RegionBoundPairs<'tcx>, /// During borrowck, if there are no outlives bounds on a generic @@ -28,7 +28,7 @@ pub struct VerifyBoundCx<'cx, 'tcx> { } impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { - pub fn new( + pub(crate) fn new( tcx: TyCtxt<'tcx>, region_bound_pairs: &'cx RegionBoundPairs<'tcx>, implicit_region_bound: Option>, @@ -38,7 +38,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } #[instrument(level = "debug", skip(self))] - pub fn param_or_placeholder_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + pub(crate) fn param_or_placeholder_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { // Start with anything like `T: 'a` we can scrape from the // environment. If the environment contains something like // `for<'a> T: 'a`, then we know that `T` outlives everything. @@ -92,7 +92,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// the clause from the environment only applies if `'0 = 'a`, /// which we don't know yet. But we would still include `'b` in /// this list. - pub fn approx_declared_bounds_from_env( + pub(crate) fn approx_declared_bounds_from_env( &self, alias_ty: ty::AliasTy<'tcx>, ) -> Vec> { @@ -101,7 +101,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } #[instrument(level = "debug", skip(self))] - pub fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> { + pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> { let alias_ty_as_ty = alias_ty.to_ty(self.tcx); // Search the env for where clauses like `P: 'a`. @@ -285,7 +285,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// This is for simplicity, and because we are not really smart /// enough to cope with such bounds anywhere. - pub fn declared_bounds_from_definition( + pub(crate) fn declared_bounds_from_definition( &self, alias_ty: ty::AliasTy<'tcx>, ) -> impl Iterator> { diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 82f7668b2d2ec..a6d1d05f6e1c8 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -304,7 +304,7 @@ pub struct RegionVariableInfo { pub universe: ty::UniverseIndex, } -pub struct RegionSnapshot { +pub(crate) struct RegionSnapshot { any_unifications: bool, } diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 4a8c7387ddc6c..450437f217647 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -33,7 +33,7 @@ use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace, relate}; use crate::traits::{Obligation, PredicateObligation}; #[derive(Clone)] -pub struct CombineFields<'infcx, 'tcx> { +pub(crate) struct CombineFields<'infcx, 'tcx> { pub infcx: &'infcx InferCtxt<'tcx>, // Immutable fields pub trace: TypeTrace<'tcx>, @@ -47,7 +47,7 @@ pub struct CombineFields<'infcx, 'tcx> { } impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { - pub fn new( + pub(crate) fn new( infcx: &'infcx InferCtxt<'tcx>, trace: TypeTrace<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -283,22 +283,22 @@ impl<'tcx> InferCtxt<'tcx> { } impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { - pub fn tcx(&self) -> TyCtxt<'tcx> { + pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } - pub fn equate<'a>( + pub(crate) fn equate<'a>( &'a mut self, structurally_relate_aliases: StructurallyRelateAliases, ) -> TypeRelating<'a, 'infcx, 'tcx> { TypeRelating::new(self, structurally_relate_aliases, ty::Invariant) } - pub fn sub<'a>(&'a mut self) -> TypeRelating<'a, 'infcx, 'tcx> { + pub(crate) fn sub<'a>(&'a mut self) -> TypeRelating<'a, 'infcx, 'tcx> { TypeRelating::new(self, StructurallyRelateAliases::No, ty::Covariant) } - pub fn sup<'a>(&'a mut self) -> TypeRelating<'a, 'infcx, 'tcx> { + pub(crate) fn sup<'a>(&'a mut self) -> TypeRelating<'a, 'infcx, 'tcx> { TypeRelating::new(self, StructurallyRelateAliases::No, ty::Contravariant) } @@ -310,14 +310,14 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { LatticeOp::new(self, LatticeOpKind::Glb) } - pub fn register_obligations( + pub(crate) fn register_obligations( &mut self, obligations: impl IntoIterator>>, ) { self.goals.extend(obligations); } - pub fn register_predicates( + pub(crate) fn register_predicates( &mut self, obligations: impl IntoIterator, ty::Predicate<'tcx>>>, ) { diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index edc0c4f078ab2..85158faa65d13 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -5,7 +5,7 @@ pub use rustc_middle::ty::relate::RelateResult; pub use rustc_next_trait_solver::relate::*; -pub use self::combine::{CombineFields, PredicateEmittingRelation}; +pub use self::combine::PredicateEmittingRelation; #[allow(hidden_glob_reexports)] pub(super) mod combine; diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 7acfea643dd1e..7537ceec307a3 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -13,7 +13,7 @@ use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases} use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; /// Enforce that `a` is equal to or a subtype of `b`. -pub struct TypeRelating<'combine, 'a, 'tcx> { +pub(crate) struct TypeRelating<'combine, 'a, 'tcx> { // Immutable except for the `InferCtxt` and the // resulting nested `goals`. fields: &'combine mut CombineFields<'a, 'tcx>, @@ -49,7 +49,7 @@ pub struct TypeRelating<'combine, 'a, 'tcx> { } impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { - pub fn new( + pub(crate) fn new( f: &'combine mut CombineFields<'infcx, 'tcx>, structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, diff --git a/compiler/rustc_infer/src/infer/snapshot/mod.rs b/compiler/rustc_infer/src/infer/snapshot/mod.rs index 964fb1f681955..07a482c2f9ac3 100644 --- a/compiler/rustc_infer/src/infer/snapshot/mod.rs +++ b/compiler/rustc_infer/src/infer/snapshot/mod.rs @@ -5,7 +5,7 @@ use tracing::{debug, instrument}; use super::InferCtxt; use super::region_constraints::RegionSnapshot; -mod fudge; +pub(crate) mod fudge; pub(crate) mod undo_log; use undo_log::{Snapshot, UndoLog}; From 25d1ef199353b395e60fb75937a8b4837cd3a9c4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Oct 2024 17:10:39 +1000 Subject: [PATCH 1293/1301] Remove `InferCtxt::err_count_on_creation`. It's no longer used meaningfully. This also means `DiagCtxtHandle::err_count_excluding_lint_errs` can be removed. --- compiler/rustc_errors/src/lib.rs | 12 ------------ compiler/rustc_infer/src/infer/at.rs | 1 - compiler/rustc_infer/src/infer/mod.rs | 20 +++----------------- 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 39acacfbe59c8..10ec521908e95 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -925,18 +925,6 @@ impl<'a> DiagCtxtHandle<'a> { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// This excludes lint errors, and delayed bugs. - #[inline] - pub fn err_count_excluding_lint_errs(&self) -> usize { - let inner = self.inner.borrow(); - inner.err_guars.len() - + inner - .stashed_diagnostics - .values() - .filter(|(diag, guar)| guar.is_some() && diag.is_lint.is_none()) - .count() - } - /// This excludes delayed bugs. #[inline] pub fn err_count(&self) -> usize { diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 6ce47db8b9bd2..f80737d1269c5 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -82,7 +82,6 @@ impl<'tcx> InferCtxt<'tcx> { reported_trait_errors: self.reported_trait_errors.clone(), reported_signature_mismatch: self.reported_signature_mismatch.clone(), tainted_by_errors: self.tainted_by_errors.clone(), - err_count_on_creation: self.err_count_on_creation, universe: self.universe.clone(), intercrate, next_trait_solver: self.next_trait_solver, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 8714e85411493..45b28b98c9460 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -279,27 +279,14 @@ pub struct InferCtxt<'tcx> { pub reported_signature_mismatch: RefCell)>>, /// When an error occurs, we want to avoid reporting "derived" - /// errors that are due to this original failure. Normally, we - /// handle this with the `err_count_on_creation` count, which - /// basically just tracks how many errors were reported when we - /// started type-checking a fn and checks to see if any new errors - /// have been reported since then. Not great, but it works. - /// - /// However, when errors originated in other passes -- notably - /// resolve -- this heuristic breaks down. Therefore, we have this - /// auxiliary flag that one can set whenever one creates a - /// type-error that is due to an error in a prior pass. + /// errors that are due to this original failure. We have this + /// flag that one can set whenever one creates a type-error that + /// is due to an error in a prior pass. /// /// Don't read this flag directly, call `is_tainted_by_errors()` /// and `set_tainted_by_errors()`. tainted_by_errors: Cell>, - /// Track how many errors were reported when this infcx is created. - /// If the number of errors increases, that's also a sign (like - /// `tainted_by_errors`) to avoid reporting certain kinds of errors. - // FIXME(matthewjasper) Merge into `tainted_by_errors` - err_count_on_creation: usize, - /// What is the innermost universe we have created? Starts out as /// `UniverseIndex::root()` but grows from there as we enter /// universal quantifiers. @@ -656,7 +643,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> { reported_trait_errors: Default::default(), reported_signature_mismatch: Default::default(), tainted_by_errors: Cell::new(None), - err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(), universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, next_trait_solver, From 0ca4784c45665a0267314ddd2a12c6710fbbe2c0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Oct 2024 17:58:49 +1000 Subject: [PATCH 1294/1301] Fix `FIXME` comment on `FixupError`. `FixupError` is isomorphic with `TyOrConstInferVar`, so this commit changes it to just be a wrapper around `TyOrConstInferVar`. Also, move the `Display` impl for `FixupError` next to `FixupError`. --- compiler/rustc_infer/src/infer/mod.rs | 39 ++++++++++------------- compiler/rustc_infer/src/infer/resolve.rs | 12 ++++--- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 45b28b98c9460..dbdcc3e2a8a04 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -495,46 +495,41 @@ pub enum NllRegionVariableOrigin { }, } -// FIXME(eddyb) investigate overlap between this and `TyOrConstInferVar`. #[derive(Copy, Clone, Debug)] -pub enum FixupError { - UnresolvedIntTy(IntVid), - UnresolvedFloatTy(FloatVid), - UnresolvedTy(TyVid), - UnresolvedConst(ConstVid), - UnresolvedEffect(EffectVid), -} - -/// See the `region_obligations` field for more information. -#[derive(Clone, Debug)] -pub struct RegionObligation<'tcx> { - pub sub_region: ty::Region<'tcx>, - pub sup_type: Ty<'tcx>, - pub origin: SubregionOrigin<'tcx>, +pub struct FixupError { + unresolved: TyOrConstInferVar, } impl fmt::Display for FixupError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use self::FixupError::*; + use TyOrConstInferVar::*; - match *self { - UnresolvedIntTy(_) => write!( + match self.unresolved { + TyInt(_) => write!( f, "cannot determine the type of this integer; \ add a suffix to specify the type explicitly" ), - UnresolvedFloatTy(_) => write!( + TyFloat(_) => write!( f, "cannot determine the type of this number; \ add a suffix to specify the type explicitly" ), - UnresolvedTy(_) => write!(f, "unconstrained type"), - UnresolvedConst(_) => write!(f, "unconstrained const value"), - UnresolvedEffect(_) => write!(f, "unconstrained effect value"), + Ty(_) => write!(f, "unconstrained type"), + Const(_) => write!(f, "unconstrained const value"), + Effect(_) => write!(f, "unconstrained effect value"), } } } +/// See the `region_obligations` field for more information. +#[derive(Clone, Debug)] +pub struct RegionObligation<'tcx> { + pub sub_region: ty::Region<'tcx>, + pub sup_type: Ty<'tcx>, + pub origin: SubregionOrigin<'tcx>, +} + /// Used to configure inference contexts before their creation. pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 671a66d504f9d..2c7b35af94760 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -138,11 +138,13 @@ impl<'a, 'tcx> FallibleTypeFolder> for FullTypeResolver<'a, 'tcx> { if !t.has_infer() { Ok(t) // micro-optimize -- if there is nothing in this type that this fold affects... } else { + use super::TyOrConstInferVar::*; + let t = self.infcx.shallow_resolve(t); match *t.kind() { - ty::Infer(ty::TyVar(vid)) => Err(FixupError::UnresolvedTy(vid)), - ty::Infer(ty::IntVar(vid)) => Err(FixupError::UnresolvedIntTy(vid)), - ty::Infer(ty::FloatVar(vid)) => Err(FixupError::UnresolvedFloatTy(vid)), + ty::Infer(ty::TyVar(vid)) => Err(FixupError { unresolved: Ty(vid) }), + ty::Infer(ty::IntVar(vid)) => Err(FixupError { unresolved: TyInt(vid) }), + ty::Infer(ty::FloatVar(vid)) => Err(FixupError { unresolved: TyFloat(vid) }), ty::Infer(_) => { bug!("Unexpected type in full type resolver: {:?}", t); } @@ -171,13 +173,13 @@ impl<'a, 'tcx> FallibleTypeFolder> for FullTypeResolver<'a, 'tcx> { let c = self.infcx.shallow_resolve_const(c); match c.kind() { ty::ConstKind::Infer(InferConst::Var(vid)) => { - return Err(FixupError::UnresolvedConst(vid)); + return Err(FixupError { unresolved: super::TyOrConstInferVar::Const(vid) }); } ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("Unexpected const in full const resolver: {:?}", c); } ty::ConstKind::Infer(InferConst::EffectVar(evid)) => { - return Err(FixupError::UnresolvedEffect(evid)); + return Err(FixupError { unresolved: super::TyOrConstInferVar::Effect(evid) }); } _ => {} } From deeb0c5cf3737d2a681334befee694cc5aa005b8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Oct 2024 08:41:38 +1000 Subject: [PATCH 1295/1301] Inline and remove `InferCtxtBuilder::with_defining_opaque_types`. It has a single use. --- compiler/rustc_infer/src/infer/mod.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index dbdcc3e2a8a04..7238c670be9c2 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -569,14 +569,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } - pub fn with_defining_opaque_types( - mut self, - defining_opaque_types: &'tcx ty::List, - ) -> Self { - self.defining_opaque_types = defining_opaque_types; - self - } - pub fn with_next_trait_solver(mut self, next_trait_solver: bool) -> Self { self.next_trait_solver = next_trait_solver; self @@ -605,14 +597,15 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// the bound values in `C` to their instantiated values in `V` /// (in other words, `S(C) = V`). pub fn build_with_canonical( - self, + mut self, span: Span, canonical: &Canonical<'tcx, T>, ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) where T: TypeFoldable>, { - let infcx = self.with_defining_opaque_types(canonical.defining_opaque_types).build(); + self.defining_opaque_types = canonical.defining_opaque_types; + let infcx = self.build(); let (value, args) = infcx.instantiate_canonical(span, canonical); (infcx, value, args) } From 3386530c9166f5b1177f81fb2037dd0e0b5a50f1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Oct 2024 14:01:07 +1000 Subject: [PATCH 1296/1301] Streamline `next_*_var*` methods. Inline and remove `next_const_var_id`, `next_int_var_id`, `next_float_var_id`, all of which have a single call site. --- compiler/rustc_infer/src/infer/mod.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 7238c670be9c2..89dfa01925368 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -892,28 +892,16 @@ impl<'tcx> InferCtxt<'tcx> { ty::Const::new_var(self.tcx, vid) } - pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid { - self.inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) - .vid - } - - fn next_int_var_id(&self) -> IntVid { - self.inner.borrow_mut().int_unification_table().new_key(ty::IntVarValue::Unknown) - } - pub fn next_int_var(&self) -> Ty<'tcx> { - Ty::new_int_var(self.tcx, self.next_int_var_id()) - } - - fn next_float_var_id(&self) -> FloatVid { - self.inner.borrow_mut().float_unification_table().new_key(ty::FloatVarValue::Unknown) + let next_int_var_id = + self.inner.borrow_mut().int_unification_table().new_key(ty::IntVarValue::Unknown); + Ty::new_int_var(self.tcx, next_int_var_id) } pub fn next_float_var(&self) -> Ty<'tcx> { - Ty::new_float_var(self.tcx, self.next_float_var_id()) + let next_float_var_id = + self.inner.borrow_mut().float_unification_table().new_key(ty::FloatVarValue::Unknown); + Ty::new_float_var(self.tcx, next_float_var_id) } /// Creates a fresh region variable with the next available index. From e80096747853fc5a0c8a98e8532c2f74ba770aa2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Oct 2024 14:33:48 +1000 Subject: [PATCH 1297/1301] Simplify two matches. Matches involving `GenericArgKind` pairs typically use a single `_` for the impossible case. This commit shortens two verbose matches in this way. --- compiler/rustc_infer/src/infer/at.rs | 16 +--------------- compiler/rustc_middle/src/ty/relate.rs | 10 +--------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index f80737d1269c5..183973af4f9f3 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -381,21 +381,7 @@ impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> { (GenericArgKind::Const(a), GenericArgKind::Const(b)) => { ValuePairs::Terms(ExpectedFound::new(true, a.into(), b.into())) } - - ( - GenericArgKind::Lifetime(_), - GenericArgKind::Type(_) | GenericArgKind::Const(_), - ) - | ( - GenericArgKind::Type(_), - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_), - ) - | ( - GenericArgKind::Const(_), - GenericArgKind::Lifetime(_) | GenericArgKind::Type(_), - ) => { - bug!("relating different kinds: {a:?} {b:?}") - } + _ => bug!("relating different kinds: {a:?} {b:?}"), }, } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 61c03922ac05f..4c7bcb1bf2e88 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -212,15 +212,7 @@ impl<'tcx> Relate> for ty::GenericArg<'tcx> { (ty::GenericArgKind::Const(a_ct), ty::GenericArgKind::Const(b_ct)) => { Ok(relation.relate(a_ct, b_ct)?.into()) } - (ty::GenericArgKind::Lifetime(unpacked), x) => { - bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) - } - (ty::GenericArgKind::Type(unpacked), x) => { - bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) - } - (ty::GenericArgKind::Const(unpacked), x) => { - bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) - } + _ => bug!("impossible case reached: can't relate: {a:?} with {b:?}"), } } } From afbff05c84c8c22909be929d20d6075f53d51e12 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 2 Oct 2024 16:52:55 +1000 Subject: [PATCH 1298/1301] Move a `use` statement so it's with the other `use` statements. --- compiler/rustc_infer/src/infer/opaque_types/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 0c5eca6ebdfd9..d3afabdb7da0e 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::{ use rustc_span::Span; use tracing::{debug, instrument}; +use super::DefineOpaqueTypes; use crate::errors::OpaqueHiddenTypeDiag; use crate::infer::{InferCtxt, InferOk}; use crate::traits::{self, Obligation}; @@ -22,8 +23,6 @@ mod table; pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap, OpaqueTypeDecl<'tcx>>; pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; -use super::DefineOpaqueTypes; - /// Information about the opaque types whose values we /// are inferring in this function (these are the `impl Trait` that /// appear in the return type). From 3fdcde76d9bcbf31e2409aefede4dd8b2f7c315f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 7 Oct 2024 09:40:52 +1100 Subject: [PATCH 1299/1301] Remove out-of-date comment. --- compiler/rustc_infer/src/infer/resolve.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 2c7b35af94760..025c3a629fabf 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -121,8 +121,6 @@ where value.try_fold_with(&mut FullTypeResolver { infcx }) } -// N.B. This type is not public because the protocol around checking the -// `err` field is not enforceable otherwise. struct FullTypeResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, } From d793766a6199d9b7339633ad0296af63c7b4f9b8 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Tue, 30 Jul 2024 09:48:36 -0400 Subject: [PATCH 1300/1301] liballoc: introduce String, Vec const-slicing This change `const`-qualifies many methods on Vec and String, notably `as_slice`, `as_str`, `len`. These changes are made behind the unstable feature flag `const_vec_string_slice` with the following tracking issue: https://github.com/rust-lang/rust/issues/129041 --- library/alloc/src/lib.rs | 1 + library/alloc/src/raw_vec.rs | 12 +-- library/alloc/src/string.rs | 38 +++++--- library/alloc/src/vec/mod.rs | 55 ++++++++--- ..._to_slice.PreCodegen.after.panic-abort.mir | 91 +++++++++---------- ...to_slice.PreCodegen.after.panic-unwind.mir | 91 +++++++++---------- tests/ui/consts/issue-94675.rs | 2 +- tests/ui/consts/issue-94675.stderr | 10 +- 8 files changed, 164 insertions(+), 136 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f0597f295b3f0..4a09b5dfd17b5 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -114,6 +114,7 @@ #![feature(const_option)] #![feature(const_pin)] #![feature(const_size_of_val)] +#![feature(const_vec_string_slice)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 436e0596e3d5e..8fdca8c420036 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -280,7 +280,7 @@ impl RawVec { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub fn ptr(&self) -> *mut T { + pub const fn ptr(&self) -> *mut T { self.inner.ptr() } @@ -293,7 +293,7 @@ impl RawVec { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub fn capacity(&self) -> usize { + pub const fn capacity(&self) -> usize { self.inner.capacity(size_of::()) } @@ -488,17 +488,17 @@ impl RawVecInner { } #[inline] - fn ptr(&self) -> *mut T { + const fn ptr(&self) -> *mut T { self.non_null::().as_ptr() } #[inline] - fn non_null(&self) -> NonNull { - self.ptr.cast().into() + const fn non_null(&self) -> NonNull { + self.ptr.cast().as_non_null_ptr() } #[inline] - fn capacity(&self, elem_size: usize) -> usize { + const fn capacity(&self, elem_size: usize) -> usize { if elem_size == 0 { usize::MAX } else { self.cap.0 } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index d58a016b502ca..2815ea4da7fea 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1059,7 +1059,8 @@ impl String { #[inline] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_bytes(self) -> Vec { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn into_bytes(self) -> Vec { self.vec } @@ -1076,8 +1077,11 @@ impl String { #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_str")] - pub fn as_str(&self) -> &str { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_str(&self) -> &str { + // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error + // at construction. + unsafe { str::from_utf8_unchecked(self.vec.as_slice()) } } /// Converts a `String` into a mutable string slice. @@ -1096,8 +1100,11 @@ impl String { #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_mut_str")] - pub fn as_mut_str(&mut self) -> &mut str { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_mut_str(&mut self) -> &mut str { + // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error + // at construction. + unsafe { str::from_utf8_unchecked_mut(self.vec.as_mut_slice()) } } /// Appends a given string slice onto the end of this `String`. @@ -1168,7 +1175,8 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn capacity(&self) -> usize { self.vec.capacity() } @@ -1431,8 +1439,9 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_bytes(&self) -> &[u8] { - &self.vec + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_bytes(&self) -> &[u8] { + self.vec.as_slice() } /// Shortens this `String` to the specified length. @@ -1784,7 +1793,8 @@ impl String { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn as_mut_vec(&mut self) -> &mut Vec { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const unsafe fn as_mut_vec(&mut self) -> &mut Vec { &mut self.vec } @@ -1805,8 +1815,9 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_confusables("length", "size")] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.vec.len() } @@ -1824,7 +1835,8 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -2565,7 +2577,7 @@ impl ops::Deref for String { #[inline] fn deref(&self) -> &str { - unsafe { str::from_utf8_unchecked(&self.vec) } + self.as_str() } } @@ -2576,7 +2588,7 @@ unsafe impl ops::DerefPure for String {} impl ops::DerefMut for String { #[inline] fn deref_mut(&mut self) -> &mut str { - unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } + self.as_mut_str() } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 13b06584223ba..cae47393a8140 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1240,7 +1240,8 @@ impl Vec { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn capacity(&self) -> usize { self.buf.capacity() } @@ -1548,8 +1549,22 @@ impl Vec { #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_slice")] - pub fn as_slice(&self) -> &[T] { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_slice(&self) -> &[T] { + // SAFETY: `slice::from_raw_parts` requires pointee is a contiguous, aligned buffer of size + // `len` containing properly-initialized `T`s. Data must not be mutated for the returned + // lifetime. Further, `len * mem::size_of::` <= `ISIZE::MAX`, and allocation does not + // "wrap" through overflowing memory addresses. + // + // * Vec API guarantees that self.buf: + // * contains only properly-initialized items within 0..len + // * is aligned, contiguous, and valid for `len` reads + // * obeys size and address-wrapping constraints + // + // * We only construct `&mut` references to `self.buf` through `&mut self` methods; borrow- + // check ensures that it is not possible to mutably alias `self.buf` within the + // returned lifetime. + unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } } /// Extracts a mutable slice of the entire vector. @@ -1566,8 +1581,22 @@ impl Vec { #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_mut_slice")] - pub fn as_mut_slice(&mut self) -> &mut [T] { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_mut_slice(&mut self) -> &mut [T] { + // SAFETY: `slice::from_raw_parts_mut` requires pointee is a contiguous, aligned buffer of + // size `len` containing properly-initialized `T`s. Data must not be accessed through any + // other pointer for the returned lifetime. Further, `len * mem::size_of::` <= + // `ISIZE::MAX` and allocation does not "wrap" through overflowing memory addresses. + // + // * Vec API guarantees that self.buf: + // * contains only properly-initialized items within 0..len + // * is aligned, contiguous, and valid for `len` reads + // * obeys size and address-wrapping constraints + // + // * We only construct references to `self.buf` through `&self` and `&mut self` methods; + // borrow-check ensures that it is not possible to construct a reference to `self.buf` + // within the returned lifetime. + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } } /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer @@ -1622,9 +1651,10 @@ impl Vec { /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] #[inline] - pub fn as_ptr(&self) -> *const T { + pub const fn as_ptr(&self) -> *const T { // We shadow the slice method of the same name to avoid going through // `deref`, which creates an intermediate reference. self.buf.ptr() @@ -1681,9 +1711,10 @@ impl Vec { /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { + pub const fn as_mut_ptr(&mut self) -> *mut T { // We shadow the slice method of the same name to avoid going through // `deref_mut`, which creates an intermediate reference. self.buf.ptr() @@ -2561,8 +2592,9 @@ impl Vec { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_confusables("length", "size")] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.len } @@ -2579,7 +2611,8 @@ impl Vec { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_is_empty")] - pub fn is_empty(&self) -> bool { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -3130,7 +3163,7 @@ impl ops::Deref for Vec { #[inline] fn deref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + self.as_slice() } } @@ -3138,7 +3171,7 @@ impl ops::Deref for Vec { impl ops::DerefMut for Vec { #[inline] fn deref_mut(&mut self) -> &mut [T] { - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + self.as_mut_slice() } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index 0ad7f5910a0b3..4d964b0afb78c 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -5,66 +5,61 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _0: &[u8]; scope 1 (inlined as Deref>::deref) { debug self => _1; - let mut _6: usize; - scope 2 (inlined Vec::::as_ptr) { + scope 2 (inlined Vec::::as_slice) { debug self => _1; - let mut _2: &alloc::raw_vec::RawVec; - scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { - debug self => _2; - let mut _3: &alloc::raw_vec::RawVecInner; - scope 4 (inlined alloc::raw_vec::RawVecInner::ptr::) { - debug self => _3; - scope 5 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _6: usize; + scope 3 (inlined Vec::::as_ptr) { + debug self => _1; + let mut _2: &alloc::raw_vec::RawVec; + scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { + debug self => _2; + let mut _3: &alloc::raw_vec::RawVecInner; + scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { debug self => _3; - let mut _4: std::ptr::NonNull; - scope 6 (inlined Unique::::cast::) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 7 (inlined NonNull::::cast::) { - debug self => _4; - scope 8 (inlined NonNull::::as_ptr) { + scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { + debug self => _3; + let mut _4: std::ptr::NonNull; + scope 7 (inlined Unique::::cast::) { + debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 8 (inlined NonNull::::cast::) { debug self => _4; - let mut _5: *const u8; + scope 9 (inlined NonNull::::as_ptr) { + debug self => _4; + let mut _5: *const u8; + } } } - } - scope 9 (inlined #[track_caller] as Into>>::into) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 10 (inlined as From>>::from) { - debug ((unique: Unique).0: std::ptr::NonNull) => _4; - debug ((unique: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 11 (inlined Unique::::as_non_null_ptr) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - } + scope 10 (inlined Unique::::as_non_null_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; } } + scope 11 (inlined NonNull::::as_ptr) { + debug self => _4; + } } - scope 12 (inlined NonNull::::as_ptr) { - debug self => _4; - } - } - } - } - scope 13 (inlined std::slice::from_raw_parts::<'_, u8>) { - debug data => _5; - debug len => _6; - let _7: *const [u8]; - scope 14 (inlined core::ub_checks::check_language_ub) { - scope 15 (inlined core::ub_checks::check_language_ub::runtime) { } } - scope 16 (inlined std::mem::size_of::) { - } - scope 17 (inlined align_of::) { - } - scope 18 (inlined slice_from_raw_parts::) { + scope 12 (inlined std::slice::from_raw_parts::<'_, u8>) { debug data => _5; debug len => _6; - scope 19 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - debug data_pointer => _5; - debug metadata => _6; + let _7: *const [u8]; + scope 13 (inlined core::ub_checks::check_language_ub) { + scope 14 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 15 (inlined std::mem::size_of::) { + } + scope 16 (inlined align_of::) { + } + scope 17 (inlined slice_from_raw_parts::) { + debug data => _5; + debug len => _6; + scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) { + debug data_pointer => _5; + debug metadata => _6; + } } } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index 0ad7f5910a0b3..4d964b0afb78c 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -5,66 +5,61 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _0: &[u8]; scope 1 (inlined as Deref>::deref) { debug self => _1; - let mut _6: usize; - scope 2 (inlined Vec::::as_ptr) { + scope 2 (inlined Vec::::as_slice) { debug self => _1; - let mut _2: &alloc::raw_vec::RawVec; - scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { - debug self => _2; - let mut _3: &alloc::raw_vec::RawVecInner; - scope 4 (inlined alloc::raw_vec::RawVecInner::ptr::) { - debug self => _3; - scope 5 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _6: usize; + scope 3 (inlined Vec::::as_ptr) { + debug self => _1; + let mut _2: &alloc::raw_vec::RawVec; + scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { + debug self => _2; + let mut _3: &alloc::raw_vec::RawVecInner; + scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { debug self => _3; - let mut _4: std::ptr::NonNull; - scope 6 (inlined Unique::::cast::) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 7 (inlined NonNull::::cast::) { - debug self => _4; - scope 8 (inlined NonNull::::as_ptr) { + scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { + debug self => _3; + let mut _4: std::ptr::NonNull; + scope 7 (inlined Unique::::cast::) { + debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 8 (inlined NonNull::::cast::) { debug self => _4; - let mut _5: *const u8; + scope 9 (inlined NonNull::::as_ptr) { + debug self => _4; + let mut _5: *const u8; + } } } - } - scope 9 (inlined #[track_caller] as Into>>::into) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 10 (inlined as From>>::from) { - debug ((unique: Unique).0: std::ptr::NonNull) => _4; - debug ((unique: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 11 (inlined Unique::::as_non_null_ptr) { - debug ((self: Unique).0: std::ptr::NonNull) => _4; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - } + scope 10 (inlined Unique::::as_non_null_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; } } + scope 11 (inlined NonNull::::as_ptr) { + debug self => _4; + } } - scope 12 (inlined NonNull::::as_ptr) { - debug self => _4; - } - } - } - } - scope 13 (inlined std::slice::from_raw_parts::<'_, u8>) { - debug data => _5; - debug len => _6; - let _7: *const [u8]; - scope 14 (inlined core::ub_checks::check_language_ub) { - scope 15 (inlined core::ub_checks::check_language_ub::runtime) { } } - scope 16 (inlined std::mem::size_of::) { - } - scope 17 (inlined align_of::) { - } - scope 18 (inlined slice_from_raw_parts::) { + scope 12 (inlined std::slice::from_raw_parts::<'_, u8>) { debug data => _5; debug len => _6; - scope 19 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - debug data_pointer => _5; - debug metadata => _6; + let _7: *const [u8]; + scope 13 (inlined core::ub_checks::check_language_ub) { + scope 14 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 15 (inlined std::mem::size_of::) { + } + scope 16 (inlined align_of::) { + } + scope 17 (inlined slice_from_raw_parts::) { + debug data => _5; + debug len => _6; + scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) { + debug data_pointer => _5; + debug metadata => _6; + } } } } diff --git a/tests/ui/consts/issue-94675.rs b/tests/ui/consts/issue-94675.rs index 56c4b6ea36f6a..2e30eebb07b2e 100644 --- a/tests/ui/consts/issue-94675.rs +++ b/tests/ui/consts/issue-94675.rs @@ -1,6 +1,6 @@ //@ known-bug: #103507 -#![feature(const_trait_impl)] +#![feature(const_trait_impl, const_vec_string_slice)] struct Foo<'a> { bar: &'a mut Vec, diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr index ebfa09b2e5d5d..a85c5e10374f8 100644 --- a/tests/ui/consts/issue-94675.stderr +++ b/tests/ui/consts/issue-94675.stderr @@ -1,11 +1,3 @@ -error[E0015]: cannot call non-const fn `Vec::::len` in constant functions - --> $DIR/issue-94675.rs:11:27 - | -LL | self.bar[0] = baz.len(); - | ^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - error[E0015]: cannot call non-const operator in constant functions --> $DIR/issue-94675.rs:11:17 | @@ -20,6 +12,6 @@ help: add `#![feature(effects)]` to the crate attributes to enable LL + #![feature(effects)] | -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0015`. From 032f862c28552b5f6d8f87d8e5898102e657eba1 Mon Sep 17 00:00:00 2001 From: "Brie Francois (IFAG CSS TI SW FW)" Date: Thu, 19 Sep 2024 16:24:10 +0200 Subject: [PATCH 1301/1301] Add support to generate documentation for tests The new option --document-tests is unstable and documented as such. In order to use it is needed to add `--cfg test` and in case the tests are not marked public to add `--document-private-items`. The implementation hide the auto generate main test function and constants. --- src/doc/rustdoc/src/unstable-features.md | 31 ++++++++++++++ src/librustdoc/clean/mod.rs | 6 ++- src/librustdoc/clean/types.rs | 7 +++- src/librustdoc/config.rs | 4 ++ src/librustdoc/core.rs | 11 +++-- src/librustdoc/fold.rs | 1 + src/librustdoc/formats/cache.rs | 10 ++++- src/librustdoc/formats/item_type.rs | 3 ++ src/librustdoc/html/render/mod.rs | 15 +++++++ src/librustdoc/html/render/print_item.rs | 8 ++-- src/librustdoc/html/static/js/main.js | 1 + src/librustdoc/json/conversions.rs | 6 ++- src/librustdoc/lib.rs | 3 ++ src/librustdoc/passes/stripper.rs | 1 + src/librustdoc/visit.rs | 1 + src/librustdoc/visit_ast.rs | 52 +++++++++++++++++++++--- 16 files changed, 142 insertions(+), 18 deletions(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index ebbe141b6f540..aa161492f7467 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -627,3 +627,34 @@ add the `--scrape-tests` flag. This flag enables the generation of links in the source code pages which allow the reader to jump to a type definition. + +### `--document-tests`: show test items + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs -Z unstable-options --cfg test --document-private-items --document-tests +``` + +By default, `rustdoc` does not document test items. + +```rust +/// by default this test function would not be documented +#[test] +fn test_in_module() { + assert_eq!(2, 1 + 1); +} +/// by default this test module would not be documented +#[cfg(test)] +mod tests { + /// by default this test function would not be documented + #[test] + fn test_in_a_test_module() { + assert_eq!(2, 1 + 1); + } +} +``` + +Note: +* `--cfg test` must be set because tests are guarded by #[cfg(test)]. +* `--document-private-items` is typically required because it is standard practice to keep test items private. By enabling this option, you ensure that private items, including tests, are documented as needed while maintaining their non-public status. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index fa73733360ca1..378e85048f435 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1022,7 +1022,11 @@ fn clean_fn_or_proc_macro<'tcx>( None => { let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id)); clean_fn_decl_legacy_const_generics(&mut func, attrs); - FunctionItem(func) + if cx.cache.document_tests && cx.cache.tests.contains(&item.owner_id.to_def_id()) { + TestItem(func) + } else { + FunctionItem(func) + } } } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ea4388b305b36..2eb7833cd3436 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -674,7 +674,10 @@ impl Item { asyncness: hir::IsAsync::NotAsync, } } - ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) | ItemKind::TyMethodItem(_) => { + ItemKind::FunctionItem(_) + | ItemKind::MethodItem(_, _) + | ItemKind::TyMethodItem(_) + | ItemKind::TestItem(_) => { let def_id = self.def_id().unwrap(); build_fn_header(def_id, tcx, tcx.asyncness(def_id)) } @@ -841,6 +844,7 @@ pub(crate) enum ItemKind { UnionItem(Union), EnumItem(Enum), FunctionItem(Box), + TestItem(Box), ModuleItem(Module), TypeAliasItem(Box), StaticItem(Static), @@ -899,6 +903,7 @@ impl ItemKind { ExternCrateItem { .. } | ImportItem(_) | FunctionItem(_) + | TestItem(_) | TypeAliasItem(_) | StaticItem(_) | ConstantItem(_) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 2cd69474b1cf0..05877cff13dfe 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -275,6 +275,8 @@ pub(crate) struct RenderOptions { pub(crate) document_private: bool, /// Document items that have `doc(hidden)`. pub(crate) document_hidden: bool, + /// Document tests. + pub(crate) document_tests: bool, /// If `true`, generate a JSON file in the crate folder instead of HTML redirection files. pub(crate) generate_redirect_map: bool, /// Show the memory layout of types in the docs. @@ -772,6 +774,7 @@ impl Options { } let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); + let document_tests = matches.opt_present("document-tests"); let with_examples = matches.opt_strs("with-examples"); let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx); @@ -840,6 +843,7 @@ impl Options { markdown_playground_url, document_private, document_hidden, + document_tests, generate_redirect_map, show_type_layout, unstable_features, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index aaf4c80f99763..d2af8109e2215 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -198,7 +198,7 @@ pub(crate) fn create_config( remap_path_prefix, .. }: RustdocOptions, - RenderOptions { document_private, .. }: &RenderOptions, + RenderOptions { document_private, document_tests, .. }: &RenderOptions, using_internal_features: Arc, ) -> rustc_interface::Config { // Add the doc cfg into the doc build. @@ -227,7 +227,8 @@ pub(crate) fn create_config( if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; let resolve_doc_links = if *document_private { ResolveDocLinks::All } else { ResolveDocLinks::Exported }; - let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false); + let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false) + || (cfgs.iter().any(|cfg| cfg == "test") && *document_tests); // plays with error output here! let sessopts = config::Options { maybe_sysroot, @@ -341,7 +342,11 @@ pub(crate) fn run_global_ctxt( impl_trait_bounds: Default::default(), generated_synthetics: Default::default(), auto_traits, - cache: Cache::new(render_options.document_private, render_options.document_hidden), + cache: Cache::new( + render_options.document_private, + render_options.document_hidden, + render_options.document_tests, + ), inlined: FxHashSet::default(), output_format, render_options, diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 95e495205aea6..da8e61851e331 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -79,6 +79,7 @@ pub(crate) trait DocFolder: Sized { ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) + | TestItem(_) | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index ff0d537b19f75..34839cab20e0b 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -91,6 +91,11 @@ pub(crate) struct Cache { /// Whether to document hidden items. /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. pub(crate) document_hidden: bool, + /// Whether to document tests. + /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. + pub(crate) document_tests: bool, + /// DefIds of all functions which are tests. + pub(crate) tests: FxHashSet, /// Crates marked with [`#[doc(masked)]`][doc_masked]. /// @@ -142,8 +147,8 @@ struct CacheBuilder<'a, 'tcx> { } impl Cache { - pub(crate) fn new(document_private: bool, document_hidden: bool) -> Self { - Cache { document_private, document_hidden, ..Cache::default() } + pub(crate) fn new(document_private: bool, document_hidden: bool, document_tests: bool) -> Self { + Cache { document_private, document_hidden, document_tests, ..Cache::default() } } /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was @@ -295,6 +300,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { | clean::TraitItem(..) | clean::TraitAliasItem(..) | clean::FunctionItem(..) + | clean::TestItem(..) | clean::ModuleItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index 383e3135faa86..0991790a4d53b 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -57,6 +57,7 @@ pub(crate) enum ItemType { TraitAlias = 25, // This number is reserved for use in JavaScript // Generic = 26, + Test = 27, } impl Serialize for ItemType { @@ -83,6 +84,7 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::UnionItem(..) => ItemType::Union, clean::EnumItem(..) => ItemType::Enum, clean::FunctionItem(..) => ItemType::Function, + clean::TestItem(..) => ItemType::Test, clean::TypeAliasItem(..) => ItemType::TypeAlias, clean::StaticItem(..) => ItemType::Static, clean::ConstantItem(..) => ItemType::Constant, @@ -176,6 +178,7 @@ impl ItemType { ItemType::Union => "union", ItemType::Enum => "enum", ItemType::Function => "fn", + ItemType::Test => "test", ItemType::TypeAlias => "type", ItemType::Static => "static", ItemType::Trait => "trait", diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 399730a01c82f..1bdb0c72ba344 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -341,6 +341,7 @@ struct AllTypes { attribute_macros: FxIndexSet, derive_macros: FxIndexSet, trait_aliases: FxIndexSet, + tests: FxIndexSet, } impl AllTypes { @@ -360,6 +361,7 @@ impl AllTypes { attribute_macros: new_set(100), derive_macros: new_set(100), trait_aliases: new_set(100), + tests: new_set(100), } } @@ -385,6 +387,7 @@ impl AllTypes { } ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)), ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)), + ItemType::Test => self.tests.insert(ItemEntry::new(new_url, name)), _ => true, }; } @@ -414,6 +417,9 @@ impl AllTypes { if !self.functions.is_empty() { sections.insert(ItemSection::Functions); } + if !self.tests.is_empty() { + sections.insert(ItemSection::Tests); + } if !self.type_aliases.is_empty() { sections.insert(ItemSection::TypeAliases); } @@ -432,6 +438,9 @@ impl AllTypes { if !self.trait_aliases.is_empty() { sections.insert(ItemSection::TraitAliases); } + if !self.tests.is_empty() { + sections.insert(ItemSection::Tests); + } sections } @@ -468,6 +477,7 @@ impl AllTypes { print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros); print_entries(f, &self.derive_macros, ItemSection::DeriveMacros); print_entries(f, &self.functions, ItemSection::Functions); + print_entries(f, &self.tests, ItemSection::Tests); print_entries(f, &self.type_aliases, ItemSection::TypeAliases); print_entries(f, &self.trait_aliases, ItemSection::TraitAliases); print_entries(f, &self.statics, ItemSection::Statics); @@ -2227,6 +2237,7 @@ pub(crate) enum ItemSection { Statics, Traits, Functions, + Tests, TypeAliases, Unions, Implementations, @@ -2259,6 +2270,7 @@ impl ItemSection { Statics, Traits, Functions, + Tests, TypeAliases, Unions, Implementations, @@ -2284,6 +2296,7 @@ impl ItemSection { Self::Unions => "unions", Self::Enums => "enums", Self::Functions => "functions", + Self::Tests => "tests", Self::TypeAliases => "types", Self::Statics => "statics", Self::Constants => "constants", @@ -2313,6 +2326,7 @@ impl ItemSection { Self::Unions => "Unions", Self::Enums => "Enums", Self::Functions => "Functions", + Self::Tests => "Tests", Self::TypeAliases => "Type Aliases", Self::Statics => "Statics", Self::Constants => "Constants", @@ -2343,6 +2357,7 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection { ItemType::Union => ItemSection::Unions, ItemType::Enum => ItemSection::Enums, ItemType::Function => ItemSection::Functions, + ItemType::Test => ItemSection::Tests, ItemType::TypeAlias => ItemSection::TypeAliases, ItemType::Static => ItemSection::Statics, ItemType::Constant => ItemSection::Constants, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 3c96f8736814e..72b8fbea8a9aa 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -187,6 +187,7 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf } } clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", + clean::TestItem(..) => "Test ", clean::TraitItem(..) => "Trait ", clean::StructItem(..) => "Struct ", clean::UnionItem(..) => "Union ", @@ -254,9 +255,9 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf match &item.kind { clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), - clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f, _) => { - item_function(buf, cx, item, f) - } + clean::FunctionItem(ref f) + | clean::ForeignFunctionItem(ref f, _) + | clean::TestItem(ref f) => item_function(buf, cx, item, f), clean::TraitItem(ref t) => item_trait(buf, cx, item, t), clean::StructItem(ref s) => item_struct(buf, cx, item, s), clean::UnionItem(ref s) => item_union(buf, cx, item, s), @@ -332,6 +333,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: ItemType::Static => 8, ItemType::Trait => 9, ItemType::Function => 10, + ItemType::Test => 11, ItemType::TypeAlias => 12, ItemType::Union => 13, _ => 14 + ty as u8, diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 984b0877d8de2..2e61897bfbeea 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -581,6 +581,7 @@ function preLoadCss(cssUrl) { block("static", "static", "Statics"); block("trait", "traits", "Traits"); block("fn", "functions", "Functions"); + block("test", "tests", "Tests"); block("type", "types", "Type Aliases"); block("union", "unions", "Unions"); // No point, because these items don't appear in modules diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index b411f9a1a52d0..4af7824e59c98 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -320,7 +320,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { StructFieldItem(f) => ItemEnum::StructField(f.into_tcx(tcx)), EnumItem(e) => ItemEnum::Enum(e.into_tcx(tcx)), VariantItem(v) => ItemEnum::Variant(v.into_tcx(tcx)), - FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), tcx)), + FunctionItem(f) | TestItem(f) => { + ItemEnum::Function(from_function(f, true, header.unwrap(), tcx)) + } ForeignFunctionItem(f, _) => { ItemEnum::Function(from_function(f, false, header.unwrap(), tcx)) } @@ -858,7 +860,7 @@ impl FromWithTcx for ItemKind { Struct => ItemKind::Struct, Union => ItemKind::Union, Enum => ItemKind::Enum, - Function | TyMethod | Method => ItemKind::Function, + Function | Test | TyMethod | Method => ItemKind::Function, TypeAlias => ItemKind::TypeAlias, Static => ItemKind::Static, Constant => ItemKind::Constant, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 10fc0ea999015..15833c0c6a726 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -596,6 +596,9 @@ fn opts() -> Vec { unstable("scrape-tests", |o| { o.optflag("", "scrape-tests", "Include test code when scraping examples") }), + unstable("document-tests", |o| { + o.optflagmulti("", "document-tests", "Generate documentation for tests") + }), unstable("with-examples", |o| { o.optmulti( "", diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 98b3446c26d23..944b8ccdd7ba9 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -57,6 +57,7 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { | clean::EnumItem(..) | clean::TraitItem(..) | clean::FunctionItem(..) + | clean::TestItem(..) | clean::VariantItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index bfa285c57fa93..cbe0ac58ab8ac 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -31,6 +31,7 @@ pub(crate) trait DocVisitor<'a>: Sized { ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) + | TestItem(_) | TypeAliasItem(_) | StaticItem(_) | ConstantItem(..) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index f789aca73784d..4b7a758516125 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -4,11 +4,10 @@ use std::mem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; -use rustc_hir::{CRATE_HIR_ID, Node}; +use rustc_hir::{self as hir, CRATE_HIR_ID, Node, TyKind}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -86,6 +85,7 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> { modules: Vec>, is_importable_from_parent: bool, inside_body: bool, + next_is_test: bool, } impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { @@ -110,6 +110,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { modules: vec![om], is_importable_from_parent: true, inside_body: false, + next_is_test: false, } } @@ -385,6 +386,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { _ => false, } { + if self.cx.cache.document_tests + && let hir::ItemKind::Fn(..) = item.kind + && self.next_is_test + { + self.cx.cache.tests.insert(item.owner_id.to_def_id()); + self.next_is_test = false; + }; self.modules .last_mut() .unwrap() @@ -499,8 +507,16 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { hir::ItemKind::Mod(ref m) => { self.enter_mod(item.owner_id.def_id, m, name, renamed, import_id); } - hir::ItemKind::Fn(..) - | hir::ItemKind::ExternCrate(..) + hir::ItemKind::Fn(fn_sig, _, _) => { + // Don't show auto created function "main" that is not in the source code (empty span) when documenting tests. + if !(self.cx.cache.document_tests + && fn_sig.span.is_empty() + && name.as_str() == "main") + { + self.add_to_current_mod(item, renamed, import_id); + } + } + hir::ItemKind::ExternCrate(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) @@ -510,11 +526,35 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { | hir::ItemKind::TraitAlias(..) => { self.add_to_current_mod(item, renamed, import_id); } - hir::ItemKind::Const(..) => { + hir::ItemKind::Const(ty, _, _) => { // Underscore constants do not correspond to a nameable item and // so are never useful in documentation. if name != kw::Underscore { - self.add_to_current_mod(item, renamed, import_id); + if self.cx.cache.document_tests + && let TyKind::Path(rustc_hir::QPath::Resolved(_, path)) = ty.kind + { + let path: String = path + .segments + .iter() + .map(|s| { + if s.ident.name == kw::PathRoot { + "" + } else { + s.ident.name.as_str() + } + }) + .intersperse("::") + .collect(); + if path == "test::TestDescAndFn" { + // Intentionally do not add this, since we want to document the test + // and not the generated constants. + self.next_is_test = true; + } else { + self.add_to_current_mod(item, renamed, import_id); + } + } else { + self.add_to_current_mod(item, renamed, import_id); + } } } hir::ItemKind::Impl(impl_) => {