diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93316b9cff7b..81fb39cdc569 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ concurrency: # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. # We add an exception for try builds (try branch) and unrolled rollup builds (try-perf), which # are all triggered on the same branch, but which should be able to run concurrently. - group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }} + group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try') && github.sha) || github.ref }} cancel-in-progress: true env: TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate" @@ -46,7 +46,7 @@ jobs: # If you want to modify CI jobs, take a look at src/ci/github-actions/jobs.yml. calculate_matrix: name: Calculate job matrix - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm outputs: jobs: ${{ steps.jobs.outputs.jobs }} run_type: ${{ steps.jobs.outputs.run_type }} @@ -73,6 +73,14 @@ jobs: needs: [ calculate_matrix ] runs-on: "${{ matrix.os }}" timeout-minutes: 360 + # The bors environment contains secrets required for elevated workflows (try and auto builds), + # which need to access e.g. S3 and upload artifacts. We want to provide access to that + # environment only on the try/auto branches, which are only accessible to bors. + # This also ensures that PR CI (which doesn't get write access to S3) works, as it cannot + # access the environment. + # + # We only enable the environment for the rust-lang/rust repository, so that CI works on forks. + environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/auto')) && 'bors') || '' }} env: CI_JOB_NAME: ${{ matrix.name }} CI_JOB_DOC_URL: ${{ matrix.doc_url }} @@ -225,8 +233,8 @@ jobs: fi exit ${STATUS} env: - AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }} + AWS_ACCESS_KEY_ID: ${{ secrets.CACHES_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CACHES_AWS_SECRET_ACCESS_KEY }} - name: create github artifacts run: src/ci/scripts/create-doc-artifacts.sh @@ -248,8 +256,8 @@ jobs: - name: upload artifacts to S3 run: src/ci/scripts/upload-artifacts.sh env: - AWS_ACCESS_KEY_ID: ${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }} + AWS_ACCESS_KEY_ID: ${{ secrets.ARTIFACTS_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTIFACTS_AWS_SECRET_ACCESS_KEY }} # Adding a condition on DEPLOY=1 or DEPLOY_ALT=1 is not needed as all deploy # builders *should* have the AWS credentials available. Still, explicitly # adding the condition is helpful as this way CI will not silently skip diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e155e253784a..aadc7c48ea83 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ and we appreciate all of them. The best way to get started is by asking for help in the [#new members](https://rust-lang.zulipchat.com/#narrow/stream/122652-new-members) -Zulip stream. We have lots of docs below of how to get started on your own, but +Zulip stream. We have a lot of documentation below on how to get started on your own, but the Zulip stream is the best place to *ask* for help. Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc @@ -14,7 +14,7 @@ standard library in the [Standard library developers Guide][std-dev-guide], comm ## Making changes to subtrees and submodules -For submodules, changes need to be made against the repository corresponding the +For submodules, changes need to be made against the repository corresponding to the submodule, and not the main `rust-lang/rust` repository. For subtrees, prefer sending a PR against the subtree's repository if it does @@ -25,7 +25,7 @@ rustc-dev-guide change that does not accompany a compiler change). The [rustc-dev-guide] is meant to help document how rustc –the Rust compiler– works, as well as to help new contributors get involved in rustc development. It is recommended -to read and understand the [rustc-dev-guide] before making a contribution. This guide +that you read and understand the [rustc-dev-guide] before making a contribution. This guide talks about the different bots in the Rust ecosystem, the Rust development tools, bootstrapping, the compiler architecture, source code representation, and more. @@ -33,7 +33,7 @@ bootstrapping, the compiler architecture, source code representation, and more. There are many ways you can get help when you're stuck. Rust has many platforms for this: [internals], [rust-zulip], and [rust-discord]. It is recommended to ask for help on -the [rust-zulip], but any of these platforms are a great way to seek help and even +the [rust-zulip], but any of these platforms are great ways to seek help and even find a mentor! You can learn more about asking questions and getting help in the [Asking Questions](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) chapter of the [rustc-dev-guide]. diff --git a/Cargo.lock b/Cargo.lock index 59f7f3dda8fc..e486f6148cc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,7 +183,20 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" dependencies = [ - "askama_derive", + "askama_derive 0.13.1", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" +dependencies = [ + "askama_derive 0.14.0", "itoa", "percent-encoding", "serde", @@ -196,7 +209,24 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" dependencies = [ - "askama_parser", + "askama_parser 0.13.0", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash 2.1.1", + "serde", + "serde_derive", + "syn 2.0.101", +] + +[[package]] +name = "askama_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" +dependencies = [ + "askama_parser 0.14.0", "basic-toml", "memchr", "proc-macro2", @@ -219,6 +249,18 @@ dependencies = [ "winnow 0.7.10", ] +[[package]] +name = "askama_parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow 0.7.10", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -540,7 +582,7 @@ name = "clippy" version = "0.1.89" dependencies = [ "anstream", - "askama", + "askama 0.13.1", "cargo_metadata 0.18.1", "clippy_config", "clippy_lints", @@ -582,13 +624,11 @@ dependencies = [ name = "clippy_dev" version = "0.0.1" dependencies = [ - "aho-corasick", "chrono", "clap", "indoc", "itertools", "opener", - "shell-escape", "walkdir", ] @@ -1219,16 +1259,16 @@ dependencies = [ [[package]] name = "fluent-bundle" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4" dependencies = [ "fluent-langneg", "fluent-syntax", "intl-memoizer", "intl_pluralrules", - "rustc-hash 1.1.0", - "self_cell 0.10.3", + "rustc-hash 2.1.1", + "self_cell", "smallvec", "unic-langid", ] @@ -1244,11 +1284,12 @@ dependencies = [ [[package]] name = "fluent-syntax" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" dependencies = [ - "thiserror 1.0.69", + "memchr", + "thiserror 2.0.12", ] [[package]] @@ -1391,7 +1432,7 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", - "askama", + "askama 0.14.0", "cargo_metadata 0.18.1", "serde", "serde_json", @@ -1894,9 +1935,9 @@ dependencies = [ [[package]] name = "intl-memoizer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" +checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" dependencies = [ "type-map", "unic-langid", @@ -2130,6 +2171,7 @@ dependencies = [ name = "lint-docs" version = "0.1.0" dependencies = [ + "rustc-literal-escaper", "serde_json", "tempfile", "walkdir", @@ -3118,9 +3160,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb332121f7845c6bd016f9655cf22f03c2999df936694b624a88669a78667d98" +checksum = "10edc2e4393515193bd766e2f6c050b0536a68e56f2b6d56c07ababfdc114ff0" dependencies = [ "anyhow", "rustc_version", @@ -3390,6 +3432,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3403,6 +3446,7 @@ dependencies = [ "rustc_macros", "rustc_parse", "rustc_parse_format", + "rustc_proc_macro", "rustc_session", "rustc_span", "rustc_target", @@ -3424,7 +3468,7 @@ dependencies = [ "rustc-demangle", "rustc_abi", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_codegen_ssa", "rustc_data_structures", "rustc_errors", @@ -3467,6 +3511,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3504,7 +3549,7 @@ dependencies = [ "rustc_abi", "rustc_apfloat", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -3680,6 +3725,7 @@ dependencies = [ "rustc_ast", "rustc_ast_passes", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3690,6 +3736,7 @@ dependencies = [ "rustc_lint_defs", "rustc_macros", "rustc_parse", + "rustc_proc_macro", "rustc_serialize", "rustc_session", "rustc_span", @@ -3768,7 +3815,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -3805,6 +3852,7 @@ dependencies = [ "itertools", "rustc_abi", "rustc_ast", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3950,6 +3998,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4023,6 +4072,7 @@ dependencies = [ "odht", "rustc_abi", "rustc_ast", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4035,6 +4085,7 @@ dependencies = [ "rustc_index", "rustc_macros", "rustc_middle", + "rustc_proc_macro", "rustc_serialize", "rustc_session", "rustc_span", @@ -4136,7 +4187,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_const_eval", "rustc_data_structures", "rustc_errors", @@ -4162,7 +4213,7 @@ version = "0.0.0" dependencies = [ "rustc_abi", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4232,7 +4283,7 @@ dependencies = [ "rustc_ast", "rustc_ast_lowering", "rustc_ast_pretty", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_expand", @@ -4278,7 +4329,7 @@ name = "rustc_privacy" version = "0.0.0" dependencies = [ "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4291,6 +4342,13 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_proc_macro" +version = "0.0.0" +dependencies = [ + "rustc-literal-escaper", +] + [[package]] name = "rustc_query_impl" version = "0.0.0" @@ -4618,7 +4676,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", - "askama", + "askama 0.14.0", "base64", "expect-test", "indexmap", @@ -4786,15 +4844,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "self_cell" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" -dependencies = [ - "self_cell 1.2.0", -] - [[package]] name = "self_cell" version = "1.2.0" @@ -4882,12 +4931,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shell-escape" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" - [[package]] name = "shlex" version = "1.3.0" diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a16219361c05..7b103126e459 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -32,7 +32,7 @@ use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; pub use crate::format::*; @@ -1526,6 +1526,19 @@ impl Expr { | ExprKind::Struct(_) ) } + + /// Creates a dummy `P`. + /// + /// Should only be used when it will be replaced afterwards or as a return value when an error was encountered. + pub fn dummy() -> P { + P(Expr { + id: DUMMY_NODE_ID, + kind: ExprKind::Dummy, + span: DUMMY_SP, + attrs: ThinVec::new(), + tokens: None, + }) + } } #[derive(Clone, Encodable, Decodable, Debug)] @@ -3417,9 +3430,9 @@ impl Item { ItemKind::Fn(i) => Some(&i.generics), ItemKind::TyAlias(i) => Some(&i.generics), ItemKind::TraitAlias(_, generics, _) - | ItemKind::Enum(_, _, generics) - | ItemKind::Struct(_, _, generics) - | ItemKind::Union(_, _, generics) => Some(&generics), + | ItemKind::Enum(_, generics, _) + | ItemKind::Struct(_, generics, _) + | ItemKind::Union(_, generics, _) => Some(&generics), ItemKind::Trait(i) => Some(&i.generics), ItemKind::Impl(i) => Some(&i.generics), } @@ -3663,15 +3676,15 @@ pub enum ItemKind { /// An enum definition (`enum`). /// /// E.g., `enum Foo { C, D }`. - Enum(Ident, EnumDef, Generics), + Enum(Ident, Generics, EnumDef), /// A struct definition (`struct`). /// /// E.g., `struct Foo { x: A }`. - Struct(Ident, VariantData, Generics), + Struct(Ident, Generics, VariantData), /// A union definition (`union`). /// /// E.g., `union Foo { x: A, y: B }`. - Union(Ident, VariantData, Generics), + Union(Ident, Generics, VariantData), /// A trait declaration (`trait`). /// /// E.g., `trait Foo { .. }`, `trait Foo { .. }` or `auto trait Foo {}`. @@ -3688,10 +3701,8 @@ pub enum ItemKind { /// /// E.g., `foo!(..)`. MacCall(P), - /// A macro definition. MacroDef(Ident, MacroDef), - /// A single delegation item (`reuse`). /// /// E.g. `reuse ::name { target_expr_template }`. @@ -3767,9 +3778,9 @@ impl ItemKind { Self::Fn(box Fn { generics, .. }) | Self::TyAlias(box TyAlias { generics, .. }) | Self::Const(box ConstItem { generics, .. }) - | Self::Enum(_, _, generics) - | Self::Struct(_, _, generics) - | Self::Union(_, _, generics) + | Self::Enum(_, generics, _) + | Self::Struct(_, generics, _) + | Self::Union(_, generics, _) | Self::Trait(box Trait { generics, .. }) | Self::TraitAlias(_, generics, _) | Self::Impl(box Impl { generics, .. }) => Some(generics), diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 21de7ff7719b..797ab297319b 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -304,6 +304,7 @@ impl HasAttrs for Stmt { } /// A newtype around an AST node that implements the traits above if the node implements them. +#[repr(transparent)] pub struct AstNodeWrapper { pub wrapped: Wrapped, pub tag: PhantomData, @@ -313,6 +314,11 @@ impl AstNodeWrapper { pub fn new(wrapped: Wrapped, _tag: Tag) -> AstNodeWrapper { AstNodeWrapper { wrapped, tag: Default::default() } } + + pub fn from_mut(wrapped: &mut Wrapped, _tag: Tag) -> &mut AstNodeWrapper { + // SAFETY: `AstNodeWrapper` is `repr(transparent)` w.r.t `Wrapped` + unsafe { &mut *<*mut Wrapped>::cast(wrapped) } + } } impl HasNodeId for AstNodeWrapper { diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 89a5a67eb534..4fc7c7475d75 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -15,6 +15,7 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(macro_metavar_expr)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index e49886721e36..f1b183e25b54 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -20,7 +20,7 @@ use thin_vec::ThinVec; use crate::ast::*; use crate::ptr::P; use crate::tokenstream::*; -use crate::visit::{AssocCtxt, BoundKind, FnCtxt}; +use crate::visit::{AssocCtxt, BoundKind, FnCtxt, try_visit, visit_opt, walk_list}; pub trait ExpectOne { fn expect_one(self, err: &'static str) -> A::Item; @@ -33,18 +33,6 @@ impl ExpectOne for SmallVec { } } -pub trait WalkItemKind { - type Ctxt; - fn walk( - &mut self, - span: Span, - id: NodeId, - visibility: &mut Visibility, - ctxt: Self::Ctxt, - visitor: &mut impl MutVisitor, - ); -} - pub trait MutVisitor: Sized { // Methods in this trait have one of three forms: // @@ -52,12 +40,6 @@ pub trait MutVisitor: Sized { // fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>; // rare // fn filter_map_t(&mut self, t: T) -> Option; // rarest // - // Any additions to this trait should happen in form of a call to a public - // `noop_*` function that only calls out to the visitor again, not other - // `noop_*` functions. This is a necessary API workaround to the problem of - // not being able to call out to the super default method in an overridden - // default method. - // // When writing these methods, it is better to use destructuring like this: // // fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { @@ -95,7 +77,7 @@ pub trait MutVisitor: Sized { walk_use_tree(self, use_tree); } - fn visit_foreign_item(&mut self, ni: &mut P) { + fn visit_foreign_item(&mut self, ni: &mut ForeignItem) { walk_item(self, ni); } @@ -103,7 +85,7 @@ pub trait MutVisitor: Sized { walk_flat_map_foreign_item(self, ni) } - fn visit_item(&mut self, i: &mut P) { + fn visit_item(&mut self, i: &mut Item) { walk_item(self, i); } @@ -123,7 +105,7 @@ pub trait MutVisitor: Sized { walk_flat_map_field_def(self, fd) } - fn visit_assoc_item(&mut self, i: &mut P, ctxt: AssocCtxt) { + fn visit_assoc_item(&mut self, i: &mut AssocItem, ctxt: AssocCtxt) { walk_assoc_item(self, i, ctxt) } @@ -135,11 +117,11 @@ pub trait MutVisitor: Sized { walk_flat_map_assoc_item(self, i, ctxt) } - fn visit_contract(&mut self, c: &mut P) { + fn visit_contract(&mut self, c: &mut FnContract) { walk_contract(self, c); } - fn visit_fn_decl(&mut self, d: &mut P) { + fn visit_fn_decl(&mut self, d: &mut FnDecl) { walk_fn_decl(self, d); } @@ -156,7 +138,7 @@ pub trait MutVisitor: Sized { walk_closure_binder(self, b); } - fn visit_block(&mut self, b: &mut P) { + fn visit_block(&mut self, b: &mut Block) { walk_block(self, b); } @@ -191,7 +173,7 @@ pub trait MutVisitor: Sized { } fn filter_map_expr(&mut self, e: P) -> Option> { - noop_filter_map_expr(self, e) + walk_filter_map_expr(self, e) } fn visit_generic_arg(&mut self, arg: &mut GenericArg) { @@ -202,7 +184,7 @@ pub trait MutVisitor: Sized { walk_ty(self, t); } - fn visit_ty_pat(&mut self, t: &mut P) { + fn visit_ty_pat(&mut self, t: &mut TyPat) { walk_ty_pat(self, t); } @@ -258,7 +240,7 @@ pub trait MutVisitor: Sized { walk_parenthesized_parameter_data(self, p); } - fn visit_local(&mut self, l: &mut P) { + fn visit_local(&mut self, l: &mut Local) { walk_local(self, l); } @@ -318,10 +300,6 @@ pub trait MutVisitor: Sized { walk_precise_capturing_arg(self, arg); } - fn visit_mt(&mut self, mt: &mut MutTy) { - walk_mt(self, mt); - } - fn visit_expr_field(&mut self, f: &mut ExprField) { walk_expr_field(self, f); } @@ -388,17 +366,8 @@ pub trait MutVisitor: Sized { } } -/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful -/// when using a `flat_map_*` or `filter_map_*` method within a `visit_` -/// method. -// -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -pub fn visit_clobber(t: &mut T, f: impl FnOnce(T) -> T) { - let old_t = std::mem::replace(t, T::dummy()); - *t = f(old_t); -} +super::common_visitor_and_walkers!((mut) MutVisitor); -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. #[inline] fn visit_vec(elems: &mut Vec, mut visit_elem: F) where @@ -409,7 +378,6 @@ where } } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. #[inline] fn visit_thin_vec(elems: &mut ThinVec, mut visit_elem: F) where @@ -420,7 +388,6 @@ where } } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. #[inline] fn visit_opt(opt: &mut Option, mut visit_elem: F) where @@ -431,30 +398,21 @@ where } } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_attrs(vis: &mut T, attrs: &mut AttrVec) { for attr in attrs.iter_mut() { vis.visit_attribute(attr); } } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. #[allow(unused)] fn visit_exprs(vis: &mut T, exprs: &mut Vec>) { exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr)) } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_thin_exprs(vis: &mut T, exprs: &mut ThinVec>) { exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr)) } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_bounds(vis: &mut T, bounds: &mut GenericBounds, ctxt: BoundKind) { - visit_vec(bounds, |bound| vis.visit_param_bound(bound, ctxt)); -} - -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_attr_args(vis: &mut T, args: &mut AttrArgs) { match args { AttrArgs::Empty => {} @@ -466,7 +424,6 @@ fn visit_attr_args(vis: &mut T, args: &mut AttrArgs) { } } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_delim_args(vis: &mut T, args: &mut DelimArgs) { let DelimArgs { dspan, delim: _, tokens: _ } = args; let DelimSpan { open, close } = dspan; @@ -542,18 +499,18 @@ fn walk_assoc_item_constraint( vis.visit_span(span); } -pub fn walk_ty(vis: &mut T, ty: &mut P) { - let Ty { id, kind, span, tokens: _ } = ty.deref_mut(); +pub fn walk_ty(vis: &mut T, ty: &mut Ty) { + let Ty { id, kind, span, tokens: _ } = ty; vis.visit_id(id); match kind { TyKind::Err(_guar) => {} TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Never | TyKind::CVarArgs => { } TyKind::Slice(ty) => vis.visit_ty(ty), - TyKind::Ptr(mt) => vis.visit_mt(mt), - TyKind::Ref(lt, mt) | TyKind::PinnedRef(lt, mt) => { + TyKind::Ptr(MutTy { ty, mutbl: _ }) => vis.visit_ty(ty), + TyKind::Ref(lt, MutTy { ty, mutbl: _ }) | TyKind::PinnedRef(lt, MutTy { ty, mutbl: _ }) => { visit_opt(lt, |lt| vis.visit_lifetime(lt)); - vis.visit_mt(mt); + vis.visit_ty(ty); } TyKind::BareFn(bft) => { let BareFnTy { safety, ext: _, generic_params, decl, decl_span } = bft.deref_mut(); @@ -594,8 +551,8 @@ pub fn walk_ty(vis: &mut T, ty: &mut P) { vis.visit_span(span); } -pub fn walk_ty_pat(vis: &mut T, ty: &mut P) { - let TyPat { id, kind, span, tokens: _ } = ty.deref_mut(); +pub fn walk_ty_pat(vis: &mut T, ty: &mut TyPat) { + let TyPat { id, kind, span, tokens: _ } = ty; vis.visit_id(id); match kind { TyPatKind::Range(start, end, _include_end) => { @@ -608,12 +565,6 @@ pub fn walk_ty_pat(vis: &mut T, ty: &mut P) { vis.visit_span(span); } -fn walk_foreign_mod(vis: &mut T, foreign_mod: &mut ForeignMod) { - let ForeignMod { extern_span: _, safety, abi: _, items } = foreign_mod; - visit_safety(vis, safety); - items.flat_map_in_place(|item| vis.flat_map_foreign_item(item)); -} - pub fn walk_variant(visitor: &mut T, variant: &mut Variant) { let Variant { ident, vis, attrs, id, data, disr_expr, span, is_placeholder: _ } = variant; visitor.visit_id(id); @@ -692,8 +643,8 @@ fn walk_parenthesized_parameter_data(vis: &mut T, args: &mut Pare vis.visit_span(inputs_span); } -fn walk_local(vis: &mut T, local: &mut P) { - let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local.deref_mut(); +fn walk_local(vis: &mut T, local: &mut Local) { + let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local; visit_opt(super_, |sp| vis.visit_span(sp)); vis.visit_id(id); visit_attrs(vis, attrs); @@ -769,39 +720,6 @@ pub fn walk_flat_map_param(vis: &mut T, mut param: Param) -> Smal smallvec![param] } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_defaultness(vis: &mut T, defaultness: &mut Defaultness) { - match defaultness { - Defaultness::Default(span) => vis.visit_span(span), - Defaultness::Final => {} - } -} - -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_safety(vis: &mut T, safety: &mut Safety) { - match safety { - Safety::Unsafe(span) => vis.visit_span(span), - Safety::Safe(span) => vis.visit_span(span), - Safety::Default => {} - } -} - -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_polarity(vis: &mut T, polarity: &mut ImplPolarity) { - match polarity { - ImplPolarity::Positive => {} - ImplPolarity::Negative(span) => vis.visit_span(span), - } -} - -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_constness(vis: &mut T, constness: &mut Const) { - match constness { - Const::Yes(span) => vis.visit_span(span), - Const::No => {} - } -} - fn walk_closure_binder(vis: &mut T, binder: &mut ClosureBinder) { match binder { ClosureBinder::NotPresent => {} @@ -863,8 +781,8 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { } } -fn walk_contract(vis: &mut T, contract: &mut P) { - let FnContract { requires, ensures } = contract.deref_mut(); +fn walk_contract(vis: &mut T, contract: &mut FnContract) { + let FnContract { requires, ensures } = contract; if let Some(pred) = requires { vis.visit_expr(pred); } @@ -873,8 +791,8 @@ fn walk_contract(vis: &mut T, contract: &mut P) { } } -fn walk_fn_decl(vis: &mut T, decl: &mut P) { - let FnDecl { inputs, output } = decl.deref_mut(); +fn walk_fn_decl(vis: &mut T, decl: &mut FnDecl) { + let FnDecl { inputs, output } = decl; inputs.flat_map_in_place(|param| vis.flat_map_param(param)); vis.visit_fn_ret_ty(output); } @@ -940,15 +858,6 @@ pub fn walk_flat_map_generic_param( smallvec![param] } -fn walk_label(vis: &mut T, Label { ident }: &mut Label) { - vis.visit_ident(ident); -} - -fn walk_lifetime(vis: &mut T, Lifetime { id, ident }: &mut Lifetime) { - vis.visit_id(id); - vis.visit_ident(ident); -} - fn walk_generics(vis: &mut T, generics: &mut Generics) { let Generics { params, where_clause, span } = generics; params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); @@ -1082,12 +991,8 @@ pub fn walk_flat_map_expr_field( smallvec![f] } -fn walk_mt(vis: &mut T, MutTy { ty, mutbl: _ }: &mut MutTy) { - vis.visit_ty(ty); -} - -pub fn walk_block(vis: &mut T, block: &mut P) { - let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut(); +pub fn walk_block(vis: &mut T, block: &mut Block) { + let Block { id, stmts, rules: _, span, tokens: _ } = block; vis.visit_id(id); stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt)); vis.visit_span(span); @@ -1104,249 +1009,6 @@ pub fn walk_item_kind( kind.walk(span, id, visibility, ctxt, vis) } -impl WalkItemKind for ItemKind { - type Ctxt = (); - fn walk( - &mut self, - span: Span, - id: NodeId, - visibility: &mut Visibility, - _ctxt: Self::Ctxt, - vis: &mut impl MutVisitor, - ) { - match self { - ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident), - ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree), - ItemKind::Static(box StaticItem { - ident, - ty, - safety: _, - mutability: _, - expr, - define_opaque, - }) => { - vis.visit_ident(ident); - vis.visit_ty(ty); - visit_opt(expr, |expr| vis.visit_expr(expr)); - walk_define_opaques(vis, define_opaque); - } - ItemKind::Const(item) => { - walk_const_item(vis, item); - } - ItemKind::Fn(func) => { - vis.visit_fn(FnKind::Fn(FnCtxt::Free, visibility, &mut *func), span, id); - } - ItemKind::Mod(safety, ident, mod_kind) => { - visit_safety(vis, safety); - vis.visit_ident(ident); - match mod_kind { - ModKind::Loaded( - items, - _inline, - ModSpans { inner_span, inject_use_span }, - _, - ) => { - items.flat_map_in_place(|item| vis.flat_map_item(item)); - vis.visit_span(inner_span); - vis.visit_span(inject_use_span); - } - ModKind::Unloaded => {} - } - } - ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), - ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm), - ItemKind::TyAlias(box TyAlias { - defaultness, - ident, - generics, - where_clauses, - bounds, - ty, - }) => { - visit_defaultness(vis, defaultness); - vis.visit_ident(ident); - vis.visit_generics(generics); - visit_bounds(vis, bounds, BoundKind::Bound); - visit_opt(ty, |ty| vis.visit_ty(ty)); - walk_ty_alias_where_clauses(vis, where_clauses); - } - ItemKind::Enum(ident, EnumDef { variants }, generics) => { - vis.visit_ident(ident); - vis.visit_generics(generics); - variants.flat_map_in_place(|variant| vis.flat_map_variant(variant)); - } - ItemKind::Struct(ident, variant_data, generics) - | ItemKind::Union(ident, variant_data, generics) => { - vis.visit_ident(ident); - vis.visit_generics(generics); - vis.visit_variant_data(variant_data); - } - ItemKind::Impl(box Impl { - defaultness, - safety, - generics, - constness, - polarity, - of_trait, - self_ty, - items, - }) => { - visit_defaultness(vis, defaultness); - visit_safety(vis, safety); - vis.visit_generics(generics); - visit_constness(vis, constness); - visit_polarity(vis, polarity); - visit_opt(of_trait, |trait_ref| vis.visit_trait_ref(trait_ref)); - vis.visit_ty(self_ty); - items.flat_map_in_place(|item| { - vis.flat_map_assoc_item(item, AssocCtxt::Impl { of_trait: of_trait.is_some() }) - }); - } - ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => { - visit_safety(vis, safety); - vis.visit_ident(ident); - vis.visit_generics(generics); - visit_bounds(vis, bounds, BoundKind::Bound); - items.flat_map_in_place(|item| vis.flat_map_assoc_item(item, AssocCtxt::Trait)); - } - ItemKind::TraitAlias(ident, generics, bounds) => { - vis.visit_ident(ident); - vis.visit_generics(generics); - visit_bounds(vis, bounds, BoundKind::Bound); - } - ItemKind::MacCall(m) => vis.visit_mac_call(m), - ItemKind::MacroDef(ident, def) => { - vis.visit_ident(ident); - vis.visit_macro_def(def) - } - ItemKind::Delegation(box Delegation { - id, - qself, - path, - ident, - rename, - body, - from_glob: _, - }) => { - vis.visit_id(id); - vis.visit_qself(qself); - vis.visit_path(path); - vis.visit_ident(ident); - if let Some(rename) = rename { - vis.visit_ident(rename); - } - if let Some(body) = body { - vis.visit_block(body); - } - } - ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { - vis.visit_qself(qself); - vis.visit_path(prefix); - if let Some(suffixes) = suffixes { - for (ident, rename) in suffixes { - vis.visit_ident(ident); - if let Some(rename) = rename { - vis.visit_ident(rename); - } - } - } - if let Some(body) = body { - vis.visit_block(body); - } - } - } - } -} - -impl WalkItemKind for AssocItemKind { - type Ctxt = AssocCtxt; - fn walk( - &mut self, - span: Span, - id: NodeId, - visibility: &mut Visibility, - ctxt: Self::Ctxt, - visitor: &mut impl MutVisitor, - ) { - match self { - AssocItemKind::Const(item) => { - walk_const_item(visitor, item); - } - AssocItemKind::Fn(func) => { - visitor.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &mut *func), span, id); - } - AssocItemKind::Type(box TyAlias { - defaultness, - ident, - generics, - where_clauses, - bounds, - ty, - }) => { - visit_defaultness(visitor, defaultness); - visitor.visit_ident(ident); - visitor.visit_generics(generics); - visit_bounds(visitor, bounds, BoundKind::Bound); - visit_opt(ty, |ty| visitor.visit_ty(ty)); - walk_ty_alias_where_clauses(visitor, where_clauses); - } - AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), - AssocItemKind::Delegation(box Delegation { - id, - qself, - path, - ident, - rename, - body, - from_glob: _, - }) => { - visitor.visit_id(id); - visitor.visit_qself(qself); - visitor.visit_path(path); - visitor.visit_ident(ident); - if let Some(rename) = rename { - visitor.visit_ident(rename); - } - if let Some(body) = body { - visitor.visit_block(body); - } - } - AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { - visitor.visit_qself(qself); - visitor.visit_path(prefix); - if let Some(suffixes) = suffixes { - for (ident, rename) in suffixes { - visitor.visit_ident(ident); - if let Some(rename) = rename { - visitor.visit_ident(rename); - } - } - } - if let Some(body) = body { - visitor.visit_block(body); - } - } - } - } -} - -fn walk_const_item(vis: &mut T, item: &mut ConstItem) { - let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item; - visit_defaultness(vis, defaultness); - vis.visit_ident(ident); - vis.visit_generics(generics); - vis.visit_ty(ty); - visit_opt(expr, |expr| vis.visit_expr(expr)); - walk_define_opaques(vis, define_opaque); -} - -fn walk_fn_header(vis: &mut T, header: &mut FnHeader) { - let FnHeader { safety, coroutine_kind, constness, ext: _ } = header; - visit_constness(vis, constness); - coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind)); - visit_safety(vis, safety); -} - pub fn walk_crate(vis: &mut T, krate: &mut Crate) { let Crate { attrs, items, spans, id, is_placeholder: _ } = krate; vis.visit_id(id); @@ -1357,27 +1019,6 @@ pub fn walk_crate(vis: &mut T, krate: &mut Crate) { vis.visit_span(inject_use_span); } -pub fn walk_item(visitor: &mut impl MutVisitor, item: &mut P>>) { - walk_item_ctxt(visitor, item, ()) -} - -pub fn walk_assoc_item(visitor: &mut impl MutVisitor, item: &mut P, ctxt: AssocCtxt) { - walk_item_ctxt(visitor, item, ctxt) -} - -fn walk_item_ctxt( - visitor: &mut impl MutVisitor, - item: &mut P>, - ctxt: K::Ctxt, -) { - let Item { attrs, id, kind, vis, span, tokens: _ } = item.deref_mut(); - visitor.visit_id(id); - visit_attrs(visitor, attrs); - visitor.visit_vis(vis); - kind.walk(*span, *id, vis, ctxt, visitor); - visitor.visit_span(span); -} - pub fn walk_flat_map_item(vis: &mut impl MutVisitor, mut item: P) -> SmallVec<[P; 1]> { vis.visit_item(&mut item); smallvec![item] @@ -1400,55 +1041,8 @@ pub fn walk_flat_map_assoc_item( smallvec![item] } -impl WalkItemKind for ForeignItemKind { - type Ctxt = (); - fn walk( - &mut self, - span: Span, - id: NodeId, - visibility: &mut Visibility, - _ctxt: Self::Ctxt, - visitor: &mut impl MutVisitor, - ) { - match self { - ForeignItemKind::Static(box StaticItem { - ident, - ty, - mutability: _, - expr, - safety: _, - define_opaque, - }) => { - visitor.visit_ident(ident); - visitor.visit_ty(ty); - visit_opt(expr, |expr| visitor.visit_expr(expr)); - walk_define_opaques(visitor, define_opaque); - } - ForeignItemKind::Fn(func) => { - visitor.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &mut *func), span, id); - } - ForeignItemKind::TyAlias(box TyAlias { - defaultness, - ident, - generics, - where_clauses, - bounds, - ty, - }) => { - visit_defaultness(visitor, defaultness); - visitor.visit_ident(ident); - visitor.visit_generics(generics); - visit_bounds(visitor, bounds, BoundKind::Bound); - visit_opt(ty, |ty| visitor.visit_ty(ty)); - walk_ty_alias_where_clauses(visitor, where_clauses); - } - ForeignItemKind::MacCall(mac) => visitor.visit_mac_call(mac), - } - } -} - -pub fn walk_pat(vis: &mut T, pat: &mut P) { - let Pat { id, kind, span, tokens: _ } = pat.deref_mut(); +pub fn walk_pat(vis: &mut T, pat: &mut Pat) { + let Pat { id, kind, span, tokens: _ } = pat; vis.visit_id(id); match kind { PatKind::Err(_guar) => {} @@ -1747,11 +1341,9 @@ pub fn walk_expr(vis: &mut T, Expr { kind, id, span, attrs, token vis.visit_span(span); } -pub fn noop_filter_map_expr(vis: &mut T, mut e: P) -> Option> { - Some({ - vis.visit_expr(&mut e); - e - }) +pub fn walk_filter_map_expr(vis: &mut T, mut e: P) -> Option> { + vis.visit_expr(&mut e); + Some(e) } pub fn walk_flat_map_stmt( @@ -1817,113 +1409,6 @@ fn walk_capture_by(vis: &mut T, capture_by: &mut CaptureBy) { } } -fn walk_define_opaques( - vis: &mut T, - define_opaque: &mut Option>, -) { - if let Some(define_opaque) = define_opaque { - for (id, path) in define_opaque { - vis.visit_id(id); - vis.visit_path(path) - } - } -} - -/// Some value for the AST node that is valid but possibly meaningless. Similar -/// to `Default` but not intended for wide use. The value will never be used -/// meaningfully, it exists just to support unwinding in `visit_clobber` in the -/// case where its closure panics. -pub trait DummyAstNode { - fn dummy() -> Self; -} - -impl DummyAstNode for Option { - fn dummy() -> Self { - Default::default() - } -} - -impl DummyAstNode for P { - fn dummy() -> Self { - P(DummyAstNode::dummy()) - } -} - -impl DummyAstNode for Item { - fn dummy() -> Self { - Item { - attrs: Default::default(), - id: DUMMY_NODE_ID, - span: Default::default(), - vis: Visibility { - kind: VisibilityKind::Public, - span: Default::default(), - tokens: Default::default(), - }, - kind: ItemKind::ExternCrate(None, Ident::dummy()), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Expr { - fn dummy() -> Self { - Expr { - id: DUMMY_NODE_ID, - kind: ExprKind::Dummy, - span: Default::default(), - attrs: Default::default(), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Ty { - fn dummy() -> Self { - Ty { - id: DUMMY_NODE_ID, - kind: TyKind::Dummy, - span: Default::default(), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Pat { - fn dummy() -> Self { - Pat { - id: DUMMY_NODE_ID, - kind: PatKind::Wild, - span: Default::default(), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Stmt { - fn dummy() -> Self { - Stmt { id: DUMMY_NODE_ID, kind: StmtKind::Empty, span: Default::default() } - } -} - -impl DummyAstNode for Crate { - fn dummy() -> Self { - Crate { - attrs: Default::default(), - items: Default::default(), - spans: Default::default(), - id: DUMMY_NODE_ID, - is_placeholder: Default::default(), - } - } -} - -impl DummyAstNode for crate::ast_traits::AstNodeWrapper { - fn dummy() -> Self { - crate::ast_traits::AstNodeWrapper::new(N::dummy(), T::dummy()) - } -} - #[derive(Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 69a186c8cf1b..e1c2dd053242 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -112,18 +112,6 @@ pub enum LifetimeCtxt { GenericArg, } -pub trait WalkItemKind { - type Ctxt; - fn walk<'a, V: Visitor<'a>>( - &'a self, - span: Span, - id: NodeId, - visibility: &'a Visibility, - ctxt: Self::Ctxt, - visitor: &mut V, - ) -> V::Result; -} - /// Each method of the `Visitor` trait is a hook to be potentially /// overridden. Each method's default implementation recursively visits /// the substructure of the input via the corresponding `walk` method; @@ -141,6 +129,9 @@ pub trait Visitor<'ast>: Sized { fn visit_ident(&mut self, _ident: &'ast Ident) -> Self::Result { Self::Result::output() } + fn visit_foreign_mod(&mut self, nm: &'ast ForeignMod) -> Self::Result { + walk_foreign_mod(self, nm) + } fn visit_foreign_item(&mut self, i: &'ast ForeignItem) -> Self::Result { walk_item(self, i) } @@ -242,7 +233,7 @@ pub trait Visitor<'ast>: Sized { fn visit_mac_call(&mut self, mac: &'ast MacCall) -> Self::Result { walk_mac(self, mac) } - fn visit_mac_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) -> Self::Result { + fn visit_macro_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) -> Self::Result { Self::Result::output() } fn visit_path(&mut self, path: &'ast Path, _id: NodeId) -> Self::Result { @@ -315,6 +306,478 @@ pub trait Visitor<'ast>: Sized { } } +#[macro_export] +macro_rules! common_visitor_and_walkers { + ($(($mut: ident))? $Visitor:ident$(<$lt:lifetime>)?) => { + pub trait WalkItemKind { + type Ctxt; + fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( + &$($lt)? $($mut)? self, + span: Span, + id: NodeId, + visibility: &$($lt)? $($mut)? Visibility, + ctxt: Self::Ctxt, + visitor: &mut V, + ) $(-> >::Result)?; + } + + // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + $(${ignore($lt)} + #[expect(unused, rustc::pass_by_value)] + #[inline] + )? + fn visit_span<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, span: &$($lt)? $($mut)? Span) $(-> >::Result)? { + $( + ${ignore($mut)} + visitor.visit_span(span); + )? + $(${ignore($lt)}V::Result::output())? + } + + // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + $(${ignore($lt)} + #[expect(unused, rustc::pass_by_value)] + #[inline] + )? + fn visit_id<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, id: &$($lt)? $($mut)? NodeId) $(-> >::Result)? { + $( + ${ignore($mut)} + visitor.visit_id(id); + )? + $(${ignore($lt)}V::Result::output())? + } + + // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + fn visit_safety<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, safety: &$($lt)? $($mut)? Safety) $(-> >::Result)? { + match safety { + Safety::Unsafe(span) => visit_span(vis, span), + Safety::Safe(span) => visit_span(vis, span), + Safety::Default => { $(${ignore($lt)}V::Result::output())? } + } + } + + fn visit_constness<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, constness: &$($lt)? $($mut)? Const) $(-> >::Result)? { + match constness { + Const::Yes(span) => visit_span(vis, span), + Const::No => { + $(>::Result::output())? + } + } + } + + fn visit_defaultness<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, defaultness: &$($lt)? $($mut)? Defaultness) $(-> >::Result)? { + match defaultness { + Defaultness::Default(span) => visit_span(vis, span), + Defaultness::Final => { + $(>::Result::output())? + } + } + } + + fn visit_polarity<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, polarity: &$($lt)? $($mut)? ImplPolarity) $(-> >::Result)? { + match polarity { + ImplPolarity::Positive => { $(>::Result::output())? } + ImplPolarity::Negative(span) => visit_span(vis, span), + } + } + + fn visit_bounds<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, bounds: &$($lt)? $($mut)? GenericBounds, ctxt: BoundKind) $(-> >::Result)? { + walk_list!(visitor, visit_param_bound, bounds, ctxt); + $(>::Result::output())? + } + + pub fn walk_label<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Label { ident }: &$($lt)? $($mut)? Label) $(-> >::Result)? { + visitor.visit_ident(ident) + } + + pub fn walk_fn_header<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, header: &$($lt)? $($mut)? FnHeader) $(-> >::Result)? { + let FnHeader { safety, coroutine_kind, constness, ext: _ } = header; + try_visit!(visit_constness(visitor, constness)); + visit_opt!(visitor, visit_coroutine_kind, coroutine_kind); + visit_safety(visitor, safety) + } + + pub fn walk_lifetime<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Lifetime { id, ident }: &$($lt)? $($mut)? Lifetime) $(-> >::Result)? { + try_visit!(visit_id(visitor, id)); + visitor.visit_ident(ident) + } + + fn walk_item_ctxt<$($lt,)? V: $Visitor$(<$lt>)?, K: WalkItemKind>( + visitor: &mut V, + item: &$($mut)? $($lt)? Item, + ctxt: K::Ctxt, + ) $(-> >::Result)? { + let Item { attrs, id, kind, vis, span, tokens: _ } = item; + try_visit!(visit_id(visitor, id)); + walk_list!(visitor, visit_attribute, attrs); + try_visit!(visitor.visit_vis(vis)); + try_visit!(kind.walk(*span, *id, vis, ctxt, visitor)); + visit_span(visitor, span) + } + + pub fn walk_item<$($lt,)? V: $Visitor$(<$lt>)?, K: WalkItemKind>( + visitor: &mut V, + item: &$($mut)? $($lt)? Item, + ) $(-> >::Result)? { + walk_item_ctxt(visitor, item, ()) + } + + pub fn walk_assoc_item<$($lt,)? V: $Visitor$(<$lt>)?>( + visitor: &mut V, + item: &$($mut)? $($lt)? AssocItem, + ctxt: AssocCtxt, + ) $(-> >::Result)? { + walk_item_ctxt(visitor, item, ctxt) + } + + impl WalkItemKind for ItemKind { + type Ctxt = (); + fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( + &$($lt)? $($mut)? self, + span: Span, + id: NodeId, + visibility: &$($lt)? $($mut)? Visibility, + _ctxt: Self::Ctxt, + vis: &mut V, + ) $(-> >::Result)? { + match self { + ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident), + // FIXME(fee1-dead): look into this weird assymetry + ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree$(${ignore($lt)}, id, false)?), + ItemKind::Static(box StaticItem { + ident, + ty, + safety: _, + mutability: _, + expr, + define_opaque, + }) => { + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_ty(ty)); + visit_opt!(vis, visit_expr, expr); + walk_define_opaques(vis, define_opaque) + } + ItemKind::Const(item) => { + walk_const_item(vis, item) + } + ItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func); + vis.visit_fn(kind, span, id) + } + ItemKind::Mod(safety, ident, mod_kind) => { + try_visit!(visit_safety(vis, safety)); + try_visit!(vis.visit_ident(ident)); + match mod_kind { + ModKind::Loaded( + items, + _inline, + ModSpans { inner_span, inject_use_span }, + _, + ) => { + $(${ignore($mut)} + items.flat_map_in_place(|item| vis.flat_map_item(item)); + )? + $(${ignore($lt)} + walk_list!(vis, visit_item, items); + )? + try_visit!(visit_span(vis, inner_span)); + try_visit!(visit_span(vis, inject_use_span)); + } + ModKind::Unloaded => {} + } + $(>::Result::output())? + } + ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), + ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm), + ItemKind::TyAlias(box TyAlias { + defaultness, + ident, + generics, + $(${ignore($lt)} #[expect(unused)])? + where_clauses, + bounds, + ty, + }) => { + try_visit!(visit_defaultness(vis, defaultness)); + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + try_visit!(visit_bounds(vis, bounds, BoundKind::Bound)); + visit_opt!(vis, visit_ty, ty); + $(${ignore($mut)} + walk_ty_alias_where_clauses(vis, where_clauses); + )? + $(>::Result::output())? + } + ItemKind::Enum(ident, generics, enum_definition) => { + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + $(${ignore($mut)} + enum_definition.variants.flat_map_in_place(|variant| vis.flat_map_variant(variant)); + )? + $(${ignore($lt)}vis.visit_enum_def(enum_definition))? + } + ItemKind::Struct(ident, generics, variant_data) + | ItemKind::Union(ident, generics, variant_data) => { + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + vis.visit_variant_data(variant_data) + } + ItemKind::Impl(box Impl { + defaultness, + safety, + generics, + constness, + polarity, + of_trait, + self_ty, + items, + }) => { + try_visit!(visit_defaultness(vis, defaultness)); + try_visit!(visit_safety(vis, safety)); + try_visit!(vis.visit_generics(generics)); + try_visit!(visit_constness(vis, constness)); + try_visit!(visit_polarity(vis, polarity)); + visit_opt!(vis, visit_trait_ref, of_trait); + try_visit!(vis.visit_ty(self_ty)); + $(${ignore($mut)} + items.flat_map_in_place(|item| { + vis.flat_map_assoc_item(item, AssocCtxt::Impl { of_trait: of_trait.is_some() }) + }); + )? + $(${ignore($lt)} + walk_list!( + vis, + visit_assoc_item, + items, + AssocCtxt::Impl { of_trait: of_trait.is_some() } + ); + >::Result::output() + )? + } + ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => { + try_visit!(visit_safety(vis, safety)); + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + try_visit!(visit_bounds(vis, bounds, BoundKind::Bound)); + $(${ignore($mut)} + items.flat_map_in_place(|item| { + vis.flat_map_assoc_item(item, AssocCtxt::Trait) + }); + )? + $(${ignore($lt)} + walk_list!(vis, visit_assoc_item, items, AssocCtxt::Trait); + >::Result::output() + )? + } + ItemKind::TraitAlias(ident, generics, bounds) => { + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + visit_bounds(vis, bounds, BoundKind::Bound) + } + ItemKind::MacCall(m) => vis.visit_mac_call(m), + ItemKind::MacroDef(ident, def) => { + try_visit!(vis.visit_ident(ident)); + // FIXME(fee1-dead) assymetry + vis.visit_macro_def(def$(${ignore($lt)}, id)?) + } + ItemKind::Delegation(box Delegation { + id, + qself, + path, + ident, + rename, + body, + from_glob: _, + }) => { + try_visit!(visit_id(vis, id)); + try_visit!(vis.visit_qself(qself)); + try_visit!(vis.visit_path(path$(${ignore($lt)}, *id)?)); + try_visit!(vis.visit_ident(ident)); + visit_opt!(vis, visit_ident, rename); + visit_opt!(vis, visit_block, body); + $(>::Result::output())? + } + ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { + try_visit!(vis.visit_qself(qself)); + try_visit!(vis.visit_path(prefix$(${ignore($lt)}, id)?)); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + try_visit!(vis.visit_ident(ident)); + visit_opt!(vis, visit_ident, rename); + } + } + visit_opt!(vis, visit_block, body); + $(>::Result::output())? + } + } + } + } + + fn walk_const_item<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, item: &$($lt)? $($mut)? ConstItem) $(-> >::Result)? { + let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item; + try_visit!(visit_defaultness(vis, defaultness)); + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + try_visit!(vis.visit_ty(ty)); + visit_opt!(vis, visit_expr, expr); + walk_define_opaques(vis, define_opaque) + } + + fn walk_foreign_mod<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, foreign_mod: &$($lt)? $($mut)? ForeignMod) $(-> >::Result)? { + let ForeignMod { extern_span: _, safety, abi: _, items } = foreign_mod; + try_visit!(visit_safety(vis, safety)); + $(${ignore($mut)} + items.flat_map_in_place(|item| vis.flat_map_foreign_item(item)); + )? + $( + walk_list!(vis, visit_foreign_item, items); + >::Result::output() + )? + } + + fn walk_define_opaques<$($lt,)? V: $Visitor$(<$lt>)?>( + visitor: &mut V, + define_opaque: &$($lt)? $($mut)? Option>, + ) $(-> >::Result)? { + if let Some(define_opaque) = define_opaque { + for (id, path) in define_opaque { + try_visit!(visit_id(visitor, id)); + // FIXME(fee1-dead): look into this weird assymetry + try_visit!(visitor.visit_path(path$(${ignore($lt)}, *id)?)); + } + } + $(>::Result::output())? + } + + impl WalkItemKind for AssocItemKind { + type Ctxt = AssocCtxt; + fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( + &$($lt)? $($mut)? self, + span: Span, + id: NodeId, + visibility: &$($lt)? $($mut)? Visibility, + ctxt: Self::Ctxt, + vis: &mut V, + ) $(-> >::Result)? { + match self { + AssocItemKind::Const(item) => { + walk_const_item(vis, item) + } + AssocItemKind::Fn(func) => { + vis.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func), span, id) + } + AssocItemKind::Type(box TyAlias { + generics, + ident, + bounds, + ty, + defaultness, + $(${ignore($lt)} #[expect(unused)])? + where_clauses, + }) => { + try_visit!(visit_defaultness(vis, defaultness)); + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + try_visit!(visit_bounds(vis, bounds, BoundKind::Bound)); + visit_opt!(vis, visit_ty, ty); + $(${ignore($mut)} + walk_ty_alias_where_clauses(vis, where_clauses); + )? + $(>::Result::output())? + } + AssocItemKind::MacCall(mac) => { + vis.visit_mac_call(mac) + } + AssocItemKind::Delegation(box Delegation { + id, + qself, + path, + ident, + rename, + body, + from_glob: _, + }) => { + try_visit!(visit_id(vis, id)); + try_visit!(vis.visit_qself(qself)); + try_visit!(vis.visit_path(path $(${ignore($lt)}, *id)?)); + try_visit!(vis.visit_ident(ident)); + visit_opt!(vis, visit_ident, rename); + visit_opt!(vis, visit_block, body); + $(>::Result::output())? + } + AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { + try_visit!(vis.visit_qself(qself)); + try_visit!(vis.visit_path(prefix$(${ignore($lt)}, id)?)); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + try_visit!(vis.visit_ident(ident)); + visit_opt!(vis, visit_ident, rename); + } + } + visit_opt!(vis, visit_block, body); + $(>::Result::output())? + } + } + } + } + + impl WalkItemKind for ForeignItemKind { + type Ctxt = (); + fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( + &$($lt)? $($mut)? self, + span: Span, + id: NodeId, + visibility: &$($lt)? $($mut)? Visibility, + _ctxt: Self::Ctxt, + vis: &mut V, + ) $(-> >::Result)? { + match self { + ForeignItemKind::Static(box StaticItem { + ident, + ty, + mutability: _, + expr, + safety: _, + define_opaque, + }) => { + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_ty(ty)); + visit_opt!(vis, visit_expr, expr); + walk_define_opaques(vis, define_opaque) + } + ForeignItemKind::Fn(func) => { + vis.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func), span, id) + } + ForeignItemKind::TyAlias(box TyAlias { + defaultness, + ident, + generics, + bounds, + ty, + $(${ignore($lt)} #[expect(unused)])? + where_clauses, + }) => { + try_visit!(visit_defaultness(vis, defaultness)); + try_visit!(vis.visit_ident(ident)); + try_visit!(vis.visit_generics(generics)); + try_visit!(visit_bounds(vis, bounds, BoundKind::Bound)); + visit_opt!(vis, visit_ty, ty); + $(${ignore($mut)} + walk_ty_alias_where_clauses(vis, where_clauses); + )? + $(>::Result::output())? + } + ForeignItemKind::MacCall(mac) => { + vis.visit_mac_call(mac) + } + } + } + } + }; +} + +common_visitor_and_walkers!(Visitor<'a>); + pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::Result { let Crate { attrs, items, spans: _, id: _, is_placeholder: _ } = krate; walk_list!(visitor, visit_attribute, attrs); @@ -334,15 +797,6 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::R V::Result::output() } -pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, Label { ident }: &'a Label) -> V::Result { - visitor.visit_ident(ident) -} - -pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) -> V::Result { - let Lifetime { id: _, ident } = lifetime; - visitor.visit_ident(ident) -} - pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V, trait_ref: &'a PolyTraitRef) -> V::Result where V: Visitor<'a>, @@ -357,163 +811,6 @@ pub fn walk_trait_ref<'a, V: Visitor<'a>>(visitor: &mut V, trait_ref: &'a TraitR visitor.visit_path(path, *ref_id) } -impl WalkItemKind for ItemKind { - type Ctxt = (); - fn walk<'a, V: Visitor<'a>>( - &'a self, - span: Span, - id: NodeId, - vis: &'a Visibility, - _ctxt: Self::Ctxt, - visitor: &mut V, - ) -> V::Result { - match self { - ItemKind::ExternCrate(_rename, ident) => try_visit!(visitor.visit_ident(ident)), - ItemKind::Use(use_tree) => try_visit!(visitor.visit_use_tree(use_tree, id, false)), - ItemKind::Static(box StaticItem { - ident, - ty, - safety: _, - mutability: _, - expr, - define_opaque, - }) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_ty(ty)); - visit_opt!(visitor, visit_expr, expr); - try_visit!(walk_define_opaques(visitor, define_opaque)); - } - ItemKind::Const(box ConstItem { - defaultness: _, - ident, - generics, - ty, - expr, - define_opaque, - }) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_ty(ty)); - visit_opt!(visitor, visit_expr, expr); - try_visit!(walk_define_opaques(visitor, define_opaque)); - } - ItemKind::Fn(func) => { - let kind = FnKind::Fn(FnCtxt::Free, vis, &*func); - try_visit!(visitor.visit_fn(kind, span, id)); - } - ItemKind::Mod(_unsafety, ident, mod_kind) => { - try_visit!(visitor.visit_ident(ident)); - match mod_kind { - ModKind::Loaded(items, _inline, _inner_span, _) => { - walk_list!(visitor, visit_item, items); - } - ModKind::Unloaded => {} - } - } - ItemKind::ForeignMod(ForeignMod { extern_span: _, safety: _, abi: _, items }) => { - walk_list!(visitor, visit_foreign_item, items); - } - ItemKind::GlobalAsm(asm) => try_visit!(visitor.visit_inline_asm(asm)), - ItemKind::TyAlias(box TyAlias { - generics, - ident, - bounds, - ty, - defaultness: _, - where_clauses: _, - }) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); - visit_opt!(visitor, visit_ty, ty); - } - ItemKind::Enum(ident, enum_definition, generics) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_enum_def(enum_definition)); - } - ItemKind::Impl(box Impl { - defaultness: _, - safety: _, - generics, - constness: _, - polarity: _, - of_trait, - self_ty, - items, - }) => { - try_visit!(visitor.visit_generics(generics)); - visit_opt!(visitor, visit_trait_ref, of_trait); - try_visit!(visitor.visit_ty(self_ty)); - walk_list!( - visitor, - visit_assoc_item, - items, - AssocCtxt::Impl { of_trait: of_trait.is_some() } - ); - } - ItemKind::Struct(ident, struct_definition, generics) - | ItemKind::Union(ident, struct_definition, generics) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_variant_data(struct_definition)); - } - ItemKind::Trait(box Trait { - safety: _, - is_auto: _, - ident, - generics, - bounds, - items, - }) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits); - walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); - } - ItemKind::TraitAlias(ident, generics, bounds) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); - } - ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)), - ItemKind::MacroDef(ident, ts) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_mac_def(ts, id)) - } - ItemKind::Delegation(box Delegation { - id, - qself, - path, - ident, - rename, - body, - from_glob: _, - }) => { - try_visit!(visitor.visit_qself(qself)); - try_visit!(visitor.visit_path(path, *id)); - try_visit!(visitor.visit_ident(ident)); - visit_opt!(visitor, visit_ident, rename); - visit_opt!(visitor, visit_block, body); - } - ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { - try_visit!(visitor.visit_qself(qself)); - try_visit!(visitor.visit_path(prefix, id)); - if let Some(suffixes) = suffixes { - for (ident, rename) in suffixes { - visitor.visit_ident(ident); - if let Some(rename) = rename { - visitor.visit_ident(rename); - } - } - } - visit_opt!(visitor, visit_block, body); - } - } - V::Result::output() - } -} - pub fn walk_enum_def<'a, V: Visitor<'a>>( visitor: &mut V, EnumDef { variants }: &'a EnumDef, @@ -761,55 +1058,6 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res V::Result::output() } -impl WalkItemKind for ForeignItemKind { - type Ctxt = (); - fn walk<'a, V: Visitor<'a>>( - &'a self, - span: Span, - id: NodeId, - vis: &'a Visibility, - _ctxt: Self::Ctxt, - visitor: &mut V, - ) -> V::Result { - match self { - ForeignItemKind::Static(box StaticItem { - ident, - ty, - mutability: _, - expr, - safety: _, - define_opaque, - }) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_ty(ty)); - visit_opt!(visitor, visit_expr, expr); - try_visit!(walk_define_opaques(visitor, define_opaque)); - } - ForeignItemKind::Fn(func) => { - let kind = FnKind::Fn(FnCtxt::Foreign, vis, &*func); - try_visit!(visitor.visit_fn(kind, span, id)); - } - ForeignItemKind::TyAlias(box TyAlias { - generics, - ident, - bounds, - ty, - defaultness: _, - where_clauses: _, - }) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); - visit_opt!(visitor, visit_ty, ty); - } - ForeignItemKind::MacCall(mac) => { - try_visit!(visitor.visit_mac_call(mac)); - } - } - V::Result::output() - } -} - pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) -> V::Result { match bound { GenericBound::Trait(trait_ref) => visitor.visit_poly_trait_ref(trait_ref), @@ -926,12 +1174,6 @@ pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FnRetTy) V::Result::output() } -pub fn walk_fn_header<'a, V: Visitor<'a>>(visitor: &mut V, fn_header: &'a FnHeader) -> V::Result { - let FnHeader { safety: _, coroutine_kind, constness: _, ext: _ } = fn_header; - visit_opt!(visitor, visit_coroutine_kind, coroutine_kind.as_ref()); - V::Result::output() -} - pub fn walk_fn_decl<'a, V: Visitor<'a>>( visitor: &mut V, FnDecl { inputs, output }: &'a FnDecl, @@ -974,111 +1216,6 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu V::Result::output() } -impl WalkItemKind for AssocItemKind { - type Ctxt = AssocCtxt; - fn walk<'a, V: Visitor<'a>>( - &'a self, - span: Span, - id: NodeId, - vis: &'a Visibility, - ctxt: Self::Ctxt, - visitor: &mut V, - ) -> V::Result { - match self { - AssocItemKind::Const(box ConstItem { - defaultness: _, - ident, - generics, - ty, - expr, - define_opaque, - }) => { - try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_ty(ty)); - visit_opt!(visitor, visit_expr, expr); - try_visit!(walk_define_opaques(visitor, define_opaque)); - } - AssocItemKind::Fn(func) => { - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), vis, &*func); - try_visit!(visitor.visit_fn(kind, span, id)); - } - AssocItemKind::Type(box TyAlias { - generics, - ident, - bounds, - ty, - defaultness: _, - where_clauses: _, - }) => { - try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_ident(ident)); - walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); - visit_opt!(visitor, visit_ty, ty); - } - AssocItemKind::MacCall(mac) => { - try_visit!(visitor.visit_mac_call(mac)); - } - AssocItemKind::Delegation(box Delegation { - id, - qself, - path, - ident, - rename, - body, - from_glob: _, - }) => { - try_visit!(visitor.visit_qself(qself)); - try_visit!(visitor.visit_path(path, *id)); - try_visit!(visitor.visit_ident(ident)); - visit_opt!(visitor, visit_ident, rename); - visit_opt!(visitor, visit_block, body); - } - AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { - try_visit!(visitor.visit_qself(qself)); - try_visit!(visitor.visit_path(prefix, id)); - if let Some(suffixes) = suffixes { - for (ident, rename) in suffixes { - visitor.visit_ident(ident); - if let Some(rename) = rename { - visitor.visit_ident(rename); - } - } - } - visit_opt!(visitor, visit_block, body); - } - } - V::Result::output() - } -} - -pub fn walk_item<'a, V: Visitor<'a>>( - visitor: &mut V, - item: &'a Item>, -) -> V::Result { - walk_item_ctxt(visitor, item, ()) -} - -pub fn walk_assoc_item<'a, V: Visitor<'a>>( - visitor: &mut V, - item: &'a AssocItem, - ctxt: AssocCtxt, -) -> V::Result { - walk_item_ctxt(visitor, item, ctxt) -} - -fn walk_item_ctxt<'a, V: Visitor<'a>, K: WalkItemKind>( - visitor: &mut V, - item: &'a Item, - ctxt: K::Ctxt, -) -> V::Result { - let Item { id, span, vis, attrs, kind, tokens: _ } = item; - walk_list!(visitor, visit_attribute, attrs); - try_visit!(visitor.visit_vis(vis)); - try_visit!(kind.walk(*span, *id, vis, ctxt, visitor)); - V::Result::output() -} - pub fn walk_struct_def<'a, V: Visitor<'a>>( visitor: &mut V, struct_definition: &'a VariantData, @@ -1401,15 +1538,3 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) - } V::Result::output() } - -fn walk_define_opaques<'a, V: Visitor<'a>>( - visitor: &mut V, - define_opaque: &'a Option>, -) -> V::Result { - if let Some(define_opaque) = define_opaque { - for (id, path) in define_opaque { - try_visit!(visitor.visit_path(path, *id)); - } - } - V::Result::output() -} diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 0de0319c6676..17b443b8ecc2 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -6,7 +6,7 @@ use rustc_ast::*; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_session::config::FmtDebug; -use rustc_span::{Ident, Span, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use super::LoweringContext; @@ -418,7 +418,7 @@ fn expand_format_args<'hir>( &FormatArgsPiece::Placeholder(_) => { // Inject empty string before placeholders when not already preceded by a literal piece. if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) { - Some(ctx.expr_str(fmt.span, kw::Empty)) + Some(ctx.expr_str(fmt.span, sym::empty)) } else { None } diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 26c0e7e5f82a..956cb580d103 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -164,7 +164,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_item(&mut self, i: &'hir Item<'hir>) { debug_assert_eq!(i.owner_id, self.owner); self.with_parent(i.hir_id(), |this| { - if let ItemKind::Struct(_, struct_def, _) = &i.kind { + if let ItemKind::Struct(_, _, struct_def) = &i.kind { // If this is a tuple or unit-like struct, register the constructor. if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def)); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f48a571b86a7..d3aacaa15a8c 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -70,44 +70,32 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { } } - pub(super) fn lower_node(&mut self, def_id: LocalDefId) -> hir::MaybeOwner<'hir> { + pub(super) fn lower_node(&mut self, def_id: LocalDefId) { let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); if let hir::MaybeOwner::Phantom = owner { let node = self.ast_index[def_id]; match node { AstOwner::NonOwner => {} - AstOwner::Crate(c) => self.lower_crate(c), - AstOwner::Item(item) => self.lower_item(item), - AstOwner::AssocItem(item, ctxt) => self.lower_assoc_item(item, ctxt), - AstOwner::ForeignItem(item) => self.lower_foreign_item(item), + AstOwner::Crate(c) => { + debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); + self.with_lctx(CRATE_NODE_ID, |lctx| { + let module = lctx.lower_mod(&c.items, &c.spans); + // FIXME(jdonszelman): is dummy span ever a problem here? + lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP); + hir::OwnerNode::Crate(module) + }) + } + AstOwner::Item(item) => { + self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item))) + } + AstOwner::AssocItem(item, ctxt) => { + self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt)) + } + AstOwner::ForeignItem(item) => self.with_lctx(item.id, |lctx| { + hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item)) + }), } } - - self.owners[def_id] - } - - #[instrument(level = "debug", skip(self, c))] - fn lower_crate(&mut self, c: &Crate) { - debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); - self.with_lctx(CRATE_NODE_ID, |lctx| { - let module = lctx.lower_mod(&c.items, &c.spans); - // FIXME(jdonszelman): is dummy span ever a problem here? - lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP); - hir::OwnerNode::Crate(module) - }) - } - - #[instrument(level = "debug", skip(self))] - fn lower_item(&mut self, item: &Item) { - self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item))) - } - - fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) { - self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt)) - } - - fn lower_foreign_item(&mut self, item: &ForeignItem) { - self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item))) } } @@ -192,7 +180,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (ty, body_id) = self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy); self.lower_define_opaque(hir_id, define_opaque); - hir::ItemKind::Static(ident, ty, *m, body_id) + hir::ItemKind::Static(*m, ident, ty, body_id) } ItemKind::Const(box ast::ConstItem { ident, @@ -212,7 +200,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ); self.lower_define_opaque(hir_id, &define_opaque); - hir::ItemKind::Const(ident, ty, generics, body_id) + hir::ItemKind::Const(ident, generics, ty, body_id) } ItemKind::Fn(box Fn { sig: FnSig { decl, header, span: fn_sig_span }, @@ -316,9 +304,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ), }, ); - hir::ItemKind::TyAlias(ident, ty, generics) + hir::ItemKind::TyAlias(ident, generics, ty) } - ItemKind::Enum(ident, enum_definition, generics) => { + ItemKind::Enum(ident, generics, enum_definition) => { let ident = self.lower_ident(*ident); let (generics, variants) = self.lower_generics( generics, @@ -330,9 +318,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }, ); - hir::ItemKind::Enum(ident, hir::EnumDef { variants }, generics) + hir::ItemKind::Enum(ident, generics, hir::EnumDef { variants }) } - ItemKind::Struct(ident, struct_def, generics) => { + ItemKind::Struct(ident, generics, struct_def) => { let ident = self.lower_ident(*ident); let (generics, struct_def) = self.lower_generics( generics, @@ -340,9 +328,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, struct_def), ); - hir::ItemKind::Struct(ident, struct_def, generics) + hir::ItemKind::Struct(ident, generics, struct_def) } - ItemKind::Union(ident, vdata, generics) => { + ItemKind::Union(ident, generics, vdata) => { let ident = self.lower_ident(*ident); let (generics, vdata) = self.lower_generics( generics, @@ -350,7 +338,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, vdata), ); - hir::ItemKind::Union(ident, vdata, generics) + hir::ItemKind::Union(ident, generics, vdata) } ItemKind::Impl(box Impl { safety, @@ -479,8 +467,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Delegation(box delegation) => { let delegation_results = self.lower_delegation(delegation, id, false); hir::ItemKind::Fn { - ident: delegation_results.ident, sig: delegation_results.sig, + ident: delegation_results.ident, generics: delegation_results.generics, body: delegation_results.body_id, has_body: true, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 19095f2e01e5..422e79ca82ff 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -444,14 +444,14 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { tcx.definitions_untracked().def_index_count(), ); + let mut lowerer = item::ItemLowerer { + tcx, + resolver: &mut resolver, + ast_index: &ast_index, + owners: &mut owners, + }; for def_id in ast_index.indices() { - item::ItemLowerer { - tcx, - resolver: &mut resolver, - ast_index: &ast_index, - owners: &mut owners, - } - .lower_node(def_id); + lowerer.lower_node(def_id); } drop(ast_index); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index cbf4f2f5eb2b..d6fe04d2994b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1010,7 +1010,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); self.extern_mod_span = old_item; } - ItemKind::Enum(_, def, _) => { + ItemKind::Enum(_, _, def) => { for variant in &def.variants { self.visibility_not_permitted( &variant.vis, @@ -1061,7 +1061,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } visit::walk_item(self, item) } - ItemKind::Struct(ident, vdata, generics) => match vdata { + ItemKind::Struct(ident, generics, vdata) => match vdata { VariantData::Struct { fields, .. } => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); @@ -1070,7 +1070,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } _ => visit::walk_item(self, item), }, - ItemKind::Union(ident, vdata, generics) => { + ItemKind::Union(ident, generics, vdata) => { if vdata.fields().is_empty() { self.dcx().emit_err(errors::FieldlessUnion { span: item.span }); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 70cf2f2a4598..3638eb31c618 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -298,14 +298,14 @@ impl<'a> State<'a> { *defaultness, ); } - ast::ItemKind::Enum(ident, enum_definition, params) => { - self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis); + ast::ItemKind::Enum(ident, generics, enum_definition) => { + self.print_enum_def(enum_definition, generics, *ident, item.span, &item.vis); } - ast::ItemKind::Struct(ident, struct_def, generics) => { + ast::ItemKind::Struct(ident, generics, struct_def) => { let (cb, ib) = self.head(visibility_qualified(&item.vis, "struct")); self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } - ast::ItemKind::Union(ident, struct_def, generics) => { + ast::ItemKind::Union(ident, generics, struct_def) => { let (cb, ib) = self.head(visibility_qualified(&item.vis, "union")); self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } diff --git a/compiler/rustc_attr_data_structures/src/version.rs b/compiler/rustc_attr_data_structures/src/version.rs index 69b0e041d819..030e95209405 100644 --- a/compiler/rustc_attr_data_structures/src/version.rs +++ b/compiler/rustc_attr_data_structures/src/version.rs @@ -1,4 +1,5 @@ use std::fmt::{self, Display}; +use std::sync::OnceLock; use rustc_macros::{ Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version, @@ -16,8 +17,29 @@ pub struct RustcVersion { impl RustcVersion { pub const CURRENT: Self = current_rustc_version!(); + pub fn current_overridable() -> Self { + *CURRENT_OVERRIDABLE.get_or_init(|| { + if let Ok(override_var) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING") + && let Some(override_) = Self::parse_str(&override_var) + { + override_ + } else { + Self::CURRENT + } + }) + } + fn parse_str(value: &str) -> Option { + // Ignore any suffixes such as "-dev" or "-nightly". + let mut components = value.split('-').next().unwrap().splitn(3, '.'); + let major = components.next()?.parse().ok()?; + let minor = components.next()?.parse().ok()?; + let patch = components.next().unwrap_or("0").parse().ok()?; + Some(RustcVersion { major, minor, patch }) + } } +static CURRENT_OVERRIDABLE: OnceLock = OnceLock::new(); + impl Display for RustcVersion { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index c1d95d07f4c6..b9b6ca261193 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -10,7 +10,7 @@ use crate::session_diagnostics; pub(crate) struct AllowInternalUnstableParser; impl CombineAttributeParser for AllowInternalUnstableParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable]; + const PATH: &'static [Symbol] = &[sym::allow_internal_unstable]; type Item = (Symbol, Span); const CONVERT: ConvertFn = AttributeKind::AllowInternalUnstable; @@ -24,7 +24,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser { pub(crate) struct AllowConstFnUnstableParser; impl CombineAttributeParser for AllowConstFnUnstableParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable]; + const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn = AttributeKind::AllowConstFnUnstable; diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 7cb1fede1741..f4d23012af73 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -4,14 +4,29 @@ use rustc_attr_data_structures::RustcVersion; use rustc_feature::{Features, GatedCfg, find_gated_cfg}; use rustc_session::Session; use rustc_session::config::ExpectedValues; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; +use rustc_session::lint::{BuiltinLintDiag, Lint}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; use crate::{fluent_generated, parse_version}; +/// Emitter of a builtin lint from `cfg_matches`. +/// +/// Used to support emiting a lint (currently on check-cfg), either: +/// - as an early buffered lint (in `rustc`) +/// - or has a "normal" lint from HIR (in `rustdoc`) +pub trait CfgMatchesLintEmitter { + fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag); +} + +impl CfgMatchesLintEmitter for NodeId { + fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) { + sess.psess.buffer_lint(lint, sp, *self, diag); + } +} + #[derive(Clone, Debug)] pub struct Condition { pub name: Symbol, @@ -25,17 +40,17 @@ pub struct Condition { pub fn cfg_matches( cfg: &MetaItemInner, sess: &Session, - lint_node_id: NodeId, + lint_emitter: impl CfgMatchesLintEmitter, features: Option<&Features>, ) -> bool { eval_condition(cfg, sess, features, &mut |cfg| { try_gate_cfg(cfg.name, cfg.span, sess, features); match sess.psess.check_config.expecteds.get(&cfg.name) { Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - sess.psess.buffer_lint( + lint_emitter.emit_span_lint( + sess, UNEXPECTED_CFGS, cfg.span, - lint_node_id, BuiltinLintDiag::UnexpectedCfgValue( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), @@ -43,10 +58,10 @@ pub fn cfg_matches( ); } None if sess.psess.check_config.exhaustive_names => { - sess.psess.buffer_lint( + lint_emitter.emit_span_lint( + sess, UNEXPECTED_CFGS, cfg.span, - lint_node_id, BuiltinLintDiag::UnexpectedCfgName( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), @@ -129,9 +144,9 @@ pub fn eval_condition( // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details if sess.psess.assume_incomplete_release { - RustcVersion::CURRENT > min_version + RustcVersion::current_overridable() > min_version } else { - RustcVersion::CURRENT >= min_version + RustcVersion::current_overridable() >= min_version } } MetaItemKind::List(mis) => { diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index fb3d5f57d4fa..1775770ec680 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -42,9 +42,9 @@ fn get( } impl SingleAttributeParser for DeprecationParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated]; + const PATH: &'static [Symbol] = &[sym::deprecated]; - fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) { + fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) { // FIXME(jdonszelmann): merge with errors from check_attrs.rs cx.emit_err(session_diagnostics::UnusedMultiple { this: cx.attr_span, diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 6ecd6b4d7dbb..f45cf984f719 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -17,7 +17,7 @@ use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; use crate::context::{AcceptContext, FinalizeContext}; @@ -33,7 +33,7 @@ pub(crate) mod transparency; pub(crate) mod util; type AcceptFn = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>); -type AcceptMapping = &'static [(&'static [rustc_span::Symbol], AcceptFn)]; +type AcceptMapping = &'static [(&'static [Symbol], AcceptFn)]; /// An [`AttributeParser`] is a type which searches for syntactic attributes. /// @@ -72,7 +72,7 @@ pub(crate) trait AttributeParser: Default + 'static { /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. pub(crate) trait SingleAttributeParser: 'static { - const PATH: &'static [rustc_span::Symbol]; + const PATH: &'static [Symbol]; /// Caled when a duplicate attribute is found. /// @@ -119,7 +119,7 @@ type ConvertFn = fn(ThinVec) -> AttributeKind; /// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. pub(crate) trait CombineAttributeParser: 'static { - const PATH: &'static [rustc_span::Symbol]; + const PATH: &'static [Symbol]; type Item; const CONVERT: ConvertFn; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 43dfb85a7c41..ab523ce0038d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,7 +1,7 @@ use rustc_abi::Align; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use super::{CombineAttributeParser, ConvertFn}; use crate::context::AcceptContext; @@ -21,7 +21,7 @@ pub(crate) struct ReprParser; impl CombineAttributeParser for ReprParser { type Item = (ReprAttr, Span); - const PATH: &'static [rustc_span::Symbol] = &[sym::repr]; + const PATH: &'static [Symbol] = &[sym::repr]; const CONVERT: ConvertFn = AttributeKind::Repr; fn extend<'a>( @@ -99,7 +99,7 @@ fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option, _first_span: Span) {} diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ce42b0507ed5..d229fc097401 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -1,6 +1,6 @@ use rustc_attr_data_structures::AttributeKind; use rustc_span::hygiene::Transparency; -use rustc_span::sym; +use rustc_span::{Span, Symbol, sym}; use super::{AcceptContext, SingleAttributeParser}; use crate::parser::ArgParser; @@ -11,9 +11,9 @@ pub(crate) struct TransparencyParser; #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] impl SingleAttributeParser for TransparencyParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; + const PATH: &'static [Symbol] = &[sym::rustc_macro_transparency]; - fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) { + fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: Span) { cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes"); } diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 05a9029c59aa..503d2f1fae16 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -26,3 +26,33 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { first_attr_value_str_by_name(attrs, sym::crate_name) } + +pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( + attrs: impl Iterator, + symbol: Symbol, +) -> bool { + let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc)); + for attr in doc_attrs { + let Some(values) = attr.meta_item_list() else { + continue; + }; + let alias_values = values.iter().filter(|v| v.has_name(sym::alias)); + for v in alias_values { + if let Some(nested) = v.meta_item_list() { + // #[doc(alias("foo", "bar"))] + let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol); + if iter.any(|s| s == symbol) { + return true; + } + } else if let Some(meta) = v.meta_item() + && let Some(lit) = meta.name_value_literal() + { + // #[doc(alias = "foo")] + if lit.symbol == symbol { + return true; + } + } + } + } + false +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 55c3df003fe1..1360fc687142 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -26,12 +26,16 @@ macro_rules! attribute_groups { ( pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { - pub(crate) static $name: LazyLock<( - BTreeMap<&'static [Symbol], Vec, &ArgParser<'_>) + Send + Sync>>>, - Vec) -> Option>> - )> = LazyLock::new(|| { - let mut accepts = BTreeMap::<_, Vec, &ArgParser<'_>) + Send + Sync>>>::new(); - let mut finalizes = Vec::) -> Option>>::new(); + type Accepts = BTreeMap< + &'static [Symbol], + Box, &ArgParser<'_>)> + >; + type Finalizes = Vec< + Box) -> Option> + >; + pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| { + let mut accepts = Accepts::new(); + let mut finalizes = Finalizes::new(); $( { thread_local! { @@ -39,11 +43,12 @@ macro_rules! attribute_groups { }; for (k, v) in <$names>::ATTRIBUTES { - accepts.entry(*k).or_default().push(Box::new(|cx, args| { + let old = accepts.insert(*k, Box::new(|cx, args| { STATE_OBJECT.with_borrow_mut(|s| { v(s, cx, args) }) })); + assert!(old.is_none()); } finalizes.push(Box::new(|cx| { @@ -110,7 +115,8 @@ impl<'a> Deref for AcceptContext<'a> { /// Context given to every attribute parser during finalization. /// -/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example. +/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create +/// errors, for example. pub(crate) struct FinalizeContext<'a> { /// The parse context, gives access to the session and the /// diagnostics context. @@ -141,10 +147,9 @@ pub struct AttributeParser<'sess> { sess: &'sess Session, features: Option<&'sess Features>, - /// *only* parse attributes with this symbol. + /// *Only* parse attributes with this symbol. /// - /// Used in cases where we want the lowering infrastructure for - /// parse just a single attribute. + /// Used in cases where we want the lowering infrastructure for parse just a single attribute. parse_only: Option, /// Can be used to instruct parsers to reduce the number of diagnostics it emits. @@ -157,9 +162,9 @@ impl<'sess> AttributeParser<'sess> { /// One example where this is necessary, is to parse `feature` attributes themselves for /// example. /// - /// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`. - /// Some attributes require access to features to parse, which would crash if you tried to do so - /// through [`parse_limited`](Self::parse_limited). + /// Try to use this as little as possible. Attributes *should* be lowered during + /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would + /// crash if you tried to do so through [`parse_limited`](Self::parse_limited). /// /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with /// that symbol are picked out of the list of instructions and parsed. Those are returned. @@ -217,19 +222,18 @@ impl<'sess> AttributeParser<'sess> { let group_cx = FinalizeContext { cx: self, target_span }; for attr in attrs { - // if we're only looking for a single attribute, - // skip all the ones we don't care about + // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { if !attr.has_name(expected) { continue; } } - // sometimes, for example for `#![doc = include_str!("readme.md")]`, + // Sometimes, for example for `#![doc = include_str!("readme.md")]`, // doc still contains a non-literal. You might say, when we're lowering attributes // that's expanded right? But no, sometimes, when parsing attributes on macros, // we already use the lowering logic and these are still there. So, when `omit_doc` - // is set we *also* want to ignore these + // is set we *also* want to ignore these. if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) { continue; } @@ -263,21 +267,17 @@ impl<'sess> AttributeParser<'sess> { let (path, args) = parser.deconstruct(); let parts = path.segments().map(|i| i.name).collect::>(); - if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) { - for f in accepts { - let cx = AcceptContext { - group_cx: &group_cx, - attr_span: lower_span(attr.span), - }; + if let Some(accept) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) { + let cx = + AcceptContext { group_cx: &group_cx, attr_span: lower_span(attr.span) }; - f(&cx, &args) - } + accept(&cx, &args) } else { - // if we're here, we must be compiling a tool attribute... Or someone forgot to - // parse their fancy new attribute. Let's warn them in any case. If you are that - // person, and you really your attribute should remain unparsed, carefully read the - // documentation in this module and if you still think so you can add an exception - // to this assertion. + // If we're here, we must be compiling a tool attribute... Or someone + // forgot to parse their fancy new attribute. Let's warn them in any case. + // If you are that person, and you really think your attribute should + // remain unparsed, carefully read the documentation in this module and if + // you still think so you can add an exception to this assertion. // FIXME(jdonszelmann): convert other attributes, and check with this that // we caught em all diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 177df3567420..63bccf520182 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -60,7 +60,8 @@ //! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define //! a "stability" of an item. So, the stability attribute has an //! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]` -//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`]. +//! and `#[unstable()]` syntactic attributes, and at the end produce a single +//! [`AttributeKind::Stability`](rustc_attr_data_structures::AttributeKind::Stability). //! //! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be //! combined into a single semantic attribute. For example: @@ -89,8 +90,9 @@ pub mod parser; mod session_diagnostics; pub use attributes::cfg::*; -pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version}; +pub use attributes::util::{ + find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, +}; pub use context::{AttributeParser, OmitDoc}; -pub use rustc_attr_data_structures::*; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 077d953cfa31..f433d3574e18 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -12,8 +12,7 @@ use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, Norma use rustc_ast_pretty::pprust; use rustc_errors::DiagCtxtHandle; use rustc_hir::{self as hir, AttrPath}; -use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; pub struct SegmentIterator<'a> { offset: usize, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index b08c10983bbc..487f78058a8c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -606,8 +606,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { hir_args: &'hir hir::GenericArgs<'hir>, search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>, ) -> Option<&'hir hir::Lifetime> { - for (kind, hir_arg) in iter::zip(args, hir_args.args) { - match (kind.unpack(), hir_arg) { + for (arg, hir_arg) in iter::zip(args, hir_args.args) { + match (arg.kind(), hir_arg) { (GenericArgKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => { if r.as_var() == needle_fr { return Some(lt); @@ -631,7 +631,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { ) => { self.dcx().span_delayed_bug( hir_arg.span(), - format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"), + format!("unmatched arg and hir arg: found {arg:?} vs {hir_arg:?}"), ); } } @@ -997,7 +997,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { ) -> bool { let tcx = self.infcx.tcx; ty.walk().any(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() + if let ty::GenericArgKind::Type(ty) = arg.kind() && let ty::Param(_) = ty.kind() { clauses.iter().any(|pred| { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 676cb618b725..2bd0ffd143be 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -40,9 +40,7 @@ use rustc_middle::ty::{ self, ParamEnv, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypingMode, fold_regions, }; use rustc_middle::{bug, span_bug}; -use rustc_mir_dataflow::impls::{ - EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, -}; +use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces}; use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex, }; @@ -324,10 +322,6 @@ fn do_mir_borrowck<'tcx>( let move_data = MoveData::gather_moves(body, tcx, |_| true); - let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, Some("borrowck")) - .into_results_cursor(body); - let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure(); let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data); @@ -346,7 +340,6 @@ fn do_mir_borrowck<'tcx>( body, &promoted, &location_table, - flow_inits, &move_data, &borrow_set, consumer_options, diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index fe899bb054fa..8664e99cae36 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -11,8 +11,6 @@ use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options}; use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TyCtxt}; -use rustc_mir_dataflow::ResultsCursor; -use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_session::config::MirIncludeSpans; @@ -75,14 +73,13 @@ pub(crate) fn replace_regions_in_mir<'tcx>( /// Computes the (non-lexical) regions from the input MIR. /// /// This may result in errors being reported. -pub(crate) fn compute_regions<'a, 'tcx>( +pub(crate) fn compute_regions<'tcx>( root_cx: &mut BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, location_table: &PoloniusLocationTable, - flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, consumer_options: Option, @@ -112,7 +109,6 @@ pub(crate) fn compute_regions<'a, 'tcx>( location_table, borrow_set, &mut polonius_facts, - flow_inits, move_data, Rc::clone(&location_map), ); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b4ff3d66f3d5..aa584713593b 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1,3 +1,4 @@ +use std::cell::OnceCell; use std::collections::VecDeque; use std::rc::Rc; @@ -197,8 +198,8 @@ pub struct RegionInferenceContext<'tcx> { /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if /// `B: A`. This is used to compute the universal regions that are required - /// to outlive a given SCC. Computed lazily. - rev_scc_graph: Option, + /// to outlive a given SCC. + rev_scc_graph: OnceCell, /// The "R0 member of [R1..Rn]" constraints, indexed by SCC. member_constraints: Rc>, @@ -502,7 +503,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint_graph, constraint_sccs, scc_annotations, - rev_scc_graph: None, + rev_scc_graph: OnceCell::new(), member_constraints, member_constraints_applied: Vec::new(), universe_causes, @@ -809,9 +810,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { member_constraint_index: NllMemberConstraintIndex, choice_regions: &[ty::RegionVid], ) { - // Lazily compute the reverse graph, we'll need it later. - self.compute_reverse_scc_graph(); - // Create a mutable vector of the options. We'll try to winnow // them down. let mut choice_regions: Vec = choice_regions.to_vec(); @@ -849,7 +847,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // R0`). Therefore, we need only keep an option `O` if `UB: O` // for all UB. let universal_region_relations = &self.universal_region_relations; - for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { + for ub in self.reverse_scc_graph().upper_bounds(scc) { debug!(?ub); choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 25cbd579ea1c..f0d72085c407 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -215,9 +215,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // FIXME: We could probably compute the LUB if there is one. let scc = self.constraint_sccs.scc(vid); let upper_bounds: Vec<_> = self - .rev_scc_graph - .as_ref() - .unwrap() + .reverse_scc_graph() .upper_bounds(scc) .filter_map(|vid| self.definitions[vid].external_name) .filter(|r| !r.is_static()) diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index 8e04791461b2..604265f89408 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -59,13 +59,10 @@ impl ReverseSccGraph { } impl RegionInferenceContext<'_> { - /// Compute the reverse SCC-based constraint graph (lazily). - pub(super) fn compute_reverse_scc_graph(&mut self) { - if self.rev_scc_graph.is_some() { - return; - } - - self.rev_scc_graph = - Some(ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions())); + /// Return the reverse graph of the region SCCs, initialising it if needed. + pub(super) fn reverse_scc_graph(&self) -> &ReverseSccGraph { + self.rev_scc_graph.get_or_init(|| { + ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions()) + }) } } diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 57516565147e..a1c74672157b 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -148,7 +148,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { let mut next_outlives_predicates = vec![]; for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates { - match k1.unpack() { + match k1.kind() { GenericArgKind::Lifetime(r1) => { let r1_vid = self.to_region_vid(r1); let r2_vid = self.to_region_vid(r2); diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 92732aba29ba..f642d34ea673 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -131,7 +131,8 @@ impl UniversalRegionRelations<'_> { assert!(self.universal_regions.is_universal_region(fr0)); let mut external_parents = vec![]; - let mut queue = vec![fr0]; + + let mut queue = vec![relation.minimal_scc_representative(fr0)]; // Keep expanding `fr` into its parents until we reach // non-local regions. diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index b7a21cf48c8f..ca1b850f7665 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -5,8 +5,6 @@ use rustc_middle::mir::{Body, Local, Location, SourceInfo}; use rustc_middle::span_bug; use rustc_middle::ty::relate::Relate; use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeVisitable}; -use rustc_mir_dataflow::ResultsCursor; -use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use tracing::debug; @@ -28,10 +26,9 @@ mod trace; /// /// N.B., this computation requires normalization; therefore, it must be /// performed before -pub(super) fn generate<'a, 'tcx>( +pub(super) fn generate<'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, location_map: &DenseLocationMap, - flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, ) { debug!("liveness::generate"); @@ -58,7 +55,7 @@ pub(super) fn generate<'a, 'tcx>( let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); - trace::trace(typeck, location_map, flow_inits, move_data, relevant_live_locals, boring_locals); + trace::trace(typeck, location_map, move_data, relevant_live_locals, boring_locals); // Mark regions that should be live where they appear within rvalues or within a call: like // args, regions, and types. diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 512288a0f7d8..5d30fa71e92c 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -7,10 +7,10 @@ use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Loc use rustc_middle::traits::query::DropckOutlivesResult; use rustc_middle::ty::relate::Relate; use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; -use rustc_mir_dataflow::ResultsCursor; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; @@ -37,10 +37,9 @@ use crate::type_check::{NormalizeLocation, TypeChecker}; /// DROP-LIVE set are to the liveness sets for regions found in the /// `dropck_outlives` result of the variable's type (in particular, /// this respects `#[may_dangle]` annotations). -pub(super) fn trace<'a, 'tcx>( +pub(super) fn trace<'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, location_map: &DenseLocationMap, - flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec, boring_locals: Vec, @@ -48,7 +47,7 @@ pub(super) fn trace<'a, 'tcx>( let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body); let cx = LivenessContext { typeck, - flow_inits, + flow_inits: None, location_map, local_use_map, move_data, @@ -65,7 +64,7 @@ pub(super) fn trace<'a, 'tcx>( } /// Contextual state for the type-liveness coroutine. -struct LivenessContext<'a, 'typeck, 'b, 'tcx> { +struct LivenessContext<'a, 'typeck, 'tcx> { /// Current type-checker, giving us our inference context etc. /// /// This also stores the body we're currently analyzing. @@ -81,8 +80,8 @@ struct LivenessContext<'a, 'typeck, 'b, 'tcx> { drop_data: FxIndexMap, DropData<'tcx>>, /// Results of dataflow tracking which variables (and paths) have been - /// initialized. - flow_inits: ResultsCursor<'b, 'tcx, MaybeInitializedPlaces<'b, 'tcx>>, + /// initialized. Computed lazily when needed by drop-liveness. + flow_inits: Option>>, /// Index indicating where each variable is assigned, used, or /// dropped. @@ -94,8 +93,8 @@ struct DropData<'tcx> { region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>, } -struct LivenessResults<'a, 'typeck, 'b, 'tcx> { - cx: LivenessContext<'a, 'typeck, 'b, 'tcx>, +struct LivenessResults<'a, 'typeck, 'tcx> { + cx: LivenessContext<'a, 'typeck, 'tcx>, /// Set of points that define the current local. defs: DenseBitSet, @@ -116,8 +115,8 @@ struct LivenessResults<'a, 'typeck, 'b, 'tcx> { stack: Vec, } -impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { - fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self { +impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { + fn new(cx: LivenessContext<'a, 'typeck, 'tcx>) -> Self { let num_points = cx.location_map.num_points(); LivenessResults { cx, @@ -459,20 +458,56 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { } } -impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { +impl<'a, 'typeck, 'tcx> LivenessContext<'a, 'typeck, 'tcx> { + /// Computes the `MaybeInitializedPlaces` dataflow analysis if it hasn't been done already. + /// + /// In practice, the results of this dataflow analysis are rarely needed but can be expensive to + /// compute on big functions, so we compute them lazily as a fast path when: + /// - there are relevant live locals + /// - there are drop points for these relevant live locals. + /// + /// This happens as part of the drop-liveness computation: it's the only place checking for + /// maybe-initializedness of `MovePathIndex`es. + fn flow_inits(&mut self) -> &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>> { + self.flow_inits.get_or_insert_with(|| { + let tcx = self.typeck.tcx(); + let body = self.typeck.body; + // FIXME: reduce the `MaybeInitializedPlaces` domain to the useful `MovePath`s. + // + // This dataflow analysis computes maybe-initializedness of all move paths, which + // explains why it can be expensive on big functions. But this data is only used in + // drop-liveness. Therefore, most of the move paths computed here are ultimately unused, + // even if the results are computed lazily and "no relevant live locals with drop + // points" is the common case. + // + // So we only need the ones for 1) relevant live locals 2) that have drop points. That's + // a much, much smaller domain: in our benchmarks, when it's not zero (the most likely + // case), there are a few dozens compared to e.g. thousands or tens of thousands of + // locals and move paths. + let flow_inits = MaybeInitializedPlaces::new(tcx, body, self.move_data) + .iterate_to_fixpoint(tcx, body, Some("borrowck")) + .into_results_cursor(body); + flow_inits + }) + } +} + +impl<'tcx> LivenessContext<'_, '_, 'tcx> { fn body(&self) -> &Body<'tcx> { self.typeck.body } + /// Returns `true` if the local variable (or some part of it) is initialized at the current /// cursor position. Callers should call one of the `seek` methods immediately before to point /// the cursor to the desired location. - fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool { - let state = self.flow_inits.get(); + fn initialized_at_curr_loc(&mut self, mpi: MovePathIndex) -> bool { + let flow_inits = self.flow_inits(); + let state = flow_inits.get(); if state.contains(mpi) { return true; } - let move_paths = &self.flow_inits.analysis().move_data().move_paths; + let move_paths = &flow_inits.analysis().move_data().move_paths; move_paths[mpi].find_descendant(move_paths, |mpi| state.contains(mpi)).is_some() } @@ -481,7 +516,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// DROP of some local variable will have an effect -- note that /// drops, as they may unwind, are always terminators. fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block)); + let terminator_location = self.body().terminator_loc(block); + self.flow_inits().seek_before_primary_effect(terminator_location); self.initialized_at_curr_loc(mpi) } @@ -491,7 +527,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// **Warning:** Does not account for the result of `Call` /// instructions. fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block)); + let terminator_location = self.body().terminator_loc(block); + self.flow_inits().seek_after_primary_effect(terminator_location); self.initialized_at_curr_loc(mpi) } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c5122571209..4f5baeff7c32 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -30,8 +30,6 @@ use rustc_middle::ty::{ TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions, }; use rustc_middle::{bug, span_bug}; -use rustc_mir_dataflow::ResultsCursor; -use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::def_id::CRATE_DEF_ID; @@ -97,10 +95,9 @@ mod relate_tys; /// - `location_table` -- for datalog polonius, the map between `Location`s and `RichLocation`s /// - `borrow_set` -- information about borrows occurring in `body` /// - `polonius_facts` -- when using Polonius, this is the generated set of Polonius facts -/// - `flow_inits` -- results of a maybe-init dataflow analysis /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `location_map` -- map between MIR `Location` and `PointIndex` -pub(crate) fn type_check<'a, 'tcx>( +pub(crate) fn type_check<'tcx>( root_cx: &mut BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, @@ -109,7 +106,6 @@ pub(crate) fn type_check<'a, 'tcx>( location_table: &PoloniusLocationTable, borrow_set: &BorrowSet<'tcx>, polonius_facts: &mut Option, - flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, location_map: Rc, ) -> MirTypeckResults<'tcx> { @@ -167,7 +163,7 @@ pub(crate) fn type_check<'a, 'tcx>( typeck.equate_inputs_and_outputs(&normalized_inputs_and_output); typeck.check_signature_annotation(); - liveness::generate(&mut typeck, &location_map, flow_inits, move_data); + liveness::generate(&mut typeck, &location_map, move_data); let opaque_type_values = opaque_types::take_opaques_and_register_member_constraints(&mut typeck); @@ -474,17 +470,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let projected_ty = curr_projected_ty.projection_ty_core( tcx, proj, - |this, field, ()| { - let ty = this.field_ty(tcx, field); - self.structurally_resolve(ty, locations) - }, - |_, _| unreachable!(), + |ty| self.structurally_resolve(ty, locations), + |ty, variant_index, field, ()| PlaceTy::field_ty(tcx, ty, variant_index, field), + |_| unreachable!(), ); curr_projected_ty = projected_ty; } trace!(?curr_projected_ty); - let ty = curr_projected_ty.ty; + // Need to renormalize `a` as typecheck may have failed to normalize + // higher-ranked aliases if normalization was ambiguous due to inference. + let a = self.normalize(a, locations); + let ty = self.normalize(curr_projected_ty.ty, locations); self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?; Ok(()) @@ -1852,7 +1849,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | ProjectionElem::Downcast(..) => {} ProjectionElem::Field(field, fty) => { let fty = self.normalize(fty, location); - let ty = base_ty.field_ty(tcx, field); + let ty = PlaceTy::field_ty(tcx, base_ty.ty, base_ty.variant_index, field); let ty = self.normalize(ty, location); debug!(?fty, ?ty); diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs index 341c50c37f6d..5a422483eef4 100644 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs @@ -221,7 +221,7 @@ fn register_member_constraints<'tcx>( .iter() .enumerate() .filter(|(i, _)| variances[*i] == ty::Invariant) - .filter_map(|(_, arg)| match arg.unpack() { + .filter_map(|(_, arg)| match arg.kind() { GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)), GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, }) diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index 1289d21308b7..4c1264c6f1ce 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -10,6 +10,7 @@ doctest = false # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -23,6 +24,9 @@ rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } rustc_parse_format = { path = "../rustc_parse_format" } +# We must use the proc_macro version that we will compile proc-macros against, +# not the one from our own sysroot. +rustc_proc_macro = { path = "../rustc_proc_macro" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 73be954cefd7..d32e6f1558e4 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -1,6 +1,11 @@ builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function builtin_macros_alloc_must_statics = allocators must be statics +builtin_macros_asm_attribute_not_supported = + this attribute is not supported on assembly +builtin_macros_asm_cfg = + the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable + builtin_macros_asm_clobber_abi = clobber_abi builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs builtin_macros_asm_clobber_outputs = generic outputs @@ -9,17 +14,6 @@ builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}` .label = previously here .arg = duplicate argument -builtin_macros_asm_expected_comma = expected token: `,` - .label = expected `,` - -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 - .label = not a string literal - builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option @@ -45,17 +39,8 @@ builtin_macros_asm_pure_combine = the `pure` option must be combined with either builtin_macros_asm_pure_no_output = asm with the `pure` option must have at least one output -builtin_macros_asm_requires_template = requires at least a template string argument - -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 @@ -71,7 +56,6 @@ builtin_macros_assert_requires_expression = macro requires an expression as an a builtin_macros_autodiff = autodiff must be applied to function builtin_macros_autodiff_missing_config = autodiff requires at least a name and mode -builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Reverse` builtin_macros_autodiff_mode_activity = {$act} can not be used in {$mode} Mode builtin_macros_autodiff_not_build = this rustc version does not support autodiff builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found} @@ -162,7 +146,10 @@ builtin_macros_expected_comma_in_list = expected token: `,` builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern -builtin_macros_expected_register_class_or_explicit_register = expected register class or explicit register +builtin_macros_expected_other = expected operand, {$is_inline_asm -> + [false] options + *[true] clobber_abi, options + }, or additional template string builtin_macros_export_macro_rules = cannot export macro_rules! macros from a `proc-macro` crate type currently @@ -255,8 +242,6 @@ builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[defa .label = this enum needs a unit variant marked with `#[default]` .suggestion = make this unit variant default by placing `#[default]` on it -builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi` - builtin_macros_non_exhaustive_default = default variant must be exhaustive .label = declared `#[non_exhaustive]` here .help = consider a manual implementation of `Default` diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 62ee71fecc27..1fb998172226 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,4 +1,3 @@ -use ast::token::IdentIsRaw; use lint::BuiltinLintDiag; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; @@ -7,39 +6,16 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::PResult; use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; -use rustc_parse::exp; -use rustc_parse::parser::{ExpKeywordPair, Parser}; +use rustc_parse::parser::asm::*; use rustc_session::lint; -use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw}; +use rustc_session::parse::feature_err; +use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; use {rustc_ast as ast, rustc_parse_format as parse}; -use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; - -/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise -/// not validated at all. -pub struct AsmArg { - pub kind: AsmArgKind, - pub span: Span, -} - -pub enum AsmArgKind { - Template(P), - Operand(Option, ast::InlineAsmOperand), - Options(Vec), - ClobberAbi(Vec<(Symbol, Span)>), -} - -pub struct AsmOption { - pub symbol: Symbol, - pub span: Span, - // A bitset, with only the bit for this option's symbol set. - pub options: ast::InlineAsmOptions, - // Used when suggesting to remove an option. - pub span_with_comma: Span, -} +use crate::{errors, fluent_generated as fluent}; /// Validated assembly arguments, ready for macro expansion. struct ValidatedAsmArgs { @@ -52,215 +28,6 @@ struct ValidatedAsmArgs { pub options_spans: Vec, } -/// Used for better error messages when operand types are used that are not -/// supported by the current macro (e.g. `in` or `out` for `global_asm!`) -/// -/// returns -/// -/// - `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>, - exp: ExpKeywordPair, - asm_macro: AsmMacro, -) -> PResult<'a, bool> { - if matches!(asm_macro, AsmMacro::Asm) { - Ok(p.eat_keyword(exp)) - } else { - let span = p.token.span; - if p.eat_keyword_noexpect(exp.kw) { - // in gets printed as `r#in` otherwise - let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() }; - Err(p.dcx().create_err(errors::AsmUnsupportedOperand { - span, - symbol, - macro_name: asm_macro.macro_name(), - })) - } else { - Ok(false) - } - } -} - -fn parse_asm_operand<'a>( - p: &mut Parser<'a>, - asm_macro: AsmMacro, -) -> PResult<'a, Option> { - let dcx = p.dcx(); - - Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: false } - } - } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: true } - } - } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { - let block = p.parse_block()?; - ast::InlineAsmOperand::Label { block } - } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const()?; - ast::InlineAsmOperand::Const { anon_const } - } else if p.eat_keyword(exp!(Sym)) { - let expr = p.parse_expr()?; - let ast::ExprKind::Path(qself, path) = &expr.kind else { - let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); - return Err(err); - }; - let sym = - ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; - ast::InlineAsmOperand::Sym { sym } - } else { - return Ok(None); - })) -} - -// Public for rustfmt. -pub fn parse_asm_args<'a>( - p: &mut Parser<'a>, - sp: Span, - asm_macro: AsmMacro, -) -> PResult<'a, Vec> { - let dcx = p.dcx(); - - if p.token == token::Eof { - return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); - } - - let mut args = Vec::new(); - - let first_template = p.parse_expr()?; - args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) }); - - let mut allow_templates = true; - - while p.token != token::Eof { - if !p.eat(exp!(Comma)) { - if allow_templates { - // After a template string, we always expect *only* a comma... - return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span })); - } else { - // ...after that delegate to `expect` to also include the other expected tokens. - return Err(p.expect(exp!(Comma)).err().unwrap()); - } - } - - // Accept trailing commas. - if p.token == token::Eof { - break; - } - - let span_start = p.token.span; - - // Parse `clobber_abi`. - if p.eat_keyword(exp!(ClobberAbi)) { - allow_templates = false; - - args.push(AsmArg { - kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), - span: span_start.to(p.prev_token.span), - }); - - continue; - } - - // Parse `options`. - if p.eat_keyword(exp!(Options)) { - allow_templates = false; - - args.push(AsmArg { - kind: AsmArgKind::Options(parse_options(p, asm_macro)?), - span: span_start.to(p.prev_token.span), - }); - - continue; - } - - // Parse operand names. - let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { - let (ident, _) = p.token.ident().unwrap(); - p.bump(); - p.expect(exp!(Eq))?; - allow_templates = false; - Some(ident.name) - } else { - None - }; - - if let Some(op) = parse_asm_operand(p, asm_macro)? { - allow_templates = false; - - args.push(AsmArg { - span: span_start.to(p.prev_token.span), - kind: AsmArgKind::Operand(name, op), - }); - } else if allow_templates { - let template = p.parse_expr()?; - // If it can't possibly expand to a string, provide diagnostics here to include other - // things it could have been. - match template.kind { - ast::ExprKind::Lit(token_lit) - if matches!( - token_lit.kind, - token::LitKind::Str | token::LitKind::StrRaw(_) - ) => {} - ast::ExprKind::MacCall(..) => {} - _ => { - let err = dcx.create_err(errors::AsmExpectedOther { - span: template.span, - is_inline_asm: matches!(asm_macro, AsmMacro::Asm), - }); - return Err(err); - } - } - - args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) }); - } else { - p.unexpected_any()? - } - } - - Ok(args) -} - fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -278,6 +45,13 @@ fn validate_asm_args<'a>( ) -> PResult<'a, ValidatedAsmArgs> { let dcx = ecx.dcx(); + let strip_unconfigured = rustc_expand::config::StripUnconfigured { + sess: ecx.sess, + features: Some(ecx.ecfg.features), + config_tokens: false, + lint_node_id: ecx.current_expansion.lint_node_id, + }; + let mut validated = ValidatedAsmArgs { templates: vec![], operands: vec![], @@ -291,6 +65,26 @@ fn validate_asm_args<'a>( let mut allow_templates = true; for arg in args { + for attr in arg.attributes.0.iter() { + match attr.name() { + Some(sym::cfg | sym::cfg_attr) => { + if !ecx.ecfg.features.asm_cfg() { + let span = attr.span(); + feature_err(ecx.sess, sym::asm_cfg, span, fluent::builtin_macros_asm_cfg) + .emit(); + } + } + _ => { + ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() }); + } + } + } + + // Skip arguments that are configured out. + if ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(arg.attributes).is_none() { + continue; + } + match arg.kind { AsmArgKind::Template(template) => { // The error for the first template is delayed. @@ -479,103 +273,6 @@ fn validate_asm_args<'a>( Ok(validated) } -fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { - p.expect(exp!(OpenParen))?; - - let mut asm_options = Vec::new(); - - while !p.eat(exp!(CloseParen)) { - const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ - (exp!(Pure), ast::InlineAsmOptions::PURE), - (exp!(Nomem), ast::InlineAsmOptions::NOMEM), - (exp!(Readonly), ast::InlineAsmOptions::READONLY), - (exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS), - (exp!(Noreturn), ast::InlineAsmOptions::NORETURN), - (exp!(Nostack), ast::InlineAsmOptions::NOSTACK), - (exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND), - (exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX), - (exp!(Raw), ast::InlineAsmOptions::RAW), - ]; - - 'blk: { - for (exp, options) in OPTIONS { - // Gives a more accurate list of expected next tokens. - let kw_matched = if asm_macro.is_supported_option(options) { - p.eat_keyword(exp) - } else { - p.eat_keyword_noexpect(exp.kw) - }; - - if kw_matched { - let span = p.prev_token.span; - let span_with_comma = - if p.token == token::Comma { span.to(p.token.span) } else { span }; - - asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); - break 'blk; - } - } - - return p.unexpected_any(); - } - - // Allow trailing commas. - if p.eat(exp!(CloseParen)) { - break; - } - p.expect(exp!(Comma))?; - } - - Ok(asm_options) -} - -fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { - p.expect(exp!(OpenParen))?; - - if p.eat(exp!(CloseParen)) { - return Err(p.dcx().create_err(errors::NonABI { span: p.token.span })); - } - - let mut new_abis = Vec::new(); - while !p.eat(exp!(CloseParen)) { - match p.parse_str_lit() { - Ok(str_lit) => { - new_abis.push((str_lit.symbol_unescaped, str_lit.span)); - } - Err(opt_lit) => { - let span = opt_lit.map_or(p.token.span, |lit| lit.span); - return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span })); - } - }; - - // Allow trailing commas - if p.eat(exp!(CloseParen)) { - break; - } - p.expect(exp!(Comma))?; - } - - Ok(new_abis) -} - -fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { - p.expect(exp!(OpenParen))?; - let result = match p.token.uninterpolate().kind { - token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), - token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { - ast::InlineAsmRegOrRegClass::Reg(symbol) - } - _ => { - return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister { - span: p.token.span, - })); - } - }; - p.bump(); - p.expect(exp!(CloseParen))?; - Ok(result) -} - fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, asm_macro: AsmMacro, diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 8c5c20c7af48..dc3bb8ab52a5 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -73,10 +73,10 @@ mod llvm_enzyme { } // Get information about the function the macro is applied to - fn extract_item_info(iitem: &P) -> Option<(Visibility, FnSig, Ident)> { + fn extract_item_info(iitem: &P) -> Option<(Visibility, FnSig, Ident, Generics)> { match &iitem.kind { - ItemKind::Fn(box ast::Fn { sig, ident, .. }) => { - Some((iitem.vis.clone(), sig.clone(), ident.clone())) + ItemKind::Fn(box ast::Fn { sig, ident, generics, .. }) => { + Some((iitem.vis.clone(), sig.clone(), ident.clone(), generics.clone())) } _ => None, } @@ -86,27 +86,23 @@ mod llvm_enzyme { ecx: &mut ExtCtxt<'_>, meta_item: &ThinVec, has_ret: bool, + mode: DiffMode, ) -> AutoDiffAttrs { let dcx = ecx.sess.dcx(); - let mode = name(&meta_item[1]); - let Ok(mode) = DiffMode::from_str(&mode) else { - dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode }); - return AutoDiffAttrs::error(); - }; // Now we check, whether the user wants autodiff in batch/vector mode, or scalar mode. // If he doesn't specify an integer (=width), we default to scalar mode, thus width=1. - let mut first_activity = 2; + let mut first_activity = 1; - let width = if let [_, _, x, ..] = &meta_item[..] + let width = if let [_, x, ..] = &meta_item[..] && let Some(x) = width(x) { - first_activity = 3; + first_activity = 2; match x.try_into() { Ok(x) => x, Err(_) => { dcx.emit_err(errors::AutoDiffInvalidWidth { - span: meta_item[2].span(), + span: meta_item[1].span(), width: x, }); return AutoDiffAttrs::error(); @@ -165,6 +161,24 @@ mod llvm_enzyme { ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); } + pub(crate) fn expand_forward( + ecx: &mut ExtCtxt<'_>, + expand_span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, + ) -> Vec { + expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Forward) + } + + pub(crate) fn expand_reverse( + ecx: &mut ExtCtxt<'_>, + expand_span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, + ) -> Vec { + expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Reverse) + } + /// We expand the autodiff macro to generate a new placeholder function which passes /// type-checking and can be called by users. The function body of the placeholder function will /// later be replaced on LLVM-IR level, so the design of the body is less important and for now @@ -198,11 +212,12 @@ mod llvm_enzyme { /// ``` /// FIXME(ZuseZ4): Once autodiff is enabled by default, make this a doc comment which is checked /// in CI. - pub(crate) fn expand( + pub(crate) fn expand_with_mode( ecx: &mut ExtCtxt<'_>, expand_span: Span, meta_item: &ast::MetaItem, mut item: Annotatable, + mode: DiffMode, ) -> Vec { if cfg!(not(llvm_enzyme)) { ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span }); @@ -210,16 +225,18 @@ mod llvm_enzyme { } let dcx = ecx.sess.dcx(); - // first get information about the annotable item: - let Some((vis, sig, primal)) = (match &item { + // first get information about the annotable item: visibility, signature, name and generic + // parameters. + // these will be used to generate the differentiated version of the function + let Some((vis, sig, primal, generics)) = (match &item { Annotatable::Item(iitem) => extract_item_info(iitem), Annotatable::Stmt(stmt) => match &stmt.kind { ast::StmtKind::Item(iitem) => extract_item_info(iitem), _ => None, }, Annotatable::AssocItem(assoc_item, Impl { .. }) => match &assoc_item.kind { - ast::AssocItemKind::Fn(box ast::Fn { sig, ident, .. }) => { - Some((assoc_item.vis.clone(), sig.clone(), ident.clone())) + ast::AssocItemKind::Fn(box ast::Fn { sig, ident, generics, .. }) => { + Some((assoc_item.vis.clone(), sig.clone(), ident.clone(), generics.clone())) } _ => None, }, @@ -243,29 +260,41 @@ mod llvm_enzyme { // create TokenStream from vec elemtents: // meta_item doesn't have a .tokens field let mut ts: Vec = vec![]; - if meta_item_vec.len() < 2 { - // At the bare minimum, we need a fnc name and a mode, even for a dummy function with no - // input and output args. + if meta_item_vec.len() < 1 { + // At the bare minimum, we need a fnc name. dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); return vec![item]; } - meta_item_inner_to_ts(&meta_item_vec[1], &mut ts); + let mode_symbol = match mode { + DiffMode::Forward => sym::Forward, + DiffMode::Reverse => sym::Reverse, + _ => unreachable!("Unsupported mode: {:?}", mode), + }; + + // Insert mode token + let mode_token = Token::new(TokenKind::Ident(mode_symbol, false.into()), Span::default()); + ts.insert(0, TokenTree::Token(mode_token, Spacing::Joint)); + ts.insert( + 1, + TokenTree::Token(Token::new(TokenKind::Comma, Span::default()), Spacing::Alone), + ); // Now, if the user gave a width (vector aka batch-mode ad), then we copy it. // If it is not given, we default to 1 (scalar mode). let start_position; let kind: LitKind = LitKind::Integer; let symbol; - if meta_item_vec.len() >= 3 - && let Some(width) = width(&meta_item_vec[2]) + if meta_item_vec.len() >= 2 + && let Some(width) = width(&meta_item_vec[1]) { - start_position = 3; + start_position = 2; symbol = Symbol::intern(&width.to_string()); } else { - start_position = 2; + start_position = 1; symbol = sym::integer(1); } + let l: Lit = Lit { kind, symbol, suffix: None }; let t = Token::new(TokenKind::Literal(l), Span::default()); let comma = Token::new(TokenKind::Comma, Span::default()); @@ -287,7 +316,7 @@ mod llvm_enzyme { ts.pop(); let ts: TokenStream = TokenStream::from_iter(ts); - let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret); + let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret, mode); if !x.is_active() { // We encountered an error, so we return the original item. // This allows us to potentially parse other attributes. @@ -303,6 +332,7 @@ mod llvm_enzyme { let (d_sig, new_args, idents, errored) = gen_enzyme_decl(ecx, &sig, &x, span); let d_body = gen_enzyme_body( ecx, &x, n_active, &sig, &d_sig, primal, &new_args, span, sig_span, idents, errored, + &generics, ); // The first element of it is the name of the function to be generated @@ -310,7 +340,7 @@ mod llvm_enzyme { defaultness: ast::Defaultness::Final, sig: d_sig, ident: first_ident(&meta_item_vec[0]), - generics: Generics::default(), + generics, contract: None, body: Some(d_body), define_opaque: None, @@ -475,6 +505,7 @@ mod llvm_enzyme { new_decl_span: Span, idents: &[Ident], errored: bool, + generics: &Generics, ) -> (P, P, P, P) { let blackbox_path = ecx.std_path(&[sym::hint, sym::black_box]); let noop = ast::InlineAsm { @@ -497,7 +528,7 @@ mod llvm_enzyme { }; let unsf_expr = ecx.expr_block(P(unsf_block)); let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path)); - let primal_call = gen_primal_call(ecx, span, primal, idents); + let primal_call = gen_primal_call(ecx, span, primal, idents, generics); let black_box_primal_call = ecx.expr_call( new_decl_span, blackbox_call_expr.clone(), @@ -546,6 +577,7 @@ mod llvm_enzyme { sig_span: Span, idents: Vec, errored: bool, + generics: &Generics, ) -> P { let new_decl_span = d_sig.span; @@ -566,6 +598,7 @@ mod llvm_enzyme { new_decl_span, &idents, errored, + generics, ); if !has_ret(&d_sig.decl.output) { @@ -608,7 +641,6 @@ mod llvm_enzyme { panic!("Did not expect Default ret ty: {:?}", span); } }; - if x.mode.is_fwd() { // Fwd mode is easy. If the return activity is Const, we support arbitrary types. // Otherwise, we only support a scalar, a pair of scalars, or an array of scalars. @@ -668,8 +700,10 @@ mod llvm_enzyme { span: Span, primal: Ident, idents: &[Ident], + generics: &Generics, ) -> P { let has_self = idents.len() > 0 && idents[0].name == kw::SelfLower; + if has_self { let args: ThinVec<_> = idents[1..].iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect(); @@ -678,7 +712,51 @@ mod llvm_enzyme { } else { let args: ThinVec<_> = idents.iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect(); - let primal_call_expr = ecx.expr_path(ecx.path_ident(span, primal)); + let mut primal_path = ecx.path_ident(span, primal); + + let is_generic = !generics.params.is_empty(); + + match (is_generic, primal_path.segments.last_mut()) { + (true, Some(function_path)) => { + let primal_generic_types = generics + .params + .iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })); + + let generated_generic_types = primal_generic_types + .map(|type_param| { + let generic_param = TyKind::Path( + None, + ast::Path { + span, + segments: thin_vec![ast::PathSegment { + ident: type_param.ident, + args: None, + id: ast::DUMMY_NODE_ID, + }], + tokens: None, + }, + ); + + ast::AngleBracketedArg::Arg(ast::GenericArg::Type(P(ast::Ty { + id: type_param.id, + span, + kind: generic_param, + tokens: None, + }))) + }) + .collect(); + + function_path.args = + Some(P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { + span, + args: generated_generic_types, + }))); + } + _ => {} + } + + let primal_call_expr = ecx.expr_path(primal_path); ecx.expr_call(span, primal_call_expr, args) } } @@ -966,4 +1044,4 @@ mod llvm_enzyme { } } -pub(crate) use llvm_enzyme::expand; +pub(crate) use llvm_enzyme::{expand_forward, expand_reverse}; diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 44cf215c6622..69f8c273797e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -34,8 +34,8 @@ pub(crate) fn expand_deriving_clone( let is_simple; match item { Annotatable::Item(annitem) => match &annitem.kind { - ItemKind::Struct(_, _, Generics { params, .. }) - | ItemKind::Enum(_, _, Generics { params, .. }) => { + ItemKind::Struct(_, Generics { params, .. }, _) + | ItemKind::Enum(_, Generics { params, .. }, _) => { let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); let has_derive_copy = cx.resolver.has_derive_copy(container_id); if has_derive_copy diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index aa01da3151eb..0a076dd670b3 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -21,7 +21,7 @@ pub(crate) fn expand_deriving_partial_ord( // Order in which to perform matching let discr_then_data = if let Annotatable::Item(item) = item - && let ItemKind::Enum(_, def, _) = &item.kind + && let ItemKind::Enum(_, _, def) = &item.kind { let dataful: Vec = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect(); match dataful.iter().filter(|&&b| b).count() { diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 446d8afeedd7..0794192621a9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -30,7 +30,7 @@ pub(crate) fn expand_deriving_coerce_pointee( item.visit_with(&mut DetectNonGenericPointeeAttr { cx }); let (name_ident, generics) = if let Annotatable::Item(aitem) = item - && let ItemKind::Struct(ident, struct_data, g) = &aitem.kind + && let ItemKind::Struct(ident, g, struct_data) = &aitem.kind { if !matches!( struct_data, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index d9aac54ee73c..f1bef526c108 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -185,7 +185,8 @@ use rustc_ast::{ self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Mutability, PatKind, VariantData, }; -use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprPacked}; +use rustc_attr_data_structures::{AttributeKind, ReprPacked}; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_hir::Attribute; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -487,7 +488,7 @@ impl<'a> TraitDef<'a> { ); let newitem = match &item.kind { - ast::ItemKind::Struct(ident, struct_def, generics) => self.expand_struct_def( + ast::ItemKind::Struct(ident, generics, struct_def) => self.expand_struct_def( cx, struct_def, *ident, @@ -495,7 +496,7 @@ impl<'a> TraitDef<'a> { from_scratch, is_packed, ), - ast::ItemKind::Enum(ident, enum_def, generics) => { + ast::ItemKind::Enum(ident, generics, enum_def) => { // We ignore `is_packed` here, because `repr(packed)` // enums cause an error later on. // @@ -503,7 +504,7 @@ impl<'a> TraitDef<'a> { // downstream in blatantly illegal code, so it is fine. self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch) } - ast::ItemKind::Union(ident, struct_def, generics) => { + ast::ItemKind::Union(ident, generics, struct_def) => { if self.supports_unions { self.expand_struct_def( cx, diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b28f7d312d93..3a2e96a5e5af 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -109,13 +109,6 @@ pub(crate) struct ProcMacro { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_non_abi)] -pub(crate) struct NonABI { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_trace_macros)] pub(crate) struct TraceMacros { @@ -187,14 +180,6 @@ mod autodiff { pub(crate) act: String, } - #[derive(Diagnostic)] - #[diag(builtin_macros_autodiff_mode)] - pub(crate) struct AutoDiffInvalidMode { - #[primary_span] - pub(crate) span: Span, - pub(crate) mode: String, - } - #[derive(Diagnostic)] #[diag(builtin_macros_autodiff_width)] pub(crate) struct AutoDiffInvalidWidth { @@ -789,51 +774,12 @@ pub(crate) struct AsmModifierInvalid { } #[derive(Diagnostic)] -#[diag(builtin_macros_asm_requires_template)] -pub(crate) struct AsmRequiresTemplate { +#[diag(builtin_macros_asm_attribute_not_supported)] +pub(crate) struct AsmAttributeNotSupported { #[primary_span] pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_expected_comma)] -pub(crate) struct AsmExpectedComma { - #[primary_span] - #[label] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_expected_string_literal)] -pub(crate) struct AsmExpectedStringLiteral { - #[primary_span] - #[label] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_underscore_input)] -pub(crate) struct AsmUnderscoreInput { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_sym_no_path)] -pub(crate) struct AsmSymNoPath { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_expected_other)] -pub(crate) struct AsmExpectedOther { - #[primary_span] - #[label(builtin_macros_asm_expected_other)] - pub(crate) span: Span, - pub(crate) is_inline_asm: bool, -} - #[derive(Diagnostic)] #[diag(builtin_macros_asm_duplicate_arg)] pub(crate) struct AsmDuplicateArg { @@ -925,16 +871,6 @@ pub(crate) struct AsmUnsupportedOption { pub(crate) macro_name: &'static str, } -#[derive(Diagnostic)] -#[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 { @@ -957,13 +893,6 @@ pub(crate) struct TestRunnerNargs { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_expected_register_class_or_explicit_register)] -pub(crate) struct ExpectedRegisterClassOrExplicitRegister { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_expected_comma_in_list)] pub(crate) struct ExpectedCommaInList { @@ -1027,3 +956,12 @@ pub(crate) struct NonGenericPointee { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(builtin_macros_expected_other)] +pub(crate) struct AsmExpectedOther { + #[primary_span] + #[label(builtin_macros_expected_other)] + pub(crate) span: Span, + pub(crate) is_inline_asm: bool, +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9cd4d17059a0..667d90429f28 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -5,10 +5,10 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(not(bootstrap), feature(autodiff))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] -#![feature(autodiff)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] @@ -20,8 +20,6 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -extern crate proc_macro; - use std::sync::Arc; use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; @@ -112,7 +110,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { register_attr! { alloc_error_handler: alloc_error_handler::expand, - autodiff: autodiff::expand, + autodiff_forward: autodiff::expand_forward, + autodiff_reverse: autodiff::expand_reverse, bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, cfg_eval: cfg_eval::expand, @@ -139,7 +138,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { CoercePointee: coerce_pointee::expand_deriving_coerce_pointee, } - let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); + let client = rustc_proc_macro::bridge::client::Client::expand1(rustc_proc_macro::quote); register(sym::quote, SyntaxExtensionKind::Bang(Arc::new(BangProcMacro { client }))); let requires = SyntaxExtensionKind::Attr(Arc::new(contracts::ExpandRequires)); register(sym::contracts_requires, requires); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 56a67b0534d9..0bc313cbdacb 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -128,9 +128,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { c.items.push(mk_main(&mut self.cx)); } - fn visit_item(&mut self, item: &mut P) { - let item = &mut **item; - + fn visit_item(&mut self, item: &mut ast::Item) { if let Some(name) = get_test_name(&item) { debug!("this is a test item"); @@ -193,7 +191,7 @@ struct EntryPointCleaner<'a> { } impl<'a> MutVisitor for EntryPointCleaner<'a> { - fn visit_item(&mut self, item: &mut P) { + fn visit_item(&mut self, item: &mut ast::Item) { self.depth += 1; ast::mut_visit::walk_item(self, item); self.depth -= 1; diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 6fd288d195c0..d92e0fdce99a 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -34,9 +34,9 @@ jobs: - name: Rustfmt run: | cargo fmt --check - rustfmt --check build_system/main.rs - rustfmt --check example/* - rustfmt --check scripts/*.rs + rustfmt --check --edition 2024 build_system/main.rs + rustfmt --check --edition 2024 example/* + rustfmt --check --edition 2024 scripts/*.rs test: diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index e5f1896b9230..a906bec8b7e2 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -43,39 +43,42 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-assembler-x64" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4b56ebe316895d3fa37775d0a87b0c889cc933f5c8b253dbcc7c7bcb7fe7e4" +checksum = "9ff8e35182c7372df00447cb90a04e584e032c42b9b9b6e8c50ddaaf0d7900d5" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cabbc01dfbd7dcd6c329ca44f0212910309c221797ac736a67a5bc8857fe1b" +checksum = "14220f9c2698015c3b94dc6b84ae045c1c45509ddc406e43c6139252757fdb7a" +dependencies = [ + "cranelift-srcgen", +] [[package]] name = "cranelift-bforest" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ffe46df300a45f1dc6f609dc808ce963f0e3a2e971682c479a2d13e3b9b8ef" +checksum = "d372ef2777ceefd75829e1390211ac240e9196bc60699218f7ea2419038288ee" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b265bed7c51e1921fdae6419791d31af77d33662ee56d7b0fa0704dc8d231cab" +checksum = "56323783e423818fa89ce8078e90a3913d2a6e0810399bfce8ebd7ee87baa81f" [[package]] name = "cranelift-codegen" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e606230a7e3a6897d603761baee0d19f88d077f17b996bb5089488a29ae96e41" +checksum = "74ffb780aab6186c6e9ba26519654b1ac55a09c0a866f6088a4efbbd84da68ed" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -98,43 +101,44 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a63bffafc23bc60969ad528e138788495999d935f0adcfd6543cb151ca8637d" +checksum = "c23ef13814d3b39c869650d5961128cbbecad83fbdff4e6836a03ecf6862d7ed" dependencies = [ - "cranelift-assembler-x64", + "cranelift-assembler-x64-meta", "cranelift-codegen-shared", + "cranelift-srcgen", ] [[package]] name = "cranelift-codegen-shared" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af50281b67324b58e843170a6a5943cf6d387c06f7eeacc9f5696e4ab7ae7d7e" +checksum = "b9f623300657679f847803ce80811454bfff89cea4f6bf684be5c468d4a73631" [[package]] name = "cranelift-control" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c20c1b38d1abfbcebb0032e497e71156c0e3b8dcb3f0a92b9863b7bcaec290c" +checksum = "31f4168af69989aa6b91fab46799ed4df6096f3209f4a6c8fb4358f49c60188f" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2c67d95507c51b4a1ff3f3555fe4bfec36b9e13c1b684ccc602736f5d5f4a2" +checksum = "ca6fa9bae1c8de26d71ac2162f069447610fd91e7780cb480ee0d76ac81eabb8" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e002691cc69c38b54fc7ec93e5be5b744f627d027031d991cc845d1d512d0ce" +checksum = "b8219205608aa0b0e6769b580284a7e055c7e0c323c1041cde7ca078add3e412" dependencies = [ "cranelift-codegen", "log", @@ -144,15 +148,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93588ed1796cbcb0e2ad160403509e2c5d330d80dd6e0014ac6774c7ebac496" +checksum = "588d0c5964f10860b04043e55aab26d7f7a206b0fd4f10c5260e8aa5773832bd" [[package]] name = "cranelift-jit" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6682f0b193d6b7873cc8e7ed67e8776a8a26f50eeabf88534e9be618b9a03" +checksum = "56bd917ddc524f84f4066f954062875bdfc0dffea068ee94e906d98de5ac7c33" dependencies = [ "anyhow", "cranelift-codegen", @@ -170,9 +174,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff19784c6de05116e63e6a34791012bd927b2a4eac56233039c46f1b6a4edac8" +checksum = "68a03c057d8a992e06596c871341e446af43ff9224f941e5b8adea39137a5391" dependencies = [ "anyhow", "cranelift-codegen", @@ -181,9 +185,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b09bdd6407bf5d89661b80cf926ce731c9e8cc184bf49102267a2369a8358e" +checksum = "19ed3c94cb97b14f92b6a94a1d45ef8c851f6a2ad9114e5d91d233f7da638fed" dependencies = [ "cranelift-codegen", "libc", @@ -192,9 +196,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.118.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685e8661a30d1cb69509f589ac643adeee79c5f63c0da316431b9fad29e6d3b4" +checksum = "a64dacef362a69375a604f6636e5e9a174fb96dba3b273646fcd9fa85c1d0997" dependencies = [ "anyhow", "cranelift-codegen", @@ -205,6 +209,12 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-srcgen" +version = "0.120.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85256fac1519a7d25a040c1d850fba67478f3f021ad5fdf738ba4425ee862dbf" + [[package]] name = "crc32fast" version = "1.4.2" @@ -331,9 +341,9 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.11.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" +checksum = "5216b1837de2149f8bc8e6d5f88a9326b63b8c836ed58ce4a0a29ec736a59734" dependencies = [ "allocator-api2", "bumpalo", @@ -436,9 +446,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "wasmtime-jit-icache-coherence" -version = "31.0.0" +version = "33.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a54f6c6c7e9d7eeee32dfcc10db7f29d505ee7dd28d00593ea241d5f70698e64" +checksum = "175e924dbc944c185808466d1e90b5a7feb610f3b9abdfe26f8ee25fd1086d1c" dependencies = [ "anyhow", "cfg-if", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 08b60de14c1f..94fcbd0a5023 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.118.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.118.0" } -cranelift-module = { version = "0.118.0" } -cranelift-native = { version = "0.118.0" } -cranelift-jit = { version = "0.118.0", optional = true } -cranelift-object = { version = "0.118.0" } +cranelift-codegen = { version = "0.120.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.120.0" } +cranelift-module = { version = "0.120.0" } +cranelift-native = { version = "0.120.0" } +cranelift-jit = { version = "0.120.0", optional = true } +cranelift-object = { version = "0.120.0" } target-lexicon = "0.13" gimli = { version = "0.31", default-features = false, features = ["write"] } object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } @@ -24,12 +24,12 @@ smallvec = "1.8.1" [patch.crates-io] # Uncomment to use an unreleased version of cranelift -#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } -#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } -#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } -#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } -#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } -#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" } # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 28edb5795ce3..4d1e4d843ffe 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -7,7 +7,7 @@ If not please open an issue. ## Download using Rustup -The Cranelift codegen backend is distributed in nightly builds on Linux and x86_64 macOS. If you want to +The Cranelift codegen backend is distributed in nightly builds on Linux, macOS and x86_64 Windows. If you want to install it using Rustup, you can do that by running: ```bash @@ -79,7 +79,7 @@ For more docs on how to build and test see [build_system/usage.txt](build_system Not all targets are available as rustup component for nightly. See notes in the platform support matrix. [^xcoff]: XCOFF object file format is not supported. -[^no-rustup]: Not available as rustup component for nightly. You can build it yourself. +[^no-rustup]: Not available as [rustup component for nightly](https://rust-lang.github.io/rustup-components-history/). You can build it yourself. ## Usage diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index 72bc422523d5..bf7cf1c0a346 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -18,7 +18,7 @@ pub(crate) fn build_backend( let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs); let mut rustflags = rustflags_from_env("RUSTFLAGS"); - rustflags.push("-Zallow-features=rustc_private".to_owned()); + rustflags.push("-Zallow-features=rustc_private,f16,f128".to_owned()); rustflags_to_cmd_env(&mut cmd, "RUSTFLAGS", &rustflags); if env::var("CG_CLIF_EXPENSIVE_CHECKS").is_ok() { diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index a73e3c87d43d..00955998e703 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -168,7 +168,8 @@ fn build_llvm_sysroot_for_triple(compiler: Compiler) -> SysrootTarget { let file_name_str = file.file_name().unwrap().to_str().unwrap(); if (file_name_str.contains("rustc_") && !file_name_str.contains("rustc_std_workspace_") - && !file_name_str.contains("rustc_demangle")) + && !file_name_str.contains("rustc_demangle") + && !file_name_str.contains("rustc_literal_escaper")) || file_name_str.contains("chalk") || file_name_str.contains("tracing") || file_name_str.contains("regex") @@ -234,7 +235,7 @@ fn build_clif_sysroot_for_triple( compiler.rustflags.extend(rustflags); let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); build_cmd.arg("--release"); - build_cmd.arg("--features").arg("backtrace panic-unwind compiler-builtins-no-f16-f128"); + build_cmd.arg("--features").arg("backtrace panic-unwind"); build_cmd.arg(format!("-Zroot-dir={}", STDLIB_SRC.to_path(dirs).display())); build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true"); build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif"); diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md index dbe36109f83e..9dcfee4f535a 100644 --- a/compiler/rustc_codegen_cranelift/docs/usage.md +++ b/compiler/rustc_codegen_cranelift/docs/usage.md @@ -47,7 +47,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=jit-mode-Cprefer-dynamic + echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=jit-mode -Cprefer-dynamic } function jit() { diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 6e345b2a6fdf..1dc799c0aeea 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -521,10 +521,28 @@ fn panic_cannot_unwind() -> ! { } #[lang = "eh_personality"] -fn eh_personality() -> ! { +// FIXME personality signature depends on target +fn eh_personality( + _version: i32, + _actions: i32, + _exception_class: u64, + _exception_object: *mut (), + _context: *mut (), +) -> i32 { loop {} } +#[lang = "panic_in_cleanup"] +fn panic_in_cleanup() -> ! { + loop {} +} + +#[cfg(all(unix, not(target_vendor = "apple")))] +#[link(name = "gcc_s")] +extern "C" { + fn _Unwind_Resume(exc: *mut ()) -> !; +} + #[lang = "drop_in_place"] #[allow(unconditional_recursion)] pub unsafe fn drop_in_place(to_drop: *mut T) { diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 2d9de2a5b8d6..5d83066cffb8 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -166,6 +166,7 @@ fn main() { enum Never {} } + #[cfg(not(target_arch = "s390x"))] // s390x doesn't have vector instructions enabled by default foo(I64X2([0, 0])); transmute_wide_pointer(); @@ -203,9 +204,11 @@ fn rust_call_abi() { rust_call_abi_callee((1, 2)); } +#[cfg_attr(target_arch = "s390x", allow(dead_code))] #[repr(simd)] struct I64X2([i64; 2]); +#[cfg_attr(target_arch = "s390x", allow(dead_code))] #[allow(improper_ctypes_definitions)] extern "C" fn foo(_a: I64X2) {} diff --git a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch index d7e3b11127c4..38bb43f8204b 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch @@ -37,6 +37,15 @@ diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index bf2b6d59f88..d5ccce03bbf 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs +@@ -300,8 +300,6 @@ impl_atomic_primitive!(AtomicI32(i32), size("32"), align(4)); + impl_atomic_primitive!(AtomicU32(u32), size("32"), align(4)); + impl_atomic_primitive!(AtomicI64(i64), size("64"), align(8)); + impl_atomic_primitive!(AtomicU64(u64), size("64"), align(8)); +-impl_atomic_primitive!(AtomicI128(i128), size("128"), align(16)); +-impl_atomic_primitive!(AtomicU128(u128), size("128"), align(16)); + + #[cfg(target_pointer_width = "16")] + impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(2)); @@ -3585,44 +3585,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { 8, u64 AtomicU64 diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 16c8488acdb5..f6e6bbc2387c 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -17,7 +17,7 @@ index 1e336bf..35e6f54 100644 @@ -2,5 +2,4 @@ // tidy-alphabetical-start -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] - #![cfg_attr(test, feature(cfg_match))] + #![cfg_attr(test, feature(cfg_select))] #![feature(alloc_layout_extra)] #![feature(array_chunks)] diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index ceff15b1180a..af4bd6dc6b85 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-03-30" +channel = "nightly-2025-05-25" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index ca6426f2ba9d..532702bb1a46 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -43,7 +43,7 @@ verbose-tests = false # disabled bootstrap will crash trying to copy llvm tools for the bootstrap # compiler. llvm-tools = false -std-features = ["panic-unwind", "compiler-builtins-no-f16-f128"] +std-features = ["panic-unwind"] EOF diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 95a4302b5e47..32c71f433b0f 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -53,6 +53,8 @@ rm -r tests/run-make/split-debuginfo # same rm -r tests/run-make/target-specs # i686 not supported by Cranelift 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/global-asm-mono-sym-fn.rs # same +rm tests/ui/asm/naked-asm-mono-sym-fn.rs # same 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 @@ -70,19 +72,13 @@ rm tests/ui/consts/precise-drop-with-coverage.rs rm tests/ui/issues/issue-85461.rs 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/float/conv-bits-runtime-const.rs -rm tests/ui/consts/const-eval/float_methods.rs -rm tests/ui/match/match-float.rs - # optimization tests # ================== rm tests/ui/codegen/issue-28950.rs # depends on stack size optimizations rm tests/ui/codegen/init-large-type.rs # same rm -r tests/run-make/fmt-write-bloat/ # tests an optimization rm tests/ui/statics/const_generics.rs # same +rm tests/ui/linking/executable-no-mangle-strip.rs # requires --gc-sections to work for statics # backend specific tests # ====================== @@ -96,6 +92,7 @@ rm -r tests/run-make/llvm-location-discriminator-limit-dummy-span # same rm tests/ui/abi/stack-protector.rs # requires stack protector support rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific +rm -r tests/ui/optimization-remark.rs # same rm -r tests/run-make/print-to-output # requires --print relocation-models # requires asm, llvm-ir and/or llvm-bc emit support @@ -118,7 +115,6 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift -rm tests/ui/abi/simd-abi-checks-avx.rs # attempts to declare function with two different signatures # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended @@ -129,7 +125,11 @@ rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contain rm -r tests/run-make/translation # same rm -r tests/run-make/missing-unstable-trait-bound # This disables support for unstable features, but running cg_clif needs some unstable features rm -r tests/run-make/const-trait-stable-toolchain # same +rm -r tests/run-make/print-request-help-stable-unstable # same rm -r tests/run-make/incr-add-rust-src-component +rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path +rm -r tests/run-make/export/extern-opt # something about rustc version mismatches +rm -r tests/run-make/export # same # genuine bugs # ============ @@ -141,6 +141,7 @@ rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort 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 tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs # same +rm tests/ui/async-await/async-drop/async-drop-initial.rs # same (rust-lang/rust#140493) rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 5f631405a9a4..5f7bf3821d77 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_target::callconv::{Conv, FnAbi, PassMode}; +use smallvec::SmallVec; use self::pass_mode::*; pub(crate) use self::returning::codegen_return; @@ -153,10 +154,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { let ret = self.lib_call_unadjusted(name, params, returns, &args)[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_ptr = self.create_stack_slot(16, 16); - ret_ptr.store(self, ret, MemFlags::trusted()); - Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]) + Cow::Owned(vec![codegen_bitcast(self, types::I128, ret)]) } else if ret_single_i128 && self.tcx.sess.target.arch == "s390x" { // Return i128 using a return area pointer on s390x. let mut params = params; @@ -184,11 +182,9 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { let sig = Signature { params, returns, call_conv: self.target_config.default_call_conv }; let func_id = self.module.declare_function(name, Linkage::Import, &sig).unwrap(); let func_ref = self.module.declare_func_in_func(func_id, &mut self.bcx.func); - if self.clif_comments.enabled() { - self.add_comment(func_ref, format!("{:?}", name)); - } let call_inst = self.bcx.ins().call(func_ref, args); if self.clif_comments.enabled() { + self.add_comment(func_ref, format!("{:?}", name)); self.add_comment(call_inst, format!("lib_call {}", name)); } let results = self.bcx.inst_results(call_inst); @@ -384,6 +380,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( args: &[Spanned>], destination: Place<'tcx>, target: Option, + _unwind: UnwindAction, ) { let func = codegen_operand(fx, func); let fn_sig = func.layout().ty.fn_sig(fx.tcx); @@ -529,7 +526,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( Some(Instance { def: InstanceKind::Virtual(_, idx), .. }) => { if fx.clif_comments.enabled() { let nop_inst = fx.bcx.ins().nop(); - fx.add_comment( + fx.add_post_comment( nop_inst, with_no_trimmed_paths!(format!( "virtual call; self arg pass mode: {:?}", @@ -555,7 +552,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( None => { if fx.clif_comments.enabled() { let nop_inst = fx.bcx.ins().nop(); - fx.add_comment(nop_inst, "indirect call"); + fx.add_post_comment(nop_inst, "indirect call"); } let func = func.load_scalar(fx); @@ -585,17 +582,18 @@ pub(crate) fn codegen_terminator_call<'tcx>( adjust_call_for_c_variadic(fx, &fn_abi, source_info, func_ref, &mut call_args); } - if fx.clif_comments.enabled() { - let nop_inst = fx.bcx.ins().nop(); - with_no_trimmed_paths!(fx.add_comment(nop_inst, format!("abi: {:?}", fn_abi))); - } - - match func_ref { + let call_inst = match func_ref { CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args), CallTarget::Indirect(sig, func_ptr) => { fx.bcx.ins().call_indirect(sig, func_ptr, &call_args) } + }; + + if fx.clif_comments.enabled() { + with_no_trimmed_paths!(fx.add_comment(call_inst, format!("abi: {:?}", fn_abi))); } + + fx.bcx.func.dfg.inst_results(call_inst).iter().copied().collect::>() }); if let Some(dest) = target { @@ -705,13 +703,16 @@ pub(crate) fn codegen_drop<'tcx>( source_info: mir::SourceInfo, drop_place: CPlace<'tcx>, target: BasicBlock, + _unwind: UnwindAction, ) { let ty = drop_place.layout().ty; let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty); + let ret_block = fx.get_block(target); // AsyncDropGlueCtorShim can't be here if let ty::InstanceKind::DropGlue(_, None) = drop_instance.def { // we don't actually need to drop anything + fx.bcx.ins().jump(ret_block, &[]); } else { match ty.kind() { ty::Dynamic(_, _, ty::Dyn) => { @@ -748,7 +749,9 @@ pub(crate) fn codegen_drop<'tcx>( let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi); let sig = fx.bcx.import_signature(sig); + // FIXME implement cleanup on exceptions fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]); + fx.bcx.ins().jump(ret_block, &[]); } ty::Dynamic(_, _, ty::DynStar) => { // IN THIS ARM, WE HAVE: @@ -792,6 +795,8 @@ pub(crate) fn codegen_drop<'tcx>( let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi); let sig = fx.bcx.import_signature(sig); fx.bcx.ins().call_indirect(sig, drop_fn, &[data]); + // FIXME implement cleanup on exceptions + fx.bcx.ins().jump(ret_block, &[]); } _ => { assert!(!matches!(drop_instance.def, InstanceKind::Virtual(_, _))); @@ -817,10 +822,37 @@ pub(crate) fn codegen_drop<'tcx>( let func_ref = fx.get_function_ref(drop_instance); fx.bcx.ins().call(func_ref, &call_args); + // FIXME implement cleanup on exceptions + fx.bcx.ins().jump(ret_block, &[]); } } } - - let target_block = fx.get_block(target); - fx.bcx.ins().jump(target_block, &[]); +} + +pub(crate) fn lib_call_arg_param(tcx: TyCtxt<'_>, ty: Type, is_signed: bool) -> AbiParam { + let param = AbiParam::new(ty); + if ty.is_int() && u64::from(ty.bits()) < tcx.data_layout.pointer_size.bits() { + match (&*tcx.sess.target.arch, &*tcx.sess.target.vendor) { + ("x86_64", _) | ("aarch64", "apple") => match (ty, is_signed) { + (types::I8 | types::I16, true) => param.sext(), + (types::I8 | types::I16, false) => param.uext(), + _ => param, + }, + ("aarch64", _) => param, + ("riscv64", _) => match (ty, is_signed) { + (types::I32, _) | (_, true) => param.sext(), + _ => param.uext(), + }, + ("s390x", _) => { + if is_signed { + param.sext() + } else { + param.uext() + } + } + _ => unimplemented!("{:?}", tcx.sess.target.arch), + } + } else { + param + } } diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 06d89bc9ea7d..6d8614aca693 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -22,8 +22,10 @@ fn reg_to_abi_param(reg: Reg) -> AbiParam { (RegKind::Integer, 3..=4) => types::I32, (RegKind::Integer, 5..=8) => types::I64, (RegKind::Integer, 9..=16) => types::I128, + (RegKind::Float, 2) => types::F16, (RegKind::Float, 4) => types::F32, (RegKind::Float, 8) => types::F64, + (RegKind::Float, 16) => types::F128, (RegKind::Vector, size) => types::I8.by(u32::try_from(size).unwrap()).unwrap(), _ => unreachable!("{:?}", reg), }; diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs index 9e048c7badb8..36087f96dd77 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs @@ -46,7 +46,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, ret_arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, ret_place: CPlace<'tcx>, - f: impl FnOnce(&mut FunctionCx<'_, '_, 'tcx>, Option) -> Inst, + f: impl FnOnce(&mut FunctionCx<'_, '_, 'tcx>, Option) -> SmallVec<[Value; 2]>, ) { let (ret_temp_place, return_ptr) = match ret_arg_abi.mode { PassMode::Ignore => (None, None), @@ -67,23 +67,21 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast { .. } => (None, None), }; - let call_inst = f(fx, return_ptr); + let results = f(fx, return_ptr); match ret_arg_abi.mode { PassMode::Ignore => {} PassMode::Direct(_) => { - let ret_val = fx.bcx.inst_results(call_inst)[0]; + let ret_val = results[0]; ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_arg_abi.layout)); } PassMode::Pair(_, _) => { - let ret_val_a = fx.bcx.inst_results(call_inst)[0]; - let ret_val_b = fx.bcx.inst_results(call_inst)[1]; + let ret_val_a = results[0]; + let ret_val_b = results[1]; ret_place .write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout)); } PassMode::Cast { ref cast, .. } => { - let results = - fx.bcx.inst_results(call_inst).iter().copied().collect::>(); let result = super::pass_mode::from_casted_value(fx, &results, ret_place.layout(), cast); ret_place.write_cvalue(fx, result); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 524e0d9fe35e..4617304105a5 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -15,9 +15,9 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use crate::constant::ConstantCx; use crate::debuginfo::{FunctionDebugContext, TypeDebugContext}; -use crate::enable_verifier; use crate::prelude::*; use crate::pretty_clif::CommentWriter; +use crate::{codegen_f16_f128, enable_verifier}; pub(crate) struct CodegenedFunction { symbol_name: String, @@ -193,6 +193,18 @@ pub(crate) fn compile_fn( name = codegened_func.symbol_name )); } + Err(ModuleError::Compilation(CodegenError::Verifier(err))) => { + let early_dcx = rustc_session::EarlyDiagCtxt::new( + rustc_session::config::ErrorOutputType::default(), + ); + let _ = early_dcx.early_err(format!("{:?}", err)); + let pretty_error = cranelift_codegen::print_errors::pretty_verifier_error( + &context.func, + Some(Box::new(&clif_comments)), + err, + ); + early_dcx.early_fatal(format!("cranelift verify error:\n{}", pretty_error)); + } Err(err) => { panic!("Error while defining {name}: {err:?}", name = codegened_func.symbol_name); } @@ -303,7 +315,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { bb_data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); }); let inst = fx.bcx.func.layout.last_inst(block).unwrap(); - fx.add_comment(inst, terminator_head); + fx.add_post_comment(inst, terminator_head); } let source_info = bb_data.terminator().source_info; @@ -337,7 +349,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { TerminatorKind::Return => { crate::abi::codegen_return(fx); } - TerminatorKind::Assert { cond, expected, msg, target, unwind: _ } => { + TerminatorKind::Assert { cond, expected, msg, target, unwind } => { if !fx.tcx.sess.overflow_checks() && msg.is_optional_overflow_check() { let target = fx.get_block(*target); fx.bcx.ins().jump(target, &[]); @@ -367,6 +379,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { fx, rustc_hir::LangItem::PanicBoundsCheck, &[index, len, location], + *unwind, Some(source_info.span), ); } @@ -379,6 +392,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { fx, rustc_hir::LangItem::PanicMisalignedPointerDereference, &[required, found, location], + *unwind, Some(source_info.span), ); } @@ -389,6 +403,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { fx, rustc_hir::LangItem::PanicNullPointerDereference, &[location], + *unwind, Some(source_info.span), ) } @@ -399,6 +414,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { fx, msg.panic_function(), &[location], + *unwind, Some(source_info.span), ); } @@ -457,7 +473,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { destination, target, fn_span, - unwind: _, + unwind, call_source: _, } => { fx.tcx.prof.generic_activity("codegen call").run(|| { @@ -468,6 +484,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { args, *destination, *target, + *unwind, ) }); } @@ -514,7 +531,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ); } TerminatorKind::UnwindTerminate(reason) => { - codegen_unwind_terminate(fx, source_info, *reason); + codegen_unwind_terminate(fx, Some(source_info.span), *reason); } TerminatorKind::UnwindResume => { // FIXME implement unwinding @@ -530,23 +547,19 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { | TerminatorKind::CoroutineDrop => { bug!("shouldn't exist at codegen {:?}", bb_data.terminator()); } - TerminatorKind::Drop { place, target, unwind: _, replace: _, drop, async_fut } => { + TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } => { assert!( async_fut.is_none() && drop.is_none(), "Async Drop must be expanded or reset to sync before codegen" ); let drop_place = codegen_place(fx, *place); - crate::abi::codegen_drop(fx, source_info, drop_place, *target); + crate::abi::codegen_drop(fx, source_info, drop_place, *target, *unwind); } }; } } -fn codegen_stmt<'tcx>( - fx: &mut FunctionCx<'_, '_, 'tcx>, - #[allow(unused_variables)] cur_block: Block, - stmt: &Statement<'tcx>, -) { +fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: &Statement<'tcx>) { let _print_guard = crate::PrintOnPanic(|| format!("stmt {:?}", stmt)); fx.set_debug_loc(stmt.source_info); @@ -557,7 +570,7 @@ fn codegen_stmt<'tcx>( if fx.clif_comments.enabled() { let inst = fx.bcx.func.layout.last_inst(cur_block).unwrap(); with_no_trimmed_paths!({ - fx.add_comment(inst, format!("{:?}", stmt)); + fx.add_post_comment(inst, format!("{:?}", stmt)); }); } } @@ -622,6 +635,15 @@ fn codegen_stmt<'tcx>( let val = operand.load_scalar(fx); match layout.ty.kind() { ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout), + // FIXME(bytecodealliance/wasmtime#8312): Remove + // once backend lowerings have been added to + // Cranelift. + ty::Float(FloatTy::F16) => { + CValue::by_val(codegen_f16_f128::neg_f16(fx, val), layout) + } + ty::Float(FloatTy::F128) => { + CValue::by_val(codegen_f16_f128::neg_f128(fx, val), layout) + } ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout), _ => unreachable!("un op Neg for {:?}", layout.ty), } @@ -793,7 +815,7 @@ fn codegen_stmt<'tcx>( let done_block = fx.bcx.create_block(); let index = fx.bcx.append_block_param(loop_block, fx.pointer_type); let zero = fx.bcx.ins().iconst(fx.pointer_type, 0); - fx.bcx.ins().jump(loop_block, &[zero]); + fx.bcx.ins().jump(loop_block, &[zero.into()]); fx.bcx.switch_to_block(loop_block); let done = fx.bcx.ins().icmp_imm(IntCC::Equal, index, times as i64); @@ -803,7 +825,7 @@ fn codegen_stmt<'tcx>( let to = lval.place_index(fx, index); to.write_cvalue(fx, operand); let index = fx.bcx.ins().iadd_imm(index, 1); - fx.bcx.ins().jump(loop_block, &[index]); + fx.bcx.ins().jump(loop_block, &[index.into()]); fx.bcx.switch_to_block(done_block); fx.bcx.ins().nop(); @@ -1058,23 +1080,28 @@ pub(crate) fn codegen_panic_nounwind<'tcx>( let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap()); let args = [msg_ptr, msg_len]; - codegen_panic_inner(fx, rustc_hir::LangItem::PanicNounwind, &args, span); + codegen_panic_inner( + fx, + rustc_hir::LangItem::PanicNounwind, + &args, + UnwindAction::Terminate(UnwindTerminateReason::Abi), + span, + ); } pub(crate) fn codegen_unwind_terminate<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - source_info: mir::SourceInfo, + span: Option, reason: UnwindTerminateReason, ) { - let args = []; - - codegen_panic_inner(fx, reason.lang_item(), &args, Some(source_info.span)); + codegen_panic_inner(fx, reason.lang_item(), &[], UnwindAction::Unreachable, span); } fn codegen_panic_inner<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, lang_item: rustc_hir::LangItem, args: &[Value], + _unwind: UnwindAction, span: Option, ) { fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); @@ -1090,6 +1117,8 @@ fn codegen_panic_inner<'tcx>( let symbol_name = fx.tcx.symbol_name(instance).name; + // FIXME implement cleanup on exceptions + fx.lib_call( symbol_name, args.iter().map(|&arg| AbiParam::new(fx.bcx.func.dfg.value_type(arg))).collect(), diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index e2346324232f..8a725680e705 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -1,5 +1,6 @@ //! Various number casting functions +use crate::codegen_f16_f128; use crate::prelude::*; pub(crate) fn clif_intcast( @@ -36,6 +37,14 @@ pub(crate) fn clif_int_or_float_cast( ) -> Value { let from_ty = fx.bcx.func.dfg.value_type(from); + // FIXME(bytecodealliance/wasmtime#8312): Remove in favour of native + // Cranelift operations once Cranelift backends have lowerings for them. + if matches!(from_ty, types::F16 | types::F128) + || matches!(to_ty, types::F16 | types::F128) && from_ty != to_ty + { + return codegen_f16_f128::codegen_cast(fx, from, from_signed, to_ty, to_signed); + } + if from_ty.is_int() && to_ty.is_int() { // int-like -> int-like clif_intcast( @@ -58,8 +67,10 @@ pub(crate) fn clif_int_or_float_cast( "__float{sign}ti{flt}f", sign = if from_signed { "" } else { "un" }, flt = match to_ty { + types::F16 => "h", types::F32 => "s", types::F64 => "d", + types::F128 => "t", _ => unreachable!("{:?}", to_ty), }, ); @@ -90,8 +101,10 @@ pub(crate) fn clif_int_or_float_cast( "__fix{sign}{flt}fti", sign = if to_signed { "" } else { "uns" }, flt = match from_ty { + types::F16 => "h", types::F32 => "s", types::F64 => "d", + types::F128 => "t", _ => unreachable!("{:?}", to_ty), }, ); @@ -145,8 +158,12 @@ pub(crate) fn clif_int_or_float_cast( } else if from_ty.is_float() && to_ty.is_float() { // float -> float match (from_ty, to_ty) { - (types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from), - (types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from), + (types::F16, types::F32 | types::F64 | types::F128) + | (types::F32, types::F64 | types::F128) + | (types::F64, types::F128) => fx.bcx.ins().fpromote(to_ty, from), + (types::F128, types::F64 | types::F32 | types::F16) + | (types::F64, types::F32 | types::F16) + | (types::F32, types::F16) => fx.bcx.ins().fdemote(to_ty, from), _ => from, } } else { diff --git a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs new file mode 100644 index 000000000000..1e202be1f185 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs @@ -0,0 +1,284 @@ +use crate::prelude::*; + +pub(crate) fn f16_to_f32(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let (value, arg_ty) = + if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" { + ( + fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value), + lib_call_arg_param(fx.tcx, types::I16, false), + ) + } else { + (value, AbiParam::new(types::F16)) + }; + fx.lib_call("__extendhfsf2", vec![arg_ty], vec![AbiParam::new(types::F32)], &[value])[0] +} + +fn f16_to_f64(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let ret = f16_to_f32(fx, value); + fx.bcx.ins().fpromote(types::F64, ret) +} + +pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" { + types::I16 + } else { + types::F16 + }; + let ret = fx.lib_call( + "__truncsfhf2", + vec![AbiParam::new(types::F32)], + vec![AbiParam::new(ret_ty)], + &[value], + )[0]; + if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret } +} + +fn f64_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" { + types::I16 + } else { + types::F16 + }; + let ret = fx.lib_call( + "__truncdfhf2", + vec![AbiParam::new(types::F64)], + vec![AbiParam::new(ret_ty)], + &[value], + )[0]; + if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret } +} + +pub(crate) fn fcmp(fx: &mut FunctionCx<'_, '_, '_>, cc: FloatCC, lhs: Value, rhs: Value) -> Value { + let ty = fx.bcx.func.dfg.value_type(lhs); + match ty { + types::F32 | types::F64 => fx.bcx.ins().fcmp(cc, lhs, rhs), + types::F16 => { + let lhs = f16_to_f32(fx, lhs); + let rhs = f16_to_f32(fx, rhs); + fx.bcx.ins().fcmp(cc, lhs, rhs) + } + types::F128 => { + let (name, int_cc) = match cc { + FloatCC::Equal => ("__eqtf2", IntCC::Equal), + FloatCC::NotEqual => ("__netf2", IntCC::NotEqual), + FloatCC::LessThan => ("__lttf2", IntCC::SignedLessThan), + FloatCC::LessThanOrEqual => ("__letf2", IntCC::SignedLessThanOrEqual), + FloatCC::GreaterThan => ("__gttf2", IntCC::SignedGreaterThan), + FloatCC::GreaterThanOrEqual => ("__getf2", IntCC::SignedGreaterThanOrEqual), + _ => unreachable!("not currently used in rustc_codegen_cranelift: {cc:?}"), + }; + let res = fx.lib_call( + name, + vec![AbiParam::new(types::F128), AbiParam::new(types::F128)], + // FIXME(rust-lang/compiler-builtins#919): This should be `I64` on non-AArch64 + // architectures, but switching it before compiler-builtins is fixed causes test + // failures. + vec![AbiParam::new(types::I32)], + &[lhs, rhs], + )[0]; + let zero = fx.bcx.ins().iconst(types::I32, 0); + let res = fx.bcx.ins().icmp(int_cc, res, zero); + res + } + _ => unreachable!("{ty:?}"), + } +} + +pub(crate) fn codegen_f128_binop( + fx: &mut FunctionCx<'_, '_, '_>, + bin_op: BinOp, + lhs: Value, + rhs: Value, +) -> Value { + let name = match bin_op { + BinOp::Add => "__addtf3", + BinOp::Sub => "__subtf3", + BinOp::Mul => "__multf3", + BinOp::Div => "__divtf3", + _ => unreachable!("handled in `codegen_float_binop`"), + }; + fx.lib_call( + name, + vec![AbiParam::new(types::F128), AbiParam::new(types::F128)], + vec![AbiParam::new(types::F128)], + &[lhs, rhs], + )[0] +} + +pub(crate) fn neg_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let bits = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value); + let bits = fx.bcx.ins().bxor_imm(bits, 0x8000); + fx.bcx.ins().bitcast(types::F16, MemFlags::new(), bits) +} + +pub(crate) fn neg_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let bits = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), value); + let (low, high) = fx.bcx.ins().isplit(bits); + let high = fx.bcx.ins().bxor_imm(high, 0x8000_0000_0000_0000_u64 as i64); + let bits = fx.bcx.ins().iconcat(low, high); + fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits) +} + +pub(crate) fn abs_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let bits = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value); + let bits = fx.bcx.ins().band_imm(bits, 0x7fff); + fx.bcx.ins().bitcast(types::F16, MemFlags::new(), bits) +} + +pub(crate) fn abs_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { + let bits = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), value); + let (low, high) = fx.bcx.ins().isplit(bits); + let high = fx.bcx.ins().band_imm(high, 0x7fff_ffff_ffff_ffff_u64 as i64); + let bits = fx.bcx.ins().iconcat(low, high); + fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits) +} + +pub(crate) fn copysign_f16(fx: &mut FunctionCx<'_, '_, '_>, lhs: Value, rhs: Value) -> Value { + let lhs = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), lhs); + let rhs = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), rhs); + let res = fx.bcx.ins().band_imm(lhs, 0x7fff); + let sign = fx.bcx.ins().band_imm(rhs, 0x8000); + let res = fx.bcx.ins().bor(res, sign); + fx.bcx.ins().bitcast(types::F16, MemFlags::new(), res) +} + +pub(crate) fn copysign_f128(fx: &mut FunctionCx<'_, '_, '_>, lhs: Value, rhs: Value) -> Value { + let lhs = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), lhs); + let rhs = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), rhs); + let (low, lhs_high) = fx.bcx.ins().isplit(lhs); + let (_, rhs_high) = fx.bcx.ins().isplit(rhs); + let high = fx.bcx.ins().band_imm(lhs_high, 0x7fff_ffff_ffff_ffff_u64 as i64); + let sign = fx.bcx.ins().band_imm(rhs_high, 0x8000_0000_0000_0000_u64 as i64); + let high = fx.bcx.ins().bor(high, sign); + let res = fx.bcx.ins().iconcat(low, high); + fx.bcx.ins().bitcast(types::F128, MemFlags::new(), res) +} + +pub(crate) fn codegen_cast( + fx: &mut FunctionCx<'_, '_, '_>, + from: Value, + from_signed: bool, + to_ty: Type, + to_signed: bool, +) -> Value { + let from_ty = fx.bcx.func.dfg.value_type(from); + if from_ty.is_float() && to_ty.is_float() { + let name = match (from_ty, to_ty) { + (types::F16, types::F32) => return f16_to_f32(fx, from), + (types::F16, types::F64) => return f16_to_f64(fx, from), + (types::F16, types::F128) => "__extendhftf2", + (types::F32, types::F128) => "__extendsftf2", + (types::F64, types::F128) => "__extenddftf2", + (types::F128, types::F64) => "__trunctfdf2", + (types::F128, types::F32) => "__trunctfsf2", + (types::F128, types::F16) => "__trunctfhf2", + (types::F64, types::F16) => return f64_to_f16(fx, from), + (types::F32, types::F16) => return f32_to_f16(fx, from), + _ => unreachable!("{from_ty:?} -> {to_ty:?}"), + }; + fx.lib_call(name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from])[0] + } else if from_ty.is_int() && to_ty == types::F16 { + let res = clif_int_or_float_cast(fx, from, from_signed, types::F32, false); + f32_to_f16(fx, res) + } else if from_ty == types::F16 && to_ty.is_int() { + let from = f16_to_f32(fx, from); + clif_int_or_float_cast(fx, from, false, to_ty, to_signed) + } else if from_ty.is_int() && to_ty == types::F128 { + let (from, from_ty) = if from_ty.bits() < 32 { + (clif_int_or_float_cast(fx, from, from_signed, types::I32, from_signed), types::I32) + } else { + (from, from_ty) + }; + let name = format!( + "__float{sign}{size}itf", + sign = if from_signed { "" } else { "un" }, + size = match from_ty { + types::I32 => 's', + types::I64 => 'd', + types::I128 => 't', + _ => unreachable!("{from_ty:?}"), + }, + ); + fx.lib_call( + &name, + vec![lib_call_arg_param(fx.tcx, from_ty, from_signed)], + vec![AbiParam::new(to_ty)], + &[from], + )[0] + } else if from_ty == types::F128 && to_ty.is_int() { + let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty }; + let name = format!( + "__fix{sign}tf{size}i", + sign = if from_signed { "" } else { "un" }, + size = match ret_ty { + types::I32 => 's', + types::I64 => 'd', + types::I128 => 't', + _ => unreachable!("{from_ty:?}"), + }, + ); + let ret = + fx.lib_call(&name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from]) + [0]; + let val = if ret_ty == to_ty { + ret + } else { + let (min, max) = match (to_ty, to_signed) { + (types::I8, false) => (0, i64::from(u8::MAX)), + (types::I16, false) => (0, i64::from(u16::MAX)), + (types::I8, true) => (i64::from(i8::MIN as u32), i64::from(i8::MAX as u32)), + (types::I16, true) => (i64::from(i16::MIN as u32), i64::from(i16::MAX as u32)), + _ => unreachable!("{to_ty:?}"), + }; + let min_val = fx.bcx.ins().iconst(types::I32, min); + let max_val = fx.bcx.ins().iconst(types::I32, max); + + let val = if to_signed { + let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, ret, min); + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, ret, max); + let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, ret); + fx.bcx.ins().select(has_overflow, max_val, bottom_capped) + } else { + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, ret, max); + fx.bcx.ins().select(has_overflow, max_val, ret) + }; + fx.bcx.ins().ireduce(to_ty, val) + }; + + if let Some(false) = fx.tcx.sess.opts.unstable_opts.saturating_float_casts { + return val; + } + + let is_not_nan = fcmp(fx, FloatCC::Equal, from, from); + let zero = type_zero_value(&mut fx.bcx, to_ty); + fx.bcx.ins().select(is_not_nan, val, zero) + } else { + unreachable!("{from_ty:?} -> {to_ty:?}"); + } +} + +pub(crate) fn fma_f16(fx: &mut FunctionCx<'_, '_, '_>, x: Value, y: Value, z: Value) -> Value { + let x = f16_to_f64(fx, x); + let y = f16_to_f64(fx, y); + let z = f16_to_f64(fx, z); + let res = fx.bcx.ins().fma(x, y, z); + f64_to_f16(fx, res) +} + +pub(crate) fn fmin_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value { + fx.lib_call( + "fminimumf128", + vec![AbiParam::new(types::F128), AbiParam::new(types::F128)], + vec![AbiParam::new(types::F128)], + &[a, b], + )[0] +} + +pub(crate) fn fmax_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value { + fx.lib_call( + "fmaximumf128", + vec![AbiParam::new(types::F128), AbiParam::new(types::F128)], + vec![AbiParam::new(types::F128)], + &[a, b], + )[0] +} diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index abe2972ba0cb..2f11b2d2dcc1 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -33,10 +33,10 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type { Integer::I128 => types::I128, }, Primitive::Float(float) => match float { - Float::F16 => unimplemented!("f16_f128"), + Float::F16 => types::F16, Float::F32 => types::F32, Float::F64 => types::F64, - Float::F128 => unimplemented!("f16_f128"), + Float::F128 => types::F128, }, // FIXME(erikdesjardins): handle non-default addrspace ptr sizes Primitive::Pointer(_) => pointer_ty(tcx), @@ -64,10 +64,10 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option types::I32, ty::Float(size) => match size { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => types::F16, FloatTy::F32 => types::F32, FloatTy::F64 => types::F64, - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => types::F128, }, ty::FnPtr(..) => pointer_ty(tcx), ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index bf16e81a06f9..6eea19211fa1 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -46,15 +46,100 @@ builtin_functions! { fn __rust_u128_mulo(a: u128, b: u128, oflow: &mut i32) -> u128; fn __rust_i128_mulo(a: i128, b: i128, oflow: &mut i32) -> i128; - // floats + // integer -> float fn __floattisf(i: i128) -> f32; fn __floattidf(i: i128) -> f64; + fn __floatsitf(i: i32) -> f128; + fn __floatditf(i: i64) -> f128; + fn __floattitf(i: i128) -> f128; fn __floatuntisf(i: u128) -> f32; fn __floatuntidf(i: u128) -> f64; + fn __floatunsitf(i: u32) -> f128; + fn __floatunditf(i: u64) -> f128; + fn __floatuntitf(i: u128) -> f128; + // float -> integer fn __fixsfti(f: f32) -> i128; fn __fixdfti(f: f64) -> i128; + fn __fixtfsi(f: f128) -> i32; + fn __fixtfdi(f: f128) -> i64; + fn __fixtfti(f: f128) -> i128; fn __fixunssfti(f: f32) -> u128; fn __fixunsdfti(f: f64) -> u128; + fn __fixunstfsi(f: f128) -> u32; + fn __fixunstfdi(f: f128) -> u64; + fn __fixunstfti(f: f128) -> u128; + // float -> float + fn __extendhfsf2(f: f16) -> f32; + fn __extendhftf2(f: f16) -> f128; + fn __extendsftf2(f: f32) -> f128; + fn __extenddftf2(f: f64) -> f128; + fn __trunctfdf2(f: f128) -> f64; + fn __trunctfsf2(f: f128) -> f32; + fn __trunctfhf2(f: f128) -> f16; + fn __truncdfhf2(f: f64) -> f16; + fn __truncsfhf2(f: f32) -> f16; + // float binops + fn __addtf3(a: f128, b: f128) -> f128; + fn __subtf3(a: f128, b: f128) -> f128; + fn __multf3(a: f128, b: f128) -> f128; + fn __divtf3(a: f128, b: f128) -> f128; + fn fmodf(a: f32, b: f32) -> f32; + fn fmod(a: f64, b: f64) -> f64; + fn fmodf128(a: f128, b: f128) -> f128; + // float comparison + fn __eqtf2(a: f128, b: f128) -> i32; + fn __netf2(a: f128, b: f128) -> i32; + fn __lttf2(a: f128, b: f128) -> i32; + fn __letf2(a: f128, b: f128) -> i32; + fn __gttf2(a: f128, b: f128) -> i32; + fn __getf2(a: f128, b: f128) -> i32; + fn fminimumf128(a: f128, b: f128) -> f128; + fn fmaximumf128(a: f128, b: f128) -> f128; + // Cranelift float libcalls + fn fmaf(a: f32, b: f32, c: f32) -> f32; + fn fma(a: f64, b: f64, c: f64) -> f64; + fn floorf(f: f32) -> f32; + fn floor(f: f64) -> f64; + fn ceilf(f: f32) -> f32; + fn ceil(f: f64) -> f64; + fn truncf(f: f32) -> f32; + fn trunc(f: f64) -> f64; + fn nearbyintf(f: f32) -> f32; + fn nearbyint(f: f64) -> f64; + // float intrinsics + fn __powisf2(a: f32, b: i32) -> f32; + fn __powidf2(a: f64, b: i32) -> f64; + // FIXME(f16_f128): `compiler-builtins` doesn't currently support `__powitf2` on MSVC. + // fn __powitf2(a: f128, b: i32) -> f128; + fn powf(a: f32, b: f32) -> f32; + fn pow(a: f64, b: f64) -> f64; + fn expf(f: f32) -> f32; + fn exp(f: f64) -> f64; + fn exp2f(f: f32) -> f32; + fn exp2(f: f64) -> f64; + fn logf(f: f32) -> f32; + fn log(f: f64) -> f64; + fn log2f(f: f32) -> f32; + fn log2(f: f64) -> f64; + fn log10f(f: f32) -> f32; + fn log10(f: f64) -> f64; + fn sinf(f: f32) -> f32; + fn sin(f: f64) -> f64; + fn cosf(f: f32) -> f32; + fn cos(f: f64) -> f64; + fn fmaf128(a: f128, b: f128, c: f128) -> f128; + fn floorf16(f: f16) -> f16; + fn floorf128(f: f128) -> f128; + fn ceilf16(f: f16) -> f16; + fn ceilf128(f: f128) -> f128; + fn truncf16(f: f16) -> f16; + fn truncf128(f: f128) -> f128; + fn rintf16(f: f16) -> f16; + fn rintf128(f: f128) -> f128; + fn sqrtf16(f: f16) -> f16; + fn sqrtf128(f: f128) -> f128; + // FIXME(f16_f128): Add other float intrinsics as compiler-builtins gains support (meaning they + // are available on all targets). // allocator // NOTE: These need to be mentioned here despite not being part of compiler_builtins because @@ -67,5 +152,4 @@ builtin_functions! { fn malloc(size: size_t) -> *mut c_void; fn realloc(p: *mut c_void, size: size_t) -> *mut c_void; fn free(p: *mut c_void) -> (); - } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs index ccdc347af660..0f4696b9337e 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs @@ -81,13 +81,36 @@ impl WriterRelocate { /// Perform the collected relocations to be usable for JIT usage. #[cfg(all(feature = "jit", not(windows)))] pub(super) fn relocate_for_jit(mut self, jit_module: &cranelift_jit::JITModule) -> Vec { + use cranelift_module::Module; + for reloc in self.relocs.drain(..) { match reloc.name { super::DebugRelocName::Section(_) => unreachable!(), super::DebugRelocName::Symbol(sym) => { - let addr = jit_module.get_finalized_function( - cranelift_module::FuncId::from_u32(sym.try_into().unwrap()), - ); + let addr = if sym & 1 << 31 == 0 { + let func_id = FuncId::from_u32(sym.try_into().unwrap()); + // FIXME make JITModule::get_address public and use it here instead. + // HACK rust_eh_personality is likely not defined in the same crate, + // so get_finalized_function won't work. Use the rust_eh_personality + // of cg_clif itself, which is likely ABI compatible. + if jit_module.declarations().get_function_decl(func_id).name.as_deref() + == Some("rust_eh_personality") + { + extern "C" { + fn rust_eh_personality() -> !; + } + rust_eh_personality as *const u8 + } else { + jit_module.get_finalized_function(func_id) + } + } else { + jit_module + .get_finalized_data(DataId::from_u32( + u32::try_from(sym).unwrap() & !(1 << 31), + )) + .0 + }; + let val = (addr as u64 as i64 + reloc.addend) as u64; self.writer.write_udata_at(reloc.offset as usize, val, reloc.size).unwrap(); } @@ -196,6 +219,16 @@ impl Writer for WriterRelocate { }); self.write_udata(0, size) } + gimli::DW_EH_PE_absptr => { + self.relocs.push(DebugReloc { + offset: self.len() as u32, + size: size.into(), + name: DebugRelocName::Symbol(symbol), + addend, + kind: object::RelocationKind::Absolute, + }); + self.write_udata(0, size.into()) + } _ => Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)), }, } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index 362333d35a41..74b82a7139ab 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -1,7 +1,6 @@ //! Unwind info generation (`.eh_frame`) use cranelift_codegen::ir::Endianness; -use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::unwind::UnwindInfo; use cranelift_object::ObjectProduct; use gimli::RunTimeEndian; @@ -18,14 +17,14 @@ pub(crate) struct UnwindContext { } impl UnwindContext { - pub(crate) fn new(isa: &dyn TargetIsa, pic_eh_frame: bool) -> Self { - let endian = match isa.endianness() { + pub(crate) fn new(module: &mut dyn Module, pic_eh_frame: bool) -> Self { + let endian = match module.isa().endianness() { Endianness::Little => RunTimeEndian::Little, Endianness::Big => RunTimeEndian::Big, }; let mut frame_table = FrameTable::default(); - let cie_id = if let Some(mut cie) = isa.create_systemv_cie() { + let cie_id = if let Some(mut cie) = module.isa().create_systemv_cie() { if pic_eh_frame { cie.fde_address_encoding = gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0); @@ -38,8 +37,15 @@ impl UnwindContext { UnwindContext { endian, frame_table, cie_id } } - pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &dyn TargetIsa) { - if let target_lexicon::OperatingSystem::MacOSX { .. } = isa.triple().operating_system { + pub(crate) fn add_function( + &mut self, + module: &mut dyn Module, + func_id: FuncId, + context: &Context, + ) { + if let target_lexicon::OperatingSystem::MacOSX { .. } = + module.isa().triple().operating_system + { // The object crate doesn't currently support DW_GNU_EH_PE_absptr, which macOS // requires for unwinding tables. In addition on arm64 it currently doesn't // support 32bit relocations as we currently use for the unwinding table. @@ -48,7 +54,7 @@ impl UnwindContext { } let unwind_info = if let Some(unwind_info) = - context.compiled_code().unwrap().create_unwind_info(isa).unwrap() + context.compiled_code().unwrap().create_unwind_info(module.isa()).unwrap() { unwind_info } else { diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index e368cf4386d0..b1f185b551c3 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -6,6 +6,7 @@ use std::os::raw::{c_char, c_int}; use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::MonoItem; use rustc_session::Session; use rustc_span::sym; @@ -84,7 +85,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec) -> ! { tcx.dcx().abort_if_errors(); - jit_module.finalize_definitions(); + let mut jit_module = jit_module.finalize_definitions(); println!( "Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed" @@ -104,7 +105,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec) -> ! { call_conv: jit_module.target_config().default_call_conv, }; let start_func_id = jit_module.declare_function("main", Linkage::Import, &start_sig).unwrap(); - let finalized_start: *const u8 = jit_module.module.get_finalized_function(start_func_id); + let finalized_start: *const u8 = jit_module.get_finalized_function(start_func_id); let f: extern "C" fn(c_int, *const *const c_char) -> c_int = unsafe { ::std::mem::transmute(finalized_start) }; @@ -119,7 +120,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec) -> ! { std::process::exit(ret); } -pub(crate) fn codegen_and_compile_fn<'tcx>( +fn codegen_and_compile_fn<'tcx>( tcx: TyCtxt<'tcx>, cx: &mut crate::CodegenCx, cached_context: &mut Context, diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index eb0dfbb69c3b..2e02e85a997d 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -66,7 +66,12 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( fx.tcx .dcx() .warn(format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic)); - crate::trap::trap_unimplemented(fx, intrinsic); + let msg = format!( + "{intrinsic} is not yet supported.\n\ + See https://github.com/rust-lang/rustc_codegen_cranelift/issues/171\n\ + Please open an issue at https://github.com/rust-lang/rustc_codegen_cranelift/issues" + ); + crate::base::codegen_panic_nounwind(fx, &msg, None); return; } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs index 387c87d123a3..d22483cf1776 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs @@ -7,7 +7,7 @@ use crate::inline_asm::{CInlineAsmOperand, codegen_inline_asm_inner}; use crate::intrinsics::*; use crate::prelude::*; -pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( +pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: &str, args: &[Spanned>], @@ -507,7 +507,12 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( "unsupported AArch64 llvm intrinsic {}; replacing with trap", intrinsic )); - crate::trap::trap_unimplemented(fx, intrinsic); + let msg = format!( + "{intrinsic} is not yet supported.\n\ + See https://github.com/rust-lang/rustc_codegen_cranelift/issues/171\n\ + Please open an issue at https://github.com/rust-lang/rustc_codegen_cranelift/issues" + ); + crate::base::codegen_panic_nounwind(fx, &msg, None); return; } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index c02d31844e03..3d67913a8fff 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -7,7 +7,7 @@ use crate::inline_asm::{CInlineAsmOperand, codegen_inline_asm_inner}; use crate::intrinsics::*; use crate::prelude::*; -pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( +pub(super) fn codegen_x86_llvm_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: &str, args: &[Spanned>], @@ -147,10 +147,10 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let offset = fx.bcx.ins().imul(index_lane, scale); let lane_ptr = fx.bcx.ins().iadd(ptr, offset); let res = fx.bcx.ins().load(lane_clif_ty, MemFlags::trusted(), lane_ptr, 0); - fx.bcx.ins().jump(next, &[res]); + fx.bcx.ins().jump(next, &[res.into()]); fx.bcx.switch_to_block(if_disabled); - fx.bcx.ins().jump(next, &[src_lane]); + fx.bcx.ins().jump(next, &[src_lane.into()]); fx.bcx.seal_block(next); fx.bcx.switch_to_block(next); @@ -1316,7 +1316,12 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( fx.tcx .dcx() .warn(format!("unsupported x86 llvm intrinsic {}; replacing with trap", intrinsic)); - crate::trap::trap_unimplemented(fx, intrinsic); + let msg = format!( + "{intrinsic} is not yet supported.\n\ + See https://github.com/rust-lang/rustc_codegen_cranelift/issues/171\n\ + Please open an issue at https://github.com/rust-lang/rustc_codegen_cranelift/issues" + ); + crate::base::codegen_panic_nounwind(fx, &msg, None); return; } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 9018d78b00ae..0de23e55e817 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -27,6 +27,7 @@ use rustc_span::{Symbol, sym}; pub(crate) use self::llvm::codegen_llvm_intrinsic_call; use crate::cast::clif_intcast; +use crate::codegen_f16_f128; use crate::prelude::*; fn bug_on_incorrect_arg_count(intrinsic: impl std::fmt::Display) -> ! { @@ -248,8 +249,10 @@ fn bool_to_zero_or_max_uint<'tcx>( let ty = fx.clif_type(ty).unwrap(); let int_ty = match ty { + types::F16 => types::I16, types::F32 => types::I32, types::F64 => types::I64, + types::F128 => types::I128, ty => ty, }; @@ -308,45 +311,83 @@ fn codegen_float_intrinsic_call<'tcx>( ret: CPlace<'tcx>, ) -> bool { let (name, arg_count, ty, clif_ty) = match intrinsic { + sym::expf16 => ("expf16", 1, fx.tcx.types.f16, types::F16), sym::expf32 => ("expf", 1, fx.tcx.types.f32, types::F32), sym::expf64 => ("exp", 1, fx.tcx.types.f64, types::F64), + sym::expf128 => ("expf128", 1, fx.tcx.types.f128, types::F128), + sym::exp2f16 => ("exp2f16", 1, fx.tcx.types.f16, types::F16), sym::exp2f32 => ("exp2f", 1, fx.tcx.types.f32, types::F32), sym::exp2f64 => ("exp2", 1, fx.tcx.types.f64, types::F64), + sym::exp2f128 => ("exp2f128", 1, fx.tcx.types.f128, types::F128), + sym::sqrtf16 => ("sqrtf16", 1, fx.tcx.types.f16, types::F16), sym::sqrtf32 => ("sqrtf", 1, fx.tcx.types.f32, types::F32), sym::sqrtf64 => ("sqrt", 1, fx.tcx.types.f64, types::F64), + sym::sqrtf128 => ("sqrtf128", 1, fx.tcx.types.f128, types::F128), + sym::powif16 => ("__powisf2", 2, fx.tcx.types.f16, types::F16), // compiler-builtins sym::powif32 => ("__powisf2", 2, fx.tcx.types.f32, types::F32), // compiler-builtins sym::powif64 => ("__powidf2", 2, fx.tcx.types.f64, types::F64), // compiler-builtins + sym::powif128 => ("__powitf2", 2, fx.tcx.types.f128, types::F128), // compiler-builtins + sym::powf16 => ("powf16", 2, fx.tcx.types.f16, types::F16), sym::powf32 => ("powf", 2, fx.tcx.types.f32, types::F32), sym::powf64 => ("pow", 2, fx.tcx.types.f64, types::F64), + sym::powf128 => ("powf128", 2, fx.tcx.types.f128, types::F128), + sym::logf16 => ("logf16", 1, fx.tcx.types.f16, types::F16), sym::logf32 => ("logf", 1, fx.tcx.types.f32, types::F32), sym::logf64 => ("log", 1, fx.tcx.types.f64, types::F64), + sym::logf128 => ("logf128", 1, fx.tcx.types.f128, types::F128), + sym::log2f16 => ("log2f16", 1, fx.tcx.types.f16, types::F16), sym::log2f32 => ("log2f", 1, fx.tcx.types.f32, types::F32), sym::log2f64 => ("log2", 1, fx.tcx.types.f64, types::F64), + sym::log2f128 => ("log2f128", 1, fx.tcx.types.f128, types::F128), + sym::log10f16 => ("log10f16", 1, fx.tcx.types.f16, types::F16), sym::log10f32 => ("log10f", 1, fx.tcx.types.f32, types::F32), sym::log10f64 => ("log10", 1, fx.tcx.types.f64, types::F64), + sym::log10f128 => ("log10f128", 1, fx.tcx.types.f128, types::F128), + sym::fabsf16 => ("fabsf16", 1, fx.tcx.types.f16, types::F16), sym::fabsf32 => ("fabsf", 1, fx.tcx.types.f32, types::F32), sym::fabsf64 => ("fabs", 1, fx.tcx.types.f64, types::F64), + sym::fabsf128 => ("fabsf128", 1, fx.tcx.types.f128, types::F128), + sym::fmaf16 => ("fmaf16", 3, fx.tcx.types.f16, types::F16), sym::fmaf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32), sym::fmaf64 => ("fma", 3, fx.tcx.types.f64, types::F64), + sym::fmaf128 => ("fmaf128", 3, fx.tcx.types.f128, types::F128), // FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation + sym::fmuladdf16 => ("fmaf16", 3, fx.tcx.types.f16, types::F16), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f16 sym::fmuladdf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f32 sym::fmuladdf64 => ("fma", 3, fx.tcx.types.f64, types::F64), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f64 + sym::fmuladdf128 => ("fmaf128", 3, fx.tcx.types.f128, types::F128), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f128 + sym::copysignf16 => ("copysignf16", 2, fx.tcx.types.f16, types::F16), sym::copysignf32 => ("copysignf", 2, fx.tcx.types.f32, types::F32), sym::copysignf64 => ("copysign", 2, fx.tcx.types.f64, types::F64), + sym::copysignf128 => ("copysignf128", 2, fx.tcx.types.f128, types::F128), + sym::floorf16 => ("floorf16", 1, fx.tcx.types.f16, types::F16), sym::floorf32 => ("floorf", 1, fx.tcx.types.f32, types::F32), sym::floorf64 => ("floor", 1, fx.tcx.types.f64, types::F64), + sym::floorf128 => ("floorf128", 1, fx.tcx.types.f128, types::F128), + sym::ceilf16 => ("ceilf16", 1, fx.tcx.types.f16, types::F16), sym::ceilf32 => ("ceilf", 1, fx.tcx.types.f32, types::F32), sym::ceilf64 => ("ceil", 1, fx.tcx.types.f64, types::F64), + sym::ceilf128 => ("ceilf128", 1, fx.tcx.types.f128, types::F128), + sym::truncf16 => ("truncf16", 1, fx.tcx.types.f16, types::F16), sym::truncf32 => ("truncf", 1, fx.tcx.types.f32, types::F32), sym::truncf64 => ("trunc", 1, fx.tcx.types.f64, types::F64), + sym::truncf128 => ("truncf128", 1, fx.tcx.types.f128, types::F128), + sym::round_ties_even_f16 => ("rintf16", 1, fx.tcx.types.f16, types::F16), sym::round_ties_even_f32 => ("rintf", 1, fx.tcx.types.f32, types::F32), sym::round_ties_even_f64 => ("rint", 1, fx.tcx.types.f64, types::F64), + sym::round_ties_even_f128 => ("rintf128", 1, fx.tcx.types.f128, types::F128), + sym::roundf16 => ("roundf16", 1, fx.tcx.types.f16, types::F16), sym::roundf32 => ("roundf", 1, fx.tcx.types.f32, types::F32), sym::roundf64 => ("round", 1, fx.tcx.types.f64, types::F64), + sym::roundf128 => ("roundf128", 1, fx.tcx.types.f128, types::F128), + sym::sinf16 => ("sinf16", 1, fx.tcx.types.f16, types::F16), sym::sinf32 => ("sinf", 1, fx.tcx.types.f32, types::F32), sym::sinf64 => ("sin", 1, fx.tcx.types.f64, types::F64), + sym::sinf128 => ("sinf128", 1, fx.tcx.types.f128, types::F128), + sym::cosf16 => ("cosf16", 1, fx.tcx.types.f16, types::F16), sym::cosf32 => ("cosf", 1, fx.tcx.types.f32, types::F32), sym::cosf64 => ("cos", 1, fx.tcx.types.f64, types::F64), + sym::cosf128 => ("cosf128", 1, fx.tcx.types.f128, types::F128), _ => return false, }; @@ -379,13 +420,26 @@ fn codegen_float_intrinsic_call<'tcx>( }; let layout = fx.layout_of(ty); + // FIXME(bytecodealliance/wasmtime#8312): Use native Cranelift operations + // for `f16` and `f128` once the lowerings have been implemented in Cranelift. let res = match intrinsic { + sym::fmaf16 | sym::fmuladdf16 => { + CValue::by_val(codegen_f16_f128::fma_f16(fx, args[0], args[1], args[2]), layout) + } sym::fmaf32 | sym::fmaf64 | sym::fmuladdf32 | sym::fmuladdf64 => { CValue::by_val(fx.bcx.ins().fma(args[0], args[1], args[2]), layout) } + sym::copysignf16 => { + CValue::by_val(codegen_f16_f128::copysign_f16(fx, args[0], args[1]), layout) + } + sym::copysignf128 => { + CValue::by_val(codegen_f16_f128::copysign_f128(fx, args[0], args[1]), layout) + } sym::copysignf32 | sym::copysignf64 => { CValue::by_val(fx.bcx.ins().fcopysign(args[0], args[1]), layout) } + sym::fabsf16 => CValue::by_val(codegen_f16_f128::abs_f16(fx, args[0]), layout), + sym::fabsf128 => CValue::by_val(codegen_f16_f128::abs_f128(fx, args[0]), layout), sym::fabsf32 | sym::fabsf64 | sym::floorf32 @@ -415,11 +469,36 @@ fn codegen_float_intrinsic_call<'tcx>( // These intrinsics aren't supported natively by Cranelift. // Lower them to a libcall. - sym::powif32 | sym::powif64 => { - let input_tys: Vec<_> = vec![AbiParam::new(clif_ty), AbiParam::new(types::I32)]; + sym::powif16 | sym::powif32 | sym::powif64 | sym::powif128 => { + let temp; + let (clif_ty, args) = if intrinsic == sym::powif16 { + temp = [codegen_f16_f128::f16_to_f32(fx, args[0]), args[1]]; + (types::F32, temp.as_slice()) + } else { + (clif_ty, args) + }; + let input_tys: Vec<_> = + vec![AbiParam::new(clif_ty), lib_call_arg_param(fx.tcx, types::I32, true)]; let ret_val = fx.lib_call(name, input_tys, vec![AbiParam::new(clif_ty)], &args)[0]; + let ret_val = if intrinsic == sym::powif16 { + codegen_f16_f128::f32_to_f16(fx, ret_val) + } else { + ret_val + }; CValue::by_val(ret_val, fx.layout_of(ty)) } + sym::powf16 => { + // FIXME(f16_f128): Rust `compiler-builtins` doesn't export `powf16` yet. + let x = codegen_f16_f128::f16_to_f32(fx, args[0]); + let y = codegen_f16_f128::f16_to_f32(fx, args[1]); + let ret_val = fx.lib_call( + "powf", + vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], + vec![AbiParam::new(types::F32)], + &[x, y], + )[0]; + CValue::by_val(codegen_f16_f128::f32_to_f16(fx, ret_val), fx.layout_of(ty)) + } _ => { let input_tys: Vec<_> = args.iter().map(|_| AbiParam::new(clif_ty)).collect(); let ret_val = fx.lib_call(name, input_tys, vec![AbiParam::new(clif_ty)], &args)[0]; @@ -791,17 +870,22 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME use a compiler fence once Cranelift supports it fx.bcx.ins().fence(); } - _ if intrinsic.as_str().starts_with("atomic_load") => { + sym::atomic_load => { intrinsic_args!(fx, args => (ptr); intrinsic); let ptr = ptr.load_scalar(fx); let ty = generic_args.type_at(0); + let _ord = generic_args.const_at(1).to_value(); // FIXME: forward this to cranelift once they support that match ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { // FIXME implement 128bit atomics if fx.tcx.is_compiler_builtins(LOCAL_CRATE) { // special case for compiler-builtins to avoid having to patch it - crate::trap::trap_unimplemented(fx, "128bit atomics not yet supported"); + crate::base::codegen_panic_nounwind( + fx, + "128bit atomics not yet supported", + None, + ); return Ok(()); } else { fx.tcx @@ -832,7 +916,11 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME implement 128bit atomics if fx.tcx.is_compiler_builtins(LOCAL_CRATE) { // special case for compiler-builtins to avoid having to patch it - crate::trap::trap_unimplemented(fx, "128bit atomics not yet supported"); + crate::base::codegen_panic_nounwind( + fx, + "128bit atomics not yet supported", + None, + ); return Ok(()); } else { fx.tcx @@ -1109,6 +1197,20 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, old); } + sym::minimumf16 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + // FIXME(bytecodealliance/wasmtime#8312): Use `fmin` directly once + // Cranelift backend lowerings are implemented. + let a = codegen_f16_f128::f16_to_f32(fx, a); + let b = codegen_f16_f128::f16_to_f32(fx, b); + let val = fx.bcx.ins().fmin(a, b); + let val = codegen_f16_f128::f32_to_f16(fx, val); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16)); + ret.write_cvalue(fx, val); + } sym::minimumf32 => { intrinsic_args!(fx, args => (a, b); intrinsic); let a = a.load_scalar(fx); @@ -1127,6 +1229,31 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); } + sym::minimumf128 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + // FIXME(bytecodealliance/wasmtime#8312): Use `fmin` once Cranelift + // backend lowerings are implemented. + let val = codegen_f16_f128::fmin_f128(fx, a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128)); + ret.write_cvalue(fx, val); + } + sym::maximumf16 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + // FIXME(bytecodealliance/wasmtime#8312): Use `fmax` directly once + // Cranelift backend lowerings are implemented. + let a = codegen_f16_f128::f16_to_f32(fx, a); + let b = codegen_f16_f128::f16_to_f32(fx, b); + let val = fx.bcx.ins().fmax(a, b); + let val = codegen_f16_f128::f32_to_f16(fx, val); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16)); + ret.write_cvalue(fx, val); + } sym::maximumf32 => { intrinsic_args!(fx, args => (a, b); intrinsic); let a = a.load_scalar(fx); @@ -1145,7 +1272,27 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); } + sym::maximumf128 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + // FIXME(bytecodealliance/wasmtime#8312): Use `fmax` once Cranelift + // backend lowerings are implemented. + let val = codegen_f16_f128::fmax_f128(fx, a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128)); + ret.write_cvalue(fx, val); + } + + sym::minnumf16 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = crate::num::codegen_float_min(fx, a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16)); + ret.write_cvalue(fx, val); + } sym::minnumf32 => { intrinsic_args!(fx, args => (a, b); intrinsic); let a = a.load_scalar(fx); @@ -1164,6 +1311,24 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); } + sym::minnumf128 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = crate::num::codegen_float_min(fx, a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128)); + ret.write_cvalue(fx, val); + } + sym::maxnumf16 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = crate::num::codegen_float_max(fx, a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16)); + ret.write_cvalue(fx, val); + } sym::maxnumf32 => { intrinsic_args!(fx, args => (a, b); intrinsic); let a = a.load_scalar(fx); @@ -1182,6 +1347,15 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); } + sym::maxnumf128 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = crate::num::codegen_float_max(fx, a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128)); + ret.write_cvalue(fx, val); + } sym::catch_unwind => { intrinsic_args!(fx, args => (f, data, catch_fn); intrinsic); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index dd6d8dbb6f5e..46a441488fa6 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -283,6 +283,20 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ret_lane.write_cvalue(fx, val); } + sym::simd_insert_dyn => { + intrinsic_args!(fx, args => (base, idx, val); intrinsic); + + if !base.layout().ty.is_simd() { + report_simd_type_validation_error(fx, intrinsic, span, base.layout().ty); + return; + } + + let idx = idx.load_scalar(fx); + + ret.write_cvalue(fx, base); + ret.write_lane_dyn(fx, idx, val); + } + sym::simd_extract => { let (v, idx) = match args { [v, idx] => (v, idx), @@ -318,6 +332,20 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ret.write_cvalue(fx, ret_lane); } + sym::simd_extract_dyn => { + intrinsic_args!(fx, args => (v, idx); intrinsic); + + if !v.layout().ty.is_simd() { + report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); + return; + } + + let idx = idx.load_scalar(fx); + + let ret_lane = v.value_lane_dyn(fx, idx); + ret.write_cvalue(fx, ret_lane); + } + sym::simd_neg | sym::simd_bswap | sym::simd_bitreverse @@ -980,10 +1008,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.switch_to_block(if_enabled); let res = fx.bcx.ins().load(lane_clif_ty, MemFlags::trusted(), ptr_lane, 0); - fx.bcx.ins().jump(next, &[res]); + fx.bcx.ins().jump(next, &[res.into()]); fx.bcx.switch_to_block(if_disabled); - fx.bcx.ins().jump(next, &[val_lane]); + fx.bcx.ins().jump(next, &[val_lane.into()]); fx.bcx.seal_block(next); fx.bcx.switch_to_block(next); @@ -1029,10 +1057,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ptr_val, Offset32::new(offset), ); - fx.bcx.ins().jump(next, &[res]); + fx.bcx.ins().jump(next, &[res.into()]); fx.bcx.switch_to_block(if_disabled); - fx.bcx.ins().jump(next, &[val_lane]); + fx.bcx.ins().jump(next, &[val_lane.into()]); fx.bcx.seal_block(next); fx.bcx.switch_to_block(next); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index ab09a6f8b38e..8ef623cde005 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -6,6 +6,9 @@ #![cfg_attr(doc, feature(rustdoc_internals))] // Note: please avoid adding other feature gates where possible #![feature(rustc_private)] +// Only used to define intrinsics in `compiler_builtins.rs`. +#![feature(f16)] +#![feature(f128)] // Note: please avoid adding other feature gates where possible #![warn(rust_2018_idioms)] #![warn(unreachable_pub)] @@ -57,6 +60,7 @@ mod allocator; mod analyze; mod base; mod cast; +mod codegen_f16_f128; mod codegen_i128; mod common; mod compiler_builtins; @@ -76,7 +80,6 @@ mod optimize; mod pointer; mod pretty_clif; mod toolchain; -mod trap; mod unsize; mod unwind_module; mod value_and_place; @@ -198,14 +201,36 @@ impl CodegenBackend for CraneliftCodegenBackend { // FIXME do `unstable_target_features` properly let unstable_target_features = target_features.clone(); + // FIXME(f16_f128): LLVM 20 (currently used by `rustc`) passes `f128` in XMM registers on + // Windows, whereas LLVM 21+ and Cranelift pass it indirectly. This means that `f128` won't + // work when linking against a LLVM-built sysroot. + let has_reliable_f128 = !sess.target.is_like_windows; + let has_reliable_f16 = match &*sess.target.arch { + // FIXME(f16_f128): LLVM 20 does not support `f16` on s390x, meaning the required + // builtins are not available in `compiler-builtins`. + "s390x" => false, + // FIXME(f16_f128): `rustc_codegen_llvm` currently disables support on Windows GNU + // targets due to GCC using a different ABI than LLVM. Therefore `f16` won't be + // available when using a LLVM-built sysroot. + "x86_64" + if sess.target.os == "windows" + && sess.target.env == "gnu" + && sess.target.abi != "llvm" => + { + false + } + _ => true, + }; + TargetConfig { target_features, unstable_target_features, - // Cranelift does not yet support f16 or f128 - has_reliable_f16: false, - has_reliable_f16_math: false, - has_reliable_f128: false, - has_reliable_f128_math: false, + // `rustc_codegen_cranelift` polyfills functionality not yet + // available in Cranelift. + has_reliable_f16, + has_reliable_f16_math: has_reliable_f16, + has_reliable_f128, + has_reliable_f128_math: has_reliable_f128, } } @@ -290,6 +315,12 @@ fn build_isa(sess: &Session, jit: bool) -> Arc { flags_builder.set("enable_llvm_abi_extensions", "true").unwrap(); + if let Some(align) = sess.opts.unstable_opts.min_function_alignment { + flags_builder + .set("log2_min_function_alignment", &align.bytes().ilog2().to_string()) + .unwrap(); + } + use rustc_session::config::OptLevel; match sess.opts.optimize { OptLevel::No => { diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index 2a4d1e3ae571..f53045df6e79 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -1,8 +1,9 @@ //! Various operations on integer and floating-point numbers +use crate::codegen_f16_f128; use crate::prelude::*; -pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> IntCC { +fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> IntCC { use BinOp::*; use IntCC::*; match bin_op { @@ -109,7 +110,7 @@ pub(crate) fn codegen_binop<'tcx>( } } -pub(crate) fn codegen_bool_binop<'tcx>( +fn codegen_bool_binop<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, @@ -350,25 +351,60 @@ pub(crate) fn codegen_float_binop<'tcx>( let lhs = in_lhs.load_scalar(fx); let rhs = in_rhs.load_scalar(fx); + // FIXME(bytecodealliance/wasmtime#8312): Remove once backend lowerings have + // been added to Cranelift. + let (lhs, rhs) = if *in_lhs.layout().ty.kind() == ty::Float(FloatTy::F16) { + (codegen_f16_f128::f16_to_f32(fx, lhs), codegen_f16_f128::f16_to_f32(fx, rhs)) + } else { + (lhs, rhs) + }; let b = fx.bcx.ins(); let res = match bin_op { + // FIXME(bytecodealliance/wasmtime#8312): Remove once backend lowerings + // have been added to Cranelift. + BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div + if *in_lhs.layout().ty.kind() == ty::Float(FloatTy::F128) => + { + codegen_f16_f128::codegen_f128_binop(fx, bin_op, lhs, rhs) + } BinOp::Add => b.fadd(lhs, rhs), BinOp::Sub => b.fsub(lhs, rhs), BinOp::Mul => b.fmul(lhs, rhs), BinOp::Div => b.fdiv(lhs, rhs), BinOp::Rem => { - let (name, ty) = match in_lhs.layout().ty.kind() { - ty::Float(FloatTy::F32) => ("fmodf", types::F32), - ty::Float(FloatTy::F64) => ("fmod", types::F64), + let (name, ty, lhs, rhs) = match in_lhs.layout().ty.kind() { + ty::Float(FloatTy::F16) => ( + "fmodf", + types::F32, + // FIXME(bytecodealliance/wasmtime#8312): Already converted + // by the FIXME above. + // fx.bcx.ins().fpromote(types::F32, lhs), + // fx.bcx.ins().fpromote(types::F32, rhs), + lhs, + rhs, + ), + ty::Float(FloatTy::F32) => ("fmodf", types::F32, lhs, rhs), + ty::Float(FloatTy::F64) => ("fmod", types::F64, lhs, rhs), + ty::Float(FloatTy::F128) => ("fmodf128", types::F128, lhs, rhs), _ => bug!(), }; - fx.lib_call( + let ret_val = fx.lib_call( name, vec![AbiParam::new(ty), AbiParam::new(ty)], vec![AbiParam::new(ty)], &[lhs, rhs], - )[0] + )[0]; + + let ret_val = if *in_lhs.layout().ty.kind() == ty::Float(FloatTy::F16) { + // FIXME(bytecodealliance/wasmtime#8312): Use native Cranelift + // operation once Cranelift backend lowerings have been + // implemented. + codegen_f16_f128::f32_to_f16(fx, ret_val) + } else { + ret_val + }; + return CValue::by_val(ret_val, in_lhs.layout()); } BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { let fltcc = match bin_op { @@ -380,16 +416,26 @@ pub(crate) fn codegen_float_binop<'tcx>( BinOp::Gt => FloatCC::GreaterThan, _ => unreachable!(), }; - let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs); + // FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift + // `fcmp` once `f16`/`f128` backend lowerings have been added to + // Cranelift. + let val = codegen_f16_f128::fcmp(fx, fltcc, lhs, rhs); return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)); } _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs), }; + // FIXME(bytecodealliance/wasmtime#8312): Remove once backend lowerings have + // been added to Cranelift. + let res = if *in_lhs.layout().ty.kind() == ty::Float(FloatTy::F16) { + codegen_f16_f128::f32_to_f16(fx, res) + } else { + res + }; CValue::by_val(res, in_lhs.layout()) } -pub(crate) fn codegen_ptr_binop<'tcx>( +fn codegen_ptr_binop<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, @@ -457,15 +503,19 @@ pub(crate) fn codegen_ptr_binop<'tcx>( // and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing // a float against itself. Only in case of NaN is it not equal to itself. pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value { - let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a); - let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b); + // FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift `fcmp` once + // `f16`/`f128` backend lowerings have been added to Cranelift. + let a_is_nan = codegen_f16_f128::fcmp(fx, FloatCC::NotEqual, a, a); + let a_ge_b = codegen_f16_f128::fcmp(fx, FloatCC::GreaterThanOrEqual, a, b); let temp = fx.bcx.ins().select(a_ge_b, b, a); fx.bcx.ins().select(a_is_nan, b, temp) } pub(crate) fn codegen_float_max(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value { - let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a); - let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b); + // FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift `fcmp` once + // `f16`/`f128` backend lowerings have been added to Cranelift. + let a_is_nan = codegen_f16_f128::fcmp(fx, FloatCC::NotEqual, a, a); + let a_le_b = codegen_f16_f128::fcmp(fx, FloatCC::LessThanOrEqual, a, b); let temp = fx.bcx.ins().select(a_le_b, b, a); fx.bcx.ins().select(a_is_nan, b, temp) } diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs index cd254b04ed9e..9400ae9fcff0 100644 --- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -8,40 +8,41 @@ //! target x86_64 //! //! function u0:22(i64) -> i8, i8 system_v { -//! ; symbol _ZN97_$LT$example..IsNotEmpty$u20$as$u20$mini_core..FnOnce$LT$$LP$$RF$$RF$$u5b$u16$u5d$$C$$RP$$GT$$GT$9call_once17hd517c453d67c0915E -//! ; instance Instance { def: Item(WithOptConstParam { did: DefId(0:42 ~ example[4e51]::{impl#0}::call_once), const_param_did: None }), args: [ReErased, ReErased] } -//! ; abi FnAbi { args: [ArgAbi { layout: TyAndLayout { ty: IsNotEmpty, layout: Layout { size: Size(0 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, abi: Aggregate { sized: true }, fields: Arbitrary { offsets: [], memory_index: [] }, largest_niche: None, variants: Single { index: 0 } } }, mode: Ignore }, ArgAbi { layout: TyAndLayout { ty: &&[u16], layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, abi: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), variants: Single { index: 0 } } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) }], ret: ArgAbi { layout: TyAndLayout { ty: (u8, u8), layout: Layout { size: Size(2 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, abi: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=255 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }), fields: Arbitrary { offsets: [Size(0 bytes), Size(1 bytes)], memory_index: [0, 1] }, largest_niche: None, variants: Single { index: 0 } } }, mode: Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) }, c_variadic: false, fixed_count: 1, conv: Rust, can_unwind: false } +//! ; symbol _ZN97_$LT$example..IsNotEmpty$u20$as$u20$mini_core..FnOnce$LT$$LP$$RF$$RF$$u5b$u16$u5d$$C$$RP$$GT$$GT$9call_once17hd361e9f5c3d1c4deE +//! ; instance Instance { def: Item(DefId(0:42 ~ example[3895]::{impl#0}::call_once)), args: ['{erased}, '{erased}] } +//! ; abi FnAbi { args: [ArgAbi { layout: TyAndLayout { ty: IsNotEmpty, layout: Layout { size: Size(0 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: Memory { sized: true }, fields: Arbitrary { offsets: [], memory_index: [] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 12266848898570219025 } }, mode: Ignore }, ArgAbi { layout: TyAndLayout { ty: &&[u16], layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) }], ret: ArgAbi { layout: TyAndLayout { ty: (u8, u8), layout: Layout { size: Size(2 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=255 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }), fields: Arbitrary { offsets: [Size(0 bytes), Size(1 bytes)], memory_index: [0, 1] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 71776127651151873 } }, mode: Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) }, c_variadic: false, fixed_count: 1, conv: Rust, can_unwind: false } //! //! ; kind loc.idx param pass mode ty -//! ; ssa _0 (u8, u8) 2b 1, 8 var=(0, 1) +//! ; ssa _0 (u8, u8) 2b 1 var=(0, 1) //! ; ret _0 - Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) (u8, u8) //! ; arg _1 - Ignore IsNotEmpty -//! ; arg _2.0 = v0 Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) &&[u16] +//! ; arg _2.0 = v0 Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) &'{erased} &'{erased} [u16] //! -//! ; kind local ty size align (abi,pref) -//! ; zst _1 IsNotEmpty 0b 1, 8 align=8,offset= -//! ; stack _2 (&&[u16],) 8b 8, 8 storage=ss0 -//! ; ssa _3 &mut IsNotEmpty 8b 8, 8 var=2 +//! ; kind local ty size align (abi) +//! ; zst _1 IsNotEmpty 0b 1 align=1,offset= +//! ; stack _2 (&'{erased} &'{erased} [u16],) 8b 8 storage=ss0 +//! ; ssa _3 &'{erased} mut IsNotEmpty 8b 8 var=2 //! -//! ss0 = explicit_slot 16 +//! ss0 = explicit_slot 16, align = 16 //! sig0 = (i64, i64) -> i8, i8 system_v -//! fn0 = colocated u0:23 sig0 ; Instance { def: Item(WithOptConstParam { did: DefId(0:46 ~ example[4e51]::{impl#1}::call_mut), const_param_did: None }), args: [ReErased, ReErased] } +//! fn0 = colocated u0:23 sig0 ; Instance { def: Item(DefId(0:46 ~ example[3895]::{impl#1}::call_mut)), args: ['{erased}, '{erased}] } //! //! block0(v0: i64): //! nop -//! ; write_cvalue: Addr(Pointer { base: Stack(ss0), offset: Offset32(0) }, None): &&[u16] <- ByVal(v0): &&[u16] +//! ; write_cvalue: Addr(Pointer { base: Stack(ss0), offset: Offset32(0) }, None): &'{erased} &'{erased} [u16] <- ByVal(v0): &'{erased} &'{erased} [u16] //! stack_store v0, ss0 //! jump block1 //! //! block1: //! nop //! ; _3 = &mut _1 -//! v1 = iconst.i64 8 -//! ; write_cvalue: Var(_3, var2): &mut IsNotEmpty <- ByVal(v1): &mut IsNotEmpty +//! v1 = iconst.i64 1 +//! ; write_cvalue: Var(_3, var2): &'{erased} mut IsNotEmpty <- ByVal(v1): &'{erased} mut IsNotEmpty //! ; -//! ; _0 = >::call_mut(move _3, _2) +//! ; _0 = >::call_mut(move _3, copy _2) //! v2 = stack_load.i64 ss0 -//! v3, v4 = call fn0(v1, v2) ; v1 = 8 +//! ; abi: FnAbi { args: [ArgAbi { layout: TyAndLayout { ty: &mut IsNotEmpty, layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(1 bytes)) }) }, ArgAbi { layout: TyAndLayout { ty: &&[u16], layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) }], ret: ArgAbi { layout: TyAndLayout { ty: (u8, u8), layout: Layout { size: Size(2 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=255 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }), fields: Arbitrary { offsets: [Size(0 bytes), Size(1 bytes)], memory_index: [0, 1] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 71776127651151873 } }, mode: Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) }, c_variadic: false, fixed_count: 1, conv: Rust, can_unwind: false } +//! v3, v4 = call fn0(v1, v2) ; v1 = 1 //! v5 -> v3 //! v6 -> v4 //! ; write_cvalue: VarPair(_0, var0, var1): (u8, u8) <- ByValPair(v3, v4): (u8, u8) @@ -73,6 +74,7 @@ pub(crate) struct CommentWriter { enabled: bool, global_comments: Vec, entity_comments: FxHashMap, + inst_post_comments: FxHashMap, } impl CommentWriter { @@ -95,7 +97,12 @@ impl CommentWriter { vec![] }; - CommentWriter { enabled, global_comments, entity_comments: FxHashMap::default() } + CommentWriter { + enabled, + global_comments, + entity_comments: FxHashMap::default(), + inst_post_comments: FxHashMap::default(), + } } } @@ -127,6 +134,25 @@ impl CommentWriter { } } } + + pub(crate) fn add_post_comment + AsRef>( + &mut self, + entity: Inst, + comment: S, + ) { + debug_assert!(self.enabled); + + use std::collections::hash_map::Entry; + match self.inst_post_comments.entry(entity) { + Entry::Occupied(mut occ) => { + occ.get_mut().push('\n'); + occ.get_mut().push_str(comment.as_ref()); + } + Entry::Vacant(vac) => { + vac.insert(comment.into()); + } + } + } } impl FuncWriter for &'_ CommentWriter { @@ -188,10 +214,13 @@ impl FuncWriter for &'_ CommentWriter { inst: Inst, indent: usize, ) -> fmt::Result { - PlainWriter.write_instruction(w, func, aliases, inst, indent)?; if let Some(comment) = self.entity_comments.get(&inst.into()) { writeln!(w, "; {}", comment.replace('\n', "\n; "))?; } + PlainWriter.write_instruction(w, func, aliases, inst, indent)?; + if let Some(comment) = self.inst_post_comments.get(&inst) { + writeln!(w, "; {}", comment.replace('\n', "\n; "))?; + } Ok(()) } } @@ -208,6 +237,14 @@ impl FunctionCx<'_, '_, '_> { ) { self.clif_comments.add_comment(entity, comment); } + + pub(crate) fn add_post_comment + AsRef>( + &mut self, + entity: Inst, + comment: S, + ) { + self.clif_comments.add_post_comment(entity, comment); + } } pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool { diff --git a/compiler/rustc_codegen_cranelift/src/trap.rs b/compiler/rustc_codegen_cranelift/src/trap.rs deleted file mode 100644 index ac3f58ee1ee6..000000000000 --- a/compiler/rustc_codegen_cranelift/src/trap.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Helpers used to print a message and abort in case of certain panics and some detected UB. - -use crate::prelude::*; - -fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) { - let puts = fx - .module - .declare_function( - "puts", - Linkage::Import, - &Signature { - call_conv: fx.target_config.default_call_conv, - params: vec![AbiParam::new(fx.pointer_type)], - returns: vec![AbiParam::new(types::I32)], - }, - ) - .unwrap(); - let puts = fx.module.declare_func_in_func(puts, &mut fx.bcx.func); - if fx.clif_comments.enabled() { - fx.add_comment(puts, "puts"); - } - - let real_msg = format!("trap at {:?} ({}): {}\0", fx.instance, fx.symbol_name, msg); - let msg_ptr = fx.anonymous_str(&real_msg); - fx.bcx.ins().call(puts, &[msg_ptr]); -} - -/// Use this when something is unimplemented, but `libcore` or `libstd` requires it to codegen. -/// -/// Trap code: user65535 -pub(crate) fn trap_unimplemented(fx: &mut FunctionCx<'_, '_, '_>, msg: impl AsRef) { - codegen_print(fx, msg.as_ref()); - - let one = fx.bcx.ins().iconst(types::I32, 1); - fx.lib_call("exit", vec![AbiParam::new(types::I32)], vec![], &[one]); - - fx.bcx.ins().trap(TrapCode::user(3).unwrap()); -} diff --git a/compiler/rustc_codegen_cranelift/src/unwind_module.rs b/compiler/rustc_codegen_cranelift/src/unwind_module.rs index b950aaa29ce0..b4eb939cf256 100644 --- a/compiler/rustc_codegen_cranelift/src/unwind_module.rs +++ b/compiler/rustc_codegen_cranelift/src/unwind_module.rs @@ -1,10 +1,10 @@ +use cranelift_codegen::Context; use cranelift_codegen::control::ControlPlane; -use cranelift_codegen::ir::{Function, Signature}; +use cranelift_codegen::ir::Signature; use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa}; -use cranelift_codegen::{Context, FinalizedMachReloc}; use cranelift_module::{ DataDescription, DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleDeclarations, - ModuleResult, + ModuleReloc, ModuleResult, }; use cranelift_object::{ObjectModule, ObjectProduct}; @@ -17,8 +17,8 @@ pub(crate) struct UnwindModule { } impl UnwindModule { - pub(crate) fn new(module: T, pic_eh_frame: bool) -> Self { - let unwind_context = UnwindContext::new(module.isa(), pic_eh_frame); + pub(crate) fn new(mut module: T, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(&mut module, pic_eh_frame); UnwindModule { module, unwind_context } } } @@ -33,13 +33,10 @@ impl UnwindModule { #[cfg(feature = "jit")] impl UnwindModule { - pub(crate) fn finalize_definitions(&mut self) { + pub(crate) fn finalize_definitions(mut self) -> cranelift_jit::JITModule { self.module.finalize_definitions().unwrap(); - let prev_unwind_context = std::mem::replace( - &mut self.unwind_context, - UnwindContext::new(self.module.isa(), false), - ); - unsafe { prev_unwind_context.register_jit(&self.module) }; + unsafe { self.unwind_context.register_jit(&self.module) }; + self.module } } @@ -94,17 +91,16 @@ impl Module for UnwindModule { ctrl_plane: &mut ControlPlane, ) -> ModuleResult<()> { self.module.define_function_with_control_plane(func, ctx, ctrl_plane)?; - self.unwind_context.add_function(func, ctx, self.module.isa()); + self.unwind_context.add_function(&mut self.module, func, ctx); Ok(()) } fn define_function_bytes( &mut self, _func_id: FuncId, - _func: &Function, _alignment: u64, _bytes: &[u8], - _relocs: &[FinalizedMachReloc], + _relocs: &[ModuleReloc], ) -> ModuleResult<()> { unimplemented!() } diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index f8a19589fdd7..cbfb215a892a 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -309,6 +309,7 @@ impl<'tcx> CValue<'tcx> { match self.0 { CValueInner::ByVal(_) | CValueInner::ByValPair(_, _) => unreachable!(), CValueInner::ByRef(ptr, None) => { + let lane_idx = clif_intcast(fx, lane_idx, fx.pointer_type, false); let field_offset = fx.bcx.ins().imul_imm(lane_idx, lane_layout.size.bytes() as i64); let field_ptr = ptr.offset_value(fx, field_offset); CValue::by_ref(field_ptr, lane_layout) @@ -324,7 +325,7 @@ impl<'tcx> CValue<'tcx> { const_val: ty::ScalarInt, ) -> CValue<'tcx> { assert_eq!(const_val.size(), layout.size, "{:#?}: {:?}", const_val, layout); - use cranelift_codegen::ir::immediates::{Ieee32, Ieee64}; + use cranelift_codegen::ir::immediates::{Ieee16, Ieee32, Ieee64, Ieee128}; let clif_ty = fx.clif_type(layout.ty).unwrap(); @@ -345,12 +346,24 @@ impl<'tcx> CValue<'tcx> { let raw_val = const_val.size().truncate(const_val.to_bits(layout.size)); fx.bcx.ins().iconst(clif_ty, raw_val as i64) } + ty::Float(FloatTy::F16) => { + fx.bcx.ins().f16const(Ieee16::with_bits(u16::try_from(const_val).unwrap())) + } ty::Float(FloatTy::F32) => { fx.bcx.ins().f32const(Ieee32::with_bits(u32::try_from(const_val).unwrap())) } ty::Float(FloatTy::F64) => { fx.bcx.ins().f64const(Ieee64::with_bits(u64::try_from(const_val).unwrap())) } + ty::Float(FloatTy::F128) => { + let value = fx + .bcx + .func + .dfg + .constants + .insert(Ieee128::with_bits(u128::try_from(const_val).unwrap()).into()); + fx.bcx.ins().f128const(value) + } _ => panic!( "CValue::const_val for non bool/char/float/integer/pointer type {:?} is not allowed", layout.ty @@ -563,27 +576,7 @@ impl<'tcx> CPlace<'tcx> { src_ty, dst_ty, ); - let data = match (src_ty, dst_ty) { - (_, _) if src_ty == dst_ty => data, - - // This is a `write_cvalue_transmute`. - (types::I32, types::F32) - | (types::F32, types::I32) - | (types::I64, types::F64) - | (types::F64, types::I64) => codegen_bitcast(fx, dst_ty, data), - _ if src_ty.is_vector() && dst_ty.is_vector() => codegen_bitcast(fx, dst_ty, data), - _ if src_ty.is_vector() || dst_ty.is_vector() => { - // FIXME(bytecodealliance/wasmtime#6104) do something more efficient for transmutes between vectors and integers. - let ptr = fx.create_stack_slot(src_ty.bytes(), src_ty.bytes()); - ptr.store(fx, data, MemFlags::trusted()); - ptr.load(fx, dst_ty, MemFlags::trusted()) - } - - // `CValue`s should never contain SSA-only types, so if you ended - // up here having seen an error like `B1 -> I8`, then before - // calling `write_cvalue` you need to add a `bint` instruction. - _ => unreachable!("write_cvalue_transmute: {:?} -> {:?}", src_ty, dst_ty), - }; + let data = if src_ty == dst_ty { data } else { codegen_bitcast(fx, dst_ty, data) }; //fx.bcx.set_val_label(data, cranelift_codegen::ir::ValueLabel::new(var.index())); fx.bcx.def_var(var, data); } @@ -591,13 +584,9 @@ impl<'tcx> CPlace<'tcx> { assert_eq!(self.layout().size, from.layout().size); if fx.clif_comments.enabled() { - use cranelift_codegen::cursor::{Cursor, CursorPosition}; - let cur_block = match fx.bcx.cursor().position() { - CursorPosition::After(block) => block, - _ => unreachable!(), - }; - fx.add_comment( - fx.bcx.func.layout.last_inst(cur_block).unwrap(), + let inst = fx.bcx.func.layout.last_inst(fx.bcx.current_block().unwrap()).unwrap(); + fx.add_post_comment( + inst, format!( "{}: {:?}: {:?} <- {:?}: {:?}", method, @@ -806,6 +795,35 @@ impl<'tcx> CPlace<'tcx> { } } + /// Write a value to an individual lane in a SIMD vector. + pub(crate) fn write_lane_dyn( + self, + fx: &mut FunctionCx<'_, '_, 'tcx>, + lane_idx: Value, + value: CValue<'tcx>, + ) { + let layout = self.layout(); + assert!(layout.ty.is_simd()); + let (_lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + assert_eq!(lane_layout, value.layout()); + + match self.inner { + CPlaceInner::Var(_, _) => unreachable!(), + CPlaceInner::VarPair(_, _, _) => unreachable!(), + CPlaceInner::Addr(ptr, None) => { + let lane_idx = clif_intcast(fx, lane_idx, fx.pointer_type, false); + let field_offset = fx + .bcx + .ins() + .imul_imm(lane_idx, i64::try_from(lane_layout.size.bytes()).unwrap()); + let field_ptr = ptr.offset_value(fx, field_offset); + CPlace::for_ptr(field_ptr, lane_layout).write_cvalue(fx, value); + } + CPlaceInner::Addr(_, Some(_)) => unreachable!(), + } + } + pub(crate) fn place_index( self, fx: &mut FunctionCx<'_, '_, 'tcx>, diff --git a/compiler/rustc_codegen_cranelift/triagebot.toml b/compiler/rustc_codegen_cranelift/triagebot.toml new file mode 100644 index 000000000000..13da0a87def3 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/triagebot.toml @@ -0,0 +1,7 @@ +# Documentation at https://forge.rust-lang.org/triagebot/index.html + +# Prevents un-canonicalized issue links (to avoid wrong issues being linked in r-l/rust) +[issue-links] + +# Prevents mentions in commits to avoid users being spammed +[no-mentions] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index d882d3eecf49..0c499ba62379 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -15,7 +15,6 @@ use rustc_target::callconv::{Conv, RiscvInterruptKind}; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::intrinsic::ArgAbiExt; use crate::type_of::LayoutGccExt; impl AbiBuilderMethods for Builder<'_, '_, '_> { @@ -125,7 +124,7 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_gcc_type(cx), PassMode::Cast { ref cast, .. } => cast.gcc_type(cx), PassMode::Indirect { .. } => { - argument_tys.push(cx.type_ptr_to(self.ret.memory_ty(cx))); + argument_tys.push(cx.type_ptr_to(self.ret.layout.gcc_type(cx))); cx.type_void() } }; @@ -176,13 +175,13 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: true } => { // This is a "byval" argument, so we don't apply the `restrict` attribute on it. on_stack_param_indices.insert(argument_tys.len()); - arg.memory_ty(cx) + arg.layout.gcc_type(cx) } PassMode::Direct(attrs) => { apply_attrs(arg.layout.immediate_gcc_type(cx), &attrs, argument_tys.len()) } PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { - apply_attrs(cx.type_ptr_to(arg.memory_ty(cx)), &attrs, argument_tys.len()) + apply_attrs(cx.type_ptr_to(arg.layout.gcc_type(cx)), &attrs, argument_tys.len()) } PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { assert!(!on_stack); diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index e63091c6082a..c853c88a6eac 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -2,8 +2,8 @@ use gccjit::FnAttribute; use gccjit::Function; #[cfg(feature = "master")] -use rustc_attr_parsing::InlineAttr; -use rustc_attr_parsing::InstructionSetAttr; +use rustc_attr_data_structures::InlineAttr; +use rustc_attr_data_structures::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; #[cfg(feature = "master")] diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index a9d7808c833b..c105916bbb2b 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -219,17 +219,22 @@ pub fn compile_codegen_unit( let mono_items = cgu.items_in_deterministic_order(tcx); for &(mono_item, data) in &mono_items { - mono_item.predefine::>(&cx, data.linkage, data.visibility); + mono_item.predefine::>( + &mut cx, + cgu_name.as_str(), + data.linkage, + data.visibility, + ); } // ... and now that we have everything pre-defined, fill out those definitions. for &(mono_item, item_data) in &mono_items { - mono_item.define::>(&mut cx, item_data); + mono_item.define::>(&mut cx, cgu_name.as_str(), item_data); } // If this codegen unit contains the main function, also create the // wrapper here - maybe_create_entry_wrapper::>(&cx); + maybe_create_entry_wrapper::>(&cx, cx.codegen_unit); // Finalize debuginfo if cx.sess().opts.debuginfo != DebugInfo::None { diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 4e2163201fd0..d1fb8d8f9d6b 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -12,7 +12,7 @@ use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout, WrappingRange}; use rustc_apfloat::{Float, Round, Status, ieee}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{ - AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, + AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; @@ -26,7 +26,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, AtomicOrdering, Instance, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; use rustc_target::callconv::FnAbi; @@ -75,7 +75,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let load_ordering = match order { // TODO(antoyo): does this make sense? - AtomicOrdering::AcquireRelease | AtomicOrdering::Release => AtomicOrdering::Acquire, + AtomicOrdering::AcqRel | AtomicOrdering::Release => AtomicOrdering::Acquire, _ => order, }; let previous_value = @@ -2474,8 +2474,8 @@ impl ToGccOrdering for AtomicOrdering { AtomicOrdering::Relaxed => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same. AtomicOrdering::Acquire => __ATOMIC_ACQUIRE, AtomicOrdering::Release => __ATOMIC_RELEASE, - AtomicOrdering::AcquireRelease => __ATOMIC_ACQ_REL, - AtomicOrdering::SequentiallyConsistent => __ATOMIC_SEQ_CST, + AtomicOrdering::AcqRel => __ATOMIC_ACQ_REL, + AtomicOrdering::SeqCst => __ATOMIC_SEQ_CST, }; ordering as i32 } diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index c133ae4fcdd2..c8130b7c0106 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -106,7 +106,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_fn_attrs(instance_def_id).inline - == rustc_attr_parsing::InlineAttr::Never) + == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 033afc0f8fbf..deb13ddf7558 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -67,7 +67,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { } #[cfg_attr(not(feature = "master"), allow(unused_mut))] - fn codegen_static(&self, def_id: DefId) { + fn codegen_static(&mut self, def_id: DefId) { let attrs = self.tcx.codegen_fn_attrs(def_id); let Ok((value, alloc)) = codegen_static_initializer(self, def_id) else { @@ -154,25 +154,20 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { // TODO(antoyo): set link section. } - if attrs.flags.contains(CodegenFnAttrFlags::USED) + if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { self.add_used_global(global.to_rvalue()); } } - - /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*. - fn add_used_global(&self, _global: RValue<'gcc>) { - // TODO(antoyo) - } - - fn add_compiler_used_global(&self, global: RValue<'gcc>) { - // NOTE: seems like GCC does not make the distinction between compiler.used and used. - self.add_used_global(global); - } } impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*. + pub fn add_used_global(&mut self, _global: RValue<'gcc>) { + // TODO(antoyo) + } + #[cfg_attr(not(feature = "master"), allow(unused_variables))] pub fn add_used_function(&self, function: Function<'gcc>) { #[cfg(feature = "master")] diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 73718994e641..c6c43201f216 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -470,10 +470,6 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.tcx.sess } - fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { - self.codegen_unit - } - fn set_frame_pointer_type(&self, _llfn: RValue<'gcc>) { // TODO(antoyo) } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index ba65c8205a50..ff1ae2d9d792 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -22,11 +22,11 @@ use rustc_codegen_ssa::traits::{ }; use rustc_middle::bug; #[cfg(feature = "master")] -use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; +use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; -use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, PassMode}; use rustc_target::spec::PanicStrategy; #[cfg(feature = "master")] @@ -200,9 +200,8 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc fn codegen_intrinsic_call( &mut self, instance: Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, RValue<'gcc>>], - llresult: RValue<'gcc>, + result: PlaceRef<'tcx, RValue<'gcc>>, span: Span, ) -> Result<(), Instance<'tcx>> { let tcx = self.tcx; @@ -221,7 +220,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let name_str = name.as_str(); let llret_ty = self.layout_of(ret_ty).gcc_type(self); - let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let simple = get_simple_intrinsic(self, name); let simple_func = get_simple_function(self, name); @@ -271,7 +269,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc args[0].immediate(), args[1].immediate(), args[2].immediate(), - llresult, + result, ); return Ok(()); } @@ -286,17 +284,10 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } sym::volatile_load | sym::unaligned_volatile_load => { - let tp_ty = fn_args.type_at(0); let ptr = args[0].immediate(); - let layout = self.layout_of(tp_ty); - let load = if let PassMode::Cast { cast: ref ty, pad_i32: _ } = fn_abi.ret.mode { - let gcc_ty = ty.gcc_type(self); - self.volatile_load(gcc_ty, ptr) - } else { - self.volatile_load(layout.gcc_type(self), ptr) - }; + let load = self.volatile_load(result.layout.gcc_type(self), ptr); // TODO(antoyo): set alignment. - if let BackendRepr::Scalar(scalar) = layout.backend_repr { + if let BackendRepr::Scalar(scalar) = result.layout.backend_repr { self.to_immediate_scalar(load, scalar) } else { load @@ -511,16 +502,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc _ => return Err(Instance::new_raw(instance.def_id(), instance.args)), }; - if !fn_abi.ret.is_ignore() { - if let PassMode::Cast { cast: ref ty, .. } = fn_abi.ret.mode { - let ptr_llty = self.type_ptr_to(ty.gcc_type(self)); - let ptr = self.pointercast(result.val.llval, ptr_llty); - self.store(value, ptr, result.val.align); - } else { - OperandRef::from_immediate_or_packed_pair(self, value, result.layout) - .val - .store(self, result); - } + if result.layout.ty.is_bool() { + OperandRef::from_immediate_or_packed_pair(self, value, result.layout) + .val + .store(self, result); + } else if !result.layout.ty.is_unit() { + let ptr_llty = self.type_ptr_to(result.layout.gcc_type(self)); + let ptr = self.pointercast(result.val.llval, ptr_llty); + self.store(value, ptr, result.val.align); } Ok(()) } @@ -585,14 +574,9 @@ impl<'a, 'gcc, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { ) { arg_abi.store(self, val, dst) } - - fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { - arg_abi.memory_ty(self) - } } pub trait ArgAbiExt<'gcc, 'tcx> { - fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; fn store( &self, bx: &mut Builder<'_, 'gcc, 'tcx>, @@ -608,12 +592,6 @@ pub trait ArgAbiExt<'gcc, 'tcx> { } impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { - /// Gets the LLVM type for a place of the original Rust type of - /// this argument/return, i.e., the result of `type_of::type_of`. - fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { - self.layout.gcc_type(cx) - } - /// Stores a direct/indirect value described by this ArgAbi into a /// place for the original Rust type of this argument/return. /// Can be used for both storing formal arguments into Rust variables @@ -1230,14 +1208,13 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, - dest: RValue<'gcc>, + dest: PlaceRef<'tcx, RValue<'gcc>>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { bx.call(bx.type_void(), None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. - let ret_align = bx.tcx.data_layout.i32_align.abi; - bx.store(bx.const_i32(0), dest, ret_align); + OperandValue::Immediate(bx.const_i32(0)).store(bx, dest); } else { if wants_msvc_seh(bx.sess()) { unimplemented!(); @@ -1261,12 +1238,12 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( // functions in play. By calling a shim we're guaranteed that our shim will have // the right personality function. #[cfg(feature = "master")] -fn codegen_gnu_try<'gcc>( - bx: &mut Builder<'_, 'gcc, '_>, +fn codegen_gnu_try<'gcc, 'tcx>( + bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, catch_func: RValue<'gcc>, - dest: RValue<'gcc>, + dest: PlaceRef<'tcx, RValue<'gcc>>, ) { let cx: &CodegenCx<'gcc, '_> = bx.cx; let (llty, func) = get_rust_try_fn(cx, &mut |mut bx| { @@ -1322,8 +1299,7 @@ fn codegen_gnu_try<'gcc>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Helper function used to get a handle to the `__rust_try` function used to diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 6e2a50d745a4..f79ba2dcfc7e 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -37,7 +37,7 @@ extern crate tracing; extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; -extern crate rustc_attr_parsing; +extern crate rustc_attr_data_structures; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; @@ -391,7 +391,7 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!() } - unsafe fn optimize( + fn optimize( _cgcx: &CodegenContext, _dcx: DiagCtxtHandle<'_>, module: &mut ModuleCodegen, @@ -409,14 +409,14 @@ impl WriteBackendMethods for GccCodegenBackend { Ok(()) } - unsafe fn optimize_thin( + fn optimize_thin( cgcx: &CodegenContext, thin: ThinModule, ) -> Result, FatalError> { back::lto::optimize_thin_module(thin, cgcx) } - unsafe fn codegen( + fn codegen( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index a2df7b2596fc..539e3ac85076 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -16,7 +16,7 @@ use crate::{attributes, base}; impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { #[cfg_attr(not(feature = "master"), allow(unused_variables))] fn predefine_static( - &self, + &mut self, def_id: DefId, _linkage: Linkage, visibility: Visibility, @@ -42,7 +42,7 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { #[cfg_attr(not(feature = "master"), allow(unused_variables))] fn predefine_fn( - &self, + &mut self, instance: Instance<'tcx>, linkage: Linkage, visibility: Visibility, diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 3185993c2076..bf8ec8c3b915 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -19,7 +19,7 @@ object = { version = "0.36.3", default-features = false, features = ["std", "rea rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 8294e29d07df..c87e70864e5a 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -172,7 +172,6 @@ impl LlvmType for CastTarget { } trait ArgAbiExt<'ll, 'tcx> { - fn memory_ty(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn store( &self, bx: &mut Builder<'_, 'll, 'tcx>, @@ -188,12 +187,6 @@ trait ArgAbiExt<'ll, 'tcx> { } impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { - /// Gets the LLVM type for a place of the original Rust type of - /// this argument/return, i.e., the result of `type_of::type_of`. - fn memory_ty(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type { - self.layout.llvm_type(cx) - } - /// Stores a direct/indirect value described by this ArgAbi into a /// place for the original Rust type of this argument/return. /// Can be used for both storing formal arguments into Rust variables @@ -302,9 +295,6 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) { arg_abi.store(self, val, dst) } - fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> &'ll Type { - arg_abi.memory_ty(self) - } } pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 176fb72dfdc5..443c2eace554 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -1,5 +1,5 @@ //! Set and unset common attributes on LLVM values. -use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry}; diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index cb329323f5d7..ee46b49a094c 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -799,7 +799,7 @@ impl Drop for ThinBuffer { } } -pub(crate) unsafe fn optimize_thin_module( +pub(crate) fn optimize_thin_module( thin_module: ThinModule, cgcx: &CodegenContext, ) -> Result, FatalError> { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 20721c746087..bde6a9cf4bc6 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -704,7 +704,7 @@ pub(crate) unsafe fn llvm_optimize( } // Unsafe due to LLVM calls. -pub(crate) unsafe fn optimize( +pub(crate) fn optimize( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: &mut ModuleCodegen, @@ -815,7 +815,7 @@ pub(crate) fn link( Ok(modules.remove(0)) } -pub(crate) unsafe fn codegen( +pub(crate) fn codegen( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index e4fac35aa449..5dda836988c8 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -86,17 +86,24 @@ pub(crate) fn compile_codegen_unit( let mut cx = CodegenCx::new(tcx, cgu, &llvm_module); let mono_items = cx.codegen_unit.items_in_deterministic_order(cx.tcx); for &(mono_item, data) in &mono_items { - mono_item.predefine::>(&cx, data.linkage, data.visibility); + mono_item.predefine::>( + &mut cx, + cgu_name.as_str(), + data.linkage, + data.visibility, + ); } // ... and now that we have everything pre-defined, fill out those definitions. for &(mono_item, item_data) in &mono_items { - mono_item.define::>(&mut cx, item_data); + mono_item.define::>(&mut cx, cgu_name.as_str(), item_data); } // If this codegen unit contains the main function, also create the // wrapper here - if let Some(entry) = maybe_create_entry_wrapper::>(&cx) { + if let Some(entry) = + maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) + { let attrs = attributes::sanitize_attrs(&cx, SanitizerSet::empty()); attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } @@ -108,14 +115,11 @@ pub(crate) fn compile_codegen_unit( } // Create the llvm.used and llvm.compiler.used variables. - if !cx.used_statics.borrow().is_empty() { - cx.create_used_variable_impl(c"llvm.used", &*cx.used_statics.borrow()); + if !cx.used_statics.is_empty() { + cx.create_used_variable_impl(c"llvm.used", &cx.used_statics); } - if !cx.compiler_used_statics.borrow().is_empty() { - cx.create_used_variable_impl( - c"llvm.compiler.used", - &*cx.compiler_used_statics.borrow(), - ); + if !cx.compiler_used_statics.is_empty() { + cx.create_used_variable_impl(c"llvm.compiler.used", &cx.compiler_used_statics); } // Run replace-all-uses-with for statics that need it. This must diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5238755c8eb9..167678c2ff13 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -612,7 +612,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { &mut self, ty: &'ll Type, ptr: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, size: Size, ) -> &'ll Value { unsafe { @@ -851,7 +851,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { &mut self, val: &'ll Value, ptr: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, size: Size, ) { debug!("Store {:?} -> {:?}", val, ptr); @@ -1307,8 +1307,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { dst: &'ll Value, cmp: &'ll Value, src: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, - failure_order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, + failure_order: rustc_middle::ty::AtomicOrdering, weak: bool, ) -> (&'ll Value, &'ll Value) { let weak = if weak { llvm::True } else { llvm::False }; @@ -1334,7 +1334,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { op: rustc_codegen_ssa::common::AtomicRmwBinOp, dst: &'ll Value, mut src: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, ) -> &'ll Value { // The only RMW operation that LLVM supports on pointers is compare-exchange. let requires_cast_to_int = self.val_ty(src) == self.type_ptr() @@ -1360,7 +1360,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn atomic_fence( &mut self, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, scope: SynchronizationScope, ) { let single_threaded = match scope { @@ -1452,9 +1452,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { fn get_static(&mut self, def_id: DefId) -> &'ll Value { // Forward to the `get_static` method of `CodegenCx` - let s = self.cx().get_static(def_id); - // Cast to default address space if globals are in a different addrspace - self.cx().const_pointercast(s, self.type_ptr()) + let global = self.cx().get_static(def_id); + if self.cx().tcx.is_thread_local_static(def_id) { + let pointer = self.call_intrinsic("llvm.threadlocal.address", &[global]); + // Cast to default address space if globals are in a different addrspace + self.pointercast(pointer, self.type_ptr()) + } else { + // Cast to default address space if globals are in a different addrspace + self.cx().const_pointercast(global, self.type_ptr()) + } } } diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index ea9ab5c02bd6..6d68eca60afc 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -103,7 +103,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_fn_attrs(instance_def_id).inline - == rustc_attr_parsing::InlineAttr::Never) + == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index bf81eb648f8a..4234352c93a9 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -411,7 +411,7 @@ impl<'ll> CodegenCx<'ll, '_> { g } - fn codegen_static_item(&self, def_id: DefId) { + fn codegen_static_item(&mut self, def_id: DefId) { unsafe { assert!( llvm::LLVMGetInitializer( @@ -527,7 +527,7 @@ impl<'ll> CodegenCx<'ll, '_> { base::set_variable_sanitizer_attrs(g, attrs); - if attrs.flags.contains(CodegenFnAttrFlags::USED) { + if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) { // `USED` and `USED_LINKER` can't be used together. assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)); @@ -551,12 +551,23 @@ impl<'ll> CodegenCx<'ll, '_> { } if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { // `USED` and `USED_LINKER` can't be used together. - assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED)); + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)); self.add_used_global(g); } } } + + /// Add a global value to a list to be stored in the `llvm.used` variable, an array of ptr. + pub(crate) fn add_used_global(&mut self, global: &'ll Value) { + self.used_statics.push(global); + } + + /// Add a global value to a list to be stored in the `llvm.compiler.used` variable, + /// an array of ptr. + pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) { + self.compiler_used_statics.push(global); + } } impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { @@ -571,18 +582,7 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { self.const_pointercast(gv, self.type_ptr()) } - fn codegen_static(&self, def_id: DefId) { + fn codegen_static(&mut self, def_id: DefId) { self.codegen_static_item(def_id) } - - /// Add a global value to a list to be stored in the `llvm.used` variable, an array of ptr. - fn add_used_global(&self, global: &'ll Value) { - self.used_statics.borrow_mut().push(global); - } - - /// Add a global value to a list to be stored in the `llvm.compiler.used` variable, - /// an array of ptr. - fn add_compiler_used_global(&self, global: &'ll Value) { - self.compiler_used_statics.borrow_mut().push(global); - } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b0d8e11d1fb5..8d6e1d8941b7 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -2,7 +2,7 @@ use std::borrow::Borrow; use std::cell::{Cell, RefCell}; use std::ffi::{CStr, c_char, c_uint}; use std::marker::PhantomData; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::str; use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx}; @@ -77,6 +77,13 @@ impl<'ll, T: Borrow>> Deref for GenericCx<'ll, T> { } } +impl<'ll, T: Borrow>> DerefMut for GenericCx<'ll, T> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + pub(crate) type SimpleCx<'ll> = GenericCx<'ll, SCx<'ll>>; /// There is one `CodegenCx` per codegen unit. Each one has its own LLVM @@ -110,11 +117,11 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// Statics that will be placed in the llvm.used variable /// See for details - pub used_statics: RefCell>, + pub used_statics: Vec<&'ll Value>, /// Statics that will be placed in the llvm.compiler.used variable /// See for details - pub compiler_used_statics: RefCell>, + pub compiler_used_statics: Vec<&'ll Value>, /// Mapping of non-scalar types to llvm types. pub type_lowering: RefCell, Option), &'ll Type>>, @@ -606,8 +613,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { const_str_cache: Default::default(), const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), - used_statics: RefCell::new(Vec::new()), - compiler_used_statics: RefCell::new(Vec::new()), + used_statics: Vec::new(), + compiler_used_statics: Vec::new(), type_lowering: Default::default(), scalar_lltypes: Default::default(), coverage_cx, @@ -801,10 +808,6 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.tcx.sess } - fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { - self.codegen_unit - } - fn set_frame_pointer_type(&self, llfn: &'ll Value) { if let Some(attr) = attributes::frame_pointer_type_attr(self) { attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[attr]); @@ -1240,6 +1243,7 @@ impl<'ll> CodegenCx<'ll, '_> { } ifn!("llvm.ptrmask", fn(ptr, t_isize) -> ptr); + ifn!("llvm.threadlocal.address", fn(ptr) -> ptr); None } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 55b1e728b70d..a9be833a6439 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -2,9 +2,7 @@ use std::sync::Arc; use itertools::Itertools; use rustc_abi::Align; -use rustc_codegen_ssa::traits::{ - BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, -}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::FxIndexMap; use rustc_index::IndexVec; use rustc_middle::ty::TyCtxt; @@ -27,7 +25,7 @@ mod unused; /// /// Those sections are then read and understood by LLVM's `llvm-cov` tool, /// which is distributed in the `llvm-tools` rustup component. -pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { +pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) { let tcx = cx.tcx; // Ensure that LLVM is using a version of the coverage mapping format that @@ -62,6 +60,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name) .filter_map(|instance| prepare_covfun_record(tcx, instance, true)) .collect::>(); + drop(instances_used); // In a single designated CGU, also prepare covfun records for functions // in this crate that were instrumented for coverage, but are unused. @@ -206,7 +205,7 @@ impl VirtualFileMapping { /// Generates the contents of the covmap record for this CGU, which mostly /// consists of a header and a list of filenames. The record is then stored /// as a global variable in the `__llvm_covmap` section. -fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_buffer: &[u8]) { +fn generate_covmap_record<'ll>(cx: &mut CodegenCx<'ll, '_>, version: u32, filenames_buffer: &[u8]) { // A covmap record consists of four target-endian u32 values, followed by // the encoded filenames table. Two of the header fields are unused in // modern versions of the LLVM coverage mapping format, and are always 0. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index 7bdbc6859529..b704cf2b1cd4 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -8,9 +8,7 @@ use std::ffi::CString; use std::sync::Arc; use rustc_abi::Align; -use rustc_codegen_ssa::traits::{ - BaseTypeCodegenMethods as _, ConstCodegenMethods, StaticCodegenMethods, -}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods as _, ConstCodegenMethods}; use rustc_middle::mir::coverage::{ BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, @@ -181,7 +179,7 @@ fn fill_region_tables<'tcx>( /// contains the function's coverage mapping data. The record is then stored /// as a global variable in the `__llvm_covfun` section. pub(crate) fn generate_covfun_record<'tcx>( - cx: &CodegenCx<'_, 'tcx>, + cx: &mut CodegenCx<'_, 'tcx>, global_file_table: &GlobalFileTable, covfun: &CovfunRecord<'tcx>, ) { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index ea7f581a3cb5..eefbd7cf6c48 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -56,7 +56,7 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { - pub(crate) fn coverageinfo_finalize(&self) { + pub(crate) fn coverageinfo_finalize(&mut self) { mapgen::finalize(self) } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 5ca573752922..e8629aeebb95 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -15,11 +15,10 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::callconv::{FnAbi, PassMode}; use rustc_target::spec::{HasTargetSpec, PanicStrategy}; use tracing::debug; -use crate::abi::{FnAbiLlvmExt, LlvmType}; +use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, Metadata}; @@ -165,9 +164,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn codegen_intrinsic_call( &mut self, instance: ty::Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, &'ll Value>], - llresult: &'ll Value, + result: PlaceRef<'tcx, &'ll Value>, span: Span, ) -> Result<(), ty::Instance<'tcx>> { let tcx = self.tcx; @@ -184,7 +182,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let name = tcx.item_name(def_id); let llret_ty = self.layout_of(ret_ty).llvm_type(self); - let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let simple = get_simple_intrinsic(self, name); let llval = match name { @@ -255,7 +252,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { args[0].immediate(), args[1].immediate(), args[2].immediate(), - llresult, + result, ); return Ok(()); } @@ -264,7 +261,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.call_intrinsic("llvm.va_copy", &[args[0].immediate(), args[1].immediate()]) } sym::va_arg => { - match fn_abi.ret.layout.backend_repr { + match result.layout.backend_repr { BackendRepr::Scalar(scalar) => { match scalar.primitive() { Primitive::Int(..) => { @@ -299,18 +296,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::volatile_load | sym::unaligned_volatile_load => { - let tp_ty = fn_args.type_at(0); let ptr = args[0].immediate(); - let load = if let PassMode::Cast { cast: ty, pad_i32: _ } = &fn_abi.ret.mode { - let llty = ty.llvm_type(self); - self.volatile_load(llty, ptr) - } else { - self.volatile_load(self.layout_of(tp_ty).llvm_type(self), ptr) - }; + let load = self.volatile_load(result.layout.llvm_type(self), ptr); let align = if name == sym::unaligned_volatile_load { 1 } else { - self.align_of(tp_ty).bytes() as u32 + result.layout.align.abi.bytes() as u32 }; unsafe { llvm::LLVMSetAlignment(load, align); @@ -629,14 +620,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } }; - if !fn_abi.ret.is_ignore() { - if let PassMode::Cast { .. } = &fn_abi.ret.mode { - self.store(llval, result.val.llval, result.val.align); - } else { - OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) - .val - .store(self, result); - } + if result.layout.ty.is_bool() { + OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) + .val + .store(self, result); + } else if !result.layout.ty.is_unit() { + self.store_to_place(llval, result.val); } Ok(()) } @@ -688,20 +677,19 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } -fn catch_unwind_intrinsic<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn catch_unwind_intrinsic<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.call(try_func_ty, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. - let ret_align = bx.tcx().data_layout.i32_align.abi; - bx.store(bx.const_i32(0), dest, ret_align); + OperandValue::Immediate(bx.const_i32(0)).store(bx, dest); } else if wants_msvc_seh(bx.sess()) { codegen_msvc_try(bx, try_func, data, catch_func, dest); } else if wants_wasm_eh(bx.sess()) { @@ -720,12 +708,12 @@ fn catch_unwind_intrinsic<'ll>( // instructions are meant to work for all targets, as of the time of this // writing, however, LLVM does not recommend the usage of these new instructions // as the old ones are still more optimized. -fn codegen_msvc_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_msvc_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { bx.set_personality_fn(bx.eh_personality()); @@ -865,17 +853,16 @@ fn codegen_msvc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // WASM's definition of the `rust_try` function. -fn codegen_wasm_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_wasm_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { bx.set_personality_fn(bx.eh_personality()); @@ -939,8 +926,7 @@ fn codegen_wasm_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Definition of the standard `try` function for Rust using the GNU-like model @@ -954,12 +940,12 @@ fn codegen_wasm_try<'ll>( // function calling it, and that function may already have other personality // functions in play. By calling a shim we're guaranteed that our shim will have // the right personality function. -fn codegen_gnu_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_gnu_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { // Codegens the shims described above: @@ -1006,19 +992,18 @@ fn codegen_gnu_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Variant of codegen_gnu_try used for emscripten where Rust panics are // implemented using C++ exceptions. Here we use exceptions of a specific type // (`struct rust_panic`) to represent Rust panics. -fn codegen_emcc_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_emcc_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { // Codegens the shims described above: @@ -1089,8 +1074,7 @@ fn codegen_emcc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Helper function to give a Block to a closure to codegen a shim function. diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5736314b96a3..fd376ea8d804 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -189,13 +189,13 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result<(Vec>, Vec), FatalError> { back::lto::run_thin(cgcx, modules, cached_modules) } - unsafe fn optimize( + fn optimize( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: &mut ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError> { - unsafe { back::write::optimize(cgcx, dcx, module, config) } + back::write::optimize(cgcx, dcx, module, config) } fn optimize_fat( cgcx: &CodegenContext, @@ -205,19 +205,19 @@ impl WriteBackendMethods for LlvmCodegenBackend { let dcx = dcx.handle(); back::lto::run_pass_manager(cgcx, dcx, module, false) } - unsafe fn optimize_thin( + fn optimize_thin( cgcx: &CodegenContext, thin: ThinModule, ) -> Result, FatalError> { - unsafe { back::lto::optimize_thin_module(thin, cgcx) } + back::lto::optimize_thin_module(thin, cgcx) } - unsafe fn codegen( + fn codegen( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { - unsafe { back::write::codegen(cgcx, dcx, module, config) } + back::write::codegen(cgcx, dcx, module, config) } fn prepare_thin( module: ModuleCodegen, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 67a66e6ec795..e27fbf94f341 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -426,14 +426,14 @@ pub(crate) enum AtomicOrdering { } impl AtomicOrdering { - pub(crate) fn from_generic(ao: rustc_codegen_ssa::common::AtomicOrdering) -> Self { - use rustc_codegen_ssa::common::AtomicOrdering as Common; + pub(crate) fn from_generic(ao: rustc_middle::ty::AtomicOrdering) -> Self { + use rustc_middle::ty::AtomicOrdering as Common; match ao { Common::Relaxed => Self::Monotonic, Common::Acquire => Self::Acquire, Common::Release => Self::Release, - Common::AcquireRelease => Self::AcquireRelease, - Common::SequentiallyConsistent => Self::SequentiallyConsistent, + Common::AcqRel => Self::AcquireRelease, + Common::SeqCst => Self::SequentiallyConsistent, } } } diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index fdf62a08065c..3f38e1e191bf 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -16,7 +16,7 @@ use crate::{base, llvm}; impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { fn predefine_static( - &self, + &mut self, def_id: DefId, linkage: Linkage, visibility: Visibility, @@ -44,7 +44,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { } fn predefine_fn( - &self, + &mut self, instance: Instance<'tcx>, linkage: Linkage, visibility: Visibility, diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index c216f0f4a09d..236568590be1 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,7 +1,10 @@ -use rustc_abi::{Align, Endian, HasDataLayout, Size}; +use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size, TyAndLayout}; +use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods}; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods, +}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; @@ -37,6 +40,7 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( align: Align, slot_size: Align, allow_higher_align: bool, + force_right_adjust: bool, ) -> (&'ll Value, Align) { let va_list_ty = bx.type_ptr(); let va_list_addr = list.immediate(); @@ -54,7 +58,10 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( let next = bx.inbounds_ptradd(addr, full_direct_size); bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi); - if size.bytes() < slot_size.bytes() && bx.tcx().sess.target.endian == Endian::Big { + if size.bytes() < slot_size.bytes() + && bx.tcx().sess.target.endian == Endian::Big + && force_right_adjust + { let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); let adjusted = bx.inbounds_ptradd(addr, adjusted_size); (adjusted, addr_align) @@ -63,14 +70,40 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( } } +enum PassMode { + Direct, + Indirect, +} + +enum SlotSize { + Bytes8 = 8, + Bytes4 = 4, +} + +enum AllowHigherAlign { + No, + Yes, +} + +enum ForceRightAdjust { + No, + Yes, +} + fn emit_ptr_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, target_ty: Ty<'tcx>, - indirect: bool, - slot_size: Align, - allow_higher_align: bool, + pass_mode: PassMode, + slot_size: SlotSize, + allow_higher_align: AllowHigherAlign, + force_right_adjust: ForceRightAdjust, ) -> &'ll Value { + let indirect = matches!(pass_mode, PassMode::Indirect); + let allow_higher_align = matches!(allow_higher_align, AllowHigherAlign::Yes); + let force_right_adjust = matches!(force_right_adjust, ForceRightAdjust::Yes); + let slot_size = Align::from_bytes(slot_size as u64).unwrap(); + let layout = bx.cx.layout_of(target_ty); let (llty, size, align) = if indirect { ( @@ -81,8 +114,15 @@ fn emit_ptr_va_arg<'ll, 'tcx>( } else { (layout.llvm_type(bx.cx), layout.size, layout.align) }; - let (addr, addr_align) = - emit_direct_ptr_va_arg(bx, list, size, align.abi, slot_size, allow_higher_align); + let (addr, addr_align) = emit_direct_ptr_va_arg( + bx, + list, + size, + align.abi, + slot_size, + allow_higher_align, + force_right_adjust, + ); if indirect { let tmp_ret = bx.load(llty, addr, addr_align); bx.load(bx.cx.layout_of(target_ty).llvm_type(bx.cx), tmp_ret, align.abi) @@ -179,8 +219,15 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( // On Stack block bx.switch_to_block(on_stack); - let stack_value = - emit_ptr_va_arg(bx, list, target_ty, false, Align::from_bytes(8).unwrap(), true); + let stack_value = emit_ptr_va_arg( + bx, + list, + target_ty, + PassMode::Direct, + SlotSize::Bytes8, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ); bx.br(end); bx.switch_to_block(end); @@ -190,6 +237,150 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( val } +fn emit_powerpc_va_arg<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + let dl = bx.cx.data_layout(); + + // struct __va_list_tag { + // unsigned char gpr; + // unsigned char fpr; + // unsigned short reserved; + // void *overflow_arg_area; + // void *reg_save_area; + // }; + let va_list_addr = list.immediate(); + + // Peel off any newtype wrappers. + let layout = { + let mut layout = bx.cx.layout_of(target_ty); + + while let Some((_, inner)) = layout.non_1zst_field(bx.cx) { + layout = inner; + } + + layout + }; + + // Rust does not currently support any powerpc softfloat targets. + let target = &bx.cx.tcx.sess.target; + let is_soft_float_abi = target.abi == "softfloat"; + assert!(!is_soft_float_abi); + + // All instances of VaArgSafe are passed directly. + let is_indirect = false; + + let (is_i64, is_int, is_f64) = match layout.layout.backend_repr() { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + rustc_abi::Primitive::Int(integer, _) => (integer.size().bits() == 64, true, false), + rustc_abi::Primitive::Float(float) => (false, false, float.size().bits() == 64), + rustc_abi::Primitive::Pointer(_) => (false, true, false), + }, + _ => unreachable!("all instances of VaArgSafe are represented as scalars"), + }; + + let num_regs_addr = if is_int || is_soft_float_abi { + va_list_addr // gpr + } else { + bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) // fpr + }; + + let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align.abi); + + // "Align" the register count when the type is passed as `i64`. + if is_i64 || (is_f64 && is_soft_float_abi) { + num_regs = bx.add(num_regs, bx.const_u8(1)); + num_regs = bx.and(num_regs, bx.const_u8(0b1111_1110)); + } + + let max_regs = 8u8; + let use_regs = bx.icmp(IntPredicate::IntULT, num_regs, bx.const_u8(max_regs)); + + let in_reg = bx.append_sibling_block("va_arg.in_reg"); + let in_mem = bx.append_sibling_block("va_arg.in_mem"); + let end = bx.append_sibling_block("va_arg.end"); + + bx.cond_br(use_regs, in_reg, in_mem); + + let reg_addr = { + bx.switch_to_block(in_reg); + + let reg_safe_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2 + 4)); + let mut reg_addr = bx.load(bx.type_ptr(), reg_safe_area_ptr, dl.pointer_align.abi); + + // Floating-point registers start after the general-purpose registers. + if !is_int && !is_soft_float_abi { + reg_addr = bx.inbounds_ptradd(reg_addr, bx.cx.const_usize(32)) + } + + // Get the address of the saved value by scaling the number of + // registers we've used by the number of. + let reg_size = if is_int || is_soft_float_abi { 4 } else { 8 }; + let reg_offset = bx.mul(num_regs, bx.cx().const_u8(reg_size)); + let reg_addr = bx.inbounds_ptradd(reg_addr, reg_offset); + + // Increase the used-register count. + let reg_incr = if is_i64 || (is_f64 && is_soft_float_abi) { 2 } else { 1 }; + let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr)); + bx.store(new_num_regs, num_regs_addr, dl.i8_align.abi); + + bx.br(end); + + reg_addr + }; + + let mem_addr = { + bx.switch_to_block(in_mem); + + bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align.abi); + + // Everything in the overflow area is rounded up to a size of at least 4. + let overflow_area_align = Align::from_bytes(4).unwrap(); + + let size = if !is_indirect { + layout.layout.size.align_to(overflow_area_align) + } else { + dl.pointer_size + }; + + let overflow_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2)); + let mut overflow_area = bx.load(bx.type_ptr(), overflow_area_ptr, dl.pointer_align.abi); + + // Round up address of argument to alignment + if layout.layout.align.abi > overflow_area_align { + overflow_area = round_pointer_up_to_alignment( + bx, + overflow_area, + layout.layout.align.abi, + bx.type_ptr(), + ); + } + + let mem_addr = overflow_area; + + // Increase the overflow area. + overflow_area = bx.inbounds_ptradd(overflow_area, bx.const_usize(size.bytes())); + bx.store(overflow_area, overflow_area_ptr, dl.pointer_align.abi); + + bx.br(end); + + mem_addr + }; + + // Return the appropriate result. + bx.switch_to_block(end); + let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]); + let val_type = layout.llvm_type(bx); + let val_addr = if is_indirect { + bx.load(bx.cx.type_ptr(), val_addr, dl.pointer_align.abi) + } else { + val_addr + }; + bx.load(val_type, val_addr, layout.align.abi) +} + fn emit_s390x_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -278,6 +469,313 @@ fn emit_s390x_va_arg<'ll, 'tcx>( bx.load(val_type, val_addr, layout.align.abi) } +fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + let dl = bx.cx.data_layout(); + + // Implementation of the systemv x86_64 ABI calling convention for va_args, see + // https://gitlab.com/x86-psABIs/x86-64-ABI (section 3.5.7). This implementation is heavily + // based on the one in clang. + + // We're able to take some shortcuts because the return type of `va_arg` must implement the + // `VaArgSafe` trait. Currently, only pointers, f64, i32, u32, i64 and u64 implement this trait. + + // typedef struct __va_list_tag { + // unsigned int gp_offset; + // unsigned int fp_offset; + // void *overflow_arg_area; + // void *reg_save_area; + // } va_list[1]; + let va_list_addr = list.immediate(); + + // Peel off any newtype wrappers. + // + // The "C" ABI does not unwrap newtypes (see `ReprOptions::inhibit_newtype_abi_optimization`). + // Here, we do actually want the unwrapped representation, because that is how LLVM/Clang + // pass such types to variadic functions. + // + // An example of a type that must be unwrapped is `Foo` below. Without the unwrapping, it has + // `BackendRepr::Memory`, but we need it to be `BackendRepr::Scalar` to generate correct code. + // + // ``` + // #[repr(C)] + // struct Empty; + // + // #[repr(C)] + // struct Foo([Empty; 8], i32); + // ``` + let layout = { + let mut layout = bx.cx.layout_of(target_ty); + + while let Some((_, inner)) = layout.non_1zst_field(bx.cx) { + layout = inner; + } + + layout + }; + + // AMD64-ABI 3.5.7p5: Step 1. Determine whether type may be passed + // in the registers. If not go to step 7. + + // AMD64-ABI 3.5.7p5: Step 2. Compute num_gp to hold the number of + // general purpose registers needed to pass type and num_fp to hold + // the number of floating point registers needed. + + let mut num_gp_registers = 0; + let mut num_fp_registers = 0; + + let mut registers_for_primitive = |p| match p { + Primitive::Int(integer, _is_signed) => { + num_gp_registers += integer.size().bytes().div_ceil(8) as u32; + } + Primitive::Float(float) => { + num_fp_registers += float.size().bytes().div_ceil(16) as u32; + } + Primitive::Pointer(_) => { + num_gp_registers += 1; + } + }; + + match layout.layout.backend_repr() { + BackendRepr::Scalar(scalar) => { + registers_for_primitive(scalar.primitive()); + } + BackendRepr::ScalarPair(scalar1, scalar2) => { + registers_for_primitive(scalar1.primitive()); + registers_for_primitive(scalar2.primitive()); + } + BackendRepr::SimdVector { .. } => { + // Because no instance of VaArgSafe uses a non-scalar `BackendRepr`. + unreachable!( + "No x86-64 SysV va_arg implementation for {:?}", + layout.layout.backend_repr() + ) + } + BackendRepr::Memory { .. } => { + let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout); + return bx.load(layout.llvm_type(bx), mem_addr, layout.align.abi); + } + }; + + // AMD64-ABI 3.5.7p5: Step 3. Verify whether arguments fit into + // registers. In the case: l->gp_offset > 48 - num_gp * 8 or + // l->fp_offset > 176 - num_fp * 16 go to step 7. + + let unsigned_int_offset = 4; + let ptr_offset = 8; + let gp_offset_ptr = va_list_addr; + let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset)); + + let gp_offset_v = bx.load(bx.type_i32(), gp_offset_ptr, Align::from_bytes(8).unwrap()); + let fp_offset_v = bx.load(bx.type_i32(), fp_offset_ptr, Align::from_bytes(4).unwrap()); + + let mut use_regs = bx.const_bool(false); + + if num_gp_registers > 0 { + let max_offset_val = 48u32 - num_gp_registers * 8; + let fits_in_gp = bx.icmp(IntPredicate::IntULE, gp_offset_v, bx.const_u32(max_offset_val)); + use_regs = fits_in_gp; + } + + if num_fp_registers > 0 { + let max_offset_val = 176u32 - num_fp_registers * 16; + let fits_in_fp = bx.icmp(IntPredicate::IntULE, fp_offset_v, bx.const_u32(max_offset_val)); + use_regs = if num_gp_registers > 0 { bx.and(use_regs, fits_in_fp) } else { fits_in_fp }; + } + + let in_reg = bx.append_sibling_block("va_arg.in_reg"); + let in_mem = bx.append_sibling_block("va_arg.in_mem"); + let end = bx.append_sibling_block("va_arg.end"); + + bx.cond_br(use_regs, in_reg, in_mem); + + // Emit code to load the value if it was passed in a register. + bx.switch_to_block(in_reg); + + // AMD64-ABI 3.5.7p5: Step 4. Fetch type from l->reg_save_area with + // an offset of l->gp_offset and/or l->fp_offset. This may require + // copying to a temporary location in case the parameter is passed + // in different register classes or requires an alignment greater + // than 8 for general purpose registers and 16 for XMM registers. + // + // FIXME(llvm): This really results in shameful code when we end up needing to + // collect arguments from different places; often what should result in a + // simple assembling of a structure from scattered addresses has many more + // loads than necessary. Can we clean this up? + let reg_save_area_ptr = + bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * unsigned_int_offset + ptr_offset)); + let reg_save_area_v = bx.load(bx.type_ptr(), reg_save_area_ptr, dl.pointer_align.abi); + + let reg_addr = match layout.layout.backend_repr() { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Int(_, _) | Primitive::Pointer(_) => { + let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v); + + // Copy into a temporary if the type is more aligned than the register save area. + let gp_align = Align::from_bytes(8).unwrap(); + copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align) + } + Primitive::Float(_) => bx.inbounds_ptradd(reg_save_area_v, fp_offset_v), + }, + BackendRepr::ScalarPair(scalar1, scalar2) => { + let ty_lo = bx.cx().scalar_pair_element_backend_type(layout, 0, false); + let ty_hi = bx.cx().scalar_pair_element_backend_type(layout, 1, false); + + let align_lo = layout.field(bx.cx, 0).layout.align().abi; + let align_hi = layout.field(bx.cx, 1).layout.align().abi; + + match (scalar1.primitive(), scalar2.primitive()) { + (Primitive::Float(_), Primitive::Float(_)) => { + // SSE registers are spaced 16 bytes apart in the register save + // area, we need to collect the two eightbytes together. + // The ABI isn't explicit about this, but it seems reasonable + // to assume that the slots are 16-byte aligned, since the stack is + // naturally 16-byte aligned and the prologue is expected to store + // all the SSE registers to the RSA. + let reg_lo_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v); + let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16)); + + let align = layout.layout.align().abi; + let tmp = bx.alloca(layout.layout.size(), align); + + let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); + let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); + + let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); + let field0 = tmp; + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + + bx.store(reg_lo, field0, align); + bx.store(reg_hi, field1, align); + + tmp + } + (Primitive::Float(_), _) | (_, Primitive::Float(_)) => { + let gp_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v); + let fp_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v); + + let (reg_lo_addr, reg_hi_addr) = match scalar1.primitive() { + Primitive::Float(_) => (fp_addr, gp_addr), + Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr), + }; + + let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); + + let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); + let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); + + let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); + let field0 = tmp; + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + + bx.store(reg_lo, field0, align_lo); + bx.store(reg_hi, field1, align_hi); + + tmp + } + (_, _) => { + // Two integer/pointer values are just contiguous in memory. + let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v); + + // Copy into a temporary if the type is more aligned than the register save area. + let gp_align = Align::from_bytes(8).unwrap(); + copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align) + } + } + } + // The Previous match on `BackendRepr` means control flow already escaped. + BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => unreachable!(), + }; + + // AMD64-ABI 3.5.7p5: Step 5. Set: + // l->gp_offset = l->gp_offset + num_gp * 8 + if num_gp_registers > 0 { + let offset = bx.const_u32(num_gp_registers * 8); + let sum = bx.add(gp_offset_v, offset); + // An alignment of 8 because `__va_list_tag` is 8-aligned and this is its first field. + bx.store(sum, gp_offset_ptr, Align::from_bytes(8).unwrap()); + } + + // l->fp_offset = l->fp_offset + num_fp * 16. + if num_fp_registers > 0 { + let offset = bx.const_u32(num_fp_registers * 16); + let sum = bx.add(fp_offset_v, offset); + bx.store(sum, fp_offset_ptr, Align::from_bytes(4).unwrap()); + } + + bx.br(end); + + bx.switch_to_block(in_mem); + let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout); + bx.br(end); + + bx.switch_to_block(end); + + let val_type = layout.llvm_type(bx); + let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]); + + bx.load(val_type, val_addr, layout.align.abi) +} + +/// Copy into a temporary if the type is more aligned than the register save area. +fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + reg_addr: &'ll Value, + layout: TyAndLayout<'tcx, Ty<'tcx>>, + src_align: Align, +) -> &'ll Value { + if layout.layout.align.abi > src_align { + let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); + bx.memcpy( + tmp, + layout.layout.align.abi, + reg_addr, + src_align, + bx.const_u32(layout.layout.size().bytes() as u32), + MemFlags::empty(), + ); + tmp + } else { + reg_addr + } +} + +fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + va_list_addr: &'ll Value, + layout: TyAndLayout<'tcx, Ty<'tcx>>, +) -> &'ll Value { + let dl = bx.cx.data_layout(); + + let overflow_arg_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(8)); + + let overflow_arg_area_v = bx.load(bx.type_ptr(), overflow_arg_area_ptr, dl.pointer_align.abi); + // AMD64-ABI 3.5.7p5: Step 7. Align l->overflow_arg_area upwards to a 16 + // byte boundary if alignment needed by type exceeds 8 byte boundary. + // It isn't stated explicitly in the standard, but in practice we use + // alignment greater than 16 where necessary. + if layout.layout.align.abi.bytes() > 8 { + unreachable!("all instances of VaArgSafe have an alignment <= 8"); + } + + // AMD64-ABI 3.5.7p5: Step 8. Fetch type from l->overflow_arg_area. + let mem_addr = overflow_arg_area_v; + + // AMD64-ABI 3.5.7p5: Step 9. Set l->overflow_arg_area to: + // l->overflow_arg_area + sizeof(type). + // AMD64-ABI 3.5.7p5: Step 10. Align l->overflow_arg_area upwards to + // an 8 byte boundary. + let size_in_bytes = layout.layout.size().bytes(); + let offset = bx.const_i32(size_in_bytes.next_multiple_of(8) as i32); + let overflow_arg_area = bx.inbounds_ptradd(overflow_arg_area_v, offset); + bx.store(overflow_arg_area, overflow_arg_area_ptr, dl.pointer_align.abi); + + mem_addr +} + fn emit_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -309,8 +807,7 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( // (*va).va_ndx let va_reg_offset = 4; let va_ndx_offset = va_reg_offset + 4; - let offset_ptr = - bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_ndx_offset)]); + let offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_ndx_offset)); let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align.abi); let offset = round_up_to_alignment(bx, offset, layout.align.abi); @@ -331,11 +828,10 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( bx.store(offset_next, offset_ptr, bx.tcx().data_layout.pointer_align.abi); // (*va).va_reg - let regsave_area_ptr = - bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_reg_offset)]); + let regsave_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_reg_offset)); let regsave_area = bx.load(bx.type_ptr(), regsave_area_ptr, bx.tcx().data_layout.pointer_align.abi); - let regsave_value_ptr = bx.inbounds_gep(bx.type_i8(), regsave_area, &[offset]); + let regsave_value_ptr = bx.inbounds_ptradd(regsave_area, offset); bx.br(end); bx.switch_to_block(from_stack); @@ -356,9 +852,9 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( bx.store(offset_next_corrected, offset_ptr, bx.tcx().data_layout.pointer_align.abi); // let stack_value_ptr = unsafe { (*va).va_stk.byte_add(offset_corrected) }; - let stack_area_ptr = bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(0)]); + let stack_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(0)); let stack_area = bx.load(bx.type_ptr(), stack_area_ptr, bx.tcx().data_layout.pointer_align.abi); - let stack_value_ptr = bx.inbounds_gep(bx.type_i8(), stack_area, &[offset_corrected]); + let stack_value_ptr = bx.inbounds_ptradd(stack_area, offset_corrected); bx.br(end); bx.switch_to_block(end); @@ -386,30 +882,62 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( // Determine the va_arg implementation to use. The LLVM va_arg instruction // is lacking in some instances, so we should only use it as a fallback. let target = &bx.cx.tcx.sess.target; - let arch = &bx.cx.tcx.sess.target.arch; - match &**arch { - // Windows x86 - "x86" if target.is_like_windows => { - emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), false) - } - // Generic x86 - "x86" => emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), true), - // Windows AArch64 - "aarch64" | "arm64ec" if target.is_like_windows => { - emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), false) - } - // macOS / iOS AArch64 - "aarch64" if target.is_like_darwin => { - emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true) + + match &*target.arch { + "x86" => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes4, + if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes }, + ForceRightAdjust::No, + ), + "aarch64" | "arm64ec" if target.is_like_windows || target.is_like_darwin => { + emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes8, + if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes }, + ForceRightAdjust::No, + ) } "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty), "s390x" => emit_s390x_va_arg(bx, addr, target_ty), + "powerpc" => emit_powerpc_va_arg(bx, addr, target_ty), + "powerpc64" | "powerpc64le" => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes8, + AllowHigherAlign::Yes, + match &*target.arch { + "powerpc64" => ForceRightAdjust::Yes, + _ => ForceRightAdjust::No, + }, + ), // Windows x86_64 "x86_64" if target.is_like_windows => { let target_ty_size = bx.cx.size_of(target_ty).bytes(); - let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two(); - emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false) + emit_ptr_va_arg( + bx, + addr, + target_ty, + if target_ty_size > 8 || !target_ty_size.is_power_of_two() { + PassMode::Indirect + } else { + PassMode::Direct + }, + SlotSize::Bytes8, + AllowHigherAlign::No, + ForceRightAdjust::No, + ) } + // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64. + "x86_64" => emit_x86_64_sysv64_va_arg(bx, addr, target_ty), "xtensa" => emit_xtensa_va_arg(bx, addr, target_ty), // For all other architecture/OS combinations fall back to using // the LLVM va_arg instruction. diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index d4c8ab80a331..337c69441779 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -19,6 +19,7 @@ regex = "1.4" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 2621935eecf9..acb4cbaa13fc 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -1,5 +1,7 @@ codegen_ssa_L4Bender_exporting_symbols_unimplemented = exporting symbols not implemented yet for L4Bender +codegen_ssa_aarch64_softfloat_neon = enabling the `neon` target feature on the current target is unsound due to ABI issues + codegen_ssa_add_native_library = failed to add native library {$library_path}: {$error} codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to AIX which is not guaranteed to work diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c5792da26781..58fa3c392cae 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -6,7 +6,7 @@ use std::fs::{File, OpenOptions, read}; use std::io::{BufWriter, Write}; use std::ops::{ControlFlow, Deref}; use std::path::{Path, PathBuf}; -use std::process::{ExitStatus, Output, Stdio}; +use std::process::{Output, Stdio}; use std::{env, fmt, fs, io, mem, str}; use cc::windows_registry; @@ -68,6 +68,23 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { } } +fn check_link_info_print_request(sess: &Session, crate_types: &[CrateType]) { + let print_native_static_libs = + sess.opts.prints.iter().any(|p| p.kind == PrintKind::NativeStaticLibs); + let has_staticlib = crate_types.iter().any(|ct| *ct == CrateType::Staticlib); + if print_native_static_libs { + if !has_staticlib { + sess.dcx() + .warn(format!("cannot output linkage information without staticlib crate-type")); + sess.dcx() + .note(format!("consider `--crate-type staticlib` to print linkage information")); + } else if !sess.opts.output_types.should_link() { + sess.dcx() + .warn(format!("cannot output linkage information when --emit link is not passed")); + } + } +} + /// Performs the linkage portion of the compilation phase. This will generate all /// of the requested outputs for this compilation session. pub fn link_binary( @@ -180,6 +197,8 @@ pub fn link_binary( } } + check_link_info_print_request(sess, &codegen_results.crate_info.crate_types); + // Remove the temporary object file and metadata if we aren't saving temps. sess.time("link_binary_remove_temps", || { // If the user requests that temporaries are saved, don't delete any. @@ -717,13 +736,10 @@ fn link_natively( // Invoke the system linker info!("{cmd:?}"); - let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); let unknown_arg_regex = Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap(); let mut prog; - let mut i = 0; loop { - i += 1; prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, flavor, tmpdir)); let Ok(ref output) = prog else { break; @@ -839,54 +855,7 @@ fn link_natively( continue; } - // Here's a terribly awful hack that really shouldn't be present in any - // compiler. Here an environment variable is supported to automatically - // retry the linker invocation if the linker looks like it segfaulted. - // - // Gee that seems odd, normally segfaults are things we want to know - // about! Unfortunately though in rust-lang/rust#38878 we're - // experiencing the linker segfaulting on Travis quite a bit which is - // causing quite a bit of pain to land PRs when they spuriously fail - // due to a segfault. - // - // The issue #38878 has some more debugging information on it as well, - // but this unfortunately looks like it's just a race condition in - // macOS's linker with some thread pool working in the background. It - // seems that no one currently knows a fix for this so in the meantime - // we're left with this... - if !retry_on_segfault || i > 3 { - break; - } - let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; - let msg_bus = "clang: error: unable to execute command: Bus error: 10"; - if out.contains(msg_segv) || out.contains(msg_bus) { - warn!( - ?cmd, %out, - "looks like the linker segfaulted when we tried to call it, \ - automatically retrying again", - ); - continue; - } - - if is_illegal_instruction(&output.status) { - warn!( - ?cmd, %out, status = %output.status, - "looks like the linker hit an illegal instruction when we \ - tried to call it, automatically retrying again.", - ); - continue; - } - - #[cfg(unix)] - fn is_illegal_instruction(status: &ExitStatus) -> bool { - use std::os::unix::prelude::*; - status.signal() == Some(libc::SIGILL) - } - - #[cfg(not(unix))] - fn is_illegal_instruction(_status: &ExitStatus) -> bool { - false - } + break; } match prog { diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 9fd984b6419e..ce6fe8a191b3 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -56,12 +56,7 @@ impl LtoModuleCodegen { } /// Optimize this module within the given codegen context. - /// - /// This function is unsafe as it'll return a `ModuleCodegen` still - /// points to LLVM data structures owned by this `LtoModuleCodegen`. - /// It's intended that the module returned is immediately code generated and - /// dropped, and then this LTO module is dropped. - pub unsafe fn optimize( + pub fn optimize( self, cgcx: &CodegenContext, ) -> Result, FatalError> { @@ -70,7 +65,7 @@ impl LtoModuleCodegen { B::optimize_fat(cgcx, &mut module)?; Ok(module) } - LtoModuleCodegen::Thin(thin) => unsafe { B::optimize_thin(cgcx, thin) }, + LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin), } } @@ -85,7 +80,7 @@ impl LtoModuleCodegen { } /// Run autodiff on Fat LTO module - pub unsafe fn autodiff( + pub fn autodiff( self, cgcx: &CodegenContext, diff_fncs: Vec, diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 5f0a0cf922af..e26f999773dc 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -128,7 +128,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap( if !tcx.sess.opts.share_generics() { if tcx.codegen_fn_attrs(mono_item.def_id()).inline - == rustc_attr_parsing::InlineAttr::Never + == rustc_attr_data_structures::InlineAttr::Never { // this is OK, we explicitly allow sharing inline(never) across crates even // without share-generics. diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 0fd4ed8475b4..a41ca8ce28bc 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -383,7 +383,7 @@ pub struct CodegenContext { pub coordinator_send: Sender>, /// `true` if the codegen should be run in parallel. /// - /// Depends on [`CodegenBackend::supports_parallel()`] and `-Zno_parallel_backend`. + /// Depends on [`ExtraBackendMethods::supports_parallel()`] and `-Zno_parallel_backend`. pub parallel: bool, } @@ -416,8 +416,7 @@ fn generate_lto_work( B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); if cgcx.lto == Lto::Fat && !autodiff.is_empty() { let config = cgcx.config(ModuleKind::Regular); - module = - unsafe { module.autodiff(cgcx, autodiff, config).unwrap_or_else(|e| e.raise()) }; + module = module.autodiff(cgcx, autodiff, config).unwrap_or_else(|e| e.raise()); } // We are adding a single work item, so the cost doesn't matter. vec![(WorkItem::LTO(module), 0)] @@ -887,9 +886,7 @@ fn execute_optimize_work_item( let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - unsafe { - B::optimize(cgcx, dcx, &mut module, module_config)?; - } + B::optimize(cgcx, dcx, &mut module, module_config)?; // After we've done the initial round of optimizations we need to // decide whether to synchronously codegen this module or ship it @@ -1020,7 +1017,7 @@ fn execute_lto_work_item( module: lto::LtoModuleCodegen, module_config: &ModuleConfig, ) -> Result, FatalError> { - let module = unsafe { module.optimize(cgcx)? }; + let module = module.optimize(cgcx)?; finish_intra_module_work(cgcx, module, module_config) } @@ -1036,7 +1033,7 @@ fn finish_intra_module_work( || module.kind == ModuleKind::Metadata || module.kind == ModuleKind::Allocator { - let module = unsafe { B::codegen(cgcx, dcx, module, module_config)? }; + let module = B::codegen(cgcx, dcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } else { Ok(WorkItemResult::NeedsLink(module)) @@ -1725,9 +1722,8 @@ fn start_executing_work( let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?; - let module = unsafe { - B::codegen(&cgcx, dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())? - }; + let module = + B::codegen(&cgcx, dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?; compiled_modules.push(module); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 775ab9071e74..f7863fe4ae26 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -7,7 +7,7 @@ use itertools::Itertools; use rustc_abi::FIRST_VARIANT; use rustc_ast as ast; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; -use rustc_attr_parsing::OptimizeAttr; +use rustc_attr_data_structures::OptimizeAttr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; @@ -492,6 +492,7 @@ where /// users main function. pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, + cgu: &CodegenUnit<'tcx>, ) -> Option { let (main_def_id, entry_type) = cx.tcx().entry_fn(())?; let main_is_local = main_def_id.is_local(); @@ -500,10 +501,10 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if main_is_local { // We want to create the wrapper in the same codegen unit as Rust's main // function. - if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) { + if !cgu.contains_item(&MonoItem::Fn(instance)) { return None; } - } else if !cx.codegen_unit().is_primary() { + } else if !cgu.is_primary() { // We want to create the wrapper only when the codegen unit is the primary one return None; } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 835ffb7d4fc6..4bcafa3be366 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use rustc_abi::ExternAbi; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; -use rustc_attr_parsing::ReprAttr::ReprAlign; -use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_data_structures::ReprAttr::ReprAlign; +use rustc_attr_data_structures::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; @@ -189,7 +189,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { ) .emit(); } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER; } Some(_) => { tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() }); @@ -220,7 +220,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { || tcx.sess.target.is_like_windows || tcx.sess.target.is_like_wasm); codegen_fn_attrs.flags |= if is_like_elf { - CodegenFnAttrFlags::USED + CodegenFnAttrFlags::USED_COMPILER } else { CodegenFnAttrFlags::USED_LINKER }; @@ -299,6 +299,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } from_target_feature_attr( tcx, + did, attr, rust_target_features, &mut codegen_fn_attrs.target_features, diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 6d0c9d8d0664..ef0d565333e4 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -59,15 +59,6 @@ pub enum AtomicRmwBinOp { AtomicUMin, } -#[derive(Copy, Clone, Debug)] -pub enum AtomicOrdering { - Relaxed, - Acquire, - Release, - AcquireRelease, - SequentiallyConsistent, -} - #[derive(Copy, Clone, Debug)] pub enum SynchronizationScope { SingleThread, diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index d49aac75d058..572d7b1e06a7 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1316,3 +1316,7 @@ pub(crate) struct XcrunSdkPathWarning { pub sdk_name: &'static str, pub stderr: String, } + +#[derive(LintDiagnostic)] +#[diag(codegen_ssa_aarch64_softfloat_neon)] +pub(crate) struct Aarch64SoftfloatNeon; diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 399c592432ac..3a11ce6befb3 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -77,7 +77,7 @@ fn dyn_trait_in_self<'tcx>( ty: Ty<'tcx>, ) -> Option> { for arg in ty.peel_refs().walk() { - if let GenericArgKind::Type(ty) = arg.unpack() + if let GenericArgKind::Type(ty) = arg.kind() && let ty::Dynamic(data, _, _) = ty.kind() { // FIXME(arbitrary_self_types): This is likely broken for receivers which diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 950f19a6f0f4..1baab62ae43a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,6 +1,6 @@ use std::cmp; -use rustc_abi::{BackendRepr, ExternAbi, HasDataLayout, Reg, WrappingRange}; +use rustc_abi::{BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange}; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::packed::Pu128; @@ -11,8 +11,8 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; +use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::{Span, sym}; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use tracing::{debug, info}; @@ -158,7 +158,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { llargs: &[Bx::Value], destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, mut unwind: mir::UnwindAction, - copied_constant_arguments: &[PlaceRef<'tcx, ::Value>], + lifetime_ends_after_call: &[(Bx::Value, Size)], instance: Option>, mergeable_succ: bool, ) -> MergingSucc { @@ -245,8 +245,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if let Some((ret_dest, target)) = destination { bx.switch_to_block(fx.llbb(target)); fx.set_debug_loc(bx, self.terminator.source_info); - for tmp in copied_constant_arguments { - bx.lifetime_end(tmp.val.llval, tmp.layout.size); + for &(tmp, size) in lifetime_ends_after_call { + bx.lifetime_end(tmp, size); } fx.store_return(bx, ret_dest, &fn_abi.ret, invokeret); } @@ -259,8 +259,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { } if let Some((ret_dest, target)) = destination { - for tmp in copied_constant_arguments { - bx.lifetime_end(tmp.val.llval, tmp.layout.size); + for &(tmp, size) in lifetime_ends_after_call { + bx.lifetime_end(tmp, size); } fx.store_return(bx, ret_dest, &fn_abi.ret, llret); self.funclet_br(fx, bx, target, mergeable_succ) @@ -827,7 +827,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { helper: &TerminatorCodegenHelper<'tcx>, bx: &mut Bx, intrinsic: ty::IntrinsicDef, - instance: Option>, + instance: Instance<'tcx>, source_info: mir::SourceInfo, target: Option, unwind: mir::UnwindAction, @@ -836,58 +836,56 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Emit a panic or a no-op for `assert_*` intrinsics. // These are intrinsics that compile to panics so that we can get a message // which mentions the offending type, even from a const context. - if let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) { - let ty = instance.unwrap().args.type_at(0); + let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) else { + return None; + }; - let do_panic = !bx - .tcx() - .check_validity_requirement((requirement, bx.typing_env().as_query_input(ty))) - .expect("expect to have layout during codegen"); + let ty = instance.args.type_at(0); - let layout = bx.layout_of(ty); + let is_valid = bx + .tcx() + .check_validity_requirement((requirement, bx.typing_env().as_query_input(ty))) + .expect("expect to have layout during codegen"); - Some(if do_panic { - let msg_str = with_no_visible_paths!({ - with_no_trimmed_paths!({ - if layout.is_uninhabited() { - // Use this error even for the other intrinsics as it is more precise. - format!("attempted to instantiate uninhabited type `{ty}`") - } else if requirement == ValidityRequirement::Zero { - format!("attempted to zero-initialize type `{ty}`, which is invalid") - } else { - format!( - "attempted to leave type `{ty}` uninitialized, which is invalid" - ) - } - }) - }); - let msg = bx.const_str(&msg_str); - - // Obtain the panic entry point. - let (fn_abi, llfn, instance) = - common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); - - // Codegen the actual panic invoke/call. - helper.do_call( - self, - bx, - fn_abi, - llfn, - &[msg.0, msg.1], - target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), - unwind, - &[], - Some(instance), - mergeable_succ, - ) - } else { - // a NOP - let target = target.unwrap(); - helper.funclet_br(self, bx, target, mergeable_succ) - }) - } else { - None + if is_valid { + // a NOP + let target = target.unwrap(); + return Some(helper.funclet_br(self, bx, target, mergeable_succ)); } + + let layout = bx.layout_of(ty); + + let msg_str = with_no_visible_paths!({ + with_no_trimmed_paths!({ + if layout.is_uninhabited() { + // Use this error even for the other intrinsics as it is more precise. + format!("attempted to instantiate uninhabited type `{ty}`") + } else if requirement == ValidityRequirement::Zero { + format!("attempted to zero-initialize type `{ty}`, which is invalid") + } else { + format!("attempted to leave type `{ty}` uninitialized, which is invalid") + } + }) + }); + let msg = bx.const_str(&msg_str); + + // Obtain the panic entry point. + let (fn_abi, llfn, instance) = + common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); + + // Codegen the actual panic invoke/call. + Some(helper.do_call( + self, + bx, + fn_abi, + llfn, + &[msg.0, msg.1], + target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), + unwind, + &[], + Some(instance), + mergeable_succ, + )) } fn codegen_call_terminator( @@ -903,42 +901,127 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn_span: Span, mergeable_succ: bool, ) -> MergingSucc { - let source_info = terminator.source_info; - let span = source_info.span; + let source_info = mir::SourceInfo { span: fn_span, ..terminator.source_info }; // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.codegen_operand(bx, func); let (instance, mut llfn) = match *callee.layout.ty.kind() { - ty::FnDef(def_id, args) => ( - Some(ty::Instance::expect_resolve( + ty::FnDef(def_id, generic_args) => { + let instance = ty::Instance::expect_resolve( bx.tcx(), bx.typing_env(), def_id, - args, + generic_args, fn_span, - )), - None, - ), + ); + + let instance = match instance.def { + // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, + // it is `func returning noop future` + ty::InstanceKind::DropGlue(_, None) => { + // Empty drop glue; a no-op. + let target = target.unwrap(); + return helper.funclet_br(self, bx, target, mergeable_succ); + } + ty::InstanceKind::Intrinsic(def_id) => { + let intrinsic = bx.tcx().intrinsic(def_id).unwrap(); + if let Some(merging_succ) = self.codegen_panic_intrinsic( + &helper, + bx, + intrinsic, + instance, + source_info, + target, + unwind, + mergeable_succ, + ) { + return merging_succ; + } + + let result_layout = + self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); + + let (result, store_in_local) = if result_layout.is_zst() { + ( + PlaceRef::new_sized(bx.const_undef(bx.type_ptr()), result_layout), + None, + ) + } else if let Some(local) = destination.as_local() { + match self.locals[local] { + LocalRef::Place(dest) => (dest, None), + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), + LocalRef::PendingOperand => { + // Currently, intrinsics always need a location to store + // the result, so we create a temporary `alloca` for the + // result. + let tmp = PlaceRef::alloca(bx, result_layout); + tmp.storage_live(bx); + (tmp, Some(local)) + } + LocalRef::Operand(_) => { + bug!("place local already assigned to"); + } + } + } else { + (self.codegen_place(bx, destination.as_ref()), None) + }; + + if result.val.align < result.layout.align.abi { + // Currently, MIR code generation does not create calls + // that store directly to fields of packed structs (in + // fact, the calls it creates write only to temps). + // + // If someone changes that, please update this code path + // to create a temporary. + span_bug!(self.mir.span, "can't directly store to unaligned value"); + } + + let args: Vec<_> = + args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); + + match self.codegen_intrinsic_call(bx, instance, &args, result, source_info) + { + Ok(()) => { + if let Some(local) = store_in_local { + let op = bx.load_operand(result); + result.storage_dead(bx); + self.overwrite_local(local, LocalRef::Operand(op)); + self.debug_introduce_local(bx, local); + } + + return if let Some(target) = target { + helper.funclet_br(self, bx, target, mergeable_succ) + } else { + bx.unreachable(); + MergingSucc::False + }; + } + Err(instance) => { + if intrinsic.must_be_overridden { + span_bug!( + fn_span, + "intrinsic {} must be overridden by codegen backend, but isn't", + intrinsic.name, + ); + } + instance + } + } + } + _ => instance, + }; + + (Some(instance), None) + } ty::FnPtr(..) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; - let def = instance.map(|i| i.def); - - // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, - // it is `func returning noop future` - if let Some(ty::InstanceKind::DropGlue(_, None)) = def { - // Empty drop glue; a no-op. - let target = target.unwrap(); - return helper.funclet_br(self, bx, target, mergeable_succ); - } - // FIXME(eddyb) avoid computing this if possible, when `instance` is // available - right now `sig` is only needed for getting the `abi` // and figuring out how many extra args were passed to a C-variadic `fn`. let sig = callee.layout.ty.fn_sig(bx.tcx()); - let abi = sig.abi(); let extra_args = &args[sig.inputs().skip_binder().len()..]; let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| { @@ -954,93 +1037,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // The arguments we'll be passing. Plus one to account for outptr, if used. let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize; - let instance = match def { - Some(ty::InstanceKind::Intrinsic(def_id)) => { - let intrinsic = bx.tcx().intrinsic(def_id).unwrap(); - if let Some(merging_succ) = self.codegen_panic_intrinsic( - &helper, - bx, - intrinsic, - instance, - source_info, - target, - unwind, - mergeable_succ, - ) { - return merging_succ; - } - - let mut llargs = Vec::with_capacity(1); - let ret_dest = self.make_return_dest( - bx, - destination, - &fn_abi.ret, - &mut llargs, - Some(intrinsic), - ); - let dest = match ret_dest { - _ if fn_abi.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => bx.const_undef(bx.type_ptr()), - ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.val.llval, - ReturnDest::DirectOperand(_) => { - bug!("Cannot use direct operand with an intrinsic call") - } - }; - - let args: Vec<_> = - args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); - - if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) { - let location = self - .get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info }); - - assert_eq!(llargs, []); - if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { - location.val.store(bx, tmp); - } - self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate()); - return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ); - } - - let instance = *instance.as_ref().unwrap(); - match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) { - Ok(()) => { - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(bx, ret_dest, &fn_abi.ret, dst.val.llval); - } - - return if let Some(target) = target { - helper.funclet_br(self, bx, target, mergeable_succ) - } else { - bx.unreachable(); - MergingSucc::False - }; - } - Err(instance) => { - if intrinsic.must_be_overridden { - span_bug!( - span, - "intrinsic {} must be overridden by codegen backend, but isn't", - intrinsic.name, - ); - } - Some(instance) - } - } - } - _ => instance, - }; - let mut llargs = Vec::with_capacity(arg_count); // We still need to call `make_return_dest` even if there's no `target`, since // `fn_abi.ret` could be `PassMode::Indirect`, even if it is uninhabited, // and `make_return_dest` adds the return-place indirect pointer to `llargs`. - let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs, None); + let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs); let destination = target.map(|target| (return_dest, target)); // Split the rust-call tupled arguments off. - let (first_args, untuple) = if abi == ExternAbi::RustCall + let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall && let Some((tup, args)) = args.split_last() { (args, Some(tup)) @@ -1048,11 +1054,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (args, None) }; - let mut copied_constant_arguments = vec![]; + // When generating arguments we sometimes introduce temporary allocations with lifetime + // that extend for the duration of a call. Keep track of those allocations and their sizes + // to generate `lifetime_end` when the call returns. + let mut lifetime_ends_after_call: Vec<(Bx::Value, Size)> = Vec::new(); 'make_args: for (i, arg) in first_args.iter().enumerate() { let mut op = self.codegen_operand(bx, &arg.node); - if let (0, Some(ty::InstanceKind::Virtual(_, idx))) = (i, def) { + if let (0, Some(ty::InstanceKind::Virtual(_, idx))) = (i, instance.map(|i| i.def)) { match op.val { Pair(data_ptr, meta) => { // In the case of Rc, we need to explicitly pass a @@ -1106,7 +1115,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Make sure that we've actually unwrapped the rcvr down // to a pointer or ref to `dyn* Trait`. if !op.layout.ty.builtin_deref(true).unwrap().is_dyn_star() { - span_bug!(span, "can't codegen a virtual call on {:#?}", op); + span_bug!(fn_span, "can't codegen a virtual call on {:#?}", op); } let place = op.deref(bx.cx()); let data_place = place.project_field(bx, 0); @@ -1122,7 +1131,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { continue; } _ => { - span_bug!(span, "can't codegen a virtual call on {:#?}", op); + span_bug!(fn_span, "can't codegen a virtual call on {:#?}", op); } } } @@ -1136,12 +1145,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.lifetime_start(tmp.val.llval, tmp.layout.size); op.val.store(bx, tmp); op.val = Ref(tmp.val); - copied_constant_arguments.push(tmp); + lifetime_ends_after_call.push((tmp.val.llval, tmp.layout.size)); } _ => {} } - self.codegen_argument(bx, op, &mut llargs, &fn_abi.args[i]); + self.codegen_argument( + bx, + op, + &mut llargs, + &fn_abi.args[i], + &mut lifetime_ends_after_call, + ); } let num_untupled = untuple.map(|tup| { self.codegen_arguments_untupled( @@ -1149,6 +1164,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &tup.node, &mut llargs, &fn_abi.args[first_args.len()..], + &mut lifetime_ends_after_call, ) }); @@ -1165,22 +1181,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir_args + 1, "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR: {instance:?} {fn_span:?} {fn_abi:?}", ); - let location = - self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info }); + let location = self.get_caller_location(bx, source_info); debug!( "codegen_call_terminator({:?}): location={:?} (fn_span {:?})", terminator, location, fn_span ); let last_arg = fn_abi.args.last().unwrap(); - self.codegen_argument(bx, location, &mut llargs, last_arg); + self.codegen_argument( + bx, + location, + &mut llargs, + last_arg, + &mut lifetime_ends_after_call, + ); } let fn_ptr = match (instance, llfn) { (Some(instance), None) => bx.get_fn_addr(instance), (_, Some(llfn)) => llfn, - _ => span_bug!(span, "no instance or llfn for call"), + _ => span_bug!(fn_span, "no instance or llfn for call"), }; + self.set_debug_loc(bx, source_info); helper.do_call( self, bx, @@ -1189,7 +1211,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &llargs, destination, unwind, - &copied_constant_arguments, + &lifetime_ends_after_call, instance, mergeable_succ, ) @@ -1479,6 +1501,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { op: OperandRef<'tcx, Bx::Value>, llargs: &mut Vec, arg: &ArgAbi<'tcx, Ty<'tcx>>, + lifetime_ends_after_call: &mut Vec<(Bx::Value, Size)>, ) { match arg.mode { PassMode::Ignore => return, @@ -1517,7 +1540,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None => arg.layout.align.abi, }; let scratch = PlaceValue::alloca(bx, arg.layout.size, required_align); + bx.lifetime_start(scratch.llval, arg.layout.size); op.val.store(bx, scratch.with_type(arg.layout)); + lifetime_ends_after_call.push((scratch.llval, arg.layout.size)); (scratch.llval, scratch.align, true) } PassMode::Cast { .. } => { @@ -1538,7 +1563,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // alignment requirements may be higher than the type's alignment, so copy // to a higher-aligned alloca. let scratch = PlaceValue::alloca(bx, arg.layout.size, required_align); + bx.lifetime_start(scratch.llval, arg.layout.size); bx.typed_place_copy(scratch, op_place_val, op.layout); + lifetime_ends_after_call.push((scratch.llval, arg.layout.size)); (scratch.llval, scratch.align, true) } else { (op_place_val.llval, op_place_val.align, true) @@ -1620,6 +1647,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operand: &mir::Operand<'tcx>, llargs: &mut Vec, args: &[ArgAbi<'tcx, Ty<'tcx>>], + lifetime_ends_after_call: &mut Vec<(Bx::Value, Size)>, ) -> usize { let tuple = self.codegen_operand(bx, operand); @@ -1632,19 +1660,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { for i in 0..tuple.layout.fields.count() { let field_ptr = tuple_ptr.project_field(bx, i); let field = bx.load_operand(field_ptr); - self.codegen_argument(bx, field, llargs, &args[i]); + self.codegen_argument(bx, field, llargs, &args[i], lifetime_ends_after_call); } } else { // If the tuple is immediate, the elements are as well. for i in 0..tuple.layout.fields.count() { let op = tuple.extract_field(self, bx, i); - self.codegen_argument(bx, op, llargs, &args[i]); + self.codegen_argument(bx, op, llargs, &args[i], lifetime_ends_after_call); } } tuple.layout.fields.count() } - fn get_caller_location( + pub(super) fn get_caller_location( &mut self, bx: &mut Bx, source_info: mir::SourceInfo, @@ -1845,7 +1873,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { dest: mir::Place<'tcx>, fn_ret: &ArgAbi<'tcx, Ty<'tcx>>, llargs: &mut Vec, - intrinsic: Option, ) -> ReturnDest<'tcx, Bx::Value> { // If the return is ignored, we can just return a do-nothing `ReturnDest`. if fn_ret.is_ignore() { @@ -1865,13 +1892,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { tmp.storage_live(bx); llargs.push(tmp.val.llval); ReturnDest::IndirectOperand(tmp, index) - } else if intrinsic.is_some() { - // Currently, intrinsics always need a location to store - // the result, so we create a temporary `alloca` for the - // result. - let tmp = PlaceRef::alloca(bx, fn_ret.layout); - tmp.storage_live(bx); - ReturnDest::IndirectOperand(tmp, index) } else { ReturnDest::DirectOperand(index) }; @@ -1881,7 +1901,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } else { - self.codegen_place(bx, mir::PlaceRef { local: dest.local, projection: dest.projection }) + self.codegen_place(bx, dest.as_ref()) }; if fn_ret.is_indirect() { if dest.val.align < dest.layout.align.abi { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 5924c8991ad6..f731613d67e8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{Instance, Ty}; use rustc_middle::{bug, mir, ty}; use rustc_session::config::DebugInfo; -use rustc_span::{BytePos, Span, Symbol, hygiene, kw}; +use rustc_span::{BytePos, Span, Symbol, hygiene, sym}; use super::operand::{OperandRef, OperandValue}; use super::place::{PlaceRef, PlaceValue}; @@ -283,7 +283,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // (after #67586 gets fixed). None } else { - let name = kw::Empty; + let name = sym::empty; let decl = &self.mir.local_decls[local]; let dbg_var = if full_debug_info { self.adjusted_span_and_dbg_scope(decl.source_info).map( @@ -318,7 +318,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None } else { Some(match whole_local_var.or(fallback_var.clone()) { - Some(var) if var.name != kw::Empty => var.name.to_string(), + Some(var) if var.name != sym::empty => var.name.to_string(), _ => format!("{local:?}"), }) }; diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index b0fcfee2adf5..a79d67bb6cdc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,9 +1,9 @@ use rustc_abi::WrappingRange; +use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; -use rustc_span::{Span, sym}; -use rustc_target::callconv::{FnAbi, PassMode}; +use rustc_span::sym; use super::FunctionCx; use super::operand::OperandRef; @@ -52,13 +52,14 @@ fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// In the `Err` case, returns the instance that should be called instead. pub fn codegen_intrinsic_call( + &mut self, bx: &mut Bx, instance: ty::Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, Bx::Value>], - llresult: Bx::Value, - span: Span, + result: PlaceRef<'tcx, Bx::Value>, + source_info: SourceInfo, ) -> Result<(), ty::Instance<'tcx>> { + let span = source_info.span; let callee_ty = instance.ty(bx.tcx(), bx.typing_env()); let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else { @@ -97,7 +98,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); - let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); + + let ret_llval = |bx: &mut Bx, llval| { + if result.layout.ty.is_bool() { + OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) + .val + .store(bx, result); + } else if !result.layout.ty.is_unit() { + bx.store_to_place(llval, result.val); + } + Ok(()) + }; let llval = match name { sym::abort => { @@ -105,6 +116,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return Ok(()); } + sym::caller_location => { + let location = self.get_caller_location(bx, source_info); + location.val.store(bx, result); + return Ok(()); + } + sym::va_start => bx.va_start(args[0].immediate()), sym::va_end => bx.va_end(args[0].immediate()), sym::size_of_val => { @@ -328,9 +345,48 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // This requires that atomic intrinsics follow a specific naming pattern: // "atomic_[_]" name if let Some(atomic) = name_str.strip_prefix("atomic_") => { - use crate::common::AtomicOrdering::*; + use rustc_middle::ty::AtomicOrdering::*; + use crate::common::{AtomicRmwBinOp, SynchronizationScope}; + let invalid_monomorphization = |ty| { + bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + span, + name, + ty, + }); + }; + + let parse_const_generic_ordering = |ord: ty::Value<'tcx>| { + let discr = ord.valtree.unwrap_branch()[0].unwrap_leaf(); + discr.to_atomic_ordering() + }; + + // Some intrinsics have the ordering already converted to a const generic parameter, we handle those first. + match name { + sym::atomic_load => { + let ty = fn_args.type_at(0); + let ordering = fn_args.const_at(1).to_value(); + if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { + invalid_monomorphization(ty); + return Ok(()); + } + let layout = bx.layout_of(ty); + let source = args[0].immediate(); + let llval = bx.atomic_load( + bx.backend_type(layout), + source, + parse_const_generic_ordering(ordering), + layout.size, + ); + + return ret_llval(bx, llval); + } + + // The rest falls back to below. + _ => {} + } + let Some((instruction, ordering)) = atomic.split_once('_') else { bx.sess().dcx().emit_fatal(errors::MissingMemoryOrdering); }; @@ -339,19 +395,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { "relaxed" => Relaxed, "acquire" => Acquire, "release" => Release, - "acqrel" => AcquireRelease, - "seqcst" => SequentiallyConsistent, + "acqrel" => AcqRel, + "seqcst" => SeqCst, _ => bx.sess().dcx().emit_fatal(errors::UnknownAtomicOrdering), }; - let invalid_monomorphization = |ty| { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - }; - match instruction { "cxchg" | "cxchgweak" => { let Some((success, failure)) = ordering.split_once('_') else { @@ -384,24 +432,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return Ok(()); } - "load" => { - let ty = fn_args.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { - let layout = bx.layout_of(ty); - let size = layout.size; - let source = args[0].immediate(); - bx.atomic_load( - bx.backend_type(layout), - source, - parse_ordering(bx, ordering), - size, - ) - } else { - invalid_monomorphization(ty); - return Ok(()); - } - } - "store" => { let ty = fn_args.type_at(0); if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { @@ -528,20 +558,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => { // Need to use backend-specific things in the implementation. - return bx.codegen_intrinsic_call(instance, fn_abi, args, llresult, span); + return bx.codegen_intrinsic_call(instance, args, result, span); } }; - if !fn_abi.ret.is_ignore() { - if let PassMode::Cast { .. } = &fn_abi.ret.mode { - bx.store_to_place(llval, result.val); - } else { - OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) - .val - .store(bx, result); - } - } - Ok(()) + ret_llval(bx, llval) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index d2a687359e0b..46fb9a895133 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,5 +1,5 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; -use rustc_attr_parsing::InstructionSetAttr; +use rustc_attr_data_structures::InstructionSetAttr; use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, MonoItemData, Visibility}; use rustc_middle::mir::{InlineAsmOperand, START_BLOCK}; diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index c2067e52afec..7b4268abe4b1 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -11,11 +11,13 @@ pub trait MonoItemExt<'a, 'tcx> { fn define>( &self, cx: &'a mut Bx::CodegenCx, + cgu_name: &str, item_data: MonoItemData, ); fn predefine>( &self, - cx: &'a Bx::CodegenCx, + cx: &'a mut Bx::CodegenCx, + cgu_name: &str, linkage: Linkage, visibility: Visibility, ); @@ -26,14 +28,10 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { fn define>( &self, cx: &'a mut Bx::CodegenCx, + cgu_name: &str, item_data: MonoItemData, ) { - debug!( - "BEGIN IMPLEMENTING '{} ({})' in cgu {}", - self, - self.to_raw_string(), - cx.codegen_unit().name() - ); + debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}", self, self.to_raw_string(), cgu_name); match *self { MonoItem::Static(def_id) => { @@ -56,26 +54,17 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { } } - debug!( - "END IMPLEMENTING '{} ({})' in cgu {}", - self, - self.to_raw_string(), - cx.codegen_unit().name() - ); + debug!("END IMPLEMENTING '{} ({})' in cgu {}", self, self.to_raw_string(), cgu_name); } fn predefine>( &self, - cx: &'a Bx::CodegenCx, + cx: &'a mut Bx::CodegenCx, + cgu_name: &str, linkage: Linkage, visibility: Visibility, ) { - debug!( - "BEGIN PREDEFINING '{} ({})' in cgu {}", - self, - self.to_raw_string(), - cx.codegen_unit().name() - ); + debug!("BEGIN PREDEFINING '{} ({})' in cgu {}", self, self.to_raw_string(), cgu_name); let symbol_name = self.symbol_name(cx.tcx()).name; @@ -97,12 +86,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { MonoItem::GlobalAsm(..) => {} } - debug!( - "END PREDEFINING '{} ({})' in cgu {}", - self, - self.to_raw_string(), - cx.codegen_unit().name() - ); + debug!("END PREDEFINING '{} ({})' in cgu {}", self, self.to_raw_string(), cgu_name); } fn to_raw_string(&self) -> String { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 8058cd1b1783..6bb3150c1c57 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::InstructionSetAttr; +use rustc_attr_data_structures::InstructionSetAttr; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; @@ -8,6 +8,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; use rustc_target::target_features::{self, Stability}; @@ -18,6 +19,7 @@ use crate::errors; /// Enabled target features are added to `target_features`. pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, + did: LocalDefId, attr: &hir::Attribute, rust_target_features: &UnordMap, target_features: &mut Vec, @@ -92,11 +94,22 @@ pub(crate) fn from_target_feature_attr( // generating code so "it's fine". if !tcx.sess.opts.actually_rustdoc { if abi_feature_constraints.incompatible.contains(&name.as_str()) { - tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { - span: item.span(), - feature: name.as_str(), - reason: "this feature is incompatible with the target ABI", - }); + // For "neon" specifically, we emit an FCW instead of a hard error. + // See . + if tcx.sess.target.arch == "aarch64" && name.as_str() == "neon" { + tcx.emit_node_span_lint( + AARCH64_SOFTFLOAT_NEON, + tcx.local_def_id_to_hir_id(did), + item.span(), + errors::Aarch64SoftfloatNeon, + ); + } else { + tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + span: item.span(), + feature: name.as_str(), + reason: "this feature is incompatible with the target ABI", + }); + } } } target_features.push(TargetFeature { name, implied: name != feature_sym }) diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index e2f1458d0623..95bf3b16685b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -21,12 +21,12 @@ use crate::back::write::TargetMachineFactoryFn; use crate::{CodegenResults, ModuleCodegen, TargetConfig}; pub trait BackendTypes { - type Value: CodegenObject; + type Value: CodegenObject + PartialEq; type Metadata: CodegenObject; type Function: CodegenObject; type BasicBlock: Copy; - type Type: CodegenObject; + type Type: CodegenObject + PartialEq; type Funclet; // FIXME(eddyb) find a common convention for all of the debuginfo-related @@ -97,13 +97,6 @@ pub trait CodegenBackend { fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs); } - - /// Returns `true` if this backend can be safely called from multiple threads. - /// - /// Defaults to `true`. - fn supports_parallel(&self) -> bool { - true - } } pub trait ExtraBackendMethods: @@ -144,4 +137,11 @@ pub trait ExtraBackendMethods: { std::thread::Builder::new().name(name).spawn(f) } + + /// Returns `true` if this backend can be safely called from multiple threads. + /// + /// Defaults to `true`. + fn supports_parallel(&self) -> bool { + true + } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index f66309cf340c..7f78bc756956 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -4,7 +4,7 @@ use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{Instance, Ty}; +use rustc_middle::ty::{AtomicOrdering, Instance, Ty}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::callconv::FnAbi; @@ -19,9 +19,7 @@ use super::misc::MiscCodegenMethods; use super::type_::{ArgAbiBuilderMethods, BaseTypeCodegenMethods, LayoutTypeCodegenMethods}; use super::{CodegenMethods, StaticBuilderMethods}; use crate::MemFlags; -use crate::common::{ - AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, -}; +use crate::common::{AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use crate::mir::operand::{OperandRef, OperandValue}; use crate::mir::place::{PlaceRef, PlaceValue}; @@ -514,11 +512,11 @@ pub trait BuilderMethods<'a, 'tcx>: fn extract_value(&mut self, agg_val: Self::Value, idx: u64) -> Self::Value; fn insert_value(&mut self, agg_val: Self::Value, elt: Self::Value, idx: u64) -> Self::Value; - fn set_personality_fn(&mut self, personality: Self::Value); + fn set_personality_fn(&mut self, personality: Self::Function); // These are used by everyone except msvc - fn cleanup_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value); - fn filter_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value); + fn cleanup_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value); + fn filter_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value); fn resume(&mut self, exn0: Self::Value, exn1: Self::Value); // These are used only by msvc diff --git a/compiler/rustc_codegen_ssa/src/traits/declare.rs b/compiler/rustc_codegen_ssa/src/traits/declare.rs index c1edeac31b0f..9f735546558b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/declare.rs +++ b/compiler/rustc_codegen_ssa/src/traits/declare.rs @@ -4,14 +4,14 @@ use rustc_middle::ty::Instance; pub trait PreDefineCodegenMethods<'tcx> { fn predefine_static( - &self, + &mut self, def_id: DefId, linkage: Linkage, visibility: Visibility, symbol_name: &str, ); fn predefine_fn( - &self, + &mut self, instance: Instance<'tcx>, linkage: Linkage, visibility: Visibility, diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 88cf8dbf0c5c..a07c569a0323 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -1,9 +1,9 @@ -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; use rustc_span::Span; -use rustc_target::callconv::FnAbi; use super::BackendTypes; use crate::mir::operand::OperandRef; +use crate::mir::place::PlaceRef; pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Remember to add all intrinsics here, in `compiler/rustc_hir_analysis/src/check/mod.rs`, @@ -14,9 +14,8 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { fn codegen_intrinsic_call( &mut self, instance: ty::Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, Self::Value>], - llresult: Self::Value, + result: PlaceRef<'tcx, Self::Value>, span: Span, ) -> Result<(), ty::Instance<'tcx>>; diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 4004947b4648..710fab279016 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -1,7 +1,6 @@ use std::cell::RefCell; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::mir::mono::CodegenUnit; use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::Session; @@ -20,9 +19,8 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { } fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function; fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value; - fn eh_personality(&self) -> Self::Value; + fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; - fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx>; fn set_frame_pointer_type(&self, llfn: Self::Function); fn apply_target_cpu_attr(&self, llfn: Self::Function); /// Declares the extern "C" main function for the entry point. Returns None if the symbol diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 239857a4298d..6d1ac717c0b8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -50,7 +50,7 @@ pub use self::type_::{ }; pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; -pub trait CodegenObject = Copy + PartialEq + fmt::Debug; +pub trait CodegenObject = Copy + fmt::Debug; pub trait CodegenMethods<'tcx> = LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>> diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index ece0ea1b2ea8..0e1e445c72f3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -5,19 +5,7 @@ use super::BackendTypes; pub trait StaticCodegenMethods: BackendTypes { fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; - fn codegen_static(&self, def_id: DefId); - - /// Mark the given global value as "used", to prevent the compiler and linker from potentially - /// removing a static variable that may otherwise appear unused. - fn add_used_global(&self, global: Self::Value); - - /// Same as add_used_global(), but only prevent the compiler from potentially removing an - /// otherwise unused symbol. The linker is still permitted to drop it. - /// - /// This corresponds to the documented semantics of the `#[used]` attribute, although - /// on some targets (non-ELF), we may use `add_used_global` for `#[used]` statics - /// instead. - fn add_compiler_used_global(&self, global: Self::Value); + fn codegen_static(&mut self, def_id: DefId); } pub trait StaticBuilderMethods: BackendTypes { diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 32d9f27d32d3..c3fc21a92854 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -158,7 +158,6 @@ pub trait ArgAbiBuilderMethods<'tcx>: BackendTypes { val: Self::Value, dst: PlaceRef<'tcx, Self::Value>, ); - fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type; } pub trait TypeCodegenMethods<'tcx> = DerivedTypeCodegenMethods<'tcx> diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index c77efdd17287..07a0609fda1a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -6,7 +6,7 @@ use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; use crate::{CompiledModule, ModuleCodegen}; -pub trait WriteBackendMethods: 'static + Sized + Clone { +pub trait WriteBackendMethods: Clone + 'static { type Module: Send + Sync; type TargetMachine; type TargetMachineError; @@ -37,7 +37,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { ) -> Result<(Vec>, Vec), FatalError>; fn print_pass_timings(&self); fn print_statistics(&self); - unsafe fn optimize( + fn optimize( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: &mut ModuleCodegen, @@ -47,11 +47,11 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { cgcx: &CodegenContext, llmod: &mut ModuleCodegen, ) -> Result<(), FatalError>; - unsafe fn optimize_thin( + fn optimize_thin( cgcx: &CodegenContext, thin: ThinModule, ) -> Result, FatalError>; - unsafe fn codegen( + fn codegen( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 02be8762d6f8..93d0d5b9a71e 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -9,7 +9,7 @@ either = "1" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index f4defd2aa134..7d4afc9d3d95 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -424,8 +424,7 @@ const_eval_unstable_in_stable_exposed = .unstable_sugg = if the {$is_function_call2 -> [true] caller *[false] function - } is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) - .bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + } is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic const_eval_unstable_intrinsic_suggestion = add `#![feature({$feature})]` to the crate attributes to enable diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index b600b8918dd0..b67a3ce03a94 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -6,7 +6,7 @@ use std::mem; use std::num::NonZero; use std::ops::Deref; -use rustc_attr_parsing::{ConstStability, StabilityLevel}; +use rustc_attr_data_structures as attrs; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -475,7 +475,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { /// Check the const stability of the given item (fn or trait). fn check_callee_stability(&mut self, def_id: DefId) { match self.tcx.lookup_const_stability(def_id) { - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + Some(attrs::ConstStability { level: attrs::StabilityLevel::Stable { .. }, .. }) => { // All good. } None => { @@ -491,8 +491,8 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { }); } } - Some(ConstStability { - level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + Some(attrs::ConstStability { + level: attrs::StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, feature, .. }) => { @@ -918,8 +918,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }); } } - Some(ConstStability { - level: StabilityLevel::Unstable { .. }, + Some(attrs::ConstStability { + level: attrs::StabilityLevel::Unstable { .. }, feature, .. }) => { @@ -930,7 +930,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { suggestion: self.crate_inject_span(), }); } - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + Some(attrs::ConstStability { + level: attrs::StabilityLevel::Stable { .. }, + .. + }) => { // All good. Note that a `#[rustc_const_stable]` intrinsic (meaning it // can be *directly* invoked from stable const code) does not always // have the `#[rustc_intrinsic_const_stable_indirect]` attribute (which controls diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 06ee7075170f..d84214152255 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -4,13 +4,12 @@ //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when //! it finds operations that are invalid in a certain context. -use rustc_attr_parsing::{AttributeKind, find_attr}; use rustc_errors::DiagCtxtHandle; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; +use {rustc_attr_data_structures as attrs, rustc_hir as hir}; pub use self::qualifs::Qualif; @@ -83,7 +82,7 @@ pub fn rustc_allow_const_fn_unstable( ) -> bool { let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); - find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms) if syms.contains(&feature_gate)) + attrs::find_attr!(attrs, attrs::AttributeKind::AllowConstFnUnstable(syms) if syms.contains(&feature_gate)) } /// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 1e5b98675c4f..177ba56b165e 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -281,7 +281,7 @@ fn build_error_for_const_call<'tcx>( let mut sugg = None; if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) { - match (args[0].unpack(), args[1].unpack()) { + match (args[0].kind(), args[1].kind()) { (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) if self_ty == rhs_ty && self_ty.is_ref() 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 46dcebc46e9c..b6e2682af367 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -90,7 +90,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { _instance: ty::Instance<'tcx>, _abi: &FnAbi<'tcx, Ty<'tcx>>, _args: &[interpret::FnArg<'tcx, Self::Provenance>], - _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _destination: &interpret::PlaceTy<'tcx, Self::Provenance>, _target: Option, _unwind: UnwindAction, ) -> interpret::InterpResult<'tcx, Option<(&'tcx Body<'tcx>, ty::Instance<'tcx>)>> { @@ -108,7 +108,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { _ecx: &mut InterpCx<'tcx, Self>, _instance: ty::Instance<'tcx>, _args: &[interpret::OpTy<'tcx, Self::Provenance>], - _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _destination: &interpret::PlaceTy<'tcx, Self::Provenance>, _target: Option, _unwind: UnwindAction, ) -> interpret::InterpResult<'tcx, Option>> { @@ -197,4 +197,9 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { ) -> &'a mut Vec> { unimplemented!() } + + fn get_default_alloc_params( + &self, + ) -> ::AllocParams { + } } 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 ce8eceebdf8d..a79ba6a63427 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -71,7 +71,12 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( // This can't use `init_stack_frame` since `body` is not a function, // so computing its ABI would fail. It's also not worth it since there are no arguments to pass. - ecx.push_stack_frame_raw(cid.instance, body, &ret, StackPopCleanup::Root { cleanup: false })?; + ecx.push_stack_frame_raw( + cid.instance, + body, + &ret.clone().into(), + StackPopCleanup::Root { cleanup: false }, + )?; ecx.storage_live_for_always_live_locals()?; // The main interpreter loop. diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 7c7daed525b2..3922b33ea842 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -22,7 +22,7 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; @@ -226,7 +226,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[FnArg<'tcx>], - _dest: &MPlaceTy<'tcx>, + _dest: &PlaceTy<'tcx>, _ret: Option, ) -> InterpResult<'tcx, Option>> { let def_id = instance.def_id(); @@ -343,7 +343,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { orig_instance: ty::Instance<'tcx>, _abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, _unwind: mir::UnwindAction, // unwinding is not supported in consts ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -385,7 +385,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx, Self::Provenance>, + dest: &PlaceTy<'tcx, Self::Provenance>, target: Option, _unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { @@ -735,6 +735,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { Cow::Owned(compute_range()) } } + + fn get_default_alloc_params(&self) -> ::AllocParams { + } } // Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 826ea0e58ecc..7c35e47bbf80 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -58,11 +58,6 @@ pub(crate) struct UnstableInStableExposed { code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", applicability = "has-placeholders" )] - #[suggestion( - const_eval_bypass_sugg, - code = "#[rustc_allow_const_fn_unstable({gate})]\n", - applicability = "has-placeholders" - )] pub attr_span: Span, } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 405208e94f4b..789baea07346 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -339,7 +339,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, - destination: &MPlaceTy<'tcx, M::Provenance>, + destination: &PlaceTy<'tcx, M::Provenance>, mut stack_pop: StackPopCleanup, ) -> InterpResult<'tcx> { // Compute callee information. @@ -487,7 +487,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Protect return place for in-place return value passing. - M::protect_in_place_function_argument(self, &destination)?; + // We only need to protect anything if this is actually an in-memory place. + if let Left(mplace) = destination.as_mplace_or_local() { + M::protect_in_place_function_argument(self, &mplace)?; + } // Don't forget to mark "initially live" locals as live. self.storage_live_for_always_live_locals()?; @@ -512,7 +515,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { (caller_abi, caller_fn_abi): (ExternAbi, &FnAbi<'tcx, Ty<'tcx>>), args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, - destination: &MPlaceTy<'tcx, M::Provenance>, + destination: &PlaceTy<'tcx, M::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -776,10 +779,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`, // as the latter "executes" the goto to the return block, but we don't want to, // only the tail called function should return to the current return block. - M::before_stack_pop(self, self.frame())?; - - let StackPopInfo { return_action, return_to_block, return_place } = - self.pop_stack_frame_raw(false)?; + let StackPopInfo { return_action, return_to_block, return_place } = self + .pop_stack_frame_raw(false, |_this, _return_place| { + // This function's return value is just discarded, the tail-callee will fill in the return place instead. + interp_ok(()) + })?; assert_eq!(return_action, ReturnAction::Normal); @@ -850,7 +854,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { (ExternAbi::Rust, fn_abi), &[FnArg::Copy(arg.into())], false, - &ret, + &ret.into(), Some(target), unwind, ) @@ -891,28 +895,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub_custom!(fluent::const_eval_unwind_past_top); } - M::before_stack_pop(self, self.frame())?; - - // Copy return value. Must of course happen *before* we deallocate the locals. - // Must be *after* `before_stack_pop` as otherwise the return place might still be protected. - let copy_ret_result = if !unwinding { - let op = self - .local_to_op(mir::RETURN_PLACE, None) - .expect("return place should always be live"); - let dest = self.frame().return_place.clone(); - let res = self.copy_op_allow_transmute(&op, &dest); - trace!("return value: {:?}", self.dump_place(&dest.into())); - // We delay actually short-circuiting on this error until *after* the stack frame is - // popped, since we want this error to be attributed to the caller, whose type defines - // this transmute. - res - } else { + // Get out the return value. Must happen *before* the frame is popped as we have to get the + // local's value out. + let return_op = + self.local_to_op(mir::RETURN_PLACE, None).expect("return place should always be live"); + // Do the actual pop + copy. + let stack_pop_info = self.pop_stack_frame_raw(unwinding, |this, return_place| { + this.copy_op_allow_transmute(&return_op, return_place)?; + trace!("return value: {:?}", this.dump_place(return_place)); interp_ok(()) - }; - - // All right, now it is time to actually pop the frame. - // 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 => {} @@ -924,7 +916,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // 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. + // Don't jump anywhere. return interp_ok(()); } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 090b2a692cfc..64467a901369 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -17,16 +17,16 @@ use tracing::trace; use super::memory::MemoryKind; 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, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, + Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, Machine, + OpTy, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, + err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; /// Directly returns an `Allocation` containing an absolute path representation of the given type. pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> { let path = crate::util::type_name(tcx, ty); - let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes()); + let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes(), ()); tcx.mk_const_alloc(alloc) } @@ -112,7 +112,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ret: Option, ) -> InterpResult<'tcx, bool> { let instance_args = instance.args; @@ -518,6 +518,103 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::fabsf64 => self.float_abs_intrinsic::(args, dest)?, sym::fabsf128 => self.float_abs_intrinsic::(args, dest)?, + sym::floorf16 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardNegative, + )?, + sym::floorf32 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardNegative, + )?, + sym::floorf64 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardNegative, + )?, + sym::floorf128 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardNegative, + )?, + + sym::ceilf16 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardPositive, + )?, + sym::ceilf32 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardPositive, + )?, + sym::ceilf64 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardPositive, + )?, + sym::ceilf128 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::TowardPositive, + )?, + + sym::truncf16 => { + self.float_round_intrinsic::(args, dest, rustc_apfloat::Round::TowardZero)? + } + sym::truncf32 => { + self.float_round_intrinsic::(args, dest, rustc_apfloat::Round::TowardZero)? + } + sym::truncf64 => { + self.float_round_intrinsic::(args, dest, rustc_apfloat::Round::TowardZero)? + } + sym::truncf128 => { + self.float_round_intrinsic::(args, dest, rustc_apfloat::Round::TowardZero)? + } + + sym::roundf16 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToAway, + )?, + sym::roundf32 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToAway, + )?, + sym::roundf64 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToAway, + )?, + sym::roundf128 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToAway, + )?, + + sym::round_ties_even_f16 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToEven, + )?, + sym::round_ties_even_f32 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToEven, + )?, + sym::round_ties_even_f64 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToEven, + )?, + sym::round_ties_even_f128 => self.float_round_intrinsic::( + args, + dest, + rustc_apfloat::Round::NearestTiesToEven, + )?, + // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), } @@ -587,7 +684,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, a: &ImmTy<'tcx, M::Provenance>, b: &ImmTy<'tcx, M::Provenance>, - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { assert_eq!(a.layout.ty, b.layout.ty); assert_matches!(a.layout.ty.kind(), ty::Int(..) | ty::Uint(..)); @@ -801,7 +898,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_min_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -822,7 +919,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_max_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -843,7 +940,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_minimum_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -859,7 +956,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_maximum_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -875,7 +972,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_copysign_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -890,7 +987,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_abs_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -900,4 +997,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(x.abs(), dest)?; interp_ok(()) } + + fn float_round_intrinsic( + &mut self, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, + mode: rustc_apfloat::Round, + ) -> InterpResult<'tcx, ()> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, + { + let x: F = self.read_scalar(&args[0])?.to_float()?; + let res = x.round_to_integral(mode).value; + let res = self.adjust_nan(res, &[x]); + self.write_scalar(res, dest)?; + interp_ok(()) + } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index a1386b4e1be4..b9e022c96043 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -147,6 +147,12 @@ pub trait Machine<'tcx>: Sized { /// already been checked before. const ALL_CONSTS_ARE_PRECHECKED: bool = true; + /// Determines whether rustc_const_eval functions that make use of the [Machine] should make + /// tracing calls (to the `tracing` library). By default this is `false`, meaning the tracing + /// calls will supposedly be optimized out. This flag is set to `true` inside Miri, to allow + /// tracing the interpretation steps, among other things. + const TRACING_ENABLED: bool = false; + /// Whether memory accesses should be alignment-checked. fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool; @@ -202,7 +208,7 @@ pub trait Machine<'tcx>: Sized { instance: ty::Instance<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Self::Provenance>], - destination: &MPlaceTy<'tcx, Self::Provenance>, + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>>; @@ -214,7 +220,7 @@ pub trait Machine<'tcx>: Sized { fn_val: Self::ExtraFnVal, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Self::Provenance>], - destination: &MPlaceTy<'tcx, Self::Provenance>, + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx>; @@ -228,7 +234,7 @@ pub trait Machine<'tcx>: Sized { ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Self::Provenance>], - destination: &MPlaceTy<'tcx, Self::Provenance>, + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>>; @@ -530,11 +536,9 @@ pub trait Machine<'tcx>: Sized { interp_ok(()) } - /// Called just before the return value is copied to the caller-provided return place. - fn before_stack_pop( - _ecx: &InterpCx<'tcx, Self>, - _frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, - ) -> InterpResult<'tcx> { + /// Called just before the frame is removed from the stack (followed by return value copy and + /// local cleanup). + fn before_stack_pop(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { interp_ok(()) } @@ -622,6 +626,10 @@ pub trait Machine<'tcx>: Sized { // Default to no caching. Cow::Owned(compute_range()) } + + /// Compute the value passed to the constructors of the `AllocBytes` type for + /// abstract machine allocations. + fn get_default_alloc_params(&self) -> ::AllocParams; } /// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines @@ -669,7 +677,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { fn_val: !, _abi: &FnAbi<$tcx, Ty<$tcx>>, _args: &[FnArg<$tcx>], - _destination: &MPlaceTy<$tcx, Self::Provenance>, + _destination: &PlaceTy<$tcx, Self::Provenance>, _target: Option, _unwind: mir::UnwindAction, ) -> InterpResult<$tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 43bf48a9b961..99a4bc1b7d6e 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -233,10 +233,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind: MemoryKind, init: AllocInit, ) -> InterpResult<'tcx, Pointer> { + let params = self.machine.get_default_alloc_params(); let alloc = if M::PANIC_ON_ALLOC_FAIL { - Allocation::new(size, align, init) + Allocation::new(size, align, init, params) } else { - Allocation::try_new(size, align, init)? + Allocation::try_new(size, align, init, params)? }; self.insert_allocation(alloc, kind) } @@ -248,7 +249,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind: MemoryKind, mutability: Mutability, ) -> InterpResult<'tcx, Pointer> { - let alloc = Allocation::from_bytes(bytes, align, mutability); + let params = self.machine.get_default_alloc_params(); + let alloc = Allocation::from_bytes(bytes, align, mutability, params); self.insert_allocation(alloc, kind) } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 36da9037e43d..39755169e6ca 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -310,7 +310,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { let ty = tcx.ty_ordering_enum(None); let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)).unwrap(); - Self::from_scalar(Scalar::from_i8(c as i8), layout) + Self::from_scalar(Scalar::Int(c.into()), layout) } pub fn from_pair(a: Self, b: Self, cx: &(impl HasTypingEnv<'tcx> + HasTyCtxt<'tcx>)) -> Self { diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index d7b03776bc48..2a2d1bb27547 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -15,9 +15,9 @@ use rustc_span::Span; 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, interp_ok, throw_ub, throw_unsup, + AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, Machine, MemPlace, MemPlaceMeta, + MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout, + interp_ok, throw_ub, throw_unsup, }; use crate::errors; @@ -76,8 +76,10 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { return_to_block: StackPopCleanup, /// The location where the result of the current stack frame should be written to, - /// and its layout in the caller. - pub return_place: MPlaceTy<'tcx, Prov>, + /// and its layout in the caller. This place is to be interpreted relative to the + /// *caller's* stack frame. We use a `PlaceTy` instead of an `MPlaceTy` since this + /// avoids having to move *all* return places into Miri's memory. + pub return_place: PlaceTy<'tcx, Prov>, /// The list of locals for this stack frame, stored in order as /// `[return_ptr, arguments..., variables..., temporaries...]`. @@ -129,7 +131,7 @@ pub struct StackPopInfo<'tcx, Prov: Provenance> { pub return_to_block: StackPopCleanup, /// [`return_place`](Frame::return_place) of the popped stack frame. - pub return_place: MPlaceTy<'tcx, Prov>, + pub return_place: PlaceTy<'tcx, Prov>, } /// State of a local variable including a memoized layout @@ -353,7 +355,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, instance: ty::Instance<'tcx>, body: &'tcx mir::Body<'tcx>, - return_place: &MPlaceTy<'tcx, M::Provenance>, + return_place: &PlaceTy<'tcx, M::Provenance>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { trace!("body: {:#?}", body); @@ -404,9 +406,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// it. /// /// This also deallocates locals, if necessary. + /// `copy_ret_val` gets called after the frame has been taken from the stack but before the locals have been deallocated. /// - /// [`M::before_stack_pop`] should be called before calling this function. - /// [`M::after_stack_pop`] is called by this function automatically. + /// [`M::before_stack_pop`] and [`M::after_stack_pop`] are called by this function + /// automatically. /// /// The high-level version of this is `return_from_current_stack_frame`. /// @@ -415,47 +418,44 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub(super) fn pop_stack_frame_raw( &mut self, unwinding: bool, + copy_ret_val: impl FnOnce(&mut Self, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { - let cleanup = self.cleanup_current_frame_locals()?; - + M::before_stack_pop(self)?; let frame = self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + // Copy return value (unless we are unwinding). + if !unwinding { + copy_ret_val(self, &frame.return_place)?; + } + let return_to_block = frame.return_to_block; let return_place = frame.return_place.clone(); - let return_action; - if cleanup { - return_action = M::after_stack_pop(self, frame, unwinding)?; - assert_ne!(return_action, ReturnAction::NoCleanup); - } else { - return_action = ReturnAction::NoCleanup; - }; - - interp_ok(StackPopInfo { return_action, return_to_block, return_place }) - } - - /// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw). - /// Returns `true` if cleanup has been done, `false` otherwise. - fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { // Cleanup: deallocate locals. // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. // We do this while the frame is still on the stack, so errors point to the callee. - let return_to_block = self.frame().return_to_block; let cleanup = match return_to_block { StackPopCleanup::Goto { .. } => true, StackPopCleanup::Root { cleanup, .. } => cleanup, }; - if cleanup { + let return_action = if cleanup { // We need to take the locals out, since we need to mutate while iterating. - let locals = mem::take(&mut self.frame_mut().locals); - for local in &locals { + for local in &frame.locals { self.deallocate_local(local.value)?; } - } - interp_ok(cleanup) + // Call the machine hook, which determines the next steps. + let return_action = M::after_stack_pop(self, frame, unwinding)?; + assert_ne!(return_action, ReturnAction::NoCleanup); + return_action + } else { + // We also skip the machine hook when there's no cleanup. This not a real "pop" anyway. + ReturnAction::NoCleanup + }; + + interp_ok(StackPopInfo { return_action, return_to_block, return_place }) } /// In the current stack frame, mark all locals as live that are not arguments and don't have diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 363ceee1970e..975325b0c1e0 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -506,7 +506,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = self.eval_callee_and_args(terminator, func, args)?; - let destination = self.force_allocation(&self.eval_place(destination)?)?; + let destination = self.eval_place(destination)?; self.init_fn_call( callee, (fn_sig.abi, fn_abi), diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index ba579e25f036..83a170926191 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -38,10 +38,29 @@ pub(crate) fn create_static_alloc<'tcx>( static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit)?; + let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit, ())?; let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into()); 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()); interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) } + +/// This struct is needed to enforce `#[must_use]` on [tracing::span::EnteredSpan] +/// while wrapping them in an `Option`. +#[must_use] +pub enum MaybeEnteredSpan { + Some(tracing::span::EnteredSpan), + None, +} + +#[macro_export] +macro_rules! enter_trace_span { + ($machine:ident, $($tt:tt)*) => { + if $machine::TRACING_ENABLED { + $crate::interpret::tracing_utils::MaybeEnteredSpan::Some(tracing::info_span!($($tt)*).entered()) + } else { + $crate::interpret::tracing_utils::MaybeEnteredSpan::None + } + } +} diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 30e96ae41435..e8f2728a7728 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -125,7 +125,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { ) -> Result<(), PrintError> { print_prefix(self)?; let args = - args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_))); if args.clone().next().is_some() { self.generic_delimiters(|cx| cx.comma_sep(args)) } else { diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index d423d8acefd0..f33f6b7cac1a 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -4,7 +4,7 @@ //! green/native threading. This is just a bare-bones enough solution for //! librustdoc, it is not production quality at all. -cfg_match! { +cfg_select! { target_os = "linux" => { mod linux; use linux as imp; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 865424fd6bbd..b34a7fdb9e41 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -19,7 +19,7 @@ #![feature(ascii_char_variants)] #![feature(assert_matches)] #![feature(auto_traits)] -#![feature(cfg_match)] +#![feature(cfg_select)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(extend_one)] diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 60f007083baf..36649a360707 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -860,7 +860,7 @@ fn get_thread_id() -> u32 { } // Memory reporting -cfg_match! { +cfg_select! { windows => { pub fn get_resident_set_size() -> Option { use windows::{ diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index 33ac279f3e0a..31abea938196 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -354,6 +354,20 @@ impl TransitiveRelation { .collect() } + /// Given an element A, elements B with the lowest index such that `A R B` + /// and `B R A`, or `A` if no such element exists. + pub fn minimal_scc_representative(&self, a: T) -> T { + match self.index(a) { + Some(a_i) => self.with_closure(|closure| { + closure + .iter(a_i.0) + .find(|i| closure.contains(*i, a_i.0)) + .map_or(a, |i| self.elements[i]) + }), + None => a, + } + } + fn with_closure(&self, op: OP) -> R where OP: FnOnce(&BitMatrix) -> R, diff --git a/compiler/rustc_data_structures/src/transitive_relation/tests.rs b/compiler/rustc_data_structures/src/transitive_relation/tests.rs index e756c546e41b..cba14b5b64bc 100644 --- a/compiler/rustc_data_structures/src/transitive_relation/tests.rs +++ b/compiler/rustc_data_structures/src/transitive_relation/tests.rs @@ -376,3 +376,44 @@ fn parent() { let p = relation.postdom_parent(3); assert_eq!(p, Some(0)); } + +#[test] +fn minimal_scc_representative_1() { + // +---------+ + // v | + // a -> c -> d -> e + // ^ ^ + // | | + // b ---+ + + // "digraph { a -> c -> d -> e -> c; b -> d; b -> e; }", + let mut relation = TransitiveRelationBuilder::default(); + relation.add("a", "c"); + relation.add("c", "d"); + relation.add("d", "e"); + relation.add("e", "c"); + relation.add("b", "d"); + relation.add("b", "e"); + let relation = relation.freeze(); + + assert_eq!(relation.minimal_scc_representative("a"), "a"); + assert_eq!(relation.minimal_scc_representative("b"), "b"); + assert_eq!(relation.minimal_scc_representative("c"), "c"); + assert_eq!(relation.minimal_scc_representative("d"), "c"); + assert_eq!(relation.minimal_scc_representative("e"), "c"); +} + +#[test] +fn minimal_scc_representative_2() { + // "digraph { a -> b; a -> a; b -> a; c -> c}", + let mut relation = TransitiveRelationBuilder::default(); + relation.add("a", "b"); + relation.add("b", "a"); + relation.add("a", "a"); + relation.add("c", "c"); + let relation = relation.freeze(); + + assert_eq!(relation.minimal_scc_representative("a"), "a"); + assert_eq!(relation.minimal_scc_representative("b"), "a"); + assert_eq!(relation.minimal_scc_representative("c"), "c"); +} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 056c476d5e12..54a331a49044 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -12,7 +12,6 @@ #![feature(decl_macro)] #![feature(panic_backtrace_config)] #![feature(panic_update_hook)] -#![feature(result_flattening)] #![feature(rustdoc_internals)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_error_codes/src/error_codes/E0658.md b/compiler/rustc_error_codes/src/error_codes/E0658.md index 24245a38ae07..65c82e4fb6ef 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0658.md +++ b/compiler/rustc_error_codes/src/error_codes/E0658.md @@ -3,10 +3,7 @@ An unstable feature was used. Erroneous code example: ```compile_fail,E0658 -#[repr(u128)] // error: use of unstable library feature 'repr128' -enum Foo { - Bar(u64), -} +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` ``` If you're using a stable or a beta version of rustc, you won't be able to use @@ -17,12 +14,9 @@ If you're using a nightly version of rustc, just add the corresponding feature to be able to use it: ``` -#![feature(repr128)] +#![feature(core_intrinsics)] -#[repr(u128)] // ok! -enum Foo { - Bar(u64), -} +use std::intrinsics; // ok! ``` [rustup]: https://rust-lang.github.io/rustup/concepts/channels.html diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 578af7fc51d4..0951859fa531 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -fluent-bundle = "0.15.2" -fluent-syntax = "0.11" +fluent-bundle = "0.16" +fluent-syntax = "0.12" icu_list = "1.2" icu_locid = "1.2" icu_provider_adapters = "1.2" diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 539ecfbb42e2..a11f81b55bb8 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1325,7 +1325,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { )); self.note("consider using `--verbose` to print the full type name to the console"); } - Box::into_inner(self.diag.take().unwrap()) + *self.diag.take().unwrap() } /// This method allows us to access the path of the file where "long types" are written to. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f8e19e507789..bd421a441f95 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -12,7 +12,6 @@ #![feature(array_windows)] #![feature(assert_matches)] #![feature(associated_type_defaults)] -#![feature(box_into_inner)] #![feature(box_patterns)] #![feature(default_field_values)] #![feature(error_reporter)] diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 0ba139ea5cc5..57dd3a3128df 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -12,6 +12,7 @@ doctest = false rustc_ast = { path = "../rustc_ast" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -22,6 +23,9 @@ rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } +# We must use the proc_macro version that we will compile proc-macros against, +# not the one from our own sysroot. +rustc_proc_macro = { path = "../rustc_proc_macro" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index f5eaf7d616b2..2accfba383e6 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -11,7 +11,7 @@ use rustc_ast::token::MetaVarKind; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; -use rustc_attr_parsing::{AttributeKind, Deprecation, Stability, find_attr}; +use rustc_attr_data_structures::{AttributeKind, Deprecation, Stability, find_attr}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; @@ -1424,7 +1424,7 @@ pub fn parse_macro_name_and_helper_attrs( /// See #73345 and #83125 for more details. /// FIXME(#73933): Remove this eventually. fn pretty_printing_compatibility_hack(item: &Item, psess: &ParseSess) { - if let ast::ItemKind::Enum(ident, enum_def, _) = &item.kind + if let ast::ItemKind::Enum(ident, _, enum_def) = &item.kind && ident.name == sym::ProceduralMasqueradeDummyType && let [variant] = &*enum_def.variants && variant.ident.name == sym::Input diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 0994813ecb9c..c50ab5959e25 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -2,7 +2,6 @@ use std::iter; -use rustc_ast::ptr::P; use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, @@ -433,7 +432,7 @@ impl<'a> StripUnconfigured<'a> { } #[instrument(level = "trace", skip(self))] - pub fn configure_expr(&self, expr: &mut P, method_receiver: bool) { + pub fn configure_expr(&self, expr: &mut ast::Expr, method_receiver: bool) { if !method_receiver { for attr in expr.attrs.iter() { self.maybe_emit_expr_attr_err(attr); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 81d4d59ee045..82a2719ca962 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -3,15 +3,14 @@ use std::rc::Rc; use std::sync::Arc; use std::{iter, mem}; -use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ - AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind, ForeignItemKind, - HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, - NodeId, PatKind, StmtKind, TyKind, token, + self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, DUMMY_NODE_ID, + ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, + MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; @@ -131,13 +130,9 @@ macro_rules! ast_fragments { pub(crate) fn mut_visit_with(&mut self, vis: &mut F) { match self { AstFragment::OptExpr(opt_expr) => { - visit_clobber(opt_expr, |opt_expr| { - if let Some(expr) = opt_expr { - vis.filter_map_expr(expr) - } else { - None - } - }); + if let Some(expr) = opt_expr.take() { + *opt_expr = vis.filter_map_expr(expr) + } } AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)* @@ -1319,10 +1314,10 @@ impl InvocationCollectorNode for P { let mut idents = Vec::new(); collect_use_tree_leaves(ut, &mut idents); - return idents; + idents + } else { + self.kind.ident().into_iter().collect() } - - if let Some(ident) = self.kind.ident() { vec![ident] } else { vec![] } } } @@ -1782,11 +1777,7 @@ impl InvocationCollectorNode for AstNodeWrapper, OptExprTag> { /// This struct is a hack to workaround unstable of `stmt_expr_attributes`. /// It can be removed once that feature is stabilized. struct MethodReceiverTag; -impl DummyAstNode for MethodReceiverTag { - fn dummy() -> MethodReceiverTag { - MethodReceiverTag - } -} + impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> { type OutputTy = Self; const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr; @@ -1852,6 +1843,57 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>( }) } +/// Required for `visit_node` obtained an owned `Node` from `&mut Node`. +trait DummyAstNode { + fn dummy() -> Self; +} + +impl DummyAstNode for ast::Crate { + fn dummy() -> Self { + ast::Crate { + attrs: Default::default(), + items: Default::default(), + spans: Default::default(), + id: DUMMY_NODE_ID, + is_placeholder: Default::default(), + } + } +} + +impl DummyAstNode for P { + fn dummy() -> Self { + P(ast::Ty { + id: DUMMY_NODE_ID, + kind: TyKind::Dummy, + span: Default::default(), + tokens: Default::default(), + }) + } +} + +impl DummyAstNode for P { + fn dummy() -> Self { + P(ast::Pat { + id: DUMMY_NODE_ID, + kind: PatKind::Wild, + span: Default::default(), + tokens: Default::default(), + }) + } +} + +impl DummyAstNode for P { + fn dummy() -> Self { + ast::Expr::dummy() + } +} + +impl DummyAstNode for AstNodeWrapper, MethodReceiverTag> { + fn dummy() -> Self { + AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag) + } +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, invocations: Vec<(Invocation, Option>)>, @@ -2155,18 +2197,19 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.expand_cfg_attr(node, &attr, pos); continue; } - _ => visit_clobber(node, |node| { - self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND) + _ => { + let n = mem::replace(node, Node::dummy()); + *node = self + .collect_attr((attr, pos, derives), n.to_annotatable(), Node::KIND) .make_ast::() - }), + } }, None if node.is_mac_call() => { - visit_clobber(node, |node| { - // Do not clobber unless it's actually a macro (uncommon case). - let (mac, attrs, _) = node.take_mac_call(); - self.check_attributes(&attrs, &mac); - self.collect_bang(mac, Node::KIND).make_ast::() - }) + let n = mem::replace(node, Node::dummy()); + let (mac, attrs, _) = n.take_mac_call(); + self.check_attributes(&attrs, &mac); + + *node = self.collect_bang(mac, Node::KIND).make_ast::() } None if node.delegation().is_some() => unreachable!(), None => { @@ -2293,18 +2336,14 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } fn visit_method_receiver_expr(&mut self, node: &mut P) { - visit_clobber(node, |node| { - let mut wrapper = AstNodeWrapper::new(node, MethodReceiverTag); - self.visit_node(&mut wrapper); - wrapper.wrapped - }) + self.visit_node(AstNodeWrapper::from_mut(node, MethodReceiverTag)) } fn filter_map_expr(&mut self, node: P) -> Option> { self.flat_map_node(AstNodeWrapper::new(node, OptExprTag)) } - fn visit_block(&mut self, node: &mut P) { + fn visit_block(&mut self, node: &mut ast::Block) { let orig_dir_ownership = mem::replace( &mut self.cx.current_expansion.dir_ownership, DirOwnership::UnownedViaBlock, diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index cd744977bb32..35b38d99c703 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -14,8 +14,6 @@ #![feature(yeet_expr)] // tidy-alphabetical-end -extern crate proc_macro as pm; - mod build; mod errors; // FIXME(Nilstrieb) Translate macro_rules diagnostics diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 93604a149f1d..783f061ec6c5 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -10,7 +10,7 @@ use rustc_ast::token::{self, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::{AttributeKind, find_attr}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_feature::Features; diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 0136292decbc..3dcb20c8c768 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -349,7 +349,7 @@ impl MutVisitor for PlaceholderExpander { fn filter_map_expr(&mut self, expr: P) -> Option> { match expr.kind { ast::ExprKind::MacCall(_) => self.remove(expr.id).make_opt_expr(), - _ => noop_filter_map_expr(self, expr), + _ => walk_filter_map_expr(self, expr), } } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index d5af9849e759..84fbbbef061e 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -1,4 +1,3 @@ -use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_errors::ErrorGuaranteed; @@ -6,6 +5,7 @@ use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::config::ProcMacroExecutionStrategy; use rustc_span::Span; use rustc_span::profiling::SpannedEventArgRecorder; +use {rustc_ast as ast, rustc_proc_macro as pm}; use crate::base::{self, *}; use crate::{errors, proc_macro_server}; diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index f00201ad202a..fb5abaefb570 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,10 +1,6 @@ use std::ops::{Bound, Range}; use ast::token::IdentIsRaw; -use pm::bridge::{ - DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, server, -}; -use pm::{Delimiter, Level}; use rustc_ast as ast; use rustc_ast::token; use rustc_ast::tokenstream::{self, DelimSpacing, Spacing, TokenStream}; @@ -15,6 +11,10 @@ use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; +use rustc_proc_macro::bridge::{ + DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, server, +}; +use rustc_proc_macro::{Delimiter, Level}; use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; use rustc_span::{BytePos, FileName, Pos, Span, Symbol, sym}; @@ -66,7 +66,7 @@ impl FromInternal for LitKind { token::CStr => LitKind::CStr, token::CStrRaw(n) => LitKind::CStrRaw(n), token::Err(_guar) => { - // This is the only place a `pm::bridge::LitKind::ErrWithGuar` + // This is the only place a `rustc_proc_macro::bridge::LitKind::ErrWithGuar` // is constructed. Note that an `ErrorGuaranteed` is available, // as required. See the comment in `to_internal`. LitKind::ErrWithGuar @@ -149,7 +149,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec)> for Vec)> for Vec for LifetimeSyntax { fn from(ident: Ident) -> Self { let name = ident.name; - if name == kw::Empty { + if name == sym::empty { unreachable!("A lifetime name should never be empty"); } else if name == kw::UnderscoreLifetime { LifetimeSyntax::Anonymous @@ -4106,11 +4106,11 @@ impl<'hir> Item<'hir> { expect_use, (&'hir UsePath<'hir>, UseKind), ItemKind::Use(p, uk), (p, *uk); - expect_static, (Ident, &'hir Ty<'hir>, Mutability, BodyId), - ItemKind::Static(ident, ty, mutbl, body), (*ident, ty, *mutbl, *body); + expect_static, (Mutability, Ident, &'hir Ty<'hir>, BodyId), + ItemKind::Static(mutbl, ident, ty, body), (*mutbl, *ident, ty, *body); - expect_const, (Ident, &'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), - ItemKind::Const(ident, ty, generics, body), (*ident, ty, generics, *body); + expect_const, (Ident, &'hir Generics<'hir>, &'hir Ty<'hir>, BodyId), + ItemKind::Const(ident, generics, ty, body), (*ident, generics, ty, *body); expect_fn, (Ident, &FnSig<'hir>, &'hir Generics<'hir>, BodyId), ItemKind::Fn { ident, sig, generics, body, .. }, (*ident, sig, generics, *body); @@ -4125,17 +4125,17 @@ impl<'hir> Item<'hir> { expect_global_asm, &'hir InlineAsm<'hir>, ItemKind::GlobalAsm { asm, .. }, asm; - expect_ty_alias, (Ident, &'hir Ty<'hir>, &'hir Generics<'hir>), - ItemKind::TyAlias(ident, ty, generics), (*ident, ty, generics); + expect_ty_alias, (Ident, &'hir Generics<'hir>, &'hir Ty<'hir>), + ItemKind::TyAlias(ident, generics, ty), (*ident, generics, ty); - expect_enum, (Ident, &EnumDef<'hir>, &'hir Generics<'hir>), - ItemKind::Enum(ident, def, generics), (*ident, def, generics); + expect_enum, (Ident, &'hir Generics<'hir>, &EnumDef<'hir>), + ItemKind::Enum(ident, generics, def), (*ident, generics, def); - expect_struct, (Ident, &VariantData<'hir>, &'hir Generics<'hir>), - ItemKind::Struct(ident, data, generics), (*ident, data, generics); + expect_struct, (Ident, &'hir Generics<'hir>, &VariantData<'hir>), + ItemKind::Struct(ident, generics, data), (*ident, generics, data); - expect_union, (Ident, &VariantData<'hir>, &'hir Generics<'hir>), - ItemKind::Union(ident, data, generics), (*ident, data, generics); + expect_union, (Ident, &'hir Generics<'hir>, &VariantData<'hir>), + ItemKind::Union(ident, generics, data), (*ident, generics, data); expect_trait, ( @@ -4278,13 +4278,13 @@ pub enum ItemKind<'hir> { Use(&'hir UsePath<'hir>, UseKind), /// A `static` item. - Static(Ident, &'hir Ty<'hir>, Mutability, BodyId), + Static(Mutability, Ident, &'hir Ty<'hir>, BodyId), /// A `const` item. - Const(Ident, &'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), + Const(Ident, &'hir Generics<'hir>, &'hir Ty<'hir>, BodyId), /// A function declaration. Fn { - ident: Ident, sig: FnSig<'hir>, + ident: Ident, generics: &'hir Generics<'hir>, body: BodyId, /// Whether this function actually has a body. @@ -4309,13 +4309,13 @@ pub enum ItemKind<'hir> { fake_body: BodyId, }, /// A type alias, e.g., `type Foo = Bar`. - TyAlias(Ident, &'hir Ty<'hir>, &'hir Generics<'hir>), + TyAlias(Ident, &'hir Generics<'hir>, &'hir Ty<'hir>), /// An enum definition, e.g., `enum Foo { C, D }`. - Enum(Ident, EnumDef<'hir>, &'hir Generics<'hir>), + Enum(Ident, &'hir Generics<'hir>, EnumDef<'hir>), /// A struct definition, e.g., `struct Foo {x: A}`. - Struct(Ident, VariantData<'hir>, &'hir Generics<'hir>), + Struct(Ident, &'hir Generics<'hir>, VariantData<'hir>), /// A union definition, e.g., `union Foo {x: A, y: B}`. - Union(Ident, VariantData<'hir>, &'hir Generics<'hir>), + Union(Ident, &'hir Generics<'hir>, VariantData<'hir>), /// A trait definition. Trait(IsAuto, Safety, Ident, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]), /// A trait alias. @@ -4352,7 +4352,7 @@ impl ItemKind<'_> { match *self { ItemKind::ExternCrate(_, ident) | ItemKind::Use(_, UseKind::Single(ident)) - | ItemKind::Static(ident, ..) + | ItemKind::Static(_, ident, ..) | ItemKind::Const(ident, ..) | ItemKind::Fn { ident, .. } | ItemKind::Macro(ident, ..) @@ -4374,11 +4374,11 @@ impl ItemKind<'_> { pub fn generics(&self) -> Option<&Generics<'_>> { Some(match self { ItemKind::Fn { generics, .. } - | ItemKind::TyAlias(_, _, generics) - | ItemKind::Const(_, _, generics, _) - | ItemKind::Enum(_, _, generics) - | ItemKind::Struct(_, _, generics) - | ItemKind::Union(_, _, generics) + | ItemKind::TyAlias(_, generics, _) + | ItemKind::Const(_, generics, _, _) + | ItemKind::Enum(_, generics, _) + | ItemKind::Struct(_, generics, _) + | ItemKind::Union(_, generics, _) | ItemKind::Trait(_, _, _, generics, _, _) | ItemKind::TraitAlias(_, generics, _) | ItemKind::Impl(Impl { generics, .. }) => generics, @@ -4802,9 +4802,9 @@ impl<'hir> Node<'hir> { pub fn ty(self) -> Option<&'hir Ty<'hir>> { match self { Node::Item(it) => match it.kind { - ItemKind::TyAlias(_, ty, _) - | ItemKind::Static(_, ty, _, _) - | ItemKind::Const(_, ty, _, _) => Some(ty), + ItemKind::TyAlias(_, _, ty) + | ItemKind::Static(_, _, ty, _) + | ItemKind::Const(_, _, ty, _) => Some(ty), ItemKind::Impl(impl_item) => Some(&impl_item.self_ty), _ => None, }, @@ -4824,7 +4824,7 @@ impl<'hir> Node<'hir> { pub fn alias_ty(self) -> Option<&'hir Ty<'hir>> { match self { - Node::Item(Item { kind: ItemKind::TyAlias(_, ty, _), .. }) => Some(ty), + Node::Item(Item { kind: ItemKind::TyAlias(_, _, ty), .. }) => Some(ty), _ => None, } } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index a60de4b1fc31..1fd44e44b9ce 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -545,15 +545,15 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: UseKind::Glob | UseKind::ListStem => {} } } - ItemKind::Static(ident, ref typ, _, body) => { + ItemKind::Static(_, ident, ref typ, body) => { try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_nested_body(body)); } - ItemKind::Const(ident, ref typ, ref generics, body) => { + ItemKind::Const(ident, ref generics, ref typ, body) => { try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_generics(generics)); + try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_nested_body(body)); } ItemKind::Fn { ident, sig, generics, body: body_id, .. } => { @@ -583,12 +583,12 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: // typeck results set correctly. try_visit!(visitor.visit_nested_body(fake_body)); } - ItemKind::TyAlias(ident, ref ty, ref generics) => { + ItemKind::TyAlias(ident, ref generics, ref ty) => { try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_generics(generics)); + try_visit!(visitor.visit_ty_unambig(ty)); } - ItemKind::Enum(ident, ref enum_definition, ref generics) => { + ItemKind::Enum(ident, ref generics, ref enum_definition) => { try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_enum_def(enum_definition)); @@ -609,8 +609,8 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_ty_unambig(self_ty)); walk_list!(visitor, visit_impl_item_ref, *items); } - ItemKind::Struct(ident, ref struct_definition, ref generics) - | ItemKind::Union(ident, ref struct_definition, ref generics) => { + ItemKind::Struct(ident, ref generics, ref struct_definition) + | ItemKind::Union(ident, ref generics, ref struct_definition) => { try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_variant_data(struct_definition)); diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 58213c4f4e46..f2b82c679b93 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -13,7 +13,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index f7f2b78f0520..a3a0e276f74c 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -37,7 +37,7 @@ hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here -hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated {$what} of a trait with uninferred generic parameters +hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the {$what} of a trait with uninferred generic parameters .suggestion = use a fully qualified path with inferred lifetimes hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index da94331aa26f..846eacce9e10 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2,8 +2,7 @@ use std::cell::LazyCell; use std::ops::ControlFlow; use rustc_abi::FieldIdx; -use rustc_attr_parsing::AttributeKind; -use rustc_attr_parsing::ReprAttr::ReprPacked; +use rustc_attr_data_structures::ReprAttr::ReprPacked; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; @@ -19,7 +18,7 @@ use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; -use rustc_middle::ty::util::{Discr, IntTypeExt}; +use rustc_middle::ty::util::Discr; use rustc_middle::ty::{ AdtDef, BottomUpFolder, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, fold_regions, @@ -31,7 +30,7 @@ use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument}; use ty::TypingMode; -use {rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_attr_data_structures as attrs, rustc_hir as hir}; use super::compare_impl_item::check_type_bounds; use super::*; @@ -1154,7 +1153,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { if let Some(reprs) = - attr::find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::Repr(r) => r) + attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr(r) => r) { for (r, _) in reprs { if let ReprPacked(pack) = r @@ -1371,9 +1370,9 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { def.destructor(tcx); // force the destructor to be evaluated if def.variants().is_empty() { - attr::find_attr!( + attrs::find_attr!( tcx.get_all_attrs(def_id), - AttributeKind::Repr(rs) => { + attrs::AttributeKind::Repr(rs) => { struct_span_code_err!( tcx.dcx(), rs.first().unwrap().1, @@ -1386,19 +1385,6 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { ); } - let repr_type_ty = def.repr().discr_type().to_ty(tcx); - if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { - if !tcx.features().repr128() { - feature_err( - &tcx.sess, - sym::repr128, - tcx.def_span(def_id), - "repr with 128-bit type is unstable", - ) - .emit(); - } - } - for v in def.variants() { if let ty::VariantDiscr::Explicit(discr_def_id) = v.discr { tcx.ensure_ok().typeck(discr_def_id.expect_local()); @@ -1576,7 +1562,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD let mut params_used = DenseBitSet::new_empty(generics.own_params.len()); for leaf in ty.walk() { - if let GenericArgKind::Type(leaf_ty) = leaf.unpack() + if let GenericArgKind::Type(leaf_ty) = leaf.kind() && let ty::Param(param) = leaf_ty.kind() { debug!("found use of ty param {:?}", param); @@ -1701,7 +1687,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorG let mut label_match = |ty: Ty<'_>, span| { for arg in ty.walk() { - if let ty::GenericArgKind::Type(ty) = arg.unpack() + if let ty::GenericArgKind::Type(ty) = arg.kind() && let ty::Alias( ty::Opaque, ty::AliasTy { def_id: captured_def_id, .. }, diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 2ee41e2efbd3..47681a78ecca 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1231,7 +1231,7 @@ fn check_region_late_boundedness<'tcx>( for (id_arg, arg) in std::iter::zip(ty::GenericArgs::identity_for_item(tcx, impl_m.def_id), impl_m_args) { - if let ty::GenericArgKind::Lifetime(r) = arg.unpack() + if let ty::GenericArgKind::Lifetime(r) = arg.kind() && let ty::ReVar(vid) = r.kind() && let r = infcx .inner @@ -1256,7 +1256,7 @@ fn check_region_late_boundedness<'tcx>( for (id_arg, arg) in std::iter::zip(ty::GenericArgs::identity_for_item(tcx, trait_m.def_id), trait_m_args) { - if let ty::GenericArgKind::Lifetime(r) = arg.unpack() + if let ty::GenericArgKind::Lifetime(r) = arg.kind() && let ty::ReVar(vid) = r.kind() && let r = infcx .inner 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 4973d8489597..3db1c40228f6 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 @@ -427,7 +427,7 @@ fn report_mismatched_rpitit_captures<'tcx>( }; trait_captured_args - .sort_by_cached_key(|arg| !matches!(arg.unpack(), ty::GenericArgKind::Lifetime(_))); + .sort_by_cached_key(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_))); let suggestion = format!("use<{}>", trait_captured_args.iter().join(", ")); tcx.emit_node_span_lint( diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 9fd158ad154d..09610a2f3ec6 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -204,24 +204,25 @@ pub(crate) fn check_intrinsic_type( // Each atomic op has variants with different suffixes (`_seq_cst`, `_acquire`, etc.). Use // string ops to strip the suffixes, because the variants all get the same treatment here. - let (n_tps, inputs, output) = match split[1] { + let (n_tps, n_cts, inputs, output) = match split[1] { "cxchg" | "cxchgweak" => ( 1, + 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]), ), - "load" => (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)), - "store" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit), + "load" => (1, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)), + "store" => (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit), "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax" - | "umin" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)), - "fence" | "singlethreadfence" => (0, Vec::new(), tcx.types.unit), + | "umin" => (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)), + "fence" | "singlethreadfence" => (0, 0, Vec::new(), tcx.types.unit), op => { tcx.dcx().emit_err(UnrecognizedAtomicOperation { span, op }); return; } }; - (n_tps, 0, 0, inputs, output, hir::Safety::Unsafe) + (n_tps, 0, n_cts, inputs, output, hir::Safety::Unsafe) } else if intrinsic_name == sym::contract_check_ensures { // contract_check_ensures::(Ret, C) -> Ret // where C: for<'a> Fn(&'a Ret) -> bool, @@ -264,6 +265,7 @@ pub(crate) fn check_intrinsic_type( vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.isize], Ty::new_imm_ptr(tcx, param(0)), ), + sym::slice_get_unchecked => (3, 0, vec![param(1), tcx.types.usize], param(0)), sym::ptr_mask => ( 1, 0, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2ec14b2f018c..b764b714fe17 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -297,34 +297,30 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() hir::ItemKind::Fn { ident, sig, .. } => { check_item_fn(tcx, def_id, ident, item.span, sig.decl) } - hir::ItemKind::Static(_, ty, ..) => { - check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid) + hir::ItemKind::Static(_, _, ty, _) => { + check_static_item(tcx, def_id, ty.span, UnsizedHandling::Forbid) } - hir::ItemKind::Const(_, ty, ..) => { - check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid) - } - hir::ItemKind::Struct(_, _, hir_generics) => { + hir::ItemKind::Const(_, _, ty, _) => check_const_item(tcx, def_id, ty.span, item.span), + hir::ItemKind::Struct(_, generics, _) => { let res = check_type_defn(tcx, item, false); - check_variances_for_type_defn(tcx, item, hir_generics); + check_variances_for_type_defn(tcx, item, generics); res } - hir::ItemKind::Union(_, _, hir_generics) => { + hir::ItemKind::Union(_, generics, _) => { let res = check_type_defn(tcx, item, true); - check_variances_for_type_defn(tcx, item, hir_generics); + check_variances_for_type_defn(tcx, item, generics); res } - hir::ItemKind::Enum(_, _, hir_generics) => { + hir::ItemKind::Enum(_, generics, _) => { let res = check_type_defn(tcx, item, true); - check_variances_for_type_defn(tcx, item, hir_generics); + check_variances_for_type_defn(tcx, item, generics); res } hir::ItemKind::Trait(..) => check_trait(tcx, item), hir::ItemKind::TraitAlias(..) => check_trait(tcx, item), // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => Ok(()), - hir::ItemKind::TyAlias(_, hir_ty, hir_generics) - if tcx.type_alias_is_lazy(item.owner_id) => - { + hir::ItemKind::TyAlias(_, generics, hir_ty) if tcx.type_alias_is_lazy(item.owner_id) => { let res = enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { let ty = tcx.type_of(def_id).instantiate_identity(); let item_ty = @@ -337,7 +333,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() check_where_clauses(wfcx, item.span, def_id); Ok(()) }); - check_variances_for_type_defn(tcx, item, hir_generics); + check_variances_for_type_defn(tcx, item, generics); res } _ => Ok(()), @@ -366,7 +362,7 @@ fn check_foreign_item<'tcx>( check_item_fn(tcx, def_id, item.ident, item.span, sig.decl) } hir::ForeignItemKind::Static(ty, ..) => { - check_item_type(tcx, def_id, ty.span, UnsizedHandling::AllowIfForeignTail) + check_static_item(tcx, def_id, ty.span, UnsizedHandling::AllowIfForeignTail) } hir::ForeignItemKind::Type => Ok(()), } @@ -742,7 +738,7 @@ fn ty_known_to_outlive<'tcx>( region: ty::Region<'tcx>, ) -> bool { test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { - infcx.register_region_obligation(infer::RegionObligation { + infcx.register_type_outlives_constraint_inner(infer::TypeOutlivesConstraint { sub_region: region, sup_type: ty, origin: infer::RelateParamBound(DUMMY_SP, ty, None), @@ -819,7 +815,7 @@ impl<'tcx> TypeVisitor> for GATArgsCollector<'tcx> { match t.kind() { ty::Alias(ty::Projection, p) if p.def_id == self.gat => { for (idx, arg) in p.args.iter().enumerate() { - match arg.unpack() { + match arg.kind() { GenericArgKind::Lifetime(lt) if !lt.is_bound() => { self.regions.insert((lt, idx)); } @@ -1048,8 +1044,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), match ty.kind() { ty::Adt(adt_def, ..) => adt_def.did().is_local(), // Arrays and slices use the inner type's `ConstParamTy`. - ty::Array(ty, ..) => ty_is_local(*ty), - ty::Slice(ty) => ty_is_local(*ty), + ty::Array(ty, ..) | ty::Slice(ty) => ty_is_local(*ty), // `&` references use the inner type's `ConstParamTy`. // `&mut` are not supported. ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty), @@ -1331,14 +1326,13 @@ enum UnsizedHandling { AllowIfForeignTail, } -fn check_item_type( +#[instrument(level = "debug", skip(tcx, ty_span, unsized_handling))] +fn check_static_item( tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, unsized_handling: UnsizedHandling, ) -> Result<(), ErrorGuaranteed> { - debug!("check_item_type: {:?}", item_id); - enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| { let ty = tcx.type_of(item_id).instantiate_identity(); let item_ty = wfcx.deeply_normalize(ty_span, Some(WellFormedLoc::Ty(item_id)), ty); @@ -1388,6 +1382,34 @@ fn check_item_type( }) } +fn check_const_item( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + ty_span: Span, + item_span: Span, +) -> Result<(), ErrorGuaranteed> { + enter_wf_checking_ctxt(tcx, ty_span, def_id, |wfcx| { + let ty = tcx.type_of(def_id).instantiate_identity(); + let ty = wfcx.deeply_normalize(ty_span, Some(WellFormedLoc::Ty(def_id)), ty); + + wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(def_id)), ty.into()); + wfcx.register_bound( + traits::ObligationCause::new( + ty_span, + wfcx.body_def_id, + ObligationCauseCode::SizedConstOrStatic, + ), + wfcx.param_env, + ty, + tcx.require_lang_item(LangItem::Sized, None), + ); + + check_where_clauses(wfcx, item_span, def_id); + + Ok(()) + }) +} + #[instrument(level = "debug", skip(tcx, hir_self_ty, hir_trait_ref))] fn check_impl<'tcx>( tcx: TyCtxt<'tcx>, @@ -1517,7 +1539,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id } else { // If we've got a generic const parameter we still want to check its // type is correct in case both it and the param type are fully concrete. - let GenericArgKind::Const(ct) = default.unpack() else { + let GenericArgKind::Const(ct) = default.kind() else { continue; }; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 4520fbe352ce..a649e7d67af3 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -31,7 +31,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt, walk_generics}; use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause}; use rustc_middle::hir::nested_filter; use rustc_middle::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; @@ -40,11 +40,10 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::{ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations}; use tracing::{debug, instrument}; use crate::errors; -use crate::hir_ty_lowering::errors::assoc_tag_str; use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason}; pub(crate) mod dump; @@ -89,6 +88,7 @@ pub(crate) fn provide(providers: &mut Providers) { opaque_ty_origin, rendered_precise_capturing_args, const_param_default, + anon_const_kind, ..*providers }; } @@ -248,13 +248,13 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( item: &'tcx hir::Item<'tcx>, ) { let (generics, suggest) = match &item.kind { - hir::ItemKind::Union(_, _, generics) - | hir::ItemKind::Enum(_, _, generics) + hir::ItemKind::Union(_, generics, _) + | hir::ItemKind::Enum(_, generics, _) | hir::ItemKind::TraitAlias(_, generics, _) | hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) - | hir::ItemKind::Struct(_, _, generics) => (generics, true), - hir::ItemKind::TyAlias(_, _, generics) => (generics, false), + | hir::ItemKind::Struct(_, generics, _) => (generics, true), + hir::ItemKind::TyAlias(_, generics, _) => (generics, false), // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. _ => return, }; @@ -444,13 +444,12 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_ident)) } - fn lower_assoc_shared( + fn lower_assoc_item_path( &self, span: Span, item_def_id: DefId, item_segment: &rustc_hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - assoc_tag: ty::AssocTag, ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { let item_args = self.lowerer().lower_generic_args_of_assoc_item( @@ -471,9 +470,9 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { .tcx .hir_expect_item(self.tcx.hir_get_parent_item(self.hir_id()).def_id); match &item.kind { - hir::ItemKind::Enum(_, _, generics) - | hir::ItemKind::Struct(_, _, generics) - | hir::ItemKind::Union(_, _, generics) => { + hir::ItemKind::Enum(_, generics, _) + | hir::ItemKind::Struct(_, generics, _) + | hir::ItemKind::Union(_, generics, _) => { let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics); let (lt_sp, sugg) = match generics.params { [] => (generics.span, format!("<{lt_name}>")), @@ -525,7 +524,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { inferred_sugg, bound, mpart_sugg, - what: assoc_tag_str(assoc_tag), + what: self.tcx.def_descr(item_def_id), })) } } @@ -626,6 +625,10 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { (input_tys, output_ty) } + + fn dyn_compatibility_violations(&self, trait_def_id: DefId) -> Vec { + hir_ty_lowering_dyn_compatibility_violations(self.tcx, trait_def_id) + } } /// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. @@ -737,7 +740,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.at(it.span).explicit_super_predicates_of(def_id); tcx.ensure_ok().predicates_of(def_id); } - hir::ItemKind::Struct(_, struct_def, _) | hir::ItemKind::Union(_, struct_def, _) => { + hir::ItemKind::Struct(_, _, struct_def) | hir::ItemKind::Union(_, _, struct_def) => { tcx.ensure_ok().generics_of(def_id); tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); @@ -759,7 +762,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure_ok().predicates_of(def_id); } - hir::ItemKind::Static(_, ty, ..) | hir::ItemKind::Const(_, ty, ..) => { + hir::ItemKind::Static(_, _, ty, _) | hir::ItemKind::Const(_, _, ty, _) => { tcx.ensure_ok().generics_of(def_id); tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); @@ -1090,7 +1093,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { let repr = tcx.repr_options_of_def(def_id); let (kind, variants) = match &item.kind { - ItemKind::Enum(_, def, _) => { + ItemKind::Enum(_, _, def) => { let mut distance_from_explicit = 0; let variants = def .variants @@ -1118,7 +1121,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { (AdtKind::Enum, variants) } - ItemKind::Struct(ident, def, _) | ItemKind::Union(ident, def, _) => { + ItemKind::Struct(ident, _, def) | ItemKind::Union(ident, _, def) => { let adt_kind = match item.kind { ItemKind::Struct(..) => AdtKind::Struct, _ => AdtKind::Union, @@ -1828,3 +1831,27 @@ fn const_param_default<'tcx>( .lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id(), identity_args)); ty::EarlyBinder::bind(ct) } + +fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKind { + let hir_id = tcx.local_def_id_to_hir_id(def); + let const_arg_id = tcx.parent_hir_id(hir_id); + match tcx.hir_node(const_arg_id) { + hir::Node::ConstArg(_) => { + if tcx.features().generic_const_exprs() { + ty::AnonConstKind::GCE + } else if tcx.features().min_generic_const_args() { + ty::AnonConstKind::MCG + } else if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Repeat(_, repeat_count), + .. + }) = tcx.hir_node(tcx.parent_hir_id(const_arg_id)) + && repeat_count.hir_id == const_arg_id + { + ty::AnonConstKind::RepeatExprCount + } else { + ty::AnonConstKind::MCG + } + } + _ => ty::AnonConstKind::NonTypeSystem, + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 2bed28d7b710..7eb896f0bf14 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -104,19 +104,27 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } } - if in_param_ty { - // We do not allow generic parameters in anon consts if we are inside - // of a const parameter type, e.g. `struct Foo` is not allowed. - None - } else if tcx.features().generic_const_exprs() { - let parent_node = tcx.parent_hir_node(hir_id); - debug!(?parent_node); - if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node - && constant.hir_id == hir_id - { - // enum variant discriminants are not allowed to use any kind of generics - None - } else if let Some(param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id) + match tcx.anon_const_kind(def_id) { + // Stable: anon consts are not able to use any generic parameters... + ty::AnonConstKind::MCG => None, + // we provide generics to repeat expr counts as a backwards compatibility hack. #76200 + ty::AnonConstKind::RepeatExprCount => Some(parent_did), + + // Even GCE anon const should not be allowed to use generic parameters as it would be + // trivially forward declared uses once desugared. E.g. `const N: [u8; ANON::]`. + // + // We could potentially mirror the hack done for defaults of generic parameters but + // this case just doesn't come up much compared to `const N: u32 = ...`. Long term the + // hack for defaulted parameters should be removed eventually anyway. + ty::AnonConstKind::GCE if in_param_ty => None, + // GCE anon consts as a default for a generic parameter should have their provided generics + // "truncated" up to whatever generic parameter this anon const is within the default of. + // + // FIXME(generic_const_exprs): This only handles `const N: usize = /*defid*/` but not type + // parameter defaults, e.g. `T = Foo`. + ty::AnonConstKind::GCE + if let Some(param_id) = + tcx.hir_opt_const_param_default_param_def_id(hir_id) => { // If the def_id we are calling generics_of on is an anon ct default i.e: // @@ -160,36 +168,17 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { has_self: generics.has_self, has_late_bound_regions: generics.has_late_bound_regions, }; - } else { - // HACK(eddyb) this provides the correct generics when - // `feature(generic_const_expressions)` is enabled, so that const expressions - // used with const generics, e.g. `Foo<{N+1}>`, can work at all. - // - // Note that we do not supply the parent generics when using - // `min_const_generics`. + } + ty::AnonConstKind::GCE => Some(parent_did), + + // Field defaults are allowed to use generic parameters, e.g. `field: u32 = /*defid: N + 1*/` + ty::AnonConstKind::NonTypeSystem + if matches!(tcx.parent_hir_node(hir_id), Node::TyPat(_) | Node::Field(_)) => + { Some(parent_did) } - } else { - let parent_node = tcx.parent_hir_node(hir_id); - let parent_node = match parent_node { - Node::ConstArg(ca) => tcx.parent_hir_node(ca.hir_id), - _ => parent_node, - }; - match parent_node { - // HACK(eddyb) this provides the correct generics for repeat - // expressions' count (i.e. `N` in `[x; N]`), and explicit - // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), - // as they shouldn't be able to cause query cycle errors. - Node::Expr(Expr { kind: ExprKind::Repeat(_, ct), .. }) - if ct.anon_const_hir_id() == Some(hir_id) => - { - Some(parent_did) - } - Node::TyPat(_) => Some(parent_did), - // Field default values inherit the ADT's generics. - Node::Field(_) => Some(parent_did), - _ => None, - } + // Default to no generic parameters for other kinds of anon consts + ty::AnonConstKind::NonTypeSystem => None, } } Node::ConstBlock(_) diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 279b1e82a716..5f1cdeddc193 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -151,7 +151,7 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( let mut mapping = FxIndexMap::default(); let generics = tcx.generics_of(assoc_item_def_id); for (param, var) in std::iter::zip(&generics.own_params, gat_vars) { - let existing = match var.unpack() { + let existing = match var.kind() { ty::GenericArgKind::Lifetime(re) => { if let ty::RegionKind::ReBound(ty::INNERMOST, bv) = re.kind() { mapping.insert(bv.var, tcx.mk_param_from_def(param)) 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 709446d09cd2..d45f0475e991 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -634,11 +634,11 @@ 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::TyAlias(_, _, generics) - | hir::ItemKind::Const(_, _, generics, _) - | hir::ItemKind::Enum(_, _, generics) - | hir::ItemKind::Struct(_, _, generics) - | hir::ItemKind::Union(_, _, generics) + hir::ItemKind::TyAlias(_, generics, _) + | hir::ItemKind::Const(_, generics, _, _) + | hir::ItemKind::Enum(_, generics, _) + | hir::ItemKind::Struct(_, generics, _) + | hir::ItemKind::Union(_, generics, _) | hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::TraitAlias(_, generics, ..) | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c20b14df7704..141d96b57e57 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -206,7 +206,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ }, Node::Item(item) => match item.kind { - ItemKind::Static(ident, ty, .., body_id) => { + ItemKind::Static(_, ident, ty, body_id) => { if ty.is_suggestable_infer_ty() { infer_placeholder_type( icx.lowerer(), @@ -220,7 +220,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ icx.lower_ty(ty) } } - ItemKind::Const(ident, ty, _, body_id) => { + ItemKind::Const(ident, _, ty, body_id) => { if ty.is_suggestable_infer_ty() { infer_placeholder_type( icx.lowerer(), @@ -234,7 +234,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ icx.lower_ty(ty) } } - ItemKind::TyAlias(_, self_ty, _) => icx.lower_ty(self_ty), + ItemKind::TyAlias(_, _, self_ty) => icx.lower_ty(self_ty), ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() { spans if spans.len() > 0 => { let guar = tcx @@ -532,5 +532,5 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> } } } - HasTait.visit_ty_unambig(tcx.hir_expect_item(def_id).expect_ty_alias().1).is_break() + HasTait.visit_ty_unambig(tcx.hir_expect_item(def_id).expect_ty_alias().2).is_break() } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 4419d5dc7d66..bdc42c7a2d90 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -4,9 +4,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; +use rustc_hir::AmbigArg; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{AmbigArg, HirId}; use rustc_middle::bug; use rustc_middle::ty::{ self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, @@ -309,7 +309,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { false => "`?Sized`", }; // There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`. - tcx.dcx().span_err( + self.dcx().span_err( unbound.span, format!( "relaxing a default bound only does something for {}; \ @@ -675,7 +675,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Good error for `where Trait::method(..): Send`. let Some(self_ty) = opt_self_ty else { - let guar = self.error_missing_qpath_self_ty( + let guar = self.report_missing_self_ty_for_resolved_path( trait_def_id, hir_ty.span, item_segment, @@ -713,21 +713,45 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Err(guar) => Ty::new_error(tcx, guar), } } - hir::QPath::TypeRelative(qself, item_segment) - if item_segment.args.is_some_and(|args| { + hir::QPath::TypeRelative(hir_self_ty, segment) + if segment.args.is_some_and(|args| { matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation) }) => { - match self - .resolve_type_relative_return_type_notation( - qself, - item_segment, - hir_ty.hir_id, - hir_ty.span, - ) - .and_then(|(candidate, item_def_id)| { - self.lower_return_type_notation_ty(candidate, item_def_id, hir_ty.span) - }) { + let self_ty = self.lower_ty(hir_self_ty); + let (item_def_id, bound) = match self.resolve_type_relative_path( + self_ty, + hir_self_ty, + ty::AssocTag::Fn, + segment, + hir_ty.hir_id, + hir_ty.span, + None, + ) { + Ok(result) => result, + Err(guar) => return Ty::new_error(tcx, guar), + }; + + // Don't let `T::method` resolve to some `for<'a> >::method`, + // which may happen via a higher-ranked where clause or supertrait. + // This is the same restrictions as associated types; even though we could + // support it, it just makes things a lot more difficult to support in + // `resolve_bound_vars`, since we'd need to introduce those as elided + // bound vars on the where clause too. + if bound.has_bound_vars() { + return Ty::new_error( + tcx, + self.dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams { + span: hir_ty.span, + inferred_sugg: Some(hir_ty.span.with_hi(segment.ident.span.lo())), + bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder()), + mpart_sugg: None, + what: tcx.def_descr(item_def_id), + }), + ); + } + + match self.lower_return_type_notation_ty(bound, item_def_id, hir_ty.span) { Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty), Err(guar) => Ty::new_error(tcx, guar), } @@ -736,99 +760,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Perform type-dependent lookup for a *method* for return type notation. - /// This generally mirrors `::lower_assoc_path`. - fn resolve_type_relative_return_type_notation( - &self, - qself: &'tcx hir::Ty<'tcx>, - item_segment: &'tcx hir::PathSegment<'tcx>, - qpath_hir_id: HirId, - span: Span, - ) -> Result<(ty::PolyTraitRef<'tcx>, DefId), ErrorGuaranteed> { - let tcx = self.tcx(); - let qself_ty = self.lower_ty(qself); - let assoc_ident = item_segment.ident; - let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { - path.res - } else { - Res::Err - }; - - let bound = match (qself_ty.kind(), qself_res) { - (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => { - // `Self` in an impl of a trait -- we have a concrete self type and a - // trait reference. - let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { - // A cycle error occurred, most likely. - self.dcx().span_bug(span, "expected cycle error"); - }; - - self.probe_single_bound_for_assoc_item( - || { - traits::supertraits( - tcx, - ty::Binder::dummy(trait_ref.instantiate_identity()), - ) - }, - AssocItemQSelf::SelfTyAlias, - ty::AssocTag::Fn, - assoc_ident, - span, - None, - )? - } - ( - &ty::Param(_), - Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), - ) => self.probe_single_ty_param_bound_for_assoc_item( - param_did.expect_local(), - qself.span, - ty::AssocTag::Fn, - assoc_ident, - span, - )?, - _ => { - if let Err(reported) = qself_ty.error_reported() { - return Err(reported); - } else { - // FIXME(return_type_notation): Provide some structured suggestion here. - let err = struct_span_code_err!( - self.dcx(), - span, - E0223, - "ambiguous associated function" - ); - return Err(err.emit()); - } - } - }; - - // Don't let `T::method` resolve to some `for<'a> >::method`, - // which may happen via a higher-ranked where clause or supertrait. - // This is the same restrictions as associated types; even though we could - // support it, it just makes things a lot more difficult to support in - // `resolve_bound_vars`, since we'd need to introduce those as elided - // bound vars on the where clause too. - if bound.has_bound_vars() { - return Err(self.tcx().dcx().emit_err( - errors::AssociatedItemTraitUninferredGenericParams { - span, - inferred_sugg: Some(span.with_hi(item_segment.ident.span.lo())), - bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder(),), - mpart_sugg: None, - what: "function", - }, - )); - } - - let trait_def_id = bound.def_id(); - let assoc_ty = self - .probe_assoc_item(assoc_ident, ty::AssocTag::Fn, qpath_hir_id, span, trait_def_id) - .expect("failed to find associated type"); - - Ok((bound, assoc_ty.def_id)) - } - /// Do the common parts of lowering an RTN type. This involves extending the /// candidate binder to include all of the early- and late-bound vars that are /// defined on the function itself, and constructing a projection to the RPITIT diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index d1ee5a5494c0..ebeb3b58208e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -35,7 +35,7 @@ pub(crate) fn validate_cmse_abi<'tcx>( _ => tcx.hir_span(hir_id), }; struct_span_code_err!( - tcx.dcx(), + dcx, span, E0781, "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers" 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 f6e5149bd2b1..c7cdf1d5bd27 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 @@ -11,7 +11,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_trait_selection::traits; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -78,15 +78,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // We don't support empty trait objects. if regular_traits.is_empty() && auto_traits.is_empty() { - let guar = self.report_trait_object_with_no_traits_error( - span, - user_written_bounds.iter().copied(), - ); + let guar = + self.report_trait_object_with_no_traits(span, user_written_bounds.iter().copied()); return Ty::new_error(tcx, guar); } // We don't support >1 principal if regular_traits.len() > 1 { - let guar = self.report_trait_object_addition_traits_error(®ular_traits); + let guar = self.report_trait_object_addition_traits(®ular_traits); return Ty::new_error(tcx, guar); } // Don't create a dyn trait if we have errors in the principal. @@ -99,8 +97,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // to avoid ICEs. for (clause, span) in user_written_bounds { if let Some(trait_pred) = clause.as_trait_clause() { - let violations = - hir_ty_lowering_dyn_compatibility_violations(tcx, trait_pred.def_id()); + let violations = self.dyn_compatibility_violations(trait_pred.def_id()); if !violations.is_empty() { let reported = report_dyn_incompatibility( tcx, @@ -132,7 +129,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if references_self { // With trait alias and type alias combined, type resolver // may not be able to catch all illegal `Self` usages (issue 139082) - let guar = tcx.dcx().emit_err(SelfInTypeAlias { span }); + let guar = self.dcx().emit_err(SelfInTypeAlias { span }); b.term = replace_dummy_self_with_error(tcx, b.term, guar); } } @@ -218,7 +215,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let pred = bound_predicate.rebind(pred); // A `Self` within the original bound will be instantiated with a // `trait_object_dummy_self`, so check for that. - let references_self = match pred.skip_binder().term.unpack() { + let references_self = match pred.skip_binder().term.kind() { ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()), // FIXME(associated_const_equality): We should walk the const instead of not doing anything ty::TermKind::Const(_) => false, @@ -360,7 +357,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); - self.complain_about_missing_type_params( + self.report_missing_type_params( missing_type_params, trait_ref.def_id, span, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 3759a224ff75..45fee0fa4024 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -3,11 +3,12 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, listify, pluralize, struct_span_code_err, + Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, listify, pluralize, + struct_span_code_err, }; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, HirId}; use rustc_middle::bug; use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; @@ -23,6 +24,7 @@ use rustc_trait_selection::traits::{ FulfillmentError, dyn_compatibility_violations_for_assoc_item, }; use smallvec::SmallVec; +use tracing::debug; use crate::errors::{ self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams, @@ -34,7 +36,7 @@ use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// On missing type parameters, emit an E0393 error and provide a structured suggestion using /// the type parameter's name as a placeholder. - pub(crate) fn complain_about_missing_type_params( + pub(crate) fn report_missing_type_params( &self, missing_type_params: Vec, def_id: DefId, @@ -56,7 +58,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit /// an error and attempt to build a reasonable structured suggestion. - pub(crate) fn complain_about_internal_fn_trait( + pub(crate) fn report_internal_fn_trait( &self, span: Span, trait_def_id: DefId, @@ -112,7 +114,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub(super) fn complain_about_assoc_item_not_found( + pub(super) fn report_unresolved_assoc_item( &self, all_candidates: impl Fn() -> I, qself: AssocItemQSelf, @@ -132,7 +134,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .filter_by_name_unhygienic(assoc_ident.name) .find(|item| tcx.hygienic_eq(assoc_ident, item.ident(tcx), r.def_id())) }) { - return self.complain_about_assoc_kind_mismatch( + return self.report_assoc_kind_mismatch( assoc_item, assoc_tag, assoc_ident, @@ -331,7 +333,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.dcx().emit_err(err) } - fn complain_about_assoc_kind_mismatch( + fn report_assoc_kind_mismatch( &self, assoc_item: &ty::AssocItem, assoc_tag: ty::AssocTag, @@ -396,12 +398,177 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) } - pub(super) fn report_ambiguous_assoc( + pub(crate) fn report_missing_self_ty_for_resolved_path( + &self, + trait_def_id: DefId, + span: Span, + item_segment: &hir::PathSegment<'tcx>, + assoc_tag: ty::AssocTag, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let path_str = tcx.def_path_str(trait_def_id); + + let def_id = self.item_def_id(); + debug!(item_def_id = ?def_id); + + // FIXME: document why/how this is different from `tcx.local_parent(def_id)` + let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id(); + debug!(?parent_def_id); + + // If the trait in segment is the same as the trait defining the item, + // use the `` syntax in the error. + let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id; + let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id; + + let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { + vec!["Self".to_string()] + } else { + // Find all the types that have an `impl` for the trait. + tcx.all_impls(trait_def_id) + .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) + .filter(|header| { + // Consider only accessible traits + tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) + && header.polarity != ty::ImplPolarity::Negative + }) + .map(|header| header.trait_ref.instantiate_identity().self_ty()) + // We don't care about blanket impls. + .filter(|self_ty| !self_ty.has_non_region_param()) + .map(|self_ty| tcx.erase_regions(self_ty).to_string()) + .collect() + }; + // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that + // references the trait. Relevant for the first case in + // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` + self.report_ambiguous_assoc_item_path( + span, + &type_names, + &[path_str], + item_segment.ident, + assoc_tag, + ) + } + + pub(super) fn report_unresolved_type_relative_path( + &self, + self_ty: Ty<'tcx>, + hir_self_ty: &hir::Ty<'_>, + assoc_tag: ty::AssocTag, + ident: Ident, + qpath_hir_id: HirId, + span: Span, + variant_def_id: Option, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let kind_str = assoc_tag_str(assoc_tag); + if variant_def_id.is_some() { + // Variant in type position + let msg = format!("expected {kind_str}, found variant `{ident}`"); + self.dcx().span_err(span, msg) + } else if self_ty.is_enum() { + let mut err = self.dcx().create_err(errors::NoVariantNamed { + span: ident.span, + ident, + ty: self_ty, + }); + + let adt_def = self_ty.ty_adt_def().expect("enum is not an ADT"); + if let Some(variant_name) = find_best_match_for_name( + &adt_def.variants().iter().map(|variant| variant.name).collect::>(), + ident.name, + None, + ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == variant_name) + { + let mut suggestion = vec![(ident.span, variant_name.to_string())]; + if let hir::Node::Stmt(&hir::Stmt { kind: hir::StmtKind::Semi(expr), .. }) + | hir::Node::Expr(expr) = tcx.parent_hir_node(qpath_hir_id) + && let hir::ExprKind::Struct(..) = expr.kind + { + match variant.ctor { + None => { + // struct + suggestion = vec![( + ident.span.with_hi(expr.span.hi()), + if variant.fields.is_empty() { + format!("{variant_name} {{}}") + } else { + format!( + "{variant_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + Some((hir::def::CtorKind::Fn, def_id)) => { + // tuple + let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + ident.span.with_hi(expr.span.hi()), + format!( + "{variant_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + Some((hir::def::CtorKind::Const, _)) => { + // unit + suggestion = vec![( + ident.span.with_hi(expr.span.hi()), + variant_name.to_string(), + )]; + } + } + } + err.multipart_suggestion_verbose( + "there is a variant with a similar name", + suggestion, + Applicability::HasPlaceholders, + ); + } else { + err.span_label(ident.span, format!("variant not found in `{self_ty}`")); + } + + if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) { + err.span_label(sp, format!("variant `{ident}` not found here")); + } + + err.emit() + } else if let Err(reported) = self_ty.error_reported() { + reported + } else { + match self.maybe_report_similar_assoc_fn(span, self_ty, hir_self_ty) { + Ok(()) => {} + Err(reported) => return reported, + } + + let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(self_ty, ident); + + self.report_ambiguous_assoc_item_path( + span, + &[self_ty.to_string()], + &traits, + ident, + assoc_tag, + ) + } + } + + pub(super) fn report_ambiguous_assoc_item_path( &self, span: Span, types: &[String], traits: &[String], - name: Symbol, + ident: Ident, assoc_tag: ty::AssocTag, ) -> ErrorGuaranteed { let kind_str = assoc_tag_str(assoc_tag); @@ -421,6 +588,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Applicability::MachineApplicable, ); } else { + let sugg_sp = span.until(ident.span); + let mut types = types.to_vec(); types.sort(); let mut traits = traits.to_vec(); @@ -428,76 +597,79 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match (&types[..], &traits[..]) { ([], []) => { err.span_suggestion_verbose( - span, + sugg_sp, format!( "if there were a type named `Type` that implements a trait named \ - `Trait` with associated {kind_str} `{name}`, you could use the \ + `Trait` with associated {kind_str} `{ident}`, you could use the \ fully-qualified path", ), - format!("::{name}"), + "::", Applicability::HasPlaceholders, ); } ([], [trait_str]) => { err.span_suggestion_verbose( - span, + sugg_sp, format!( "if there were a type named `Example` that implemented `{trait_str}`, \ you could use the fully-qualified path", ), - format!("::{name}"), + format!("::"), Applicability::HasPlaceholders, ); } ([], traits) => { - err.span_suggestions( - span, + err.span_suggestions_with_style( + sugg_sp, format!( "if there were a type named `Example` that implemented one of the \ - traits with associated {kind_str} `{name}`, you could use the \ + traits with associated {kind_str} `{ident}`, you could use the \ fully-qualified path", ), - traits.iter().map(|trait_str| format!("::{name}")), + traits.iter().map(|trait_str| format!("::")), Applicability::HasPlaceholders, + SuggestionStyle::ShowAlways, ); } ([type_str], []) => { err.span_suggestion_verbose( - span, + sugg_sp, format!( - "if there were a trait named `Example` with associated {kind_str} `{name}` \ + "if there were a trait named `Example` with associated {kind_str} `{ident}` \ implemented for `{type_str}`, you could use the fully-qualified path", ), - format!("<{type_str} as Example>::{name}"), + format!("<{type_str} as Example>::"), Applicability::HasPlaceholders, ); } (types, []) => { - err.span_suggestions( - span, + err.span_suggestions_with_style( + sugg_sp, format!( - "if there were a trait named `Example` with associated {kind_str} `{name}` \ + "if there were a trait named `Example` with associated {kind_str} `{ident}` \ implemented for one of the types, you could use the fully-qualified \ path", ), types .into_iter() - .map(|type_str| format!("<{type_str} as Example>::{name}")), + .map(|type_str| format!("<{type_str} as Example>::")), Applicability::HasPlaceholders, + SuggestionStyle::ShowAlways, ); } (types, traits) => { let mut suggestions = vec![]; for type_str in types { for trait_str in traits { - suggestions.push(format!("<{type_str} as {trait_str}>::{name}")); + suggestions.push(format!("<{type_str} as {trait_str}>::")); } } - err.span_suggestions( - span, + err.span_suggestions_with_style( + sugg_sp, "use fully-qualified syntax", suggestions, Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, ); } } @@ -505,7 +677,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.emit() } - pub(crate) fn complain_about_ambiguous_inherent_assoc( + pub(crate) fn report_ambiguous_inherent_assoc_item( &self, name: Ident, candidates: Vec, @@ -518,12 +690,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { "multiple applicable items in scope" ); err.span_label(name.span, format!("multiple `{name}` found")); - self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span); + self.note_ambiguous_inherent_assoc_item(&mut err, candidates, span); err.emit() } // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. - fn note_ambiguous_inherent_assoc_ty( + fn note_ambiguous_inherent_assoc_item( &self, err: &mut Diag<'_>, candidates: Vec, @@ -566,7 +738,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. - pub(crate) fn complain_about_inherent_assoc_not_found( + pub(crate) fn report_unresolved_inherent_assoc_item( &self, name: Ident, self_ty: Ty<'tcx>, @@ -1046,7 +1218,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub fn report_prohibit_generics_error<'a>( + pub fn report_prohibited_generic_args<'a>( &self, segments: impl Iterator> + Clone, args_visitors: impl Iterator> + Clone, @@ -1128,7 +1300,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.emit() } - pub fn report_trait_object_addition_traits_error( + pub fn report_trait_object_addition_traits( &self, regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>, ) -> ErrorGuaranteed { @@ -1171,7 +1343,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.emit() } - pub fn report_trait_object_with_no_traits_error( + pub fn report_trait_object_with_no_traits( &self, span: Span, user_written_clauses: impl IntoIterator, Span)>, 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 483b61add338..9abae33ffdbb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -103,7 +103,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // In case there is an associated type with the same name // Add the suggestion to this error if let Some(mut sugg) = - tcx.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) + self.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) && let Suggestions::Enabled(ref mut s1) = diag.suggestions && let Suggestions::Enabled(ref mut s2) = sugg.suggestions { @@ -141,7 +141,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let generics = match tcx.hir_node_by_def_id(parent_item) { hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, variant, generics), + kind: hir::ItemKind::Struct(_, generics, variant), .. }) => { if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { @@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } generics } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, def, generics), .. }) => { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { if !def .variants .iter() @@ -269,7 +269,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::Node::Field(field) => { // Enums can't have unsized fields, fields can only have an unsized tail field. if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, variant, _), .. + kind: hir::ItemKind::Struct(_, _, variant), .. }) = tcx.parent_hir_node(field.hir_id) && variant .fields() 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 5e79e9320153..2a37a8bdbd47 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -33,27 +33,25 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ - self, AssocTag, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, - TyCtxt, TypeVisitableExt, TypingMode, Upcast, fold_regions, + self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, + TypeVisitableExt, TypingMode, Upcast, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_session::parse::feature_err; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; use tracing::{debug, instrument}; -use self::errors::assoc_tag_str; use crate::check::check_abi_fn_ptr; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -150,7 +148,7 @@ pub trait HirTyLowerer<'tcx> { assoc_ident: Ident, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>; - /// Lower an associated type/const (from a trait) to a projection. + /// Lower a path to an associated item (of a trait) to a projection. /// /// This method has to be defined by the concrete lowering context because /// dealing with higher-ranked trait references depends on its capabilities: @@ -162,13 +160,12 @@ pub trait HirTyLowerer<'tcx> { /// /// The canonical example of this is associated type `T::P` where `T` is a type /// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`. - fn lower_assoc_shared( + fn lower_assoc_item_path( &self, span: Span, item_def_id: DefId, item_segment: &hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - assoc_tag: ty::AssocTag, ) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed>; fn lower_fn_sig( @@ -203,6 +200,10 @@ pub trait HirTyLowerer<'tcx> { { self } + + /// Performs minimalistic dyn compat checks outside of bodies, but full within bodies. + /// Outside of bodies we could end up in cycles, so we delay most checks to later phases. + fn dyn_compatibility_violations(&self, trait_def_id: DefId) -> Vec; } /// The "qualified self" of an associated item path. @@ -245,39 +246,46 @@ pub enum FeedConstTy<'a, 'tcx> { } #[derive(Debug, Clone, Copy)] -enum LowerAssocMode { - Type { permit_variants: bool }, +enum LowerTypeRelativePathMode { + Type(PermitVariants), Const, } -impl LowerAssocMode { +impl LowerTypeRelativePathMode { fn assoc_tag(self) -> ty::AssocTag { match self { - LowerAssocMode::Type { .. } => ty::AssocTag::Type, - LowerAssocMode::Const => ty::AssocTag::Const, + Self::Type(_) => ty::AssocTag::Type, + Self::Const => ty::AssocTag::Const, } } fn def_kind(self) -> DefKind { match self { - LowerAssocMode::Type { .. } => DefKind::AssocTy, - LowerAssocMode::Const => DefKind::AssocConst, + Self::Type(_) => DefKind::AssocTy, + Self::Const => DefKind::AssocConst, } } - fn permit_variants(self) -> bool { + fn permit_variants(self) -> PermitVariants { match self { - LowerAssocMode::Type { permit_variants } => permit_variants, + Self::Type(permit_variants) => permit_variants, // FIXME(mgca): Support paths like `Option::::None` or `Option::::Some` which // resolve to const ctors/fn items respectively. - LowerAssocMode::Const => false, + Self::Const => PermitVariants::No, } } } +/// Whether to permit a path to resolve to an enum variant. #[derive(Debug, Clone, Copy)] -enum LoweredAssoc<'tcx> { - Term(DefId, GenericArgsRef<'tcx>), +pub enum PermitVariants { + Yes, + No, +} + +#[derive(Debug, Clone, Copy)] +enum TypeRelativePath<'tcx> { + AssocItem(DefId, GenericArgsRef<'tcx>), Variant { adt: Ty<'tcx>, variant_did: DefId }, } @@ -603,7 +611,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if !infer_args && has_default { // No type parameter provided, but a default exists. if let Some(prev) = - preceding_args.iter().find_map(|arg| match arg.unpack() { + preceding_args.iter().find_map(|arg| match arg.kind() { GenericArgKind::Type(ty) => ty.error_reported().err(), _ => None, }) @@ -745,7 +753,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_ref.path.segments.split_last().unwrap().1.iter(), GenericsArgsErrExtend::None, ); - self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); + self.report_internal_fn_trait(span, trait_def_id, trait_segment, false); let (generic_args, arg_count) = self.lower_generic_args_of_path( trait_ref.path.span, @@ -920,7 +928,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_segment: &hir::PathSegment<'tcx>, is_impl: bool, ) -> ty::TraitRef<'tcx> { - self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); + self.report_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); let (generic_args, _) = self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty)); @@ -933,7 +941,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn probe_trait_that_defines_assoc_item( &self, trait_def_id: DefId, - assoc_tag: AssocTag, + assoc_tag: ty::AssocTag, assoc_ident: Ident, ) -> bool { self.tcx() @@ -976,7 +984,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, ty_param_def_id: LocalDefId, ty_param_span: Span, - assoc_tag: AssocTag, + assoc_tag: ty::AssocTag, assoc_ident: Ident, span: Span, ) -> Result, ErrorGuaranteed> { @@ -1011,7 +1019,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, all_candidates: impl Fn() -> I, qself: AssocItemQSelf, - assoc_tag: AssocTag, + assoc_tag: ty::AssocTag, assoc_ident: Ident, span: Span, constraint: Option<&hir::AssocItemConstraint<'tcx>>, @@ -1026,15 +1034,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); let Some(bound) = matching_candidates.next() else { - let reported = self.complain_about_assoc_item_not_found( + return Err(self.report_unresolved_assoc_item( all_candidates, qself, assoc_tag, assoc_ident, span, constraint, - ); - return Err(reported); + )); }; debug!(?bound); @@ -1127,7 +1134,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ok(bound) } - /// Lower a [type-relative] path referring to an associated type or to an enum variant. + /// Lower a [type-relative](hir::QPath::TypeRelative) path in type position to a type. /// /// If the path refers to an enum variant and `permit_variants` holds, /// the returned type is simply the provided self type `qself_ty`. @@ -1148,61 +1155,63 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// described in the previous paragraph and their modeling of projections would likely be /// very similar in nature. /// - /// [type-relative]: hir::QPath::TypeRelative /// [#22519]: https://github.com/rust-lang/rust/issues/22519 /// [iat]: https://github.com/rust-lang/rust/issues/8995#issuecomment-1569208403 // // NOTE: When this function starts resolving `Trait::AssocTy` successfully // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. #[instrument(level = "debug", skip_all, ret)] - pub fn lower_assoc_path_ty( + pub fn lower_type_relative_ty_path( &self, - hir_ref_id: HirId, + self_ty: Ty<'tcx>, + hir_self_ty: &'tcx hir::Ty<'tcx>, + segment: &'tcx hir::PathSegment<'tcx>, + qpath_hir_id: HirId, span: Span, - qself_ty: Ty<'tcx>, - qself: &'tcx hir::Ty<'tcx>, - assoc_segment: &'tcx hir::PathSegment<'tcx>, - permit_variants: bool, + permit_variants: PermitVariants, ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { let tcx = self.tcx(); - match self.lower_assoc_path_shared( - hir_ref_id, + match self.lower_type_relative_path( + self_ty, + hir_self_ty, + segment, + qpath_hir_id, span, - qself_ty, - qself, - assoc_segment, - LowerAssocMode::Type { permit_variants }, + LowerTypeRelativePathMode::Type(permit_variants), )? { - LoweredAssoc::Term(def_id, args) => { + TypeRelativePath::AssocItem(def_id, args) => { let alias_ty = ty::AliasTy::new_from_args(tcx, def_id, args); let ty = Ty::new_alias(tcx, alias_ty.kind(tcx), alias_ty); Ok((ty, tcx.def_kind(def_id), def_id)) } - LoweredAssoc::Variant { adt, variant_did } => Ok((adt, DefKind::Variant, variant_did)), + TypeRelativePath::Variant { adt, variant_did } => { + Ok((adt, DefKind::Variant, variant_did)) + } } } + /// Lower a [type-relative][hir::QPath::TypeRelative] path to a (type-level) constant. #[instrument(level = "debug", skip_all, ret)] - fn lower_assoc_path_const( + fn lower_type_relative_const_path( &self, - hir_ref_id: HirId, + self_ty: Ty<'tcx>, + hir_self_ty: &'tcx hir::Ty<'tcx>, + segment: &'tcx hir::PathSegment<'tcx>, + qpath_hir_id: HirId, span: Span, - qself_ty: Ty<'tcx>, - qself: &'tcx hir::Ty<'tcx>, - assoc_segment: &'tcx hir::PathSegment<'tcx>, ) -> Result, ErrorGuaranteed> { let tcx = self.tcx(); - let (def_id, args) = match self.lower_assoc_path_shared( - hir_ref_id, + let (def_id, args) = match self.lower_type_relative_path( + self_ty, + hir_self_ty, + segment, + qpath_hir_id, span, - qself_ty, - qself, - assoc_segment, - LowerAssocMode::Const, + LowerTypeRelativePathMode::Const, )? { - LoweredAssoc::Term(def_id, args) => { + TypeRelativePath::AssocItem(def_id, args) => { if !tcx.associated_item(def_id).is_type_const_capable(tcx) { - let mut err = tcx.dcx().struct_span_err( + let mut err = self.dcx().struct_span_err( span, "use of trait associated const without `#[type_const]`", ); @@ -1213,75 +1222,134 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // FIXME(mgca): implement support for this once ready to support all adt ctor expressions, // not just const ctors - LoweredAssoc::Variant { .. } => { + TypeRelativePath::Variant { .. } => { span_bug!(span, "unexpected variant res for type associated const path") } }; Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(def_id, args))) } + /// Lower a [type-relative][hir::QPath::TypeRelative] (and type-level) path. #[instrument(level = "debug", skip_all, ret)] - fn lower_assoc_path_shared( + fn lower_type_relative_path( &self, - hir_ref_id: HirId, + self_ty: Ty<'tcx>, + hir_self_ty: &'tcx hir::Ty<'tcx>, + segment: &'tcx hir::PathSegment<'tcx>, + qpath_hir_id: HirId, span: Span, - qself_ty: Ty<'tcx>, - qself: &'tcx hir::Ty<'tcx>, - assoc_segment: &'tcx hir::PathSegment<'tcx>, - mode: LowerAssocMode, - ) -> Result, ErrorGuaranteed> { - debug!(%qself_ty, ?assoc_segment.ident); + mode: LowerTypeRelativePathMode, + ) -> Result, ErrorGuaranteed> { + debug!(%self_ty, ?segment.ident); let tcx = self.tcx(); - let assoc_ident = assoc_segment.ident; - // Check if we have an enum variant or an inherent associated type. - let mut variant_resolution = None; - if let Some(adt_def) = self.probe_adt(span, qself_ty) { + let mut variant_def_id = None; + if let Some(adt_def) = self.probe_adt(span, self_ty) { if adt_def.is_enum() { let variant_def = adt_def .variants() .iter() - .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did())); + .find(|vd| tcx.hygienic_eq(segment.ident, vd.ident(tcx), adt_def.did())); if let Some(variant_def) = variant_def { - if mode.permit_variants() { - tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); + if let PermitVariants::Yes = mode.permit_variants() { + tcx.check_stability(variant_def.def_id, Some(qpath_hir_id), span, None); let _ = self.prohibit_generic_args( - slice::from_ref(assoc_segment).iter(), - GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def }, + slice::from_ref(segment).iter(), + GenericsArgsErrExtend::EnumVariant { + qself: hir_self_ty, + assoc_segment: segment, + adt_def, + }, ); - return Ok(LoweredAssoc::Variant { - adt: qself_ty, + return Ok(TypeRelativePath::Variant { + adt: self_ty, variant_did: variant_def.def_id, }); } else { - variant_resolution = Some(variant_def.def_id); + variant_def_id = Some(variant_def.def_id); } } } // FIXME(inherent_associated_types, #106719): Support self types other than ADTs. - if let Some((did, args)) = self.probe_inherent_assoc_shared( - assoc_segment, + if let Some((did, args)) = self.probe_inherent_assoc_item( + segment, adt_def.did(), - qself_ty, - hir_ref_id, + self_ty, + qpath_hir_id, span, mode.assoc_tag(), )? { - return Ok(LoweredAssoc::Term(did, args)); + return Ok(TypeRelativePath::AssocItem(did, args)); } } - let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { - path.res - } else { - Res::Err + let (item_def_id, bound) = self.resolve_type_relative_path( + self_ty, + hir_self_ty, + mode.assoc_tag(), + segment, + qpath_hir_id, + span, + variant_def_id, + )?; + + let (item_def_id, args) = self.lower_assoc_item_path(span, item_def_id, segment, bound)?; + + if let Some(variant_def_id) = variant_def_id { + tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, qpath_hir_id, span, |lint| { + lint.primary_message("ambiguous associated item"); + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + segment.ident, + also, + tcx.def_kind_descr(kind, def_id) + ); + lint.span_note(tcx.def_span(def_id), note_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(mode.def_kind(), item_def_id, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!( + "<{} as {}>::{}", + self_ty, + tcx.item_name(bound.def_id()), + segment.ident + ), + Applicability::MachineApplicable, + ); + }); + } + + Ok(TypeRelativePath::AssocItem(item_def_id, args)) + } + + /// Resolve a [type-relative](hir::QPath::TypeRelative) (and type-level) path. + fn resolve_type_relative_path( + &self, + self_ty: Ty<'tcx>, + hir_self_ty: &'tcx hir::Ty<'tcx>, + assoc_tag: ty::AssocTag, + segment: &'tcx hir::PathSegment<'tcx>, + qpath_hir_id: HirId, + span: Span, + variant_def_id: Option, + ) -> Result<(DefId, ty::PolyTraitRef<'tcx>), ErrorGuaranteed> { + let tcx = self.tcx(); + + let self_ty_res = match hir_self_ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => path.res, + _ => Res::Err, }; - // Find the type of the associated item, and the trait where the associated - // item is declared. - let bound = match (qself_ty.kind(), qself_res) { + // Find the type of the assoc item, and the trait where the associated item is declared. + let bound = match (self_ty.kind(), self_ty_res) { (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => { // `Self` in an impl of a trait -- we have a concrete self type and a // trait reference. @@ -1292,14 +1360,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.probe_single_bound_for_assoc_item( || { - traits::supertraits( - tcx, - ty::Binder::dummy(trait_ref.instantiate_identity()), - ) + let trait_ref = ty::Binder::dummy(trait_ref.instantiate_identity()); + traits::supertraits(tcx, trait_ref) }, AssocItemQSelf::SelfTyAlias, - mode.assoc_tag(), - assoc_ident, + assoc_tag, + segment.ident, span, None, )? @@ -1309,167 +1375,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), ) => self.probe_single_ty_param_bound_for_assoc_item( param_did.expect_local(), - qself.span, - mode.assoc_tag(), - assoc_ident, + hir_self_ty.span, + assoc_tag, + segment.ident, span, )?, _ => { - let kind_str = assoc_tag_str(mode.assoc_tag()); - let reported = if variant_resolution.is_some() { - // Variant in type position - let msg = format!("expected {kind_str}, found variant `{assoc_ident}`"); - self.dcx().span_err(span, msg) - } else if qself_ty.is_enum() { - let mut err = self.dcx().create_err(NoVariantNamed { - span: assoc_ident.span, - ident: assoc_ident, - ty: qself_ty, - }); - - let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(variant_name) = find_best_match_for_name( - &adt_def - .variants() - .iter() - .map(|variant| variant.name) - .collect::>(), - assoc_ident.name, - None, - ) && let Some(variant) = - adt_def.variants().iter().find(|s| s.name == variant_name) - { - let mut suggestion = vec![(assoc_ident.span, variant_name.to_string())]; - if let hir::Node::Stmt(&hir::Stmt { - kind: hir::StmtKind::Semi(expr), .. - }) - | hir::Node::Expr(expr) = tcx.parent_hir_node(hir_ref_id) - && let hir::ExprKind::Struct(..) = expr.kind - { - match variant.ctor { - None => { - // struct - suggestion = vec![( - assoc_ident.span.with_hi(expr.span.hi()), - if variant.fields.is_empty() { - format!("{variant_name} {{}}") - } else { - format!( - "{variant_name} {{ {} }}", - variant - .fields - .iter() - .map(|f| format!("{}: /* value */", f.name)) - .collect::>() - .join(", ") - ) - }, - )]; - } - Some((hir::def::CtorKind::Fn, def_id)) => { - // tuple - let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); - let inputs = fn_sig.inputs().skip_binder(); - suggestion = vec![( - assoc_ident.span.with_hi(expr.span.hi()), - format!( - "{variant_name}({})", - inputs - .iter() - .map(|i| format!("/* {i} */")) - .collect::>() - .join(", ") - ), - )]; - } - Some((hir::def::CtorKind::Const, _)) => { - // unit - suggestion = vec![( - assoc_ident.span.with_hi(expr.span.hi()), - variant_name.to_string(), - )]; - } - } - } - err.multipart_suggestion_verbose( - "there is a variant with a similar name", - suggestion, - Applicability::HasPlaceholders, - ); - } else { - err.span_label( - assoc_ident.span, - format!("variant not found in `{qself_ty}`"), - ); - } - - if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) { - err.span_label(sp, format!("variant `{assoc_ident}` not found here")); - } - - err.emit() - } else if let Err(reported) = qself_ty.error_reported() { - reported - } else { - self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?; - - let traits: Vec<_> = - self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); - - // Don't print `ty::Error` to the user. - self.report_ambiguous_assoc( - span, - &[qself_ty.to_string()], - &traits, - assoc_ident.name, - mode.assoc_tag(), - ) - }; - return Err(reported); + return Err(self.report_unresolved_type_relative_path( + self_ty, + hir_self_ty, + assoc_tag, + segment.ident, + qpath_hir_id, + span, + variant_def_id, + )); } }; - let trait_did = bound.def_id(); let assoc_item = self - .probe_assoc_item(assoc_ident, mode.assoc_tag(), hir_ref_id, span, trait_did) + .probe_assoc_item(segment.ident, assoc_tag, qpath_hir_id, span, bound.def_id()) .expect("failed to find associated item"); - let (def_id, args) = self.lower_assoc_shared( - span, - assoc_item.def_id, - assoc_segment, - bound, - mode.assoc_tag(), - )?; - let result = LoweredAssoc::Term(def_id, args); - if let Some(variant_def_id) = variant_resolution { - tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { - lint.primary_message("ambiguous associated item"); - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - assoc_ident, - also, - tcx.def_kind_descr(kind, def_id) - ); - lint.span_note(tcx.def_span(def_id), note_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(mode.def_kind(), assoc_item.def_id, " also"); - - lint.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), - Applicability::MachineApplicable, - ); - }); - } - Ok(result) + Ok((assoc_item.def_id, bound)) } - fn probe_inherent_assoc_shared( + /// Search for inherent associated items for use at the type level. + fn probe_inherent_assoc_item( &self, segment: &hir::PathSegment<'tcx>, adt_did: DefId, @@ -1624,7 +1556,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect(); match &applicable_candidates[..] { - &[] => Err(self.complain_about_inherent_assoc_not_found( + &[] => Err(self.report_unresolved_inherent_assoc_item( name, self_ty, candidates, @@ -1635,7 +1567,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &[applicable_candidate] => Ok(applicable_candidate), - &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc( + &[_, ..] => Err(self.report_ambiguous_inherent_assoc_item( name, applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), span, @@ -1763,9 +1695,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect() } - /// Lower a qualified path to a type. + /// Lower a [resolved][hir::QPath::Resolved] associated type path to a projection. #[instrument(level = "debug", skip_all)] - fn lower_qpath_ty( + fn lower_resolved_assoc_ty_path( &self, span: Span, opt_self_ty: Option>, @@ -1773,7 +1705,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_segment: Option<&hir::PathSegment<'tcx>>, item_segment: &hir::PathSegment<'tcx>, ) -> Ty<'tcx> { - match self.lower_qpath_shared( + match self.lower_resolved_assoc_item_path( span, opt_self_ty, item_def_id, @@ -1788,9 +1720,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Lower a qualified path to a const. + /// Lower a [resolved][hir::QPath::Resolved] associated const path to a (type-level) constant. #[instrument(level = "debug", skip_all)] - fn lower_qpath_const( + fn lower_resolved_assoc_const_path( &self, span: Span, opt_self_ty: Option>, @@ -1798,7 +1730,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_segment: Option<&hir::PathSegment<'tcx>>, item_segment: &hir::PathSegment<'tcx>, ) -> Const<'tcx> { - match self.lower_qpath_shared( + match self.lower_resolved_assoc_item_path( span, opt_self_ty, item_def_id, @@ -1814,8 +1746,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path. #[instrument(level = "debug", skip_all)] - fn lower_qpath_shared( + fn lower_resolved_assoc_item_path( &self, span: Span, opt_self_ty: Option>, @@ -1830,7 +1763,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?trait_def_id); let Some(self_ty) = opt_self_ty else { - return Err(self.error_missing_qpath_self_ty( + return Err(self.report_missing_self_ty_for_resolved_path( trait_def_id, span, item_segment, @@ -1849,57 +1782,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ok((item_def_id, item_args)) } - fn error_missing_qpath_self_ty( - &self, - trait_def_id: DefId, - span: Span, - item_segment: &hir::PathSegment<'tcx>, - assoc_tag: ty::AssocTag, - ) -> ErrorGuaranteed { - let tcx = self.tcx(); - let path_str = tcx.def_path_str(trait_def_id); - - let def_id = self.item_def_id(); - debug!(item_def_id = ?def_id); - - // FIXME: document why/how this is different from `tcx.local_parent(def_id)` - let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id(); - debug!(?parent_def_id); - - // If the trait in segment is the same as the trait defining the item, - // use the `` syntax in the error. - let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id; - let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id; - - let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { - vec!["Self".to_string()] - } else { - // Find all the types that have an `impl` for the trait. - tcx.all_impls(trait_def_id) - .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) - .filter(|header| { - // Consider only accessible traits - tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) - && header.polarity != ty::ImplPolarity::Negative - }) - .map(|header| header.trait_ref.instantiate_identity().self_ty()) - // We don't care about blanket impls. - .filter(|self_ty| !self_ty.has_non_region_param()) - .map(|self_ty| tcx.erase_regions(self_ty).to_string()) - .collect() - }; - // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that - // references the trait. Relevant for the first case in - // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` - self.report_ambiguous_assoc( - span, - &type_names, - &[path_str], - item_segment.ident.name, - assoc_tag, - ) - } - pub fn prohibit_generic_args<'a>( &self, segments: impl Iterator> + Clone, @@ -1908,7 +1790,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let args_visitors = segments.clone().flat_map(|segment| segment.args().args); let mut result = Ok(()); if let Some(_) = args_visitors.clone().next() { - result = Err(self.report_prohibit_generics_error( + result = Err(self.report_prohibited_generic_args( segments.clone(), args_visitors, err_extend, @@ -2069,14 +1951,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { generic_segments } - /// Lower a type `Path` to a type. + /// Lower a [resolved][hir::QPath::Resolved] path to a type. #[instrument(level = "debug", skip_all)] - pub fn lower_path( + pub fn lower_resolved_ty_path( &self, opt_self_ty: Option>, path: &hir::Path<'tcx>, hir_id: HirId, - permit_variants: bool, + permit_variants: PermitVariants, ) -> Ty<'tcx> { debug!(?path.res, ?opt_self_ty, ?path.segments); let tcx = self.tcx(); @@ -2107,7 +1989,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); self.lower_path_segment(span, did, path.segments.last().unwrap()) } - Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { + Res::Def(kind @ DefKind::Variant, def_id) + if let PermitVariants::Yes = permit_variants => + { // Lower "variant type" as if it were a real type. // The resulting `Ty` is type of the variant's enum for now. assert_eq!(opt_self_ty, None); @@ -2202,7 +2086,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else { None }; - self.lower_qpath_ty( + self.lower_resolved_assoc_ty_path( span, opt_self_ty, def_id, @@ -2298,7 +2182,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Convert a [`hir::ConstArg`] to a [`ty::Const`](Const). + /// Lower a [`hir::ConstArg`] to a (type-level) [`ty::Const`](Const). #[instrument(skip(self), level = "debug")] pub fn lower_const_arg( &self, @@ -2323,7 +2207,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if tcx.features().generic_const_parameter_types() && (anon_const_type.has_free_regions() || anon_const_type.has_erased_regions()) { - let e = tcx.dcx().span_err( + let e = self.dcx().span_err( const_arg.span(), "anonymous constants with lifetimes in their type are not yet supported", ); @@ -2334,7 +2218,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // use this type to feed the `type_of` and query results must not contain inference // variables otherwise we will ICE. if anon_const_type.has_non_region_infer() { - let e = tcx.dcx().span_err( + let e = self.dcx().span_err( const_arg.span(), "anonymous constants with inferred types are not yet supported", ); @@ -2344,7 +2228,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // We error when the type contains unsubstituted generics since we do not currently // give the anon const any of the generics from the parent. if anon_const_type.has_non_region_param() { - let e = tcx.dcx().span_err( + let e = self.dcx().span_err( const_arg.span(), "anonymous constants referencing generics are not yet supported", ); @@ -2363,13 +2247,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::ConstArgKind::Path(hir::QPath::Resolved(maybe_qself, path)) => { debug!(?maybe_qself, ?path); let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); - self.lower_const_path_resolved(opt_self_ty, path, hir_id) + self.lower_resolved_const_path(opt_self_ty, path, hir_id) } - hir::ConstArgKind::Path(hir::QPath::TypeRelative(qself, segment)) => { - debug!(?qself, ?segment); - let ty = self.lower_ty(qself); - self.lower_assoc_path_const(hir_id, const_arg.span(), ty, qself, segment) - .unwrap_or_else(|guar| Const::new_error(tcx, guar)) + hir::ConstArgKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => { + debug!(?hir_self_ty, ?segment); + let self_ty = self.lower_ty(hir_self_ty); + self.lower_type_relative_const_path( + self_ty, + hir_self_ty, + segment, + hir_id, + const_arg.span(), + ) + .unwrap_or_else(|guar| Const::new_error(tcx, guar)) } hir::ConstArgKind::Path(qpath @ hir::QPath::LangItem(..)) => { ty::Const::new_error_with_message( @@ -2383,7 +2273,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - fn lower_const_path_resolved( + /// Lower a [resolved][hir::QPath::Resolved] path to a (type-level) constant. + fn lower_resolved_const_path( &self, opt_self_ty: Option>, path: &hir::Path<'tcx>, @@ -2420,7 +2311,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else { None }; - self.lower_qpath_const( + self.lower_resolved_assoc_const_path( span, opt_self_ty, did, @@ -2630,7 +2521,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => { debug!(?maybe_qself, ?path); 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) + self.lower_resolved_ty_path(opt_self_ty, path, hir_ty.hir_id, PermitVariants::No) } &hir::TyKind::OpaqueDef(opaque_ty) => { // If this is an RPITIT and we are using the new RPITIT lowering scheme, we @@ -2684,12 +2575,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span }); Ty::new_error(tcx, guar) } - hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => { - debug!(?qself, ?segment); - let ty = self.lower_ty(qself); - self.lower_assoc_path_ty(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false) - .map(|(ty, _, _)| ty) - .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) + hir::TyKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => { + debug!(?hir_self_ty, ?segment); + let self_ty = self.lower_ty(hir_self_ty); + self.lower_type_relative_ty_path( + self_ty, + hir_self_ty, + segment, + hir_ty.hir_id, + hir_ty.span, + PermitVariants::No, + ) + .map(|(ty, _, _)| ty) + .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => { let def_id = tcx.require_lang_item(lang_item, Some(span)); diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 64c1a78bd1c8..4633f3951a78 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -145,9 +145,9 @@ fn diagnostic_hir_wf_check<'tcx>( ref item => bug!("Unexpected TraitItem {:?}", item), }, hir::Node::Item(item) => match item.kind { - hir::ItemKind::TyAlias(_, ty, _) - | hir::ItemKind::Static(_, ty, _, _) - | hir::ItemKind::Const(_, ty, _, _) => vec![ty], + hir::ItemKind::TyAlias(_, _, ty) + | hir::ItemKind::Static(_, _, ty, _) + | hir::ItemKind::Const(_, _, ty, _) => vec![ty], hir::ItemKind::Impl(impl_) => match &impl_.of_trait { Some(t) => t .path diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 010c6c376fe3..a64c24f5455b 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -203,7 +203,9 @@ pub fn check_crate(tcx: TyCtxt<'_>) { tcx.ensure_ok().eval_static_initializer(item_def_id); check::maybe_check_static_with_link_section(tcx, item_def_id); } - DefKind::Const if tcx.generics_of(item_def_id).is_empty() => { + DefKind::Const if !tcx.generics_of(item_def_id).own_requires_monomorphization() => { + // FIXME(generic_const_items): Passing empty instead of identity args is fishy but + // seems to be fine for now. Revisit this! let instance = ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty()); let cid = GlobalId { instance, promoted: None }; let typing_env = ty::TypingEnv::fully_monomorphized(); diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index c99eb12efcca..4f35b87be301 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -119,7 +119,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( explicit_map: &mut ExplicitPredicatesMap<'tcx>, ) { for arg in ty.walk() { - let leaf_ty = match arg.unpack() { + let leaf_ty = match arg.kind() { GenericArgKind::Type(ty) => ty, // No predicates from lifetimes or constants, except potentially @@ -299,7 +299,7 @@ fn check_explicit_predicates<'tcx>( // binding) and thus infer an outlives requirement that `X: // 'b`. if let Some(self_ty) = ignored_self_ty - && let GenericArgKind::Type(ty) = outlives_predicate.0.unpack() + && let GenericArgKind::Type(ty) = outlives_predicate.0.kind() && ty.walk().any(|arg| arg == self_ty.into()) { debug!("skipping self ty = {ty:?}"); diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index daa908c8c78e..499f5572f470 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -69,8 +69,8 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { .map(|(&def_id, set)| { let predicates = &*tcx.arena.alloc_from_iter(set.as_ref().skip_binder().iter().filter_map( - |(ty::OutlivesPredicate(kind1, region2), &span)| { - match kind1.unpack() { + |(ty::OutlivesPredicate(arg1, region2), &span)| { + match arg1.kind() { GenericArgKind::Type(ty1) => Some(( ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty1, *region2)) .upcast(tcx), diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 044fb64ca821..301f5de9424e 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -14,7 +14,7 @@ pub(crate) type RequiredPredicates<'tcx> = /// outlives_component and add it to `required_predicates` pub(crate) fn insert_outlives_predicate<'tcx>( tcx: TyCtxt<'tcx>, - kind: GenericArg<'tcx>, + arg: GenericArg<'tcx>, outlived_region: Region<'tcx>, span: Span, required_predicates: &mut RequiredPredicates<'tcx>, @@ -25,7 +25,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( return; } - match kind.unpack() { + match arg.kind() { GenericArgKind::Type(ty) => { // `T: 'outlived_region` for some type `T` // But T could be a lot of things: @@ -135,7 +135,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( if !is_free_region(r) { return; } - required_predicates.entry(ty::OutlivesPredicate(kind, outlived_region)).or_insert(span); + required_predicates.entry(ty::OutlivesPredicate(arg, outlived_region)).or_insert(span); } GenericArgKind::Const(_) => { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 92cfece77c47..960ec7f66ab1 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -200,8 +200,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // Trait are always invariant so we can take advantage of that. let variance_i = self.invariant(variance); - for k in args { - match k.unpack() { + for arg in args { + match arg.kind() { GenericArgKind::Lifetime(lt) => { self.add_constraints_from_region(current, lt, variance_i) } @@ -294,7 +294,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } for projection in data.projection_bounds() { - match projection.skip_binder().term.unpack() { + match projection.skip_binder().term.kind() { ty::TermKind::Ty(ty) => { self.add_constraints_from_ty(current, ty, self.invariant); } @@ -373,7 +373,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { (None, Some(self.tcx().variances_of(def_id))) }; - for (i, k) in args.iter().enumerate() { + for (i, arg) in args.iter().enumerate() { let variance_decl = if let Some(InferredIndex(start)) = local { // Parameter on an item defined within current crate: // variance not yet inferred, so return a symbolic @@ -389,7 +389,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { "add_constraints_from_args: variance_decl={:?} variance_i={:?}", variance_decl, variance_i ); - match k.unpack() { + match arg.kind() { GenericArgKind::Lifetime(lt) => { self.add_constraints_from_region(current, lt, variance_i) } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 04f9c831b0ac..b23b3125c59a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -477,10 +477,10 @@ impl<'a> State<'a> { hir::ForeignItemKind::Fn(sig, arg_idents, generics) => { let (cb, ib) = self.head(""); self.print_fn( - sig.decl, sig.header, Some(item.ident.name), generics, + sig.decl, arg_idents, None, ); @@ -593,7 +593,7 @@ impl<'a> State<'a> { self.end(ib); self.end(cb); } - hir::ItemKind::Static(ident, ty, m, expr) => { + hir::ItemKind::Static(m, ident, ty, expr) => { let (cb, ib) = self.head("static"); if m.is_mut() { self.word_space("mut"); @@ -609,7 +609,7 @@ impl<'a> State<'a> { self.word(";"); self.end(cb); } - hir::ItemKind::Const(ident, ty, generics, expr) => { + hir::ItemKind::Const(ident, generics, ty, expr) => { let (cb, ib) = self.head("const"); self.print_ident(ident); self.print_generic_params(generics.params); @@ -626,7 +626,7 @@ impl<'a> State<'a> { } hir::ItemKind::Fn { ident, sig, generics, body, .. } => { let (cb, ib) = self.head(""); - self.print_fn(sig.decl, sig.header, Some(ident.name), generics, &[], Some(body)); + self.print_fn(sig.header, Some(ident.name), generics, sig.decl, &[], Some(body)); self.word(" "); self.end(ib); self.end(cb); @@ -660,7 +660,7 @@ impl<'a> State<'a> { self.end(cb); self.end(ib); } - hir::ItemKind::TyAlias(ident, ty, generics) => { + hir::ItemKind::TyAlias(ident, generics, ty) => { let (cb, ib) = self.head("type"); self.print_ident(ident); self.print_generic_params(generics.params); @@ -673,16 +673,16 @@ impl<'a> State<'a> { self.word(";"); self.end(cb); } - hir::ItemKind::Enum(ident, ref enum_definition, params) => { - self.print_enum_def(enum_definition, params, ident.name, item.span); + hir::ItemKind::Enum(ident, generics, ref enum_def) => { + self.print_enum_def(ident.name, generics, enum_def, item.span); } - hir::ItemKind::Struct(ident, ref struct_def, generics) => { + hir::ItemKind::Struct(ident, generics, ref struct_def) => { let (cb, ib) = self.head("struct"); - self.print_struct(struct_def, generics, ident.name, item.span, true, cb, ib); + self.print_struct(ident.name, generics, struct_def, item.span, true, cb, ib); } - hir::ItemKind::Union(ident, ref struct_def, generics) => { + hir::ItemKind::Union(ident, generics, ref struct_def) => { let (cb, ib) = self.head("union"); - self.print_struct(struct_def, generics, ident.name, item.span, true, cb, ib); + self.print_struct(ident.name, generics, struct_def, item.span, true, cb, ib); } hir::ItemKind::Impl(&hir::Impl { constness, @@ -791,9 +791,9 @@ impl<'a> State<'a> { fn print_enum_def( &mut self, - enum_definition: &hir::EnumDef<'_>, - generics: &hir::Generics<'_>, name: Symbol, + generics: &hir::Generics<'_>, + enum_def: &hir::EnumDef<'_>, span: rustc_span::Span, ) { let (cb, ib) = self.head("enum"); @@ -801,7 +801,7 @@ impl<'a> State<'a> { self.print_generic_params(generics.params); self.print_where_clause(generics); self.space(); - self.print_variants(enum_definition.variants, span, cb, ib); + self.print_variants(enum_def.variants, span, cb, ib); } fn print_variants( @@ -834,9 +834,9 @@ impl<'a> State<'a> { fn print_struct( &mut self, - struct_def: &hir::VariantData<'_>, - generics: &hir::Generics<'_>, name: Symbol, + generics: &hir::Generics<'_>, + struct_def: &hir::VariantData<'_>, span: rustc_span::Span, print_finalizer: bool, cb: BoxMarker, @@ -886,7 +886,7 @@ impl<'a> State<'a> { pub fn print_variant(&mut self, v: &hir::Variant<'_>) { let (cb, ib) = self.head(""); let generics = hir::Generics::empty(); - self.print_struct(&v.data, generics, v.ident.name, v.span, false, cb, ib); + self.print_struct(v.ident.name, generics, &v.data, v.span, false, cb, ib); if let Some(ref d) = v.disr_expr { self.space(); self.word_space("="); @@ -902,7 +902,7 @@ impl<'a> State<'a> { arg_idents: &[Option], body_id: Option, ) { - self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_idents, body_id); + self.print_fn(m.header, Some(ident.name), generics, m.decl, arg_idents, body_id); } fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) { @@ -2141,10 +2141,10 @@ impl<'a> State<'a> { fn print_fn( &mut self, - decl: &hir::FnDecl<'_>, header: hir::FnHeader, name: Option, generics: &hir::Generics<'_>, + decl: &hir::FnDecl<'_>, arg_idents: &[Option], body_id: Option, ) { @@ -2483,7 +2483,6 @@ impl<'a> State<'a> { self.print_formal_generic_params(generic_params); let generics = hir::Generics::empty(); self.print_fn( - decl, hir::FnHeader { safety: safety.into(), abi, @@ -2492,6 +2491,7 @@ impl<'a> State<'a> { }, name, generics, + decl, arg_idents, None, ); diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index f00125c3e090..c963f0ddefd4 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 23309102c4da..5e79d10612db 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -169,7 +169,7 @@ hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value in hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` - .teach_help = certain types, like `{$ty}`, must be casted before passing them to a variadic function, because of arcane ABI rules dictated by the C standard + .teach_help = certain types, like `{$ty}`, must be cast before passing them to a variadic function to match the implicit cast that a C compiler would perform as part of C's numeric promotion rules hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len -> [1] auto trait {$traits} diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index c044c4f7c37d..e144a6ab5999 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -408,6 +408,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.expr_ty, fcx.ty_to_string(self.cast_ty) ); + + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) + && matches!(self.expr.kind, ExprKind::AddrOf(..)) + { + err.note(format!( + "casting reference expression `{}` because `&` binds tighter than `as`", + snippet + )); + } + let mut sugg = None; let mut sugg_mutref = false; if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index fd899425f62d..ddc80fab2ce1 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -37,7 +37,7 @@ use std::ops::Deref; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; use rustc_hir as hir; @@ -47,7 +47,7 @@ use rustc_infer::infer::relate::RelateResult; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{ IfExpressionCause, MatchExpressionArmCause, Obligation, PredicateObligation, - PredicateObligations, + PredicateObligations, SelectionError, }; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ @@ -677,7 +677,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return Err(TypeError::Mismatch); } - // Dyn-compatibility violations or miscellaneous. + Err(SelectionError::TraitDynIncompatible(_)) => { + // Dyn compatibility errors in coercion will *always* be due to the + // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` + // writen in source somewhere (otherwise we will never have lowered + // the dyn trait from HIR to middle). + // + // There's no reason to emit yet another dyn compatibility error, + // especially since the span will differ slightly and thus not be + // deduplicated at all! + self.fcx.set_tainted_by_errors( + self.fcx + .dcx() + .span_delayed_bug(self.cause.span, "dyn compatibility during coercion"), + ); + } Err(err) => { let guar = self.err_ctxt().report_selection_error( obligation.clone(), @@ -1190,9 +1204,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (ty::FnDef(..), ty::FnDef(..)) => { // Don't reify if the function types have a LUB, i.e., they // are the same function and their parameters have a LUB. - match self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - { + match self.commit_if_ok(|_| { + // We need to eagerly handle nested obligations due to lazy norm. + if self.next_trait_solver() { + let ocx = ObligationCtxt::new(self); + let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?; + if ocx.select_where_possible().is_empty() { + Ok(InferOk { + value, + obligations: ocx.into_pending_obligations(), + }) + } else { + Err(TypeError::Mismatch) + } + } else { + self.at(cause, self.param_env).lub(prev_ty, new_ty) + } + }) { // We have a LUB of prev_ty and new_ty, just return it. Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), Err(_) => { @@ -1840,17 +1868,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.err_ctxt().report_mismatched_types(cause, fcx.param_env, expected, found, ty_err); let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..)); - - let parent_id = fcx.tcx.parent_hir_id(block_or_return_id); - let parent = fcx.tcx.hir_node(parent_id); + let parent = fcx.tcx.parent_hir_node(block_or_return_id); if let Some(expr) = expression && let hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent - && !matches!(fcx.tcx.hir_body(body).value.kind, hir::ExprKind::Block(..)) { - fcx.suggest_missing_semicolon(&mut err, expr, expected, true); + let needs_block = + !matches!(fcx.tcx.hir_body(body).value.kind, hir::ExprKind::Block(..)); + fcx.suggest_missing_semicolon(&mut err, expr, expected, needs_block, true); } // Verify that this is a tail expression of a function, otherwise the // label pointing out the cause for the type coercion will be wrong @@ -1858,7 +1885,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let Some(expr) = expression && due_to_block { - fcx.suggest_missing_semicolon(&mut err, expr, expected, false); + fcx.suggest_missing_semicolon(&mut err, expr, expected, false, false); let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( &mut err, expr, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d1bc54ed73ea..152c88ad92a5 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -84,7 +84,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_expected_due_to_let_ty(err, expr, error); self.annotate_loop_expected_due_to_inference(err, expr, error); - if self.annotate_mut_binding_to_immutable_binding(err, expr, error) { + if self.annotate_mut_binding_to_immutable_binding(err, expr, expr_ty, expected, error) { return; } @@ -722,8 +722,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )) => { if let Some(hir::Node::Item(hir::Item { kind: - hir::ItemKind::Static(ident, ty, ..) - | hir::ItemKind::Const(ident, ty, ..), + hir::ItemKind::Static(_, ident, ty, _) + | hir::ItemKind::Const(ident, _, ty, _), .. })) = self.tcx.hir_get_if_local(*def_id) { @@ -799,17 +799,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Detect the following case /// /// ```text - /// fn change_object(mut a: &Ty) { + /// fn change_object(mut b: &Ty) { /// let a = Ty::new(); /// b = a; /// } /// ``` /// - /// where the user likely meant to modify the value behind there reference, use `a` as an out + /// where the user likely meant to modify the value behind there reference, use `b` as an out /// parameter, instead of mutating the local binding. When encountering this we suggest: /// /// ```text - /// fn change_object(a: &'_ mut Ty) { + /// fn change_object(b: &'_ mut Ty) { /// let a = Ty::new(); /// *b = a; /// } @@ -818,13 +818,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diag<'_>, expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected: Ty<'tcx>, error: Option>, ) -> bool { - if let Some(TypeError::Sorts(ExpectedFound { expected, found })) = error + if let Some(TypeError::Sorts(ExpectedFound { .. })) = error && let ty::Ref(_, inner, hir::Mutability::Not) = expected.kind() // The difference between the expected and found values is one level of borrowing. - && self.can_eq(self.param_env, *inner, found) + && self.can_eq(self.param_env, *inner, expr_ty) // We have an `ident = expr;` assignment. && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(lhs, rhs, _), .. }) = diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2c28ffd1fe3d..082ddac7e5ae 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -532,14 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Break(destination, ref expr_opt) => { self.check_expr_break(destination, expr_opt.as_deref(), expr) } - ExprKind::Continue(destination) => { - if destination.target_id.is_ok() { - tcx.types.never - } else { - // There was an error; make type-check fail. - Ty::new_misc_error(tcx) - } - } + ExprKind::Continue(destination) => self.check_expr_continue(destination, expr), ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), ExprKind::Become(call) => self.check_expr_become(call, expr), ExprKind::Let(let_expr) => self.check_expr_let(let_expr, expr.hir_id), @@ -918,7 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self, &cause, |mut err| { - self.suggest_missing_semicolon(&mut err, expr, e_ty, false); + self.suggest_missing_semicolon(&mut err, expr, e_ty, false, false); self.suggest_mismatched_types_on_tail( &mut err, expr, ty, e_ty, target_id, ); @@ -989,6 +982,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_expr_continue( + &self, + destination: hir::Destination, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + if let Ok(target_id) = destination.target_id { + if let hir::Node::Expr(hir::Expr { kind: ExprKind::Loop(..), .. }) = + self.tcx.hir_node(target_id) + { + self.tcx.types.never + } else { + // Liveness linting assumes `continue`s all point to loops. We'll report an error + // in `check_mod_loops`, but make sure we don't run liveness (#113379, #121623). + let guar = self.dcx().span_delayed_bug( + expr.span, + "found `continue` not pointing to loop, but no error reported", + ); + Ty::new_error(self.tcx, guar) + } + } else { + // There was an error; make type-check fail. + Ty::new_misc_error(self.tcx) + } + } + fn check_expr_return( &self, expr_opt: Option<&'tcx hir::Expr<'tcx>>, @@ -1775,10 +1793,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_ty = if !args.is_empty() { let coerce_to = expected .to_option(self) - .and_then(|uty| match *self.try_structurally_resolve_type(expr.span, uty).kind() { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }) + .and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index()) .unwrap_or_else(|| self.next_ty_var(expr.span)); let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); assert_eq!(self.diverges.get(), Diverges::Maybe); @@ -1856,10 +1871,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let uty = match expected { - ExpectHasType(uty) => match *uty.kind() { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }, + ExpectHasType(uty) => uty.builtin_index(), _ => None, }; @@ -1882,62 +1894,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We defer checking whether the element type is `Copy` as it is possible to have // an inference variable as a repeat count and it seems unlikely that `Copy` would // have inference side effects required for type checking to succeed. - if tcx.features().generic_arg_infer() { - self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count)); - // If the length is 0, we don't create any elements, so we don't copy any. - // If the length is 1, we don't copy that one element, we move it. Only check - // for `Copy` if the length is larger, or unevaluated. - } else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) { - self.enforce_repeat_element_needs_copy_bound(element, element_ty); - } + self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count)); let ty = Ty::new_array_with_const_len(tcx, t, count); self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None)); ty } - /// Requires that `element_ty` is `Copy` (unless it's a const expression itself). - pub(super) fn enforce_repeat_element_needs_copy_bound( - &self, - element: &hir::Expr<'_>, - element_ty: Ty<'tcx>, - ) { - let tcx = self.tcx; - // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy. - match &element.kind { - hir::ExprKind::ConstBlock(..) => return, - hir::ExprKind::Path(qpath) => { - let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); - if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res - { - return; - } - } - _ => {} - } - // If someone calls a const fn or constructs a const value, they can extract that - // out into a separate constant (or a const block in the future), so we check that - // to tell them that in the diagnostic. Does not affect typeck. - let is_constable = match element.kind { - hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { - ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn, - _ => traits::IsConstable::No, - }, - hir::ExprKind::Path(qpath) => { - match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) { - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor, - _ => traits::IsConstable::No, - } - } - _ => traits::IsConstable::No, - }; - - let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); - let code = - traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span }; - self.require_type_meets(element_ty, element.span, code, lang_item); - } - fn check_expr_tuple( &self, elts: &'tcx [hir::Expr<'tcx>], diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 3493d359028d..203413188282 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -13,6 +13,7 @@ use hir::Expr; use hir::def::DefKind; use hir::pat_util::EnumerateAndAdjustIterator as _; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_ast::UnsafeBinderCastKind; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def::{CtorOf, Res}; use rustc_hir::def_id::LocalDefId; @@ -1393,10 +1394,18 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.cat_res(expr.hir_id, expr.span, expr_ty, res) } - // both type ascription and unsafe binder casts don't affect - // the place-ness of the subexpression. + // type ascription doesn't affect the place-ness of the subexpression. hir::ExprKind::Type(e, _) => self.cat_expr(e), - hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e), + + hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Unwrap, e, _) => { + let base = self.cat_expr(e)?; + Ok(self.cat_projection( + expr.hir_id, + base, + expr_ty, + ProjectionKind::UnwrapUnsafeBinder, + )) + } hir::ExprKind::AddrOf(..) | hir::ExprKind::Call(..) @@ -1427,6 +1436,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) | hir::ExprKind::OffsetOf(..) + | hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, ..) | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr_ty)), } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index ee0436f73e1d..2bc007b3ad4b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -80,12 +80,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| { predicate_args.iter().find_map(|arg| { arg.walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() + if let ty::GenericArgKind::Type(ty) = arg.kind() && let ty::Param(param_ty) = *ty.kind() && matches(ty::ParamTerm::Ty(param_ty)) { Some(arg) - } else if let ty::GenericArgKind::Const(ct) = arg.unpack() + } else if let ty::GenericArgKind::Const(ct) = arg.kind() && let ty::ConstKind::Param(param_ct) = ct.kind() && matches(ty::ParamTerm::Const(param_ct)) { @@ -357,7 +357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, error: &mut traits::FulfillmentError<'tcx>, def_id: DefId, - param: ty::GenericArg<'tcx>, + arg: ty::GenericArg<'tcx>, qpath: &hir::QPath<'tcx>, ) -> bool { match qpath { @@ -365,7 +365,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for segment in path.segments.iter().rev() { if let Res::Def(kind, def_id) = segment.res && !matches!(kind, DefKind::Mod | DefKind::ForeignMod) - && self.point_at_generic_if_possible(error, def_id, param, segment) + && self.point_at_generic_if_possible(error, def_id, arg, segment) { return true; } @@ -373,7 +373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Handle `Self` param specifically, since it's separated in // the path representation if let Some(self_ty) = self_ty - && let ty::GenericArgKind::Type(ty) = param.unpack() + && let ty::GenericArgKind::Type(ty) = arg.kind() && ty == self.tcx.types.self_param { error.obligation.cause.span = self_ty @@ -384,12 +384,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } hir::QPath::TypeRelative(self_ty, segment) => { - if self.point_at_generic_if_possible(error, def_id, param, segment) { + if self.point_at_generic_if_possible(error, def_id, arg, segment) { return true; } // Handle `Self` param specifically, since it's separated in // the path representation - if let ty::GenericArgKind::Type(ty) = param.unpack() + if let ty::GenericArgKind::Type(ty) = arg.kind() && ty == self.tcx.types.self_param { error.obligation.cause.span = self_ty @@ -424,10 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the args list does not, then we should chop off all of the lifetimes, // since they're all elided. let segment_args = segment.args().args; - if matches!(own_args[0].unpack(), ty::GenericArgKind::Lifetime(_)) + if matches!(own_args[0].kind(), ty::GenericArgKind::Lifetime(_)) && segment_args.first().is_some_and(|arg| arg.is_ty_or_const()) && let Some(offset) = own_args.iter().position(|arg| { - matches!(arg.unpack(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_)) + matches!(arg.kind(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_)) }) && let Some(new_index) = index.checked_sub(offset) { @@ -750,7 +750,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ok(expr); } - let ty::GenericArgKind::Type(in_ty) = in_ty.unpack() else { + let ty::GenericArgKind::Type(in_ty) = in_ty.kind() else { return Err(expr); }; @@ -1045,7 +1045,7 @@ fn find_param_in_ty<'tcx>( if arg == param_to_point_at { return true; } - if let ty::GenericArgKind::Type(ty) = arg.unpack() + if let ty::GenericArgKind::Type(ty) = arg.kind() && let ty::Alias(ty::Projection | ty::Inherent, ..) = ty.kind() { // This logic may seem a bit strange, but typically when diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d2cdfe22a3ad..59aba6fae5e4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -4,12 +4,12 @@ use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize}; -use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, HirId, Node, QPath}; +use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath}; use rustc_hir_analysis::check::potentially_plural_count; -use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants}; use rustc_index::IndexVec; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace}; use rustc_middle::ty::adjustment::AllowTwoPhase; @@ -104,24 +104,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_repeat_exprs(&self) { let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut(); debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len()); - for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) { - // We want to emit an error if the const is not structurally resolveable as otherwise - // we can find up conservatively proving `Copy` which may infer the repeat expr count - // to something that never required `Copy` in the first place. - let count = - self.structurally_resolve_const(element.span, self.normalize(element.span, count)); - // Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count - // is erroneous/unknown. The user might wind up specifying a repeat count of 0/1. - if count.references_error() { - continue; - } + let deferred_repeat_expr_checks = deferred_repeat_expr_checks + .drain(..) + .flat_map(|(element, element_ty, count)| { + // Actual constants as the repeat element are inserted repeatedly instead + // of being copied via `Copy`, so we don't need to attempt to structurally + // resolve the repeat count which may unnecessarily error. + match &element.kind { + hir::ExprKind::ConstBlock(..) => return None, + hir::ExprKind::Path(qpath) => { + let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); + if let Res::Def(DefKind::Const | DefKind::AssocConst, _) = res { + return None; + } + } + _ => {} + } - // If the length is 0, we don't create any elements, so we don't copy any. - // If the length is 1, we don't copy that one element, we move it. Only check - // for `Copy` if the length is larger. - if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) { - self.enforce_repeat_element_needs_copy_bound(element, element_ty); + // We want to emit an error if the const is not structurally resolveable + // as otherwise we can wind up conservatively proving `Copy` which may + // infer the repeat expr count to something that never required `Copy` in + // the first place. + let count = self + .structurally_resolve_const(element.span, self.normalize(element.span, count)); + + // Avoid run on "`NotCopy: Copy` is not implemented" errors when the + // repeat expr count is erroneous/unknown. The user might wind up + // specifying a repeat count of 0/1. + if count.references_error() { + return None; + } + + Some((element, element_ty, count)) + }) + // We collect to force the side effects of structurally resolving the repeat + // count to happen in one go, to avoid side effects from proving `Copy` + // affecting whether repeat counts are known or not. If we did not do this we + // would get results that depend on the order that we evaluate each repeat + // expr's `Copy` check. + .collect::>(); + + let enforce_copy_bound = |element: &hir::Expr<'_>, element_ty| { + // If someone calls a const fn or constructs a const value, they can extract that + // out into a separate constant (or a const block in the future), so we check that + // to tell them that in the diagnostic. Does not affect typeck. + let is_constable = match element.kind { + hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { + ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => { + traits::IsConstable::Fn + } + _ => traits::IsConstable::No, + }, + hir::ExprKind::Path(qpath) => { + match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) { + Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor, + _ => traits::IsConstable::No, + } + } + _ => traits::IsConstable::No, + }; + + let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); + let code = traits::ObligationCauseCode::RepeatElementCopy { + is_constable, + elt_span: element.span, + }; + self.require_type_meets(element_ty, element.span, code, lang_item); + }; + + for (element, element_ty, count) in deferred_repeat_expr_checks { + match count.kind() { + ty::ConstKind::Value(val) => { + if val.try_to_target_usize(self.tcx).is_none_or(|count| count > 1) { + enforce_copy_bound(element, element_ty) + } else { + // If the length is 0 or 1 we don't actually copy the element, we either don't create it + // or we just use the one value. + } + } + + // If the length is a generic parameter or some rigid alias then conservatively + // require `element_ty: Copy` as it may wind up being `>1` after monomorphization. + ty::ConstKind::Param(_) + | ty::ConstKind::Expr(_) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Unevaluated(_) => enforce_copy_bound(element, element_ty), + + ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => { + unreachable!() + } } } } @@ -1484,25 +1556,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SuggestionText::Reorder => Some("reorder these arguments".to_string()), SuggestionText::DidYouMean => Some("did you mean".to_string()), }; - if let Some(suggestion_text) = suggestion_text { + if let Some(suggestion_text) = suggestion_text + && !full_call_span.in_external_macro(self.sess().source_map()) + { let source_map = self.sess().source_map(); - let (mut suggestion, suggestion_span) = if let Some(call_span) = - full_call_span.find_ancestor_inside_same_ctxt(error_span) - { - ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi())) + let suggestion_span = if let Some(args_span) = error_span.trim_start(full_call_span) { + // Span of the braces, e.g. `(a, b, c)`. + args_span } else { - ( - format!( - "{}(", - source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| { - fn_def_id.map_or("".to_string(), |fn_def_id| { - tcx.item_name(fn_def_id).to_string() - }) - }) - ), - error_span, - ) + // The arg span of a function call that wasn't even given braces + // like what might happen with delegation reuse. + // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. + full_call_span.shrink_to_hi() }; + let mut suggestion = "(".to_owned(); let mut needs_comma = false; for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { if needs_comma { @@ -2105,15 +2172,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *qpath { QPath::Resolved(ref maybe_qself, path) => { let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself).raw); - let ty = self.lowerer().lower_path(self_ty, path, hir_id, true); + let ty = self.lowerer().lower_resolved_ty_path( + self_ty, + path, + hir_id, + PermitVariants::Yes, + ); (path.res, LoweredTy::from_raw(self, path_span, ty)) } - QPath::TypeRelative(qself, segment) => { - let ty = self.lower_ty(qself); + QPath::TypeRelative(hir_self_ty, segment) => { + let self_ty = self.lower_ty(hir_self_ty); - let result = self - .lowerer() - .lower_assoc_path_ty(hir_id, path_span, ty.raw, qself, segment, true); + let result = self.lowerer().lower_type_relative_ty_path( + self_ty.raw, + hir_self_ty, + segment, + hir_id, + path_span, + PermitVariants::Yes, + ); let ty = result .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index de189b301092..a5c0829b8d9e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -14,7 +14,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, HirId, ItemLocalMap}; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; use rustc_infer::infer; -use rustc_infer::traits::Obligation; +use rustc_infer::traits::{DynCompatibilityViolation, Obligation}; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym}; @@ -81,7 +81,7 @@ pub(crate) struct FnCtxt<'a, 'tcx> { /// you get indicates whether any subexpression that was /// evaluating up to and including `X` diverged. /// - /// We currently use this flag only for diagnostic purposes: + /// We currently use this flag for the following purposes: /// /// - To warn about unreachable code: if, after processing a /// sub-expression but before we have applied the effects of the @@ -94,6 +94,8 @@ pub(crate) struct FnCtxt<'a, 'tcx> { /// warning. This corresponds to something like `{return; /// foo();}` or `{return; 22}`, where we would warn on the /// `foo()` or `22`. + /// - To assign the `!` type to block expressions with diverging + /// statements. /// /// An expression represents dead code if, after checking it, /// the diverges flag is set to something other than `Maybe`. @@ -308,17 +310,16 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { )) } - fn lower_assoc_shared( + fn lower_assoc_item_path( &self, span: Span, item_def_id: DefId, item_segment: &rustc_hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - _assoc_tag: ty::AssocTag, ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> { let trait_ref = self.instantiate_binder_with_fresh_vars( span, - // FIXME(mgca): this should be assoc const if that is the `kind` + // FIXME(mgca): `item_def_id` can be an AssocConst; rename this variant. infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id), poly_trait_ref, ); @@ -389,6 +390,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { }; (input_tys, output_ty) } + + fn dyn_compatibility_violations(&self, trait_def_id: DefId) -> Vec { + self.tcx.dyn_compatibility_violations(trait_def_id).to_vec() + } } /// The `ty` representation of a user-provided type. Depending on the use-site diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 1079262b5af8..1c3bc338d85b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -764,6 +764,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expression: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, needs_block: bool, + parent_is_closure: bool, ) { if expected.is_unit() { // `BlockTailExpression` only relevant if the tail expr would be @@ -799,6 +800,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } + ExprKind::Path(..) | ExprKind::Lit(_) + if parent_is_closure + && !expression.span.in_external_macro(self.tcx.sess.source_map()) => + { + err.span_suggestion_verbose( + expression.span.shrink_to_lo(), + "consider ignoring the value", + "_ = ", + Applicability::MachineApplicable, + ); + } _ => (), } } @@ -2701,6 +2713,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } + // Don't try to suggest ref/deref on an `if` expression, because: + // - The `if` could be part of a desugared `if else` statement, + // which would create impossible suggestions such as `if ... { ... } else &if { ... } else { ... }`. + // - In general the suggestions it creates such as `&if ... { ... } else { ... }` are not very helpful. + // We try to generate a suggestion such as `if ... { &... } else { &... }` instead. + if let hir::ExprKind::If(_c, then, els) = expr.kind { + // The `then` of a `Expr::If` always contains a block, and that block may have a final expression that we can borrow + // If the block does not have a final expression, it will return () and we do not make a suggestion to borrow that. + let ExprKind::Block(then, _) = then.kind else { return None }; + let Some(then) = then.expr else { return None }; + let (mut suggs, help, app, verbose, mutref) = + self.suggest_deref_or_ref(then, checked_ty, expected)?; + + // If there is no `else`, the return type of this `if` will be (), so suggesting to change the `then` block is useless + let els_expr = match els?.kind { + ExprKind::Block(block, _) => block.expr?, + _ => els?, + }; + let (else_suggs, ..) = + self.suggest_deref_or_ref(els_expr, checked_ty, expected)?; + suggs.extend(else_suggs); + + return Some((suggs, help, app, verbose, mutref)); + } + if let Some((sugg, msg)) = self.can_use_as_ref(expr) { return Some(( sugg, diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 956671fc66ed..7d99b0e78694 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -218,7 +218,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { ); } let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take(); - intravisit::walk_pat(self, p); + if let PatKind::Guard(subpat, _) = p.kind { + // We'll visit the guard when checking it. Don't gather its locals twice. + self.visit_pat(subpat); + } else { + intravisit::walk_pat(self, p); + } self.outermost_fn_param_pat = old_outermost_fn_param_pat; } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 161f5e981d43..5a8148221631 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -2,6 +2,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(array_windows)] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] @@ -195,13 +196,16 @@ fn typeck_with_inspect<'tcx>( fcx.write_ty(id, expected_type); }; - // Whether to check repeat exprs before/after inference fallback is somewhat arbitrary of a decision - // as neither option is strictly more permissive than the other. However, we opt to check repeat exprs - // first as errors from not having inferred array lengths yet seem less confusing than errors from inference - // fallback arbitrarily inferring something incompatible with `Copy` inference side effects. + // Whether to check repeat exprs before/after inference fallback is somewhat + // arbitrary of a decision as neither option is strictly more permissive than + // the other. However, we opt to check repeat exprs first as errors from not + // having inferred array lengths yet seem less confusing than errors from inference + // fallback arbitrarily inferring something incompatible with `Copy` inference + // side effects. // - // This should also be forwards compatible with moving repeat expr checks to a custom goal kind or using - // marker traits in the future. + // FIXME(#140855): This should also be forwards compatible with moving + // repeat expr checks to a custom goal kind or using marker traits in + // the future. fcx.check_repeat_exprs(); fcx.type_inference_fallback(); diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index a614b4f00ffe..53b5dff9c6b5 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -291,6 +291,14 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { probe::ObjectPick => { let trait_def_id = pick.item.container_id(self.tcx); + // If the trait is not object safe (specifically, we care about when + // the receiver is not valid), then there's a chance that we will not + // actually be able to recover the object by derefing the receiver like + // we should if it were valid. + if !self.tcx.is_dyn_compatible(trait_def_id) { + return ty::GenericArgs::extend_with_error(self.tcx, trait_def_id, &[]); + } + // This shouldn't happen for non-region error kinds, but may occur // when we have error regions. Specifically, since we canonicalize // during method steps, we may successfully deref when we assemble diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index bda051f15608..725240b480ba 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1,7 +1,9 @@ +use std::assert_matches::debug_assert_matches; use std::cell::{Cell, RefCell}; use std::cmp::max; use std::ops::Deref; +use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; @@ -15,7 +17,7 @@ use rustc_infer::traits::ObligationCauseCode; use rustc_middle::middle::stability; use rustc_middle::query::Providers; use rustc_middle::ty::elaborate::supertrait_def_ids; -use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; use rustc_middle::ty::{ self, AssocItem, AssocItemContainer, GenericArgs, GenericArgsRef, GenericParamDefKind, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, Upcast, @@ -806,8 +808,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ); } } - ty::Param(p) => { - self.assemble_inherent_candidates_from_param(p); + ty::Param(_) => { + self.assemble_inherent_candidates_from_param(raw_self_ty); } ty::Bool | ty::Char @@ -908,18 +910,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) { + fn assemble_inherent_candidates_from_param(&mut self, param_ty: Ty<'tcx>) { + debug_assert_matches!(param_ty.kind(), ty::Param(_)); + + let tcx = self.tcx; let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::ClauseKind::Trait(trait_predicate) => { - match *trait_predicate.trait_ref.self_ty().kind() { - ty::Param(p) if p == param_ty => { - Some(bound_predicate.rebind(trait_predicate.trait_ref)) - } - _ => None, - } - } + ty::ClauseKind::Trait(trait_predicate) => DeepRejectCtxt::relate_rigid_rigid(tcx) + .types_may_unify(param_ty, trait_predicate.trait_ref.self_ty()) + .then(|| bound_predicate.rebind(trait_predicate.trait_ref)), ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::TypeOutlives(_) | ty::ClauseKind::Projection(_) @@ -2333,10 +2333,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.fcx.tcx.hir_attrs(hir_id); + + if is_doc_alias_attrs_contain_symbol(attrs.into_iter(), method.name) { + return true; + } + for attr in attrs { - if attr.has_name(sym::doc) { - // do nothing - } else if attr.has_name(sym::rustc_confusables) { + if attr.has_name(sym::rustc_confusables) { let Some(confusables) = attr.meta_item_list() else { continue; }; @@ -2348,33 +2351,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return true; } } - continue; - } else { - continue; - }; - let Some(values) = attr.meta_item_list() else { - continue; - }; - for v in values { - if !v.has_name(sym::alias) { - continue; - } - if let Some(nested) = v.meta_item_list() { - // #[doc(alias("foo", "bar"))] - for n in nested { - if let Some(lit) = n.lit() - && method.name == lit.symbol - { - return true; - } - } - } else if let Some(meta) = v.meta_item() - && let Some(lit) = meta.name_value_literal() - && method.name == lit.symbol - { - // #[doc(alias = "foo")] - return true; - } } } false diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6a9fd7cdd483..3eae95d5b738 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use hir::Expr; use rustc_ast::ast::Mutability; -use rustc_attr_parsing::{AttributeKind, find_attr}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; @@ -599,7 +599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); let mut ty_file = None; - let (mut ty_str, short_ty_str) = + let (ty_str, short_ty_str) = if trait_missing_method && let ty::Dynamic(predicates, _, _) = rcvr_ty.kind() { (predicates.to_string(), with_forced_trimmed_paths!(predicates.to_string())) } else { @@ -738,10 +738,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(within_macro_span, "due to this macro variable"); } - if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { - ty_str = short_ty_str; - } - if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); } @@ -2230,7 +2226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let infer_args = self.tcx.mk_args_from_iter(args.into_iter().map(|arg| { if !arg.is_suggestable(self.tcx, true) { has_unsuggestable_args = true; - match arg.unpack() { + match arg.kind() { GenericArgKind::Lifetime(_) => self .next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) .into(), @@ -2847,7 +2843,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let [first] = ***args else { return; }; - let ty::GenericArgKind::Type(ty) = first.unpack() else { + let ty::GenericArgKind::Type(ty) = first.kind() else { return; }; let Ok(pick) = self.lookup_probe_for_diagnostic( diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 8ab71e5220bb..4b2e87f5674a 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -902,7 +902,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_field<'a>(p: &&Projection<'a>) -> bool { match p.kind { ProjectionKind::Field(_, _) => true, - ProjectionKind::Deref | ProjectionKind::OpaqueCast => false, + ProjectionKind::Deref + | ProjectionKind::OpaqueCast + | ProjectionKind::UnwrapUnsafeBinder => false, p @ (ProjectionKind::Subslice | ProjectionKind::Index) => { bug!("ProjectionKind {:?} was unexpected", p) } @@ -2197,7 +2199,8 @@ fn restrict_capture_precision( } ProjectionKind::Deref => {} ProjectionKind::OpaqueCast => {} - ProjectionKind::Field(..) => {} // ignore + ProjectionKind::Field(..) => {} + ProjectionKind::UnwrapUnsafeBinder => {} } } @@ -2268,6 +2271,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String ProjectionKind::Index => String::from("Index"), ProjectionKind::Subslice => String::from("Subslice"), ProjectionKind::OpaqueCast => String::from("OpaqueCast"), + ProjectionKind::UnwrapUnsafeBinder => String::from("UnwrapUnsafeBinder"), }; if i != 0 { projections_str.push(','); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 9be041f75d76..b2497cb0de16 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -9,17 +9,21 @@ //! which creates a new `TypeckResults` which doesn't contain any inference variables. use std::mem; +use std::ops::ControlFlow; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::ExtendUnord; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{E0720, ErrorGuaranteed}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, InferKind, Visitor}; use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_infer::traits::solve::Goal; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; use rustc_middle::ty::{ - self, DefiningScopeKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, fold_regions, + self, DefiningScopeKind, OpaqueHiddenType, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + fold_regions, }; use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; @@ -595,6 +599,35 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { entry.span = prev.span.substitute_dummy(hidden_type.span); } } + + let recursive_opaques: Vec<_> = self + .typeck_results + .concrete_opaque_types + .iter() + .filter(|&(&def_id, hidden_ty)| { + hidden_ty + .ty + .visit_with(&mut HasRecursiveOpaque { + def_id, + seen: Default::default(), + opaques: &self.typeck_results.concrete_opaque_types, + tcx, + }) + .is_break() + }) + .map(|(def_id, hidden_ty)| (*def_id, hidden_ty.span)) + .collect(); + for (def_id, span) in recursive_opaques { + let guar = self + .fcx + .dcx() + .struct_span_err(span, "cannot resolve opaque type") + .with_code(E0720) + .emit(); + self.typeck_results + .concrete_opaque_types + .insert(def_id, OpaqueHiddenType { span, ty: Ty::new_error(tcx, guar) }); + } } fn visit_field_id(&mut self, hir_id: HirId) { @@ -959,3 +992,34 @@ impl<'tcx> TypeFolder> for EagerlyNormalizeConsts<'tcx> { self.tcx.try_normalize_erasing_regions(self.typing_env, ct).unwrap_or(ct) } } + +struct HasRecursiveOpaque<'a, 'tcx> { + def_id: LocalDefId, + seen: FxHashSet, + opaques: &'a FxIndexMap>, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TypeVisitor> for HasRecursiveOpaque<'_, 'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + if let ty::Alias(ty::Opaque, alias_ty) = *t.kind() + && let Some(def_id) = alias_ty.def_id.as_local() + { + if self.def_id == def_id { + return ControlFlow::Break(()); + } + + if self.seen.insert(def_id) + && let Some(hidden_ty) = self.opaques.get(&def_id) + { + ty::EarlyBinder::bind(hidden_ty.ty) + .instantiate(self.tcx, alias_ty.args) + .visit_with(self)?; + } + } + + t.super_visit_with(self) + } +} diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 2cd67cc4da21..5fe795bd23a1 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -347,7 +347,7 @@ impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> { fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: match (a.unpack(), b.unpack()) { + values: match (a.kind(), b.kind()) { (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { ValuePairs::Regions(ExpectedFound::new(a, b)) } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index a1a0926cd818..060447ba7206 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -17,8 +17,7 @@ use tracing::debug; use crate::infer::InferCtxt; use crate::infer::canonical::{ - Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, - OriginalQueryValues, + Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarKind, OriginalQueryValues, }; impl<'tcx> InferCtxt<'tcx> { @@ -174,10 +173,8 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { match r.kind() { ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r, - ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( - CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) }, - r, - ), + ty::RePlaceholder(placeholder) => canonicalizer + .canonical_var_for_region(CanonicalVarKind::PlaceholderRegion(placeholder), r), ty::ReVar(vid) => { let universe = infcx @@ -186,10 +183,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { .unwrap_region_constraints() .probe_value(vid) .unwrap_err(); - canonicalizer.canonical_var_for_region( - CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) }, - r, - ) + canonicalizer.canonical_var_for_region(CanonicalVarKind::Region(universe), r) } _ => { @@ -294,7 +288,7 @@ struct Canonicalizer<'cx, 'tcx> { /// Set to `None` to disable the resolution of inference variables. infcx: Option<&'cx InferCtxt<'tcx>>, tcx: TyCtxt<'tcx>, - variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>, + variables: SmallVec<[CanonicalVarKind<'tcx>; 8]>, query_state: &'cx mut OriginalQueryValues<'tcx>, // Note that indices is only used once `var_values` is big enough to be // heap-allocated. @@ -368,9 +362,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { ui = ty::UniverseIndex::ROOT; } self.canonicalize_ty_var( - CanonicalVarInfo { - kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - }, + CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), t, ) } @@ -382,10 +374,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { if nt != t { return self.fold_ty(nt); } else { - self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) }, - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t) } } ty::Infer(ty::FloatVar(vid)) => { @@ -393,10 +382,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { if nt != t { return self.fold_ty(nt); } else { - self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) }, - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t) } } @@ -408,10 +394,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { if !self.canonicalize_mode.preserve_universes() { placeholder.universe = ty::UniverseIndex::ROOT; } - self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) }, - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t) } ty::Bound(debruijn, _) => { @@ -483,10 +466,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { // FIXME: perf problem described in #55921. ui = ty::UniverseIndex::ROOT; } - return self.canonicalize_const_var( - CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, - ct, - ); + return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct); } } } @@ -501,10 +481,8 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { } } ty::ConstKind::Placeholder(placeholder) => { - return self.canonicalize_const_var( - CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderConst(placeholder) }, - ct, - ); + return self + .canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct); } _ => {} } @@ -515,6 +493,14 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { ct } } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.flags().intersects(self.needs_canonical_flags) { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { + if c.flags().intersects(self.needs_canonical_flags) { c.super_fold_with(self) } else { c } + } } impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { @@ -595,7 +581,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { debug_assert!(!out_value.has_infer() && !out_value.has_placeholders()); let canonical_variables = - tcx.mk_canonical_var_infos(&canonicalizer.universe_canonicalized_variables()); + tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_variables()); let max_universe = canonical_variables .iter() @@ -610,18 +596,22 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { /// or returns an existing variable if `kind` has already been /// seen. `kind` is expected to be an unbound variable (or /// potentially a free region). - fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar { + fn canonical_var( + &mut self, + var_kind: CanonicalVarKind<'tcx>, + value: GenericArg<'tcx>, + ) -> BoundVar { let Canonicalizer { variables, query_state, indices, .. } = self; let var_values = &mut query_state.var_values; - let universe = info.universe(); + let universe = var_kind.universe(); if universe != ty::UniverseIndex::ROOT { assert!(self.canonicalize_mode.preserve_universes()); // Insert universe into the universe map. To preserve the order of the // universes in the value being canonicalized, we don't update the - // universe in `info` until we have finished canonicalizing. + // universe in `var_kind` until we have finished canonicalizing. match query_state.universe_map.binary_search(&universe) { Err(idx) => query_state.universe_map.insert(idx, universe), Ok(_) => {} @@ -636,14 +626,14 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { if !var_values.spilled() { // `var_values` is stack-allocated. `indices` isn't used yet. Do a // direct linear search of `var_values`. - if let Some(idx) = var_values.iter().position(|&k| k == kind) { + if let Some(idx) = var_values.iter().position(|&v| v == value) { // `kind` is already present in `var_values`. BoundVar::new(idx) } else { // `kind` isn't present in `var_values`. Append it. Likewise - // for `info` and `variables`. - variables.push(info); - var_values.push(kind); + // for `var_kind` and `variables`. + variables.push(var_kind); + var_values.push(value); assert_eq!(variables.len(), var_values.len()); // If `var_values` has become big enough to be heap-allocated, @@ -653,7 +643,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { *indices = var_values .iter() .enumerate() - .map(|(i, &kind)| (kind, BoundVar::new(i))) + .map(|(i, &value)| (value, BoundVar::new(i))) .collect(); } // The cv is the index of the appended element. @@ -661,9 +651,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } } else { // `var_values` is large. Do a hashmap search via `indices`. - *indices.entry(kind).or_insert_with(|| { - variables.push(info); - var_values.push(kind); + *indices.entry(value).or_insert_with(|| { + variables.push(var_kind); + var_values.push(value); assert_eq!(variables.len(), var_values.len()); BoundVar::new(variables.len() - 1) }) @@ -673,7 +663,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { /// Replaces the universe indexes used in `var_values` with their index in /// `query_state.universe_map`. This minimizes the maximum universe used in /// the canonicalized value. - fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> { + fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> { if self.query_state.universe_map.len() == 1 { return self.variables; } @@ -688,37 +678,33 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { self.variables .iter() - .map(|v| CanonicalVarInfo { - kind: match v.kind { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { - return *v; - } - CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) - } - CanonicalVarKind::Region(u) => { - CanonicalVarKind::Region(reverse_universe_map[&u]) - } - CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), - CanonicalVarKind::PlaceholderTy(placeholder) => { - CanonicalVarKind::PlaceholderTy(ty::Placeholder { - universe: reverse_universe_map[&placeholder.universe], - ..placeholder - }) - } - CanonicalVarKind::PlaceholderRegion(placeholder) => { - CanonicalVarKind::PlaceholderRegion(ty::Placeholder { - universe: reverse_universe_map[&placeholder.universe], - ..placeholder - }) - } - CanonicalVarKind::PlaceholderConst(placeholder) => { - CanonicalVarKind::PlaceholderConst(ty::Placeholder { - universe: reverse_universe_map[&placeholder.universe], - ..placeholder - }) - } - }, + .map(|&kind| match kind { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + return kind; + } + CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + } + CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), + CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), + CanonicalVarKind::PlaceholderTy(placeholder) => { + CanonicalVarKind::PlaceholderTy(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderRegion(placeholder) => { + CanonicalVarKind::PlaceholderRegion(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderConst(placeholder) => { + CanonicalVarKind::PlaceholderConst(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } }) .collect() } @@ -740,20 +726,17 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { &mut self, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - self.canonical_var_for_region( - CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT) }, - r, - ) + self.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r) } /// Creates a canonical variable (with the given `info`) /// representing the region `r`; return a region referencing it. fn canonical_var_for_region( &mut self, - info: CanonicalVarInfo<'tcx>, + var_kind: CanonicalVarKind<'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - let var = self.canonical_var(info, r.into()); + let var = self.canonical_var(var_kind, r.into()); let br = ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }; ty::Region::new_bound(self.cx(), self.binder_index, br) } @@ -762,9 +745,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { /// if `ty_var` is bound to anything; if so, canonicalize /// *that*. Otherwise, create a new canonical variable for /// `ty_var`. - fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> { + fn canonicalize_ty_var( + &mut self, + var_kind: CanonicalVarKind<'tcx>, + ty_var: Ty<'tcx>, + ) -> Ty<'tcx> { debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var))); - let var = self.canonical_var(info, ty_var.into()); + let var = self.canonical_var(var_kind, ty_var.into()); Ty::new_bound(self.tcx, self.binder_index, var.into()) } @@ -774,13 +761,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { /// `const_var`. fn canonicalize_const_var( &mut self, - info: CanonicalVarInfo<'tcx>, - const_var: ty::Const<'tcx>, + var_kind: CanonicalVarKind<'tcx>, + ct_var: ty::Const<'tcx>, ) -> ty::Const<'tcx> { debug_assert!( - !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var)) + !self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var)) ); - let var = self.canonical_var(info, const_var.into()); + let var = self.canonical_var(var_kind, ct_var.into()); ty::Const::new_bound(self.tcx, self.binder_index, var) } } diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs index f5ee5702d09c..67f13192b522 100644 --- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs +++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs @@ -61,15 +61,15 @@ where value } else { let delegate = FnMutDelegate { - regions: &mut |br: ty::BoundRegion| match var_values[br.var].unpack() { + regions: &mut |br: ty::BoundRegion| match var_values[br.var].kind() { GenericArgKind::Lifetime(l) => l, r => bug!("{:?} is a region but value is {:?}", br, r), }, - types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].unpack() { + types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].kind() { GenericArgKind::Type(ty) => ty, r => bug!("{:?} is a type but value is {:?}", bound_ty, r), }, - consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].unpack() { + consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].kind() { GenericArgKind::Const(ct) => ct, c => bug!("{:?} is a const but value is {:?}", bound_ct, c), }, diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 3be07dbe208f..5dffedc70996 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -81,14 +81,14 @@ impl<'tcx> InferCtxt<'tcx> { fn instantiate_canonical_vars( &self, span: Span, - variables: &List>, + variables: &List>, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> CanonicalVarValues<'tcx> { CanonicalVarValues { var_values: self.tcx.mk_args_from_iter( variables .iter() - .map(|info| self.instantiate_canonical_var(span, info, &universe_map)), + .map(|kind| self.instantiate_canonical_var(span, kind, &universe_map)), ), } } @@ -104,10 +104,10 @@ impl<'tcx> InferCtxt<'tcx> { pub fn instantiate_canonical_var( &self, span: Span, - cv_info: CanonicalVarInfo<'tcx>, + kind: CanonicalVarKind<'tcx>, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> GenericArg<'tcx> { - match cv_info.kind { + match kind { CanonicalVarKind::Ty(ty_kind) => { let ty = match ty_kind { CanonicalTyVarKind::General(ui) => { diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 1ae864c454f2..008ef6900089 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -12,23 +12,20 @@ use std::iter; use rustc_index::{Idx, IndexVec}; use rustc_middle::arena::ArenaAllocatable; +use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; -use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument}; use crate::infer::canonical::instantiate::{CanonicalExt, instantiate_value}; use crate::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues, - QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse, + QueryRegionConstraints, QueryResponse, }; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; -use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult}; +use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult, SubregionOrigin}; use crate::traits::query::NoSolution; -use crate::traits::{ - Obligation, ObligationCause, PredicateObligation, PredicateObligations, ScrubbedTraitError, - TraitEngine, -}; +use crate::traits::{ObligationCause, PredicateObligations, ScrubbedTraitError, TraitEngine}; impl<'tcx> InferCtxt<'tcx> { /// This method is meant to be invoked as the final step of a canonical query @@ -169,15 +166,13 @@ impl<'tcx> InferCtxt<'tcx> { where R: Debug + TypeFoldable>, { - let InferOk { value: result_args, mut obligations } = + let InferOk { value: result_args, obligations } = self.query_response_instantiation(cause, param_env, original_values, query_response)?; - obligations.extend(self.query_outlives_constraints_into_obligations( - cause, - param_env, - &query_response.value.region_constraints.outlives, - &result_args, - )); + for (predicate, _category) in &query_response.value.region_constraints.outlives { + let predicate = instantiate_value(self.tcx, &result_args, *predicate); + self.register_outlives_constraint(predicate, cause); + } let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -250,7 +245,7 @@ impl<'tcx> InferCtxt<'tcx> { let result_value = query_response.instantiate_projected(self.tcx, &result_args, |v| { v.var_values[BoundVar::new(index)] }); - match (original_value.unpack(), result_value.unpack()) { + match (original_value.kind(), result_value.kind()) { (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2)) if re1.is_erased() && re2.is_erased() => { @@ -407,7 +402,7 @@ impl<'tcx> InferCtxt<'tcx> { // [(?A, Vec), ('static, '?1), (?B, ?0)] for (original_value, result_value) in iter::zip(&original_values.var_values, result_values) { - match result_value.unpack() { + match result_value.kind() { GenericArgKind::Type(result_value) => { // e.g., here `result_value` might be `?0` in the example above... if let ty::Bound(debruijn, b) = *result_value.kind() { @@ -445,17 +440,17 @@ impl<'tcx> InferCtxt<'tcx> { // a fresh inference variable. let result_args = CanonicalVarValues { var_values: self.tcx.mk_args_from_iter( - query_response.variables.iter().enumerate().map(|(index, info)| { - if info.universe() != ty::UniverseIndex::ROOT { + query_response.variables.iter().enumerate().map(|(index, var_kind)| { + if var_kind.universe() != ty::UniverseIndex::ROOT { // A variable from inside a binder of the query. While ideally these shouldn't // exist at all, we have to deal with them for now. - self.instantiate_canonical_var(cause.span, info, |u| { + self.instantiate_canonical_var(cause.span, var_kind, |u| { universe_map[u.as_usize()] }) - } else if info.is_existential() { + } else if var_kind.is_existential() { match opt_values[BoundVar::new(index)] { Some(k) => k, - None => self.instantiate_canonical_var(cause.span, info, |u| { + None => self.instantiate_canonical_var(cause.span, var_kind, |u| { universe_map[u.as_usize()] }), } @@ -525,47 +520,6 @@ impl<'tcx> InferCtxt<'tcx> { self.unify_canonical_vars(cause, param_env, original_values, instantiated_query_response) } - /// Converts the region constraints resulting from a query into an - /// iterator of obligations. - fn query_outlives_constraints_into_obligations( - &self, - cause: &ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - uninstantiated_region_constraints: &[QueryOutlivesConstraint<'tcx>], - result_args: &CanonicalVarValues<'tcx>, - ) -> impl Iterator> { - uninstantiated_region_constraints.iter().map(move |&constraint| { - let predicate = instantiate_value(self.tcx, result_args, constraint); - self.query_outlives_constraint_to_obligation(predicate, cause.clone(), param_env) - }) - } - - pub fn query_outlives_constraint_to_obligation( - &self, - (predicate, _): QueryOutlivesConstraint<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Obligation<'tcx, ty::Predicate<'tcx>> { - let ty::OutlivesPredicate(k1, r2) = predicate; - - let atom = match k1.unpack() { - GenericArgKind::Lifetime(r1) => ty::PredicateKind::Clause( - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r1, r2)), - ), - GenericArgKind::Type(t1) => ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( - ty::OutlivesPredicate(t1, r2), - )), - GenericArgKind::Const(..) => { - // Consts cannot outlive one another, so we don't expect to - // encounter this branch. - span_bug!(cause.span, "unexpected const outlives {:?}", predicate); - } - }; - let predicate = ty::Binder::dummy(atom); - - Obligation::new(self.tcx, cause, param_env, predicate) - } - /// Given two sets of values for the same set of canonical variables, unify them. /// The second set is produced lazily by supplying indices from the first set. fn unify_canonical_vars( @@ -579,7 +533,7 @@ impl<'tcx> InferCtxt<'tcx> { for (index, value1) in variables1.var_values.iter().enumerate() { let value2 = variables2(BoundVar::new(index)); - match (value1.unpack(), value2.unpack()) { + match (value1.kind(), value2.kind()) { (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { obligations.extend( self.at(cause, param_env) @@ -593,10 +547,10 @@ impl<'tcx> InferCtxt<'tcx> { // no action needed } (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => { - obligations.extend( - self.at(cause, param_env) - .eq(DefineOpaqueTypes::Yes, v1, v2)? - .into_obligations(), + self.inner.borrow_mut().unwrap_region_constraints().make_eqregion( + SubregionOrigin::RelateRegionParamBound(cause.span, None), + v1, + v2, ); } (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 359b9da11ced..f7c702d321b2 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -89,6 +89,57 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid) } + fn is_changed_arg(&self, arg: ty::GenericArg<'tcx>) -> bool { + match arg.kind() { + ty::GenericArgKind::Lifetime(_) => { + // Lifetimes should not change affect trait selection. + false + } + ty::GenericArgKind::Type(ty) => { + if let ty::Infer(infer_ty) = *ty.kind() { + match infer_ty { + ty::InferTy::TyVar(vid) => { + !self.probe_ty_var(vid).is_err_and(|_| self.root_var(vid) == vid) + } + ty::InferTy::IntVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.int_unification_table().probe_value(vid), + ty::IntVarValue::Unknown + if inner.int_unification_table().find(vid) == vid + ) + } + ty::InferTy::FloatVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.float_unification_table().probe_value(vid), + ty::FloatVarValue::Unknown + if inner.float_unification_table().find(vid) == vid + ) + } + ty::InferTy::FreshTy(_) + | ty::InferTy::FreshIntTy(_) + | ty::InferTy::FreshFloatTy(_) => true, + } + } else { + true + } + } + ty::GenericArgKind::Const(ct) => { + if let ty::ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + ty::InferConst::Var(vid) => !self + .probe_const_var(vid) + .is_err_and(|_| self.root_const_var(vid) == vid), + ty::InferConst::Fresh(_) => true, + } + } else { + true + } + } + } + } + fn next_region_infer(&self) -> ty::Region<'tcx> { self.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) } @@ -214,7 +265,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { } fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) { - self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span)); + self.register_type_outlives_constraint(ty, r, &ObligationCause::dummy_with_span(span)); } type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries; diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b408d76010d7..96e03e3bea56 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -150,7 +150,7 @@ pub struct InferCtxtInner<'tcx> { /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. - region_obligations: Vec>, + region_obligations: Vec>, /// Caches for opaque type inference. opaque_type_storage: OpaqueTypeStorage<'tcx>, @@ -173,7 +173,7 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - pub fn region_obligations(&self) -> &[RegionObligation<'tcx>] { + pub fn region_obligations(&self) -> &[TypeOutlivesConstraint<'tcx>] { &self.region_obligations } @@ -488,7 +488,7 @@ impl fmt::Display for FixupError { /// See the `region_obligations` field for more information. #[derive(Clone, Debug)] -pub struct RegionObligation<'tcx> { +pub struct TypeOutlivesConstraint<'tcx> { pub sub_region: ty::Region<'tcx>, pub sup_type: Ty<'tcx>, pub origin: SubregionOrigin<'tcx>, @@ -738,19 +738,6 @@ impl<'tcx> InferCtxt<'tcx> { }) } - pub fn region_outlives_predicate( - &self, - cause: &traits::ObligationCause<'tcx>, - predicate: ty::PolyRegionOutlivesPredicate<'tcx>, - ) { - self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| { - let origin = SubregionOrigin::from_obligation_cause(cause, || { - RelateRegionParamBound(cause.span, None) - }); - self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` - }) - } - /// Number of type variables created so far. pub fn num_ty_vars(&self) -> usize { self.inner.borrow_mut().type_variables().num_vars() @@ -1385,7 +1372,7 @@ impl<'tcx> TyOrConstInferVar { /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). pub fn maybe_from_generic_arg(arg: GenericArg<'tcx>) -> Option { - match arg.unpack() { + match arg.kind() { GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), GenericArgKind::Const(ct) => Self::maybe_from_const(ct), GenericArgKind::Lifetime(_) => None, @@ -1396,7 +1383,7 @@ impl<'tcx> TyOrConstInferVar { /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). pub fn maybe_from_term(term: Term<'tcx>) -> Option { - match term.unpack() { + match term.kind() { TermKind::Ty(ty) => Self::maybe_from_ty(ty), TermKind::Const(ct) => Self::maybe_from_const(ct), } diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index 46752840e1ba..ab65da3913da 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -24,6 +24,12 @@ pub struct OpaqueTypeStorageEntries { duplicate_entries: usize, } +impl rustc_type_ir::inherent::OpaqueTypeStorageEntries for OpaqueTypeStorageEntries { + fn needs_reevaluation(self, canonicalized: usize) -> bool { + self.opaque_types != canonicalized + } +} + impl<'tcx> OpaqueTypeStorage<'tcx> { #[instrument(level = "debug")] pub(crate) fn remove( diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 5fd98e35e5ce..10827c9fd037 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -76,23 +76,56 @@ use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; use crate::infer::resolve::OpportunisticRegionResolver; use crate::infer::snapshot::undo_log::UndoLog; -use crate::infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use crate::infer::{ + self, GenericKind, InferCtxt, SubregionOrigin, TypeOutlivesConstraint, VerifyBound, +}; use crate::traits::{ObligationCause, ObligationCauseCode}; impl<'tcx> InferCtxt<'tcx> { + pub fn register_outlives_constraint( + &self, + ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, + cause: &ObligationCause<'tcx>, + ) { + match arg.kind() { + ty::GenericArgKind::Lifetime(r1) => { + self.register_region_outlives_constraint(ty::OutlivesPredicate(r1, r2), cause); + } + ty::GenericArgKind::Type(ty1) => { + self.register_type_outlives_constraint(ty1, r2, cause); + } + ty::GenericArgKind::Const(_) => unreachable!(), + } + } + + pub fn register_region_outlives_constraint( + &self, + ty::OutlivesPredicate(r_a, r_b): ty::RegionOutlivesPredicate<'tcx>, + cause: &ObligationCause<'tcx>, + ) { + let origin = SubregionOrigin::from_obligation_cause(cause, || { + SubregionOrigin::RelateRegionParamBound(cause.span, None) + }); + // `'a: 'b` ==> `'b <= 'a` + self.sub_regions(origin, r_b, r_a); + } + /// Registers that the given region obligation must be resolved /// from within the scope of `body_id`. These regions are enqueued /// and later processed by regionck, when full type information is /// available (see `region_obligations` field for more /// information). #[instrument(level = "debug", skip(self))] - pub fn register_region_obligation(&self, obligation: RegionObligation<'tcx>) { + pub fn register_type_outlives_constraint_inner( + &self, + obligation: TypeOutlivesConstraint<'tcx>, + ) { let mut inner = self.inner.borrow_mut(); - inner.undo_log.push(UndoLog::PushRegionObligation); + inner.undo_log.push(UndoLog::PushTypeOutlivesConstraint); inner.region_obligations.push(obligation); } - pub fn register_region_obligation_with_cause( + pub fn register_type_outlives_constraint( &self, sup_type: Ty<'tcx>, sub_region: Region<'tcx>, @@ -124,11 +157,15 @@ impl<'tcx> InferCtxt<'tcx> { ) }); - self.register_region_obligation(RegionObligation { sup_type, sub_region, origin }); + self.register_type_outlives_constraint_inner(TypeOutlivesConstraint { + sup_type, + sub_region, + origin, + }); } /// Trait queries just want to pass back type obligations "as is" - pub fn take_registered_region_obligations(&self) -> Vec> { + pub fn take_registered_region_obligations(&self) -> Vec> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } @@ -166,7 +203,7 @@ impl<'tcx> InferCtxt<'tcx> { ); } - for RegionObligation { sup_type, sub_region, origin } in my_region_obligations { + for TypeOutlivesConstraint { sup_type, sub_region, origin } in my_region_obligations { let outlives = ty::Binder::dummy(ty::OutlivesPredicate(sup_type, sub_region)); let ty::OutlivesPredicate(sup_type, sub_region) = deeply_normalize_ty(outlives, origin.clone()) @@ -466,8 +503,8 @@ where opt_variances: Option<&[ty::Variance]>, ) { let constraint = origin.to_constraint_category(); - for (index, k) in args.iter().enumerate() { - match k.unpack() { + for (index, arg) in args.iter().enumerate() { + match arg.kind() { GenericArgKind::Lifetime(lt) => { let variance = if let Some(variances) = opt_variances { variances[index] diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 210b8f37d883..0e17b100276a 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -329,7 +329,7 @@ struct Generalizer<'me, 'tcx> { impl<'tcx> Generalizer<'_, 'tcx> { /// Create an error that corresponds to the term kind in `root_term` fn cyclic_term_error(&self) -> TypeError<'tcx> { - match self.root_term.unpack() { + match self.root_term.kind() { ty::TermKind::Ty(ty) => TypeError::CyclicTy(ty), ty::TermKind::Const(ct) => TypeError::CyclicConst(ct), } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 4b0ace8c554d..a95f24b5b95d 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -55,6 +55,14 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { ct.super_fold_with(self) } } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if !p.has_non_region_infer() { p } else { p.super_fold_with(self) } + } + + fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { + if !c.has_non_region_infer() { c } else { c.super_fold_with(self) } + } } /// The opportunistic region resolver opportunistically resolves regions diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index b7412d3d6a6d..6193f35f3eb6 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -26,7 +26,7 @@ pub(crate) enum UndoLog<'tcx> { RegionConstraintCollector(region_constraints::UndoLog<'tcx>), RegionUnificationTable(sv::UndoLog>>), ProjectionCache(traits::UndoLog<'tcx>), - PushRegionObligation, + PushTypeOutlivesConstraint, } macro_rules! impl_from { @@ -72,7 +72,7 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) } UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo), - UndoLog::PushRegionObligation => { + UndoLog::PushTypeOutlivesConstraint => { self.region_obligations.pop(); } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index e28639576f03..75d92ae7a2ed 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -45,7 +45,7 @@ use crate::interface::Compiler; use crate::{errors, limits, proc_macro_decls, util}; pub fn parse<'a>(sess: &'a Session) -> ast::Crate { - let krate = sess + let mut krate = sess .time("parse_crate", || { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { Input::File(file) => new_parser_from_file(&sess.psess, file, None), @@ -64,6 +64,12 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate { input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1"); } + rustc_builtin_macros::cmdline_attrs::inject( + &mut krate, + &sess.psess, + &sess.opts.unstable_opts.crate_attr, + ); + krate } @@ -282,6 +288,7 @@ fn configure_and_expand( resolver.resolve_crate(&krate); CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate); + CStore::from_tcx(tcx).report_incompatible_async_drop_feature(tcx, &krate); krate } @@ -804,17 +811,11 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( compiler: &Compiler, - mut krate: rustc_ast::Crate, + krate: rustc_ast::Crate, f: F, ) -> T { let sess = &compiler.sess; - rustc_builtin_macros::cmdline_attrs::inject( - &mut krate, - &sess.psess, - &sess.opts.unstable_opts.crate_attr, - ); - let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); let crate_name = get_crate_name(sess, &pre_configured_attrs); diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 7718f16984da..64751eaf1fea 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 08180bf8f8b2..17485a838f31 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -13,6 +13,8 @@ lint_ambiguous_negative_literals = `-` has lower precedence than method calls, w lint_ambiguous_wide_pointer_comparisons = ambiguous wide pointer comparison, the comparison includes metadata which may not be expected .addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses .addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + .cast_suggestion = use untyped pointers to only compare their addresses + .expect_suggestion = or expect the lint to compare the pointers metadata and addresses lint_associated_const_elided_lifetime = {$elided -> [true] `&` without an explicit lifetime name cannot be used here @@ -805,6 +807,11 @@ lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're lint_type_ir_trait_usage = do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler +lint_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 + lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value @@ -835,6 +842,7 @@ lint_unexpected_cfg_name_similar_name = there is a config with a similar name lint_unexpected_cfg_name_similar_name_different_values = there is a config with a similar name and different values lint_unexpected_cfg_name_similar_name_no_value = there is a config with a similar name and no value lint_unexpected_cfg_name_similar_name_value = there is a config with a similar name and value +lint_unexpected_cfg_name_version_syntax = there is a similar config predicate: `version("..")` lint_unexpected_cfg_name_with_similar_value = found config with similar value lint_unexpected_cfg_value = unexpected `cfg` condition value: {$has_value -> @@ -952,7 +960,8 @@ lint_unused_doc_comment = unused doc comment .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion lint_unused_extern_crate = unused extern crate - .suggestion = remove it + .label = unused + .suggestion = remove the unused `extern crate` lint_unused_import_braces = braces around {$node} is unnecessary diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 95e31e4af1e3..47b80135bae9 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -545,22 +545,22 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { return; } let (def, ty) = match item.kind { - hir::ItemKind::Struct(_, _, ast_generics) => { - if !ast_generics.params.is_empty() { + hir::ItemKind::Struct(_, generics, _) => { + if !generics.params.is_empty() { return; } let def = cx.tcx.adt_def(item.owner_id); (def, Ty::new_adt(cx.tcx, def, ty::List::empty())) } - hir::ItemKind::Union(_, _, ast_generics) => { - if !ast_generics.params.is_empty() { + hir::ItemKind::Union(_, generics, _) => { + if !generics.params.is_empty() { return; } let def = cx.tcx.adt_def(item.owner_id); (def, Ty::new_adt(cx.tcx, def, ty::List::empty())) } - hir::ItemKind::Enum(_, _, ast_generics) => { - if !ast_generics.params.is_empty() { + hir::ItemKind::Enum(_, generics, _) => { + if !generics.params.is_empty() { return; } let def = cx.tcx.adt_def(item.owner_id); @@ -1422,7 +1422,7 @@ impl TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(_, hir_ty, generics) = item.kind else { return }; + let hir::ItemKind::TyAlias(_, generics, hir_ty) = item.kind else { return }; // There must not be a where clause. if generics.predicates.is_empty() { @@ -2125,9 +2125,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { use rustc_middle::middle::resolve_bound_vars::ResolvedArg; let def_id = item.owner_id.def_id; - if let hir::ItemKind::Struct(_, _, hir_generics) - | hir::ItemKind::Enum(_, _, hir_generics) - | hir::ItemKind::Union(_, _, hir_generics) = item.kind + if let hir::ItemKind::Struct(_, generics, _) + | hir::ItemKind::Enum(_, generics, _) + | hir::ItemKind::Union(_, generics, _) = item.kind { let inferred_outlives = cx.tcx.inferred_outlives_of(def_id); if inferred_outlives.is_empty() { @@ -2135,7 +2135,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { } let ty_generics = cx.tcx.generics_of(def_id); - let num_where_predicates = hir_generics + let num_where_predicates = generics .predicates .iter() .filter(|predicate| predicate.kind.in_where_clause()) @@ -2145,7 +2145,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { let mut lint_spans = Vec::new(); let mut where_lint_spans = Vec::new(); let mut dropped_where_predicate_count = 0; - for (i, where_predicate) in hir_generics.predicates.iter().enumerate() { + for (i, where_predicate) in generics.predicates.iter().enumerate() { let (relevant_lifetimes, bounds, predicate_span, in_where_clause) = match where_predicate.kind { hir::WherePredicateKind::RegionPredicate(predicate) => { @@ -2228,7 +2228,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { } else if i + 1 < num_where_predicates { // If all the bounds on a predicate were inferable and there are // further predicates, we want to eat the trailing comma. - let next_predicate_span = hir_generics.predicates[i + 1].span; + let next_predicate_span = generics.predicates[i + 1].span; if next_predicate_span.from_expansion() { where_lint_spans.push(predicate_span); } else { @@ -2237,7 +2237,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { } } else { // Eat the optional trailing comma after the last predicate. - let where_span = hir_generics.where_clause_span; + let where_span = generics.where_clause_span; if where_span.from_expansion() { where_lint_spans.push(predicate_span); } else { @@ -2255,18 +2255,18 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // If all predicates in where clause are inferable, drop the entire clause // (including the `where`) - if hir_generics.has_where_clause_predicates + if generics.has_where_clause_predicates && dropped_where_predicate_count == num_where_predicates { - let where_span = hir_generics.where_clause_span; + let where_span = generics.where_clause_span; // Extend the where clause back to the closing `>` of the // generics, except for tuple struct, which have the `where` // after the fields of the struct. let full_where_span = - if let hir::ItemKind::Struct(_, hir::VariantData::Tuple(..), _) = item.kind { + if let hir::ItemKind::Struct(_, _, hir::VariantData::Tuple(..)) = item.kind { where_span } else { - hir_generics.span.shrink_to_hi().to(where_span) + generics.span.shrink_to_hi().to(where_span) }; // Due to macro expansions, the `full_where_span` might not actually contain all diff --git a/compiler/rustc_lint/src/default_could_be_derived.rs b/compiler/rustc_lint/src/default_could_be_derived.rs index 78d129642dc7..7734f441df2e 100644 --- a/compiler/rustc_lint/src/default_could_be_derived.rs +++ b/compiler/rustc_lint/src/default_could_be_derived.rs @@ -95,8 +95,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived { kind: hir::ItemKind::Struct( _, - hir::VariantData::Struct { fields, recovered: _ }, _generics, + hir::VariantData::Struct { fields, recovered: _ }, ), .. })) => fields.iter().map(|f| (f.ident.name, f)).collect::>(), diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index f9601fa5ef1d..0c62c14af631 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -18,7 +18,7 @@ use tracing::debug; use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; -mod diagnostics; +pub(super) mod diagnostics; macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); @@ -40,7 +40,7 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; self.context.opt_span_lint(lint_id.lint, span, |diag| { - diagnostics::decorate_lint(self.context.sess(), self.tcx, diagnostic, diag); + diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, diagnostic, diag); }); } } @@ -277,7 +277,7 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> ast_visit::walk_attribute(self, attr); } - fn visit_mac_def(&mut self, mac: &'ast ast::MacroDef, id: ast::NodeId) { + fn visit_macro_def(&mut self, mac: &'ast ast::MacroDef, id: ast::NodeId) { lint_callback!(self, check_mac_def, mac); self.check_id(id); } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 40ca9e05d95d..71b621e8d20e 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -18,7 +18,7 @@ use crate::lints::{self, ElidedNamedLifetime}; mod check_cfg; -pub(super) fn decorate_lint( +pub fn decorate_builtin_lint( sess: &Session, tcx: Option>, diagnostic: BuiltinLintDiag, @@ -187,6 +187,27 @@ pub(super) fn decorate_lint( lints::ReservedMultihash { suggestion }.decorate_lint(diag); } } + BuiltinLintDiag::HiddenUnicodeCodepoints { + label, + count, + span_label, + labels, + escape, + spans, + } => { + lints::HiddenUnicodeCodepointsDiag { + label: &label, + count, + span_label, + labels: labels.map(|spans| lints::HiddenUnicodeCodepointsDiagLabels { spans }), + sub: if escape { + lints::HiddenUnicodeCodepointsDiagSub::Escape { spans } + } else { + lints::HiddenUnicodeCodepointsDiagSub::NoEscape { spans } + }, + } + .decorate_lint(diag); + } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag); } @@ -292,8 +313,8 @@ pub(super) fn decorate_lint( BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); } - BuiltinLintDiag::UnusedExternCrate { removal_span } => { - lints::UnusedExternCrate { removal_span }.decorate_lint(diag); + BuiltinLintDiag::UnusedExternCrate { span, removal_span } => { + lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag); } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs index 946dbc34f713..e2f5dd315d57 100644 --- a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -140,6 +140,14 @@ pub(super) fn unexpected_cfg_name( let code_sugg = if is_feature_cfg && is_from_cargo { lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures + // Suggest correct `version("..")` predicate syntax + } else if let Some((_value, value_span)) = value + && name == sym::version + { + lints::unexpected_cfg_name::CodeSuggestion::VersionSyntax { + between_name_and_value: name_span.between(value_span), + after_value: value_span.shrink_to_hi(), + } // Suggest the most probable if we found one } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { is_feature_cfg |= best_match == sym::feature; diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs deleted file mode 100644 index 491c2826baaa..000000000000 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ /dev/null @@ -1,136 +0,0 @@ -use ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_control_chars}; -use rustc_ast as ast; -use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::{BytePos, Span, Symbol}; - -use crate::lints::{ - HiddenUnicodeCodepointsDiag, HiddenUnicodeCodepointsDiagLabels, HiddenUnicodeCodepointsDiagSub, -}; -use crate::{EarlyContext, EarlyLintPass, LintContext}; - -declare_lint! { - #[allow(text_direction_codepoint_in_literal)] - /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the - /// visual representation of text on screen in a way that does not correspond to their on - /// memory representation. - /// - /// ### Explanation - /// - /// The unicode characters `\u{202A}`, `\u{202B}`, `\u{202D}`, `\u{202E}`, `\u{2066}`, - /// `\u{2067}`, `\u{2068}`, `\u{202C}` and `\u{2069}` make the flow of text on screen change - /// its direction on software that supports these codepoints. This makes the text "abc" display - /// as "cba" on screen. By leveraging software that supports these, people can write specially - /// crafted literals that make the surrounding code seem like it's performing one action, when - /// in reality it is performing another. Because of this, we proactively lint against their - /// presence to avoid surprises. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(text_direction_codepoint_in_literal)] - /// fn main() { - /// println!("{:?}", '‮'); - /// } - /// ``` - /// - /// {{produces}} - /// - pub TEXT_DIRECTION_CODEPOINT_IN_LITERAL, - Deny, - "detect special Unicode codepoints that affect the visual representation of text on screen, \ - changing the direction in which text flows", -} - -declare_lint_pass!(HiddenUnicodeCodepoints => [TEXT_DIRECTION_CODEPOINT_IN_LITERAL]); - -impl HiddenUnicodeCodepoints { - fn lint_text_direction_codepoint( - &self, - cx: &EarlyContext<'_>, - text: Symbol, - span: Span, - padding: u32, - point_at_inner_spans: bool, - label: &str, - ) { - // Obtain the `Span`s for each of the forbidden chars. - let spans: Vec<_> = text - .as_str() - .char_indices() - .filter_map(|(i, c)| { - TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = span.lo() + BytePos(i as u32 + padding); - (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) - }) - }) - .collect(); - - let count = spans.len(); - let labels = point_at_inner_spans - .then_some(HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() }); - let sub = if point_at_inner_spans && !spans.is_empty() { - HiddenUnicodeCodepointsDiagSub::Escape { spans } - } else { - HiddenUnicodeCodepointsDiagSub::NoEscape { spans } - }; - - cx.emit_span_lint( - TEXT_DIRECTION_CODEPOINT_IN_LITERAL, - span, - HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub }, - ); - } - - fn check_literal( - &mut self, - cx: &EarlyContext<'_>, - text: Symbol, - lit_kind: ast::token::LitKind, - span: Span, - label: &'static str, - ) { - if !contains_text_flow_control_chars(text.as_str()) { - return; - } - let (padding, point_at_inner_spans) = match lit_kind { - // account for `"` or `'` - ast::token::LitKind::Str | ast::token::LitKind::Char => (1, true), - // account for `c"` - ast::token::LitKind::CStr => (2, true), - // account for `r###"` - ast::token::LitKind::StrRaw(n) => (n as u32 + 2, true), - // account for `cr###"` - ast::token::LitKind::CStrRaw(n) => (n as u32 + 3, true), - // suppress bad literals. - ast::token::LitKind::Err(_) => return, - // Be conservative just in case new literals do support these. - _ => (0, false), - }; - self.lint_text_direction_codepoint(cx, text, span, padding, point_at_inner_spans, label); - } -} - -impl EarlyLintPass for HiddenUnicodeCodepoints { - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - if let ast::AttrKind::DocComment(_, comment) = attr.kind { - if contains_text_flow_control_chars(comment.as_str()) { - self.lint_text_direction_codepoint(cx, comment, attr.span, 0, false, "doc comment"); - } - } - } - - #[inline] - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - // byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString` - match &expr.kind { - ast::ExprKind::Lit(token_lit) => { - self.check_literal(cx, token_lit.symbol, token_lit.kind, expr.span, "literal"); - } - ast::ExprKind::FormatArgs(args) => { - let (lit_kind, text) = args.uncooked_fmt_str; - self.check_literal(cx, text, lit_kind, args.span, "format string"); - } - _ => {} - }; - } -} diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index a8f45d043be9..8124d7f7c86a 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -461,7 +461,7 @@ fn extract_def_id_from_arg<'tcx>( generics: &'tcx ty::Generics, arg: ty::GenericArg<'tcx>, ) -> DefId { - match arg.unpack() { + match arg.kind() { ty::GenericArgKind::Lifetime(re) => match re.kind() { ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id, ty::ReBound( diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4ff586a79a6e..0a52e42e4428 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -48,7 +48,6 @@ mod errors; mod expect; mod for_loops_over_fallibles; mod foreign_modules; -pub mod hidden_unicode_codepoints; mod if_let_rescope; mod impl_trait_overcaptures; mod internal; @@ -75,6 +74,7 @@ mod reference_casting; mod shadowed_into_iter; mod static_mut_refs; mod traits; +mod transmute; mod types; mod unit_bindings; mod unqualified_local_imports; @@ -91,7 +91,6 @@ use deref_into_dyn_supertrait::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; -use hidden_unicode_codepoints::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; use internal::*; @@ -118,6 +117,7 @@ use shadowed_into_iter::ShadowedIntoIter; pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use static_mut_refs::*; use traits::*; +use transmute::CheckTransmutes; use types::*; use unit_bindings::*; use unqualified_local_imports::*; @@ -126,6 +126,7 @@ use unused::*; #[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; +pub use early::diagnostics::decorate_builtin_lint; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder; @@ -174,7 +175,6 @@ early_lint_methods!( DeprecatedAttr: DeprecatedAttr::default(), WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, - HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, IncompleteInternalFeatures: IncompleteInternalFeatures, RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, @@ -245,6 +245,7 @@ late_lint_methods!( IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, UnqualifiedLocalImports: UnqualifiedLocalImports, + CheckTransmutes: CheckTransmutes, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7268a7f704fc..10d0e2c93a83 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1782,13 +1782,20 @@ pub(crate) enum InvalidNanComparisonsSuggestion { #[derive(LintDiagnostic)] pub(crate) enum AmbiguousWidePointerComparisons<'a> { #[diag(lint_ambiguous_wide_pointer_comparisons)] - Spanful { + SpanfulEq { #[subdiagnostic] addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion<'a>, #[subdiagnostic] addr_metadata_suggestion: Option>, }, #[diag(lint_ambiguous_wide_pointer_comparisons)] + SpanfulCmp { + #[subdiagnostic] + cast_suggestion: AmbiguousWidePointerComparisonsCastSuggestion<'a>, + #[subdiagnostic] + expect_suggestion: AmbiguousWidePointerComparisonsExpectSuggestion<'a>, + }, + #[diag(lint_ambiguous_wide_pointer_comparisons)] #[help(lint_addr_metadata_suggestion)] #[help(lint_addr_suggestion)] Spanless, @@ -1816,48 +1823,67 @@ pub(crate) struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { } #[derive(Subdiagnostic)] -pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { - #[multipart_suggestion( - lint_addr_suggestion, - style = "verbose", - // FIXME(#53934): make machine-applicable again - applicability = "maybe-incorrect" +#[multipart_suggestion( + lint_addr_suggestion, + style = "verbose", + // FIXME(#53934): make machine-applicable again + applicability = "maybe-incorrect" +)] +pub(crate) struct AmbiguousWidePointerComparisonsAddrSuggestion<'a> { + pub(crate) ne: &'a str, + pub(crate) deref_left: &'a str, + pub(crate) deref_right: &'a str, + pub(crate) l_modifiers: &'a str, + pub(crate) r_modifiers: &'a str, + #[suggestion_part(code = "{ne}std::ptr::addr_eq({deref_left}")] + pub(crate) left: Span, + #[suggestion_part(code = "{l_modifiers}, {deref_right}")] + pub(crate) middle: Span, + #[suggestion_part(code = "{r_modifiers})")] + pub(crate) right: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_cast_suggestion, + style = "verbose", + // FIXME(#53934): make machine-applicable again + applicability = "maybe-incorrect" +)] +pub(crate) struct AmbiguousWidePointerComparisonsCastSuggestion<'a> { + pub(crate) deref_left: &'a str, + pub(crate) deref_right: &'a str, + pub(crate) paren_left: &'a str, + pub(crate) paren_right: &'a str, + pub(crate) l_modifiers: &'a str, + pub(crate) r_modifiers: &'a str, + #[suggestion_part(code = "({deref_left}")] + pub(crate) left_before: Option, + #[suggestion_part(code = "{l_modifiers}{paren_left}.cast::<()>()")] + pub(crate) left_after: Span, + #[suggestion_part(code = "({deref_right}")] + pub(crate) right_before: Option, + #[suggestion_part(code = "{r_modifiers}{paren_right}.cast::<()>()")] + pub(crate) right_after: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_expect_suggestion, + style = "verbose", + // FIXME(#53934): make machine-applicable again + applicability = "maybe-incorrect" +)] +pub(crate) struct AmbiguousWidePointerComparisonsExpectSuggestion<'a> { + pub(crate) paren_left: &'a str, + pub(crate) paren_right: &'a str, + // FIXME(#127436): Adjust once resolved + #[suggestion_part( + code = r#"{{ #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] {paren_left}"# )] - AddrEq { - ne: &'a str, - deref_left: &'a str, - deref_right: &'a str, - l_modifiers: &'a str, - r_modifiers: &'a str, - #[suggestion_part(code = "{ne}std::ptr::addr_eq({deref_left}")] - left: Span, - #[suggestion_part(code = "{l_modifiers}, {deref_right}")] - middle: Span, - #[suggestion_part(code = "{r_modifiers})")] - right: Span, - }, - #[multipart_suggestion( - lint_addr_suggestion, - style = "verbose", - // FIXME(#53934): make machine-applicable again - applicability = "maybe-incorrect" - )] - Cast { - deref_left: &'a str, - deref_right: &'a str, - paren_left: &'a str, - paren_right: &'a str, - l_modifiers: &'a str, - r_modifiers: &'a str, - #[suggestion_part(code = "({deref_left}")] - left_before: Option, - #[suggestion_part(code = "{l_modifiers}{paren_left}.cast::<()>()")] - left_after: Span, - #[suggestion_part(code = "({deref_right}")] - right_before: Option, - #[suggestion_part(code = "{r_modifiers}{paren_right}.cast::<()>()")] - right_after: Span, - }, + pub(crate) before: Span, + #[suggestion_part(code = "{paren_right} }}")] + pub(crate) after: Span, } #[derive(LintDiagnostic)] @@ -2273,6 +2299,16 @@ pub(crate) mod unexpected_cfg_name { pub(crate) enum CodeSuggestion { #[help(lint_unexpected_cfg_define_features)] DefineFeatures, + #[multipart_suggestion( + lint_unexpected_cfg_name_version_syntax, + applicability = "machine-applicable" + )] + VersionSyntax { + #[suggestion_part(code = "(")] + between_name_and_value: Span, + #[suggestion_part(code = ")")] + after_value: Span, + }, #[suggestion( lint_unexpected_cfg_name_similar_name_value, applicability = "maybe-incorrect", @@ -3041,7 +3077,9 @@ pub(crate) struct ByteSliceInPackedStructWithDerive { #[derive(LintDiagnostic)] #[diag(lint_unused_extern_crate)] pub(crate) struct UnusedExternCrate { - #[suggestion(code = "", applicability = "machine-applicable")] + #[label] + pub span: Span, + #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub removal_span: Span, } diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 9ed11d9cc82f..b877f909fc02 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { && parent_opt_item_name != Some(kw::Underscore) && let Some(parent) = parent.as_local() && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) - && let ItemKind::Const(ident, ty, _, _) = item.kind + && let ItemKind::Const(ident, _, ty, _) = item.kind && let TyKind::Tup(&[]) = ty.kind { Some(ident.span) diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index d1138e8f1fa5..31c180744668 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,5 +1,6 @@ use rustc_abi::ExternAbi; -use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprAttr}; +use rustc_attr_data_structures::{AttributeKind, ReprAttr}; +use rustc_attr_parsing::AttributeParser; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind}; @@ -512,7 +513,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir_attrs(it.hir_id()); match it.kind { - hir::ItemKind::Static(ident, ..) + hir::ItemKind::Static(_, ident, ..) if !ast::attr::contains_name(attrs, sym::no_mangle) => { NonUpperCaseGlobals::check_upper_case(cx, "static variable", &ident); diff --git a/compiler/rustc_lint/src/transmute.rs b/compiler/rustc_lint/src/transmute.rs new file mode 100644 index 000000000000..bc1d4587d076 --- /dev/null +++ b/compiler/rustc_lint/src/transmute.rs @@ -0,0 +1,278 @@ +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{self as hir}; +use rustc_macros::LintDiagnostic; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint, impl_lint_pass}; +use rustc_span::sym; + +use crate::{LateContext, LateLintPass}; + +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", +} + +declare_lint! { + /// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives. + /// + /// ### Example + /// + /// ```rust + /// fn bytes_at_home(x: [u8; 4]) -> u32 { + /// unsafe { std::mem::transmute(x) } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Using an explicit method is preferable over calls to + /// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as + /// they more clearly communicate the intent, are easier to review, and + /// are less likely to accidentally result in unsoundness. + pub UNNECESSARY_TRANSMUTES, + Warn, + "detects transmutes that can also be achieved by other operations" +} + +pub(crate) struct CheckTransmutes; + +impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]); + +impl<'tcx> LateLintPass<'tcx> for CheckTransmutes { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Call(callee, [arg]) = expr.kind else { + return; + }; + let hir::ExprKind::Path(qpath) = callee.kind else { + return; + }; + let Res::Def(DefKind::Fn, def_id) = cx.qpath_res(&qpath, callee.hir_id) else { + return; + }; + if !cx.tcx.is_intrinsic(def_id, sym::transmute) { + return; + }; + let body_owner_def_id = cx.tcx.hir_enclosing_body_owner(expr.hir_id); + let const_context = cx.tcx.hir_body_const_context(body_owner_def_id); + let args = cx.typeck_results().node_args(callee.hir_id); + + let src = args.type_at(0); + let dst = args.type_at(1); + + check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst); + check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst); + } +} + +/// Check for transmutes that exhibit undefined behavior. +/// For example, transmuting pointers to integers in a const context. +/// +/// 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 check_ptr_transmute_in_const<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + body_owner_def_id: LocalDefId, + const_context: Option, + src: Ty<'tcx>, + dst: Ty<'tcx>, +) { + if matches!(const_context, Some(hir::ConstContext::ConstFn)) + || matches!(cx.tcx.def_kind(body_owner_def_id), DefKind::AssocConst) + { + if src.is_raw_ptr() && dst.is_integral() { + cx.tcx.emit_node_span_lint( + PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, + expr.hir_id, + expr.span, + UndefinedTransmuteLint, + ); + } + } +} + +/// Check for transmutes that overlap with stdlib methods. +/// For example, transmuting `[u8; 4]` to `u32`. +/// +/// We chose not to lint u8 -> bool transmutes, see #140431. +fn check_unnecessary_transmute<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + callee: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + const_context: Option, + src: Ty<'tcx>, + dst: Ty<'tcx>, +) { + let callee_span = callee.span.find_ancestor_inside(expr.span).unwrap_or(callee.span); + let (sugg, help) = match (src.kind(), dst.kind()) { + // dont check the length; transmute does that for us. + // [u8; _] => primitive + (ty::Array(t, _), ty::Uint(_) | ty::Float(_) | ty::Int(_)) + if *t.kind() == ty::Uint(ty::UintTy::U8) => + { + ( + Some(vec![(callee_span, format!("{dst}::from_ne_bytes"))]), + Some( + "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order", + ), + ) + } + // primitive => [u8; _] + (ty::Uint(_) | ty::Float(_) | ty::Int(_), ty::Array(t, _)) + if *t.kind() == ty::Uint(ty::UintTy::U8) => + { + ( + Some(vec![(callee_span, format!("{src}::to_ne_bytes"))]), + Some( + "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order", + ), + ) + } + // char → u32 + (ty::Char, ty::Uint(ty::UintTy::U32)) => { + (Some(vec![(callee_span, "u32::from".to_string())]), None) + } + // char (→ u32) → i32 + (ty::Char, ty::Int(ty::IntTy::I32)) => ( + Some(vec![ + (callee_span, "u32::from".to_string()), + (expr.span.shrink_to_hi(), ".cast_signed()".to_string()), + ]), + None, + ), + // u32 → char + (ty::Uint(ty::UintTy::U32), ty::Char) => ( + Some(vec![(callee_span, "char::from_u32_unchecked".to_string())]), + Some("consider using `char::from_u32(…).unwrap()`"), + ), + // i32 → char + (ty::Int(ty::IntTy::I32), ty::Char) => ( + Some(vec![ + (callee_span, "char::from_u32_unchecked(i32::cast_unsigned".to_string()), + (expr.span.shrink_to_hi(), ")".to_string()), + ]), + Some("consider using `char::from_u32(i32::cast_unsigned(…)).unwrap()`"), + ), + // uNN → iNN + (ty::Uint(_), ty::Int(_)) => { + (Some(vec![(callee_span, format!("{src}::cast_signed"))]), None) + } + // iNN → uNN + (ty::Int(_), ty::Uint(_)) => { + (Some(vec![(callee_span, format!("{src}::cast_unsigned"))]), None) + } + // fNN → usize, isize + (ty::Float(_), ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize)) => ( + Some(vec![ + (callee_span, format!("{src}::to_bits")), + (expr.span.shrink_to_hi(), format!(" as {dst}")), + ]), + None, + ), + // fNN (→ uNN) → iNN + (ty::Float(_), ty::Int(..)) => ( + Some(vec![ + (callee_span, format!("{src}::to_bits")), + (expr.span.shrink_to_hi(), ".cast_signed()".to_string()), + ]), + None, + ), + // fNN → uNN + (ty::Float(_), ty::Uint(..)) => { + (Some(vec![(callee_span, format!("{src}::to_bits"))]), None) + } + // xsize → fNN + (ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize), ty::Float(_)) => ( + Some(vec![ + (callee_span, format!("{dst}::from_bits")), + (arg.span.shrink_to_hi(), " as _".to_string()), + ]), + None, + ), + // iNN (→ uNN) → fNN + (ty::Int(_), ty::Float(_)) => ( + Some(vec![ + (callee_span, format!("{dst}::from_bits({src}::cast_unsigned")), + (expr.span.shrink_to_hi(), ")".to_string()), + ]), + None, + ), + // uNN → fNN + (ty::Uint(_), ty::Float(_)) => { + (Some(vec![(callee_span, format!("{dst}::from_bits"))]), None) + } + // bool → x8 in const context since `From::from` is not const yet + // FIXME: Consider arg expr's precedence to avoid parentheses. + // FIXME(const_traits): Remove this when `From::from` is constified. + (ty::Bool, ty::Int(..) | ty::Uint(..)) if const_context.is_some() => ( + Some(vec![ + (callee_span, "".to_string()), + (expr.span.shrink_to_hi(), format!(" as {dst}")), + ]), + None, + ), + // bool → x8 using `x8::from` + (ty::Bool, ty::Int(..) | ty::Uint(..)) => { + (Some(vec![(callee_span, format!("{dst}::from"))]), None) + } + _ => return, + }; + + cx.tcx.node_span_lint(UNNECESSARY_TRANSMUTES, expr.hir_id, expr.span, |diag| { + diag.primary_message("unnecessary transmute"); + if let Some(sugg) = sugg { + diag.multipart_suggestion("replace this with", sugg, Applicability::MachineApplicable); + } + if let Some(help) = help { + diag.help(help); + } + }); +} + +#[derive(LintDiagnostic)] +#[diag(lint_undefined_transmute)] +#[note] +#[note(lint_note2)] +#[help] +pub(crate) struct UndefinedTransmuteLint; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f1c06dfe6ce0..77dc63351136 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -24,7 +24,8 @@ mod improper_ctypes; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, - AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, + AmbiguousWidePointerComparisonsAddrSuggestion, AmbiguousWidePointerComparisonsCastSuggestion, + AmbiguousWidePointerComparisonsExpectSuggestion, AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment, @@ -362,6 +363,7 @@ fn lint_wide_pointer<'tcx>( let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" }; let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne)); let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn; + let via_method_call = matches!(&e.kind, ExprKind::MethodCall(..) | ExprKind::Call(..)); let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo()); let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo()); @@ -376,9 +378,21 @@ fn lint_wide_pointer<'tcx>( cx.emit_span_lint( AMBIGUOUS_WIDE_POINTER_COMPARISONS, e.span, - AmbiguousWidePointerComparisons::Spanful { - addr_metadata_suggestion: (is_eq_ne && !is_dyn_comparison).then(|| { - AmbiguousWidePointerComparisonsAddrMetadataSuggestion { + if is_eq_ne { + AmbiguousWidePointerComparisons::SpanfulEq { + addr_metadata_suggestion: (!is_dyn_comparison).then(|| { + AmbiguousWidePointerComparisonsAddrMetadataSuggestion { + ne, + deref_left, + deref_right, + l_modifiers, + r_modifiers, + left, + middle, + right, + } + }), + addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion { ne, deref_left, deref_right, @@ -387,21 +401,11 @@ fn lint_wide_pointer<'tcx>( left, middle, right, - } - }), - addr_suggestion: if is_eq_ne { - AmbiguousWidePointerComparisonsAddrSuggestion::AddrEq { - ne, - deref_left, - deref_right, - l_modifiers, - r_modifiers, - left, - middle, - right, - } - } else { - AmbiguousWidePointerComparisonsAddrSuggestion::Cast { + }, + } + } else { + AmbiguousWidePointerComparisons::SpanfulCmp { + cast_suggestion: AmbiguousWidePointerComparisonsCastSuggestion { deref_left, deref_right, l_modifiers, @@ -412,8 +416,14 @@ fn lint_wide_pointer<'tcx>( left_after: l_span.shrink_to_hi(), right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()), right_after: r_span.shrink_to_hi(), - } - }, + }, + expect_suggestion: AmbiguousWidePointerComparisonsExpectSuggestion { + paren_left: if via_method_call { "" } else { "(" }, + paren_right: if via_method_call { "" } else { ")" }, + before: e.span.shrink_to_lo(), + after: e.span.shrink_to_hi(), + }, + } }, ); } @@ -1700,7 +1710,7 @@ impl ImproperCTypesDefinitions { && cx.tcx.sess.target.os == "aix" && !adt_def.all_fields().next().is_none() { - let struct_variant_data = item.expect_struct().1; + let struct_variant_data = item.expect_struct().2; for field_def in struct_variant_data.fields().iter().skip(1) { // Struct fields (after the first field) are checked for the // power alignment rule, as fields after the first are likely @@ -1725,9 +1735,9 @@ impl ImproperCTypesDefinitions { impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { match item.kind { - hir::ItemKind::Static(_, ty, ..) - | hir::ItemKind::Const(_, ty, ..) - | hir::ItemKind::TyAlias(_, ty, ..) => { + hir::ItemKind::Static(_, _, ty, _) + | hir::ItemKind::Const(_, _, ty, _) + | hir::ItemKind::TyAlias(_, _, ty) => { self.check_ty_maybe_containing_foreign_fnptr( cx, ty, @@ -1794,7 +1804,7 @@ declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - if let hir::ItemKind::Enum(_, ref enum_definition, _) = it.kind { + if let hir::ItemKind::Enum(_, _, ref enum_definition) = it.kind { let t = cx.tcx.type_of(it.owner_id).instantiate_identity(); let ty = cx.tcx.erase_regions(t); let Ok(layout) = cx.layout_of(ty) else { return }; diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 7cb00262b6ff..d44f45177bde 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; use rustc_span::Span; -use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_data_structures as attrs, rustc_hir as hir}; use crate::LateContext; use crate::context::LintContext; @@ -131,18 +131,18 @@ fn report_bin_hex_error( cx: &LateContext<'_>, hir_id: HirId, span: Span, - ty: attr::IntType, + ty: attrs::IntType, size: Size, repr_str: String, val: u128, negative: bool, ) { let (t, actually) = match ty { - attr::IntType::SignedInt(t) => { + attrs::IntType::SignedInt(t) => { let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) }; (t.name_str(), actually.to_string()) } - attr::IntType::UnsignedInt(t) => { + attrs::IntType::UnsignedInt(t) => { let actually = size.truncate(val); (t.name_str(), actually.to_string()) } @@ -264,7 +264,7 @@ fn lint_int_literal<'tcx>( cx, hir_id, span, - attr::IntType::SignedInt(ty::ast_int_ty(t)), + attrs::IntType::SignedInt(ty::ast_int_ty(t)), Integer::from_int_ty(cx, t).size(), repr_str, v, @@ -336,7 +336,7 @@ fn lint_uint_literal<'tcx>( cx, hir_id, span, - attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), + attrs::IntType::UnsignedInt(ty::ast_uint_ty(t)), Integer::from_uint_ty(cx, t).size(), repr_str, lit_val, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 50a27d7e84f5..1620f425794f 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,8 +1,7 @@ use std::iter; -use rustc_ast as ast; use rustc_ast::util::{classify, parser}; -use rustc_ast::{ExprKind, StmtKind}; +use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind}; use rustc_errors::{MultiSpan, pluralize}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -780,26 +779,30 @@ trait UnusedDelimLint { right_pos: Option, is_kw: bool, ) { - let spans = match value.kind { - ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => stmt - .span - .find_ancestor_inside(value.span) - .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))), + let span_with_attrs = match value.kind { + ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => { + // For the statements with attributes, like `{ #[allow()] println!("Hello!") }`, + // the span should contains the attributes, or the suggestion will remove them. + if let Some(attr_lo) = stmt.attrs().iter().map(|attr| attr.span.lo()).min() { + stmt.span.with_lo(attr_lo) + } else { + stmt.span + } + } ast::ExprKind::Paren(ref expr) => { // For the expr with attributes, like `let _ = (#[inline] || println!("Hello!"));`, // the span should contains the attributes, or the suggestion will remove them. - let expr_span_with_attrs = - if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() { - expr.span.with_lo(attr_lo) - } else { - expr.span - }; - expr_span_with_attrs.find_ancestor_inside(value.span).map(|expr_span| { - (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi())) - }) + if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() { + expr.span.with_lo(attr_lo) + } else { + expr.span + } } _ => return, }; + let spans = span_with_attrs + .find_ancestor_inside(value.span) + .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))); let keep_space = ( left_pos.is_some_and(|s| s >= value.span.lo()), right_pos.is_some_and(|s| s <= value.span.hi()), diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3cea24634fee..7c7ba85d4848 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -16,6 +16,7 @@ declare_lint_pass! { /// that are used by other parts of the compiler. HardwiredLints => [ // tidy-alphabetical-start + AARCH64_SOFTFLOAT_NEON, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, AMBIGUOUS_GLOB_IMPORTS, @@ -78,7 +79,6 @@ declare_lint_pass! { PRIVATE_BOUNDS, PRIVATE_INTERFACES, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, - PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, REDUNDANT_LIFETIMES, @@ -103,6 +103,7 @@ declare_lint_pass! { TAIL_EXPR_DROP_ORDER, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, + TEXT_DIRECTION_CODEPOINT_IN_LITERAL, TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS, TYVAR_BEHIND_RAW_POINTER, @@ -117,7 +118,6 @@ declare_lint_pass! { UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, - UNNECESSARY_TRANSMUTES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -3783,7 +3783,6 @@ declare_lint! { } declare_lint! { - #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that /// change the visual representation of text on screen in a way that does not correspond to /// their on memory representation. @@ -3793,7 +3792,7 @@ declare_lint! { /// ```rust,compile_fail /// #![deny(text_direction_codepoint_in_comment)] /// fn main() { - /// println!("{:?}"); // '‮'); + #[doc = " println!(\"{:?}\"); // '\u{202E}');"] /// } /// ``` /// @@ -3808,7 +3807,43 @@ declare_lint! { /// their use. pub TEXT_DIRECTION_CODEPOINT_IN_COMMENT, Deny, - "invisible directionality-changing codepoints in comment" + "invisible directionality-changing codepoints in comment", + crate_level_only +} + +declare_lint! { + /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the + /// visual representation of text on screen in a way that does not correspond to their on + /// memory representation. + /// + /// ### Explanation + /// + /// The unicode characters `\u{202A}`, `\u{202B}`, `\u{202D}`, `\u{202E}`, `\u{2066}`, + /// `\u{2067}`, `\u{2068}`, `\u{202C}` and `\u{2069}` make the flow of text on screen change + /// its direction on software that supports these codepoints. This makes the text "abc" display + /// as "cba" on screen. By leveraging software that supports these, people can write specially + /// crafted literals that make the surrounding code seem like it's performing one action, when + /// in reality it is performing another. Because of this, we proactively lint against their + /// presence to avoid surprises. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(text_direction_codepoint_in_literal)] + /// fn main() { + // ` - convince tidy that backticks match + #[doc = " println!(\"{:?}\", '\u{202E}');"] + // ` + /// } + /// ``` + /// + /// {{produces}} + /// + pub TEXT_DIRECTION_CODEPOINT_IN_LITERAL, + Deny, + "detect special Unicode codepoints that affect the visual representation of text on screen, \ + changing the direction in which text flows", + crate_level_only } declare_lint! { @@ -4850,64 +4885,6 @@ declare_lint! { @feature_gate = supertrait_item_shadowing; } -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", -} - -declare_lint! { - /// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives. - /// - /// ### Example - /// - /// ```rust - /// fn bytes_at_home(x: [u8; 4]) -> u32 { - /// unsafe { std::mem::transmute(x) } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Using an explicit method is preferable over calls to - /// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as - /// they more clearly communicate the intent, are easier to review, and - /// are less likely to accidentally result in unsoundness. - pub UNNECESSARY_TRANSMUTES, - Warn, - "detects transmutes that are shadowed by std methods" -} - declare_lint! { /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, /// that runs a custom `Drop` destructor. @@ -5043,14 +5020,14 @@ declare_lint! { /// /// ```text /// error: this function function definition is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType` - /// --> $DIR/wasm_c_abi_transition.rs:17:1 - /// | - /// | pub extern "C" fn my_fun(_x: MyType) {} - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// | - /// = 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 #138762 - /// = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target + /// --> $DIR/wasm_c_abi_transition.rs:17:1 + /// | + /// | pub extern "C" fn my_fun(_x: MyType) {} + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// | + /// = 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 #138762 + /// = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target /// ``` /// /// ### Explanation @@ -5067,3 +5044,44 @@ declare_lint! { reference: "issue #138762 ", }; } + +declare_lint! { + /// The `aarch64_softfloat_neon` lint detects usage of `#[target_feature(enable = "neon")]` on + /// softfloat aarch64 targets. Enabling this target feature causes LLVM to alter the ABI of + /// function calls, making this attribute unsound to use. + /// + /// ### Example + /// + /// ```rust,ignore (needs aarch64-unknown-none-softfloat) + /// #[target_feature(enable = "neon")] + /// fn with_neon() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: enabling the `neon` target feature on the current target is unsound due to ABI issues + /// --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:18 + /// | + /// | #[target_feature(enable = "neon")] + /// | ^^^^^^^^^^^^^^^ + /// | + /// = 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 #134375 + /// ``` + /// + /// ### Explanation + /// + /// If a function like `with_neon` above ends up containing calls to LLVM builtins, those will + /// not use the correct ABI. This is caused by a lack of support in LLVM for mixing code with + /// and without the `neon` target feature. The target feature should never have been stabilized + /// on this target due to this issue, but the problem was not known at the time of + /// stabilization. + pub AARCH64_SOFTFLOAT_NEON, + Warn, + "detects code that could be affected by ABI issues on aarch64 softfloat targets", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, + reference: "issue #134375 ", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index b4069b317bfa..6cbdc245d212 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -698,6 +698,14 @@ pub enum BuiltinLintDiag { is_string: bool, suggestion: Span, }, + HiddenUnicodeCodepoints { + label: String, + count: usize, + span_label: Span, + labels: Option>, + escape: bool, + spans: Vec<(char, Span)>, + }, TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), @@ -736,6 +744,7 @@ pub enum BuiltinLintDiag { ty: String, }, UnusedExternCrate { + span: Span, removal_span: Span, }, ExternCrateNotIdiomatic { diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index ee377277017a..2196f71299a5 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -51,6 +51,7 @@ impl Parse for Query { let key = Pat::parse_single(&arg_content)?; arg_content.parse::()?; let arg = arg_content.parse()?; + let _ = arg_content.parse::>()?; let result = input.parse()?; // Parse the query modifiers diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 26878c488b74..a163518fd195 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -10,6 +10,7 @@ libloading = "0.8.0" odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -22,6 +23,9 @@ rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } +# We must use the proc_macro version that we will compile proc-macros against, +# not the one from our own sysroot. +rustc_proc_macro = { path = "../rustc_proc_macro" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index d997ba198aca..bccffe392437 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -1,6 +1,10 @@ metadata_as_needed_compatibility = linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds +metadata_async_drop_types_in_dependency = + found async drop types in dependency `{$extern_crate}`, but async_drop feature is disabled for `{$local_crate}` + .help = if async drop type will be dropped in a crate without `feature(async_drop)`, sync Drop will be used + metadata_bad_panic_strategy = the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 07fb2de8a3e0..de19c10bc57c 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -7,7 +7,6 @@ use std::str::FromStr; use std::time::Duration; use std::{cmp, env, iter}; -use proc_macro::bridge::client::ProcMacro; use rustc_ast::expand::allocator::{AllocatorKind, alloc_error_handler_name, global_fn_name}; use rustc_ast::{self as ast, *}; use rustc_data_structures::fx::FxHashSet; @@ -22,7 +21,9 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_hir::definitions::Definitions; use rustc_index::IndexVec; use rustc_middle::bug; +use rustc_middle::ty::data_structures::IndexSet; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; +use rustc_proc_macro::bridge::client::ProcMacro; use rustc_session::config::{ self, CrateType, ExtendedTargetModifierInfo, ExternLocation, OptionsTargetModifiers, TargetModifier, @@ -281,7 +282,7 @@ impl CStore { .filter_map(|(cnum, data)| data.as_deref_mut().map(|data| (cnum, data))) } - fn push_dependencies_in_postorder(&self, deps: &mut Vec, cnum: CrateNum) { + fn push_dependencies_in_postorder(&self, deps: &mut IndexSet, cnum: CrateNum) { if !deps.contains(&cnum) { let data = self.get_crate_data(cnum); for dep in data.dependencies() { @@ -290,12 +291,12 @@ impl CStore { } } - deps.push(cnum); + deps.insert(cnum); } } - pub(crate) fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> Vec { - let mut deps = Vec::new(); + pub(crate) fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> IndexSet { + let mut deps = IndexSet::default(); if cnum == LOCAL_CRATE { for (cnum, _) in self.iter_crate_data() { self.push_dependencies_in_postorder(&mut deps, cnum); @@ -306,10 +307,11 @@ impl CStore { deps } - fn crate_dependencies_in_reverse_postorder(&self, cnum: CrateNum) -> Vec { - let mut deps = self.crate_dependencies_in_postorder(cnum); - deps.reverse(); - deps + fn crate_dependencies_in_reverse_postorder( + &self, + cnum: CrateNum, + ) -> impl Iterator { + self.crate_dependencies_in_postorder(cnum).into_iter().rev() } pub(crate) fn injected_panic_runtime(&self) -> Option { @@ -473,6 +475,27 @@ impl CStore { } } + // Report about async drop types in dependency if async drop feature is disabled + pub fn report_incompatible_async_drop_feature(&self, tcx: TyCtxt<'_>, krate: &Crate) { + if tcx.features().async_drop() { + return; + } + for (_cnum, data) in self.iter_crate_data() { + if data.is_proc_macro_crate() { + continue; + } + if data.has_async_drops() { + let extern_crate = data.name(); + let local_crate = tcx.crate_name(LOCAL_CRATE); + tcx.dcx().emit_warn(errors::AsyncDropTypesInDependency { + span: krate.spans.inner_span.shrink_to_lo(), + extern_crate, + local_crate, + }); + } + } + } + pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index c45daeda85db..16f59793e632 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -811,3 +811,13 @@ pub struct UnknownTargetModifierUnsafeAllowed { pub span: Span, pub flag_name: String, } + +#[derive(Diagnostic)] +#[diag(metadata_async_drop_types_in_dependency)] +#[help] +pub struct AsyncDropTypesInDependency { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, +} diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 97b67140fa2d..389a4ab74662 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -16,8 +16,6 @@ #![feature(trusted_len)] // tidy-alphabetical-end -extern crate proc_macro; - pub use rmeta::provide; mod dependency_format; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 10123cb9a9dd..79015aab5d30 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -435,7 +435,7 @@ impl<'a> CrateLocator<'a> { info!("lib candidate: {}", spf.path.display()); let (rlibs, rmetas, dylibs, interfaces) = - candidates.entry(hash.to_string()).or_default(); + candidates.entry(hash).or_default(); { // As a perforamnce optimisation we canonicalize the path and skip // ones we've already seeen. This allows us to ignore crates diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bd813cadedcd..1dae858b7ef1 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -6,7 +6,6 @@ use std::sync::{Arc, OnceLock}; use std::{io, iter, mem}; pub(super) use cstore_impl::provide; -use proc_macro::bridge::client::ProcMacro; use rustc_ast as ast; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; @@ -26,6 +25,7 @@ use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::ty::Visibility; use rustc_middle::ty::codec::TyDecoder; use rustc_middle::{bug, implement_ty_decoder}; +use rustc_proc_macro::bridge::client::ProcMacro; use rustc_serialize::opaque::MemDecoder; use rustc_serialize::{Decodable, Decoder}; use rustc_session::Session; @@ -1984,6 +1984,10 @@ impl CrateMetadata { self.root.header.hash } + pub(crate) fn has_async_drops(&self) -> bool { + self.root.tables.adt_async_destructor.len > 0 + } + fn num_def_ids(&self) -> usize { self.root.tables.def_keys.size() } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 76bae39ef8c0..375928c22459 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -2,7 +2,7 @@ use std::any::Any; use std::mem; use std::sync::Arc; -use rustc_attr_parsing::Deprecation; +use rustc_attr_data_structures::Deprecation; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -425,6 +425,7 @@ provide! { tcx, def_id, other, cdata, doc_link_traits_in_scope => { tcx.arena.alloc_from_iter(cdata.get_doc_link_traits_in_scope(def_id.index)) } + anon_const_kind => { table } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { @@ -548,8 +549,9 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { has_global_allocator: |tcx, LocalCrate| CStore::from_tcx(tcx).has_global_allocator(), has_alloc_error_handler: |tcx, LocalCrate| CStore::from_tcx(tcx).has_alloc_error_handler(), postorder_cnums: |tcx, ()| { - tcx.arena - .alloc_slice(&CStore::from_tcx(tcx).crate_dependencies_in_postorder(LOCAL_CRATE)) + tcx.arena.alloc_from_iter( + CStore::from_tcx(tcx).crate_dependencies_in_postorder(LOCAL_CRATE).into_iter(), + ) }, crates: |tcx, ()| { // The list of loaded crates is now frozen in query cache, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 7ac72ef814a9..3ab989d2d3ba 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1569,6 +1569,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } + if let DefKind::AnonConst = def_kind { + record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); + } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c86cf567283f..077835283e96 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -40,7 +40,7 @@ use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextKey}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Ident, Span, Symbol}; use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; -use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_data_structures as attrs, rustc_hir as hir}; use crate::creader::CrateMetadataRef; @@ -200,7 +200,7 @@ type ExpnHashTable = LazyTable>>; #[derive(MetadataEncodable, MetadataDecodable)] pub(crate) struct ProcMacroData { proc_macro_decls_static: DefIndex, - stability: Option, + stability: Option, macros: LazyArray, } @@ -422,10 +422,10 @@ define_tables! { safety: Table, def_span: Table>, def_ident_span: Table>, - lookup_stability: Table>, - lookup_const_stability: Table>, - lookup_default_body_stability: Table>, - lookup_deprecation_entry: Table>, + lookup_stability: Table>, + lookup_const_stability: Table>, + lookup_default_body_stability: Table>, + lookup_deprecation_entry: Table>, explicit_predicates_of: Table>>, generics_of: Table>, type_of: Table>>>, @@ -480,6 +480,7 @@ define_tables! { doc_link_traits_in_scope: Table>, assumed_wf_types_for_rpitit: Table, Span)>>, opaque_ty_origin: Table>>, + anon_const_kind: Table>, } #[derive(TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index fee707f7b4c9..3de97c8c0d99 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -310,7 +310,7 @@ impl<'tcx> TyCtxt<'tcx> { /// This should only be used for determining the context of a body, a return /// value of `Some` does not always suggest that the owner of the body is `const`, /// just that it has to be checked as if it were. - pub fn hir_body_const_context(self, def_id: impl Into) -> Option { + pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option { let def_id = def_id.into(); let ccx = match self.hir_body_owner_kind(def_id) { BodyOwnerKind::Const { inline } => ConstContext::Const { inline }, @@ -920,7 +920,7 @@ impl<'tcx> TyCtxt<'tcx> { }) => until_within(*outer_span, generics.where_clause_span), // Constants and Statics. Node::Item(Item { - kind: ItemKind::Const(_, ty, ..) | ItemKind::Static(_, ty, ..), + kind: ItemKind::Const(_, _, ty, _) | ItemKind::Static(_, _, ty, _), span: outer_span, .. }) diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index c3d10615cf10..a34a3419d68b 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -43,6 +43,9 @@ pub enum ProjectionKind { /// /// This is unused if `-Znext-solver` is enabled. OpaqueCast, + + /// `unwrap_binder!(expr)` + UnwrapUnsafeBinder, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 5b8603744961..2bbc48b633c8 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -27,7 +27,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; pub use rustc_type_ir as ir; -pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind}; +pub use rustc_type_ir::CanonicalTyVarKind; use smallvec::SmallVec; use crate::mir::ConstraintCategory; @@ -35,9 +35,9 @@ use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt} pub type CanonicalQueryInput<'tcx, V> = ir::CanonicalQueryInput, V>; pub type Canonical<'tcx, V> = ir::Canonical, V>; -pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo>; +pub type CanonicalVarKind<'tcx> = ir::CanonicalVarKind>; pub type CanonicalVarValues<'tcx> = ir::CanonicalVarValues>; -pub type CanonicalVarInfos<'tcx> = &'tcx List>; +pub type CanonicalVarKinds<'tcx> = &'tcx List>; /// When we canonicalize a value to form a query, we wind up replacing /// various parts of it with canonical variables. This struct stores diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 00da1a6aeec7..f21cf5fa45e6 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -96,49 +96,46 @@ bitflags::bitflags! { /// `#[cold]`: a hint to LLVM that this function, when called, is never on /// the hot path. const COLD = 1 << 0; - /// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this - /// function is never null and the function has no side effects other than allocating. - const ALLOCATOR = 1 << 1; - /// An indicator that function will never unwind. Will become obsolete - /// once C-unwind is fully stabilized. - const NEVER_UNWIND = 1 << 3; + /// `#[rustc_nounwind]`: An indicator that function will never unwind. + const NEVER_UNWIND = 1 << 1; /// `#[naked]`: an indicator to LLVM that no function prologue/epilogue /// should be generated. - const NAKED = 1 << 4; + const NAKED = 1 << 2; /// `#[no_mangle]`: an indicator that the function's name should be the same /// as its symbol. - const NO_MANGLE = 1 << 5; + const NO_MANGLE = 1 << 3; /// `#[rustc_std_internal_symbol]`: an indicator that this symbol is a /// "weird symbol" for the standard library in that it has slightly /// different linkage, visibility, and reachability rules. - const RUSTC_STD_INTERNAL_SYMBOL = 1 << 6; + const RUSTC_STD_INTERNAL_SYMBOL = 1 << 4; /// `#[thread_local]`: indicates a static is actually a thread local /// piece of memory - const THREAD_LOCAL = 1 << 8; - /// `#[used]`: indicates that LLVM can't eliminate this function (but the + const THREAD_LOCAL = 1 << 5; + /// `#[used(compiler)]`: indicates that LLVM can't eliminate this function (but the /// linker can!). - const USED = 1 << 9; - /// `#[track_caller]`: allow access to the caller location - const TRACK_CALLER = 1 << 10; - /// #[ffi_pure]: applies clang's `pure` attribute to a foreign function - /// declaration. - const FFI_PURE = 1 << 11; - /// #[ffi_const]: applies clang's `const` attribute to a foreign function - /// declaration. - const FFI_CONST = 1 << 12; - // (Bit 13 was used for `#[cmse_nonsecure_entry]`, but is now unused.) - // (Bit 14 was used for `#[coverage(off)]`, but is now unused.) + const USED_COMPILER = 1 << 6; /// `#[used(linker)]`: /// indicates that neither LLVM nor the linker will eliminate this function. - const USED_LINKER = 1 << 15; + const USED_LINKER = 1 << 7; + /// `#[track_caller]`: allow access to the caller location + const TRACK_CALLER = 1 << 8; + /// #[ffi_pure]: applies clang's `pure` attribute to a foreign function + /// declaration. + const FFI_PURE = 1 << 9; + /// #[ffi_const]: applies clang's `const` attribute to a foreign function + /// declaration. + const FFI_CONST = 1 << 10; + /// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this + /// function is never null and the function has no side effects other than allocating. + const ALLOCATOR = 1 << 11; /// `#[rustc_deallocator]`: a hint to LLVM that the function only deallocates memory. - const DEALLOCATOR = 1 << 16; + const DEALLOCATOR = 1 << 12; /// `#[rustc_reallocator]`: a hint to LLVM that the function only reallocates memory. - const REALLOCATOR = 1 << 17; + const REALLOCATOR = 1 << 13; /// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory. - const ALLOCATOR_ZEROED = 1 << 18; + const ALLOCATOR_ZEROED = 1 << 14; /// `#[no_builtins]`: indicates that disable implicit builtin knowledge of functions for the function. - const NO_BUILTINS = 1 << 19; + const NO_BUILTINS = 1 << 15; } } rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 9912e659b05f..454ab8c107f0 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -5,7 +5,7 @@ use std::num::NonZero; use rustc_ast::NodeId; use rustc_attr_data_structures::{ - self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, + self as attrs, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; use rustc_data_structures::unord::UnordMap; use rustc_errors::{Applicability, Diag, EmissionGuarantee}; @@ -411,7 +411,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(Stability { - level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by }, + level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by }, feature, .. }) => { @@ -494,7 +494,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(DefaultBodyStability { - level: attr::StabilityLevel::Unstable { reason, issue, is_soft, .. }, + level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, .. }, feature, }) => { if span.allows_unstable(feature) { @@ -643,7 +643,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(ConstStability { - level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. }, + level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. }, feature, .. }) => { diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 57aafbb26bc8..dd55d039794f 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -27,12 +27,21 @@ use crate::ty; /// Functionality required for the bytes of an `Allocation`. pub trait AllocBytes: Clone + fmt::Debug + Deref + DerefMut { + /// The type of extra parameters passed in when creating an allocation. + /// Can be used by `interpret::Machine` instances to make runtime-configuration-dependent + /// decisions about the allocation strategy. + type AllocParams; + /// Create an `AllocBytes` from a slice of `u8`. - fn from_bytes<'a>(slice: impl Into>, _align: Align) -> Self; + fn from_bytes<'a>( + slice: impl Into>, + _align: Align, + _params: Self::AllocParams, + ) -> Self; /// Create a zeroed `AllocBytes` of the specified size and alignment. /// Returns `None` if we ran out of memory on the host. - fn zeroed(size: Size, _align: Align) -> Option; + fn zeroed(size: Size, _align: Align, _params: Self::AllocParams) -> Option; /// Gives direct access to the raw underlying storage. /// @@ -51,11 +60,13 @@ pub trait AllocBytes: Clone + fmt::Debug + Deref + DerefMut`. impl AllocBytes for Box<[u8]> { - fn from_bytes<'a>(slice: impl Into>, _align: Align) -> Self { + type AllocParams = (); + + fn from_bytes<'a>(slice: impl Into>, _align: Align, _params: ()) -> Self { Box::<[u8]>::from(slice.into()) } - fn zeroed(size: Size, _align: Align) -> Option { + fn zeroed(size: Size, _align: Align, _params: ()) -> Option { let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes().try_into().ok()?).ok()?; // SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]> let bytes = unsafe { bytes.assume_init() }; @@ -172,9 +183,8 @@ fn all_zero(buf: &[u8]) -> bool { } /// Custom encoder for [`Allocation`] to more efficiently represent the case where all bytes are 0. -impl Encodable for Allocation +impl Encodable for Allocation> where - Bytes: AllocBytes, ProvenanceMap: Encodable, Extra: Encodable, { @@ -192,9 +202,8 @@ where } } -impl Decodable for Allocation +impl Decodable for Allocation> where - Bytes: AllocBytes, ProvenanceMap: Decodable, Extra: Decodable, { @@ -203,7 +212,7 @@ where let len = decoder.read_usize(); let bytes = if all_zero { vec![0u8; len] } else { decoder.read_raw_bytes(len).to_vec() }; - let bytes = Bytes::from_bytes(bytes, align); + let bytes = as AllocBytes>::from_bytes(bytes, align, ()); let provenance = Decodable::decode(decoder); let init_mask = Decodable::decode(decoder); @@ -395,8 +404,9 @@ impl Allocation { slice: impl Into>, align: Align, mutability: Mutability, + params: ::AllocParams, ) -> Self { - let bytes = Bytes::from_bytes(slice, align); + let bytes = Bytes::from_bytes(slice, align, params); let size = Size::from_bytes(bytes.len()); Self { bytes, @@ -408,14 +418,18 @@ impl Allocation { } } - pub fn from_bytes_byte_aligned_immutable<'a>(slice: impl Into>) -> Self { - Allocation::from_bytes(slice, Align::ONE, Mutability::Not) + pub fn from_bytes_byte_aligned_immutable<'a>( + slice: impl Into>, + params: ::AllocParams, + ) -> Self { + Allocation::from_bytes(slice, Align::ONE, Mutability::Not, params) } fn new_inner( size: Size, align: Align, init: AllocInit, + params: ::AllocParams, fail: impl FnOnce() -> R, ) -> Result { // We raise an error if we cannot create the allocation on the host. @@ -424,7 +438,7 @@ impl Allocation { // deterministic. However, we can be non-deterministic here because all uses of const // evaluation (including ConstProp!) will make compilation fail (via hard error // or ICE) upon encountering a `MemoryExhausted` error. - let bytes = Bytes::zeroed(size, align).ok_or_else(fail)?; + let bytes = Bytes::zeroed(size, align, params).ok_or_else(fail)?; Ok(Allocation { bytes, @@ -444,8 +458,13 @@ impl Allocation { /// Try to create an Allocation of `size` bytes, failing if there is not enough memory /// available to the compiler to do so. - pub fn try_new<'tcx>(size: Size, align: Align, init: AllocInit) -> InterpResult<'tcx, Self> { - Self::new_inner(size, align, init, || { + pub fn try_new<'tcx>( + size: Size, + align: Align, + init: AllocInit, + params: ::AllocParams, + ) -> InterpResult<'tcx, Self> { + Self::new_inner(size, align, init, params, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) @@ -457,8 +476,13 @@ impl Allocation { /// /// Example use case: To obtain an Allocation filled with specific data, /// first call this function and then call write_scalar to fill in the right data. - pub fn new(size: Size, align: Align, init: AllocInit) -> Self { - match Self::new_inner(size, align, init, || { + pub fn new( + size: Size, + align: Align, + init: AllocInit, + params: ::AllocParams, + ) -> Self { + match Self::new_inner(size, align, init, params, || { panic!( "interpreter ran out of memory: cannot create allocation of {} bytes", size.bytes() @@ -488,7 +512,7 @@ impl Allocation { pub fn adjust_from_tcx<'tcx, Prov: Provenance, Bytes: AllocBytes>( &self, cx: &impl HasDataLayout, - mut alloc_bytes: impl FnMut(&[u8], Align) -> InterpResult<'tcx, Bytes>, + alloc_bytes: impl FnOnce(&[u8], Align) -> InterpResult<'tcx, Bytes>, mut adjust_ptr: impl FnMut(Pointer) -> InterpResult<'tcx, Pointer>, ) -> InterpResult<'tcx, Allocation> { // Copy the data. diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 4a5c42c721c1..97db45a70d7f 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -9,7 +9,6 @@ use super::{ ReportedErrorInfo, }; use crate::mir; -use crate::query::TyCtxtEnsureOk; use crate::ty::{self, GenericArgs, TyCtxt, TypeVisitableExt}; impl<'tcx> TyCtxt<'tcx> { @@ -197,24 +196,3 @@ impl<'tcx> TyCtxt<'tcx> { } } } - -impl<'tcx> TyCtxtEnsureOk<'tcx> { - /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts - /// that can't take any generic arguments like const items or enum discriminants. If a - /// generic parameter is used within the constant `ErrorHandled::TooGeneric` will be returned. - #[instrument(skip(self), level = "debug")] - pub fn const_eval_poly(self, def_id: DefId) { - // In some situations def_id will have generic parameters within scope, but they aren't allowed - // to be used. So we can't use `Instance::mono`, instead we feed unresolved generic parameters - // into `const_eval` which will return `ErrorHandled::TooGeneric` if any of them are - // encountered. - let args = GenericArgs::identity_for_item(self.tcx, def_id); - let instance = ty::Instance::new_raw(def_id, self.tcx.erase_regions(args)); - let cid = GlobalId { instance, promoted: None }; - let typing_env = ty::TypingEnv::post_analysis(self.tcx, def_id); - // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should - // improve caching of queries. - let inputs = self.tcx.erase_regions(typing_env.as_query_input(cid)); - self.eval_to_const_value_raw(inputs) - } -} diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 9d462093b9ea..7ba0e5b5e07e 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -180,27 +180,27 @@ impl Scalar { #[inline] pub fn from_i8(i: i8) -> Self { - Self::from_int(i, Size::from_bits(8)) + Self::Int(i.into()) } #[inline] pub fn from_i16(i: i16) -> Self { - Self::from_int(i, Size::from_bits(16)) + Self::Int(i.into()) } #[inline] pub fn from_i32(i: i32) -> Self { - Self::from_int(i, Size::from_bits(32)) + Self::Int(i.into()) } #[inline] pub fn from_i64(i: i64) -> Self { - Self::from_int(i, Size::from_bits(64)) + Self::Int(i.into()) } #[inline] pub fn from_i128(i: i128) -> Self { - Self::from_int(i, Size::from_bits(128)) + Self::Int(i.into()) } #[inline] diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 57ae7dc55c5c..6b262a275005 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1624,7 +1624,11 @@ pub fn write_allocations<'tcx>( Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { write!(w, " (static: {}", tcx.def_path_str(did))?; if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) - && tcx.hir_body_const_context(body.source.def_id()).is_some() + && body + .source + .def_id() + .as_local() + .is_some_and(|def_id| tcx.hir_body_const_context(def_id).is_some()) { // Statics may be cyclic and evaluating them too early // in the MIR pipeline may cause cycle errors even though diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index d59b6df44ed5..06e41e64fdc7 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -88,26 +88,31 @@ impl<'tcx> PlaceTy<'tcx> { /// /// Note that the resulting type has not been normalized. #[instrument(level = "debug", skip(tcx), ret)] - pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx> { - if let Some(variant_index) = self.variant_index { - match *self.ty.kind() { + pub fn field_ty( + tcx: TyCtxt<'tcx>, + self_ty: Ty<'tcx>, + variant_idx: Option, + f: FieldIdx, + ) -> Ty<'tcx> { + if let Some(variant_index) = variant_idx { + match *self_ty.kind() { ty::Adt(adt_def, args) if adt_def.is_enum() => { adt_def.variant(variant_index).fields[f].ty(tcx, args) } ty::Coroutine(def_id, args) => { let mut variants = args.as_coroutine().state_tys(def_id, tcx); let Some(mut variant) = variants.nth(variant_index.into()) else { - bug!("variant {variant_index:?} of coroutine out of range: {self:?}"); + bug!("variant {variant_index:?} of coroutine out of range: {self_ty:?}"); }; - variant - .nth(f.index()) - .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")) + variant.nth(f.index()).unwrap_or_else(|| { + bug!("field {f:?} out of range of variant: {self_ty:?} {variant_idx:?}") + }) } - _ => bug!("can't downcast non-adt non-coroutine type: {self:?}"), + _ => bug!("can't downcast non-adt non-coroutine type: {self_ty:?}"), } } else { - match self.ty.kind() { + match self_ty.kind() { ty::Adt(adt_def, args) if !adt_def.is_enum() => { adt_def.non_enum_variant().fields[f].ty(tcx, args) } @@ -116,26 +121,25 @@ impl<'tcx> PlaceTy<'tcx> { .upvar_tys() .get(f.index()) .copied() - .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")), + .unwrap_or_else(|| bug!("field {f:?} out of range: {self_ty:?}")), ty::CoroutineClosure(_, args) => args .as_coroutine_closure() .upvar_tys() .get(f.index()) .copied() - .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")), + .unwrap_or_else(|| bug!("field {f:?} out of range: {self_ty:?}")), // Only prefix fields (upvars and current state) are // accessible without a variant index. - ty::Coroutine(_, args) => args - .as_coroutine() - .prefix_tys() - .get(f.index()) - .copied() - .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")), + ty::Coroutine(_, args) => { + args.as_coroutine().prefix_tys().get(f.index()).copied().unwrap_or_else(|| { + bug!("field {f:?} out of range of prefixes for {self_ty}") + }) + } ty::Tuple(tys) => tys .get(f.index()) .copied() - .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")), - _ => bug!("can't project out of {self:?}"), + .unwrap_or_else(|| bug!("field {f:?} out of range: {self_ty:?}")), + _ => bug!("can't project out of {self_ty:?}"), } } } @@ -148,11 +152,11 @@ impl<'tcx> PlaceTy<'tcx> { elems.iter().fold(self, |place_ty, &elem| place_ty.projection_ty(tcx, elem)) } - /// Convenience wrapper around `projection_ty_core` for - /// `PlaceElem`, where we can just use the `Ty` that is already - /// stored inline on field projection elems. + /// Convenience wrapper around `projection_ty_core` for `PlaceElem`, + /// where we can just use the `Ty` that is already stored inline on + /// field projection elems. pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty) + self.projection_ty_core(tcx, &elem, |ty| ty, |_, _, _, ty| ty, |ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` @@ -164,8 +168,9 @@ impl<'tcx> PlaceTy<'tcx> { self, tcx: TyCtxt<'tcx>, elem: &ProjectionElem, - mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>, - mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>, + mut structurally_normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, + mut handle_field: impl FnMut(Ty<'tcx>, Option, FieldIdx, T) -> Ty<'tcx>, + mut handle_opaque_cast_and_subtype: impl FnMut(T) -> Ty<'tcx>, ) -> PlaceTy<'tcx> where V: ::std::fmt::Debug, @@ -176,16 +181,16 @@ impl<'tcx> PlaceTy<'tcx> { } let answer = match *elem { ProjectionElem::Deref => { - let ty = self.ty.builtin_deref(true).unwrap_or_else(|| { + let ty = structurally_normalize(self.ty).builtin_deref(true).unwrap_or_else(|| { bug!("deref projection of non-dereferenceable ty {:?}", self) }); PlaceTy::from_ty(ty) } ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { - PlaceTy::from_ty(self.ty.builtin_index().unwrap()) + PlaceTy::from_ty(structurally_normalize(self.ty).builtin_index().unwrap()) } ProjectionElem::Subslice { from, to, from_end } => { - PlaceTy::from_ty(match self.ty.kind() { + PlaceTy::from_ty(match structurally_normalize(self.ty).kind() { ty::Slice(..) => self.ty, ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from), ty::Array(inner, size) if from_end => { @@ -201,17 +206,18 @@ impl<'tcx> PlaceTy<'tcx> { ProjectionElem::Downcast(_name, index) => { PlaceTy { ty: self.ty, variant_index: Some(index) } } - ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)), - ProjectionElem::OpaqueCast(ty) => { - PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty)) - } - ProjectionElem::Subtype(ty) => { - PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty)) - } + ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field( + structurally_normalize(self.ty), + self.variant_index, + f, + fty, + )), + ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)), + ProjectionElem::Subtype(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)), // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general. ProjectionElem::UnwrapUnsafeBinder(ty) => { - PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty)) + PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)) } }; debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer); diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 5bd111fa2f22..6035056baaf9 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -6,7 +6,7 @@ use rustc_span::ErrorGuaranteed; use crate::query::CyclePlaceholder; use crate::ty::adjustment::CoerceUnsizedInfo; -use crate::ty::{self, Ty}; +use crate::ty::{self, Ty, TyCtxt}; use crate::{mir, traits}; #[derive(Copy, Clone)] @@ -207,6 +207,11 @@ impl EraseType for ty::Binder<'_, ty::FnSig<'_>> { type Result = [u8; size_of::>>()]; } +impl EraseType for ty::Binder<'_, ty::CoroutineWitnessTypes>> { + type Result = + [u8; size_of::>>>()]; +} + impl EraseType for ty::Binder<'_, &'_ ty::List>> { type Result = [u8; size_of::>>>()]; } @@ -311,6 +316,7 @@ trivial! { rustc_middle::ty::Asyncness, rustc_middle::ty::AsyncDestructor, rustc_middle::ty::BoundVariableKind, + rustc_middle::ty::AnonConstKind, rustc_middle::ty::DeducedParamAttrs, rustc_middle::ty::Destructor, rustc_middle::ty::fast_reject::SimplifiedType, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b2133fea08cc..279033ee0724 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -922,6 +922,12 @@ rustc_queries! { separate_provide_extern } + query coroutine_hidden_types( + def_id: DefId + ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { + desc { "looking up the hidden types stored across await points in a coroutine" } + } + /// Gets a map with the variances of every item in the local crate. /// ///
@@ -1131,7 +1137,7 @@ rustc_queries! { /// their respective impl (i.e., part of the derive macro) query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx ( LocalDefIdSet, - LocalDefIdMap> + LocalDefIdMap> ) { arena_cache desc { "finding live symbols in crate" } @@ -2586,6 +2592,11 @@ rustc_queries! { desc { "estimating codegen size of `{}`", key } cache_on_disk_if { true } } + + query anon_const_kind(def_id: DefId) -> ty::AnonConstKind { + desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } } rustc_with_all_queries! { define_callbacks! } diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index ff9096695d4d..df67bb505a68 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -117,6 +117,10 @@ impl<'tcx> CapturedPlace<'tcx> { } }, + HirProjectionKind::UnwrapUnsafeBinder => { + write!(&mut symbol, "__unwrap").unwrap(); + } + // Ignore derefs for now, as they are likely caused by // autoderefs that don't appear in the original code. HirProjectionKind::Deref => {} diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 5ff87959a800..e0f70737add0 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -18,7 +18,7 @@ use rustc_span::source_map::Spanned; use rustc_span::{Span, SpanDecoder, SpanEncoder}; use crate::arena::ArenaAllocatable; -use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; +use crate::infer::canonical::{CanonicalVarKind, CanonicalVarKinds}; use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance}; use crate::mir::mono::MonoItem; use crate::mir::{self}; @@ -310,11 +310,11 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Region<'tcx> { } } -impl<'tcx, D: TyDecoder<'tcx>> Decodable for CanonicalVarInfos<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for CanonicalVarKinds<'tcx> { fn decode(decoder: &mut D) -> Self { let len = decoder.read_usize(); decoder.interner().mk_canonical_var_infos_from_iter( - (0..len).map::, _>(|_| Decodable::decode(decoder)), + (0..len).map::, _>(|_| Decodable::decode(decoder)), ) } } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index dc5fe2d8f8b0..455ac6604126 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_type_ir::walk::TypeWalker; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; @@ -259,3 +259,16 @@ impl<'tcx> Const<'tcx> { TypeWalker::new(self.into()) } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum AnonConstKind { + /// `feature(generic_const_exprs)` anon consts are allowed to use arbitrary generic parameters in scope + GCE, + /// stable `min_const_generics` anon consts are not allowed to use any generic parameters + MCG, + /// anon consts used as the length of a repeat expr are syntactically allowed to use generic parameters + /// but must not depend on the actual instantiation. See #76200 for more information + RepeatExprCount, + /// anon consts outside of the type system, e.g. enum discriminants + NonTypeSystem, +} diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 9f5e31d894cb..b087ae25486e 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -26,6 +26,19 @@ impl ConstInt { } } +/// An enum to represent the compiler-side view of `intrinsics::AtomicOrdering`. +/// This lives here because there's a method in this file that needs it and it is entirely unclear +/// where else to put this... +#[derive(Debug, Copy, Clone)] +pub enum AtomicOrdering { + // These values must match `intrinsics::AtomicOrdering`! + Relaxed = 0, + Release = 1, + Acquire = 2, + AcqRel = 3, + SeqCst = 4, +} + impl std::fmt::Debug for ConstInt { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { int, signed, is_ptr_sized_integral } = *self; @@ -318,6 +331,25 @@ impl ScalarInt { self.to_uint(tcx.data_layout.pointer_size).try_into().unwrap() } + #[inline] + pub fn to_atomic_ordering(self) -> AtomicOrdering { + use AtomicOrdering::*; + let val = self.to_u32(); + if val == Relaxed as u32 { + Relaxed + } else if val == Release as u32 { + Release + } else if val == Acquire as u32 { + Acquire + } else if val == AcqRel as u32 { + AcqRel + } else if val == SeqCst as u32 { + SeqCst + } else { + panic!("not a valid atomic ordering") + } + } + /// Converts the `ScalarInt` to `bool`. /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte. /// Errors if it is not a valid `bool`. @@ -422,9 +454,9 @@ macro_rules! from_scalar_int_for_x { impl From for $ty { #[inline] fn from(int: ScalarInt) -> Self { - // The `unwrap` cannot fail because to_bits (if it succeeds) + // The `unwrap` cannot fail because to_uint (if it succeeds) // is guaranteed to return a value that fits into the size. - int.to_bits(Size::from_bytes(size_of::<$ty>())) + int.to_uint(Size::from_bytes(size_of::<$ty>())) .try_into().unwrap() } } @@ -450,6 +482,49 @@ impl From for ScalarInt { } } +macro_rules! from_x_for_scalar_int_signed { + ($($ty:ty),*) => { + $( + impl From<$ty> for ScalarInt { + #[inline] + fn from(u: $ty) -> Self { + Self { + data: u128::from(u.cast_unsigned()), // go via the unsigned type of the same size + size: NonZero::new(size_of::<$ty>() as u8).unwrap(), + } + } + } + )* + } +} + +macro_rules! from_scalar_int_for_x_signed { + ($($ty:ty),*) => { + $( + impl From for $ty { + #[inline] + fn from(int: ScalarInt) -> Self { + // The `unwrap` cannot fail because to_int (if it succeeds) + // is guaranteed to return a value that fits into the size. + int.to_int(Size::from_bytes(size_of::<$ty>())) + .try_into().unwrap() + } + } + )* + } +} + +from_x_for_scalar_int_signed!(i8, i16, i32, i64, i128); +from_scalar_int_for_x_signed!(i8, i16, i32, i64, i128); + +impl From for ScalarInt { + #[inline] + fn from(c: std::cmp::Ordering) -> Self { + // Here we rely on `cmp::Ordering` having the same values in host and target! + ScalarInt::from(c as i8) + } +} + /// Error returned when a conversion from ScalarInt to char fails. #[derive(Debug)] pub struct CharTryFromScalarInt; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0759fa3da428..57b20a1bba60 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -60,7 +60,7 @@ use tracing::{debug, instrument}; use crate::arena::Arena; use crate::dep_graph::{DepGraph, DepKindStruct}; -use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarInfo, CanonicalVarInfos}; +use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, CanonicalVarKinds}; use crate::lint::lint_level; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; @@ -107,9 +107,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_predefined_opaques_in_body(data) } type LocalDefIds = &'tcx ty::List; - type CanonicalVars = CanonicalVarInfos<'tcx>; - fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { - self.mk_canonical_var_infos(infos) + type CanonicalVarKinds = CanonicalVarKinds<'tcx>; + fn mk_canonical_var_kinds( + self, + kinds: &[ty::CanonicalVarKind], + ) -> Self::CanonicalVarKinds { + self.mk_canonical_var_kinds(kinds) } type ExternalConstraints = ExternalConstraints<'tcx>; @@ -340,7 +343,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn coroutine_hidden_types( self, def_id: DefId, - ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List>>> { + ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { self.coroutine_hidden_types(def_id) } @@ -833,7 +836,7 @@ pub struct CtxtInterners<'tcx> { const_lists: InternedSet<'tcx, List>>, args: InternedSet<'tcx, GenericArgs<'tcx>>, type_lists: InternedSet<'tcx, List>>, - canonical_var_infos: InternedSet<'tcx, List>>, + canonical_var_kinds: InternedSet<'tcx, List>>, region: InternedSet<'tcx, RegionKind<'tcx>>, poly_existential_predicates: InternedSet<'tcx, List>>, predicate: InternedSet<'tcx, WithCachedTypeInfo>>>, @@ -872,7 +875,7 @@ impl<'tcx> CtxtInterners<'tcx> { type_lists: InternedSet::with_capacity(N * 4), region: InternedSet::with_capacity(N * 4), poly_existential_predicates: InternedSet::with_capacity(N / 4), - canonical_var_infos: InternedSet::with_capacity(N / 2), + canonical_var_kinds: InternedSet::with_capacity(N / 2), predicate: InternedSet::with_capacity(N), clauses: InternedSet::with_capacity(N), projs: InternedSet::with_capacity(N * 4), @@ -1579,7 +1582,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns the same `AllocId` if called again with the same bytes. pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId { // Create an allocation that just contains these bytes. - let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes); + let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes, ()); let alloc = self.mk_const_alloc(alloc); self.reserve_and_set_memory_dedup(alloc, salt) } @@ -2675,7 +2678,7 @@ slice_interners!( const_lists: pub mk_const_list(Const<'tcx>), args: pub mk_args(GenericArg<'tcx>), type_lists: pub mk_type_list(Ty<'tcx>), - canonical_var_infos: pub mk_canonical_var_infos(CanonicalVarInfo<'tcx>), + canonical_var_kinds: pub mk_canonical_var_kinds(CanonicalVarKind<'tcx>), poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>), projs: pub mk_projs(ProjectionKind), place_elems: pub mk_place_elems(PlaceElem<'tcx>), @@ -2778,7 +2781,7 @@ impl<'tcx> TyCtxt<'tcx> { return false; } - if !matches!(args[0].unpack(), ty::GenericArgKind::Type(_)) { + if !matches!(args[0].kind(), ty::GenericArgKind::Type(_)) { return false; } @@ -2800,7 +2803,7 @@ impl<'tcx> TyCtxt<'tcx> { }; for (param, arg) in std::iter::zip(&generics.own_params, own_args) { - match (¶m.kind, arg.unpack()) { + match (¶m.kind, arg.kind()) { (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} @@ -3055,9 +3058,9 @@ impl<'tcx> TyCtxt<'tcx> { pub fn mk_canonical_var_infos_from_iter(self, iter: I) -> T::Output where I: Iterator, - T: CollectAndApply, &'tcx List>>, + T: CollectAndApply, &'tcx List>>, { - T::collect_and_apply(iter, |xs| self.mk_canonical_var_infos(xs)) + T::collect_and_apply(iter, |xs| self.mk_canonical_var_kinds(xs)) } pub fn mk_place_elems_from_iter(self, iter: I) -> T::Output diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index 45a0b1288db8..f4fead7e9526 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -86,4 +86,12 @@ impl<'tcx> TypeFolder> for RegionEraserVisitor<'tcx> { p } } + + fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { + if c.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) { + c.super_fold_with(self) + } else { + c + } + } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 8d6871d2f1fe..b2057fa36d7f 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -177,6 +177,10 @@ where fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } + + fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { + if c.has_vars_bound_at_or_above(self.current_index) { c.super_fold_with(self) } else { c } + } } impl<'tcx> TyCtxt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 542c0b3e6ebb..5e038f916750 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -137,7 +137,7 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for GenericArg<'tcx> { type Kind = GenericArgKind<'tcx>; fn kind(self) -> Self::Kind { - self.unpack() + self.kind() } } @@ -218,7 +218,7 @@ impl<'tcx> From> for GenericArg<'tcx> { impl<'tcx> From> for GenericArg<'tcx> { fn from(value: ty::Term<'tcx>) -> Self { - match value.unpack() { + match value.kind() { ty::TermKind::Ty(t) => t.into(), ty::TermKind::Const(c) => c.into(), } @@ -227,7 +227,7 @@ impl<'tcx> From> for GenericArg<'tcx> { impl<'tcx> GenericArg<'tcx> { #[inline] - pub fn unpack(self) -> GenericArgKind<'tcx> { + pub fn kind(self) -> GenericArgKind<'tcx> { let ptr = unsafe { self.ptr.map_addr(|addr| NonZero::new_unchecked(addr.get() & !TAG_MASK)) }; // SAFETY: use of `Interned::new_unchecked` here is ok because these @@ -251,7 +251,7 @@ impl<'tcx> GenericArg<'tcx> { #[inline] pub fn as_region(self) -> Option> { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(re) => Some(re), _ => None, } @@ -259,7 +259,7 @@ impl<'tcx> GenericArg<'tcx> { #[inline] pub fn as_type(self) -> Option> { - match self.unpack() { + match self.kind() { GenericArgKind::Type(ty) => Some(ty), _ => None, } @@ -267,7 +267,7 @@ impl<'tcx> GenericArg<'tcx> { #[inline] pub fn as_const(self) -> Option> { - match self.unpack() { + match self.kind() { GenericArgKind::Const(ct) => Some(ct), _ => None, } @@ -275,7 +275,7 @@ impl<'tcx> GenericArg<'tcx> { #[inline] pub fn as_term(self) -> Option> { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(_) => None, GenericArgKind::Type(ty) => Some(ty.into()), GenericArgKind::Const(ct) => Some(ct.into()), @@ -300,7 +300,7 @@ impl<'tcx> GenericArg<'tcx> { } pub fn is_non_region_infer(self) -> bool { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(_) => false, // FIXME: This shouldn't return numerical/float. GenericArgKind::Type(ty) => ty.is_ty_or_numeric_infer(), @@ -327,7 +327,7 @@ impl<'a, 'tcx> Lift> for GenericArg<'a> { type Lifted = GenericArg<'tcx>; fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(lt) => tcx.lift(lt).map(|lt| lt.into()), GenericArgKind::Type(ty) => tcx.lift(ty).map(|ty| ty.into()), GenericArgKind::Const(ct) => tcx.lift(ct).map(|ct| ct.into()), @@ -340,7 +340,7 @@ impl<'tcx> TypeFoldable> for GenericArg<'tcx> { self, folder: &mut F, ) -> Result { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into), GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into), GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), @@ -348,7 +348,7 @@ impl<'tcx> TypeFoldable> for GenericArg<'tcx> { } fn fold_with>>(self, folder: &mut F) -> Self { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(), GenericArgKind::Type(ty) => ty.fold_with(folder).into(), GenericArgKind::Const(ct) => ct.fold_with(folder).into(), @@ -358,7 +358,7 @@ impl<'tcx> TypeFoldable> for GenericArg<'tcx> { impl<'tcx> TypeVisitable> for GenericArg<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(lt) => lt.visit_with(visitor), GenericArgKind::Type(ty) => ty.visit_with(visitor), GenericArgKind::Const(ct) => ct.visit_with(visitor), @@ -368,7 +368,7 @@ impl<'tcx> TypeVisitable> for GenericArg<'tcx> { impl<'tcx, E: TyEncoder<'tcx>> Encodable for GenericArg<'tcx> { fn encode(&self, e: &mut E) { - self.unpack().encode(e) + self.kind().encode(e) } } @@ -390,7 +390,7 @@ impl<'tcx> GenericArgs<'tcx> { /// /// If any of the generic arguments are not types. pub fn into_type_list(&self, tcx: TyCtxt<'tcx>) -> &'tcx List> { - tcx.mk_type_list_from_iter(self.iter().map(|arg| match arg.unpack() { + tcx.mk_type_list_from_iter(self.iter().map(|arg| match arg.kind() { GenericArgKind::Type(ty) => ty, _ => bug!("`into_type_list` called on generic arg with non-types"), })) @@ -527,7 +527,7 @@ impl<'tcx> GenericArgs<'tcx> { /// Returns generic arguments that are not lifetimes. #[inline] pub fn non_erasable_generics(&self) -> impl DoubleEndedIterator> { - self.iter().filter_map(|k| match k.unpack() { + self.iter().filter_map(|arg| match arg.kind() { ty::GenericArgKind::Lifetime(_) => None, generic => Some(generic), }) diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 5f6305bb48ad..ac45ce887c9a 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -60,7 +60,7 @@ where impl<'a, 'tcx> HashStable> for ty::GenericArg<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.unpack().hash_stable(hcx, hasher); + self.kind().hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b2a58897c31b..af31f7ed33b4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -74,8 +74,8 @@ pub use self::closure::{ place_to_string_for_capture, }; pub use self::consts::{ - Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, ValTreeKind, - Value, + AnonConstKind, AtomicOrdering, Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, + UnevaluatedConst, ValTree, ValTreeKind, Value, }; pub use self::context::{ CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, @@ -504,7 +504,7 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for Term<'tcx> { type Kind = TermKind<'tcx>; fn kind(self) -> Self::Kind { - self.unpack() + self.kind() } } @@ -521,7 +521,7 @@ unsafe impl<'tcx> Sync for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): Sync impl Debug for Term<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.unpack() { + match self.kind() { TermKind::Ty(ty) => write!(f, "Term::Ty({ty:?})"), TermKind::Const(ct) => write!(f, "Term::Const({ct:?})"), } @@ -542,7 +542,7 @@ impl<'tcx> From> for Term<'tcx> { impl<'a, 'tcx> HashStable> for Term<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.unpack().hash_stable(hcx, hasher); + self.kind().hash_stable(hcx, hasher); } } @@ -551,14 +551,14 @@ impl<'tcx> TypeFoldable> for Term<'tcx> { self, folder: &mut F, ) -> Result { - match self.unpack() { + match self.kind() { ty::TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into), ty::TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), } } fn fold_with>>(self, folder: &mut F) -> Self { - match self.unpack() { + match self.kind() { ty::TermKind::Ty(ty) => ty.fold_with(folder).into(), ty::TermKind::Const(ct) => ct.fold_with(folder).into(), } @@ -567,7 +567,7 @@ impl<'tcx> TypeFoldable> for Term<'tcx> { impl<'tcx> TypeVisitable> for Term<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { - match self.unpack() { + match self.kind() { ty::TermKind::Ty(ty) => ty.visit_with(visitor), ty::TermKind::Const(ct) => ct.visit_with(visitor), } @@ -576,7 +576,7 @@ impl<'tcx> TypeVisitable> for Term<'tcx> { impl<'tcx, E: TyEncoder<'tcx>> Encodable for Term<'tcx> { fn encode(&self, e: &mut E) { - self.unpack().encode(e) + self.kind().encode(e) } } @@ -589,7 +589,7 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for Term<'tcx> { impl<'tcx> Term<'tcx> { #[inline] - pub fn unpack(self) -> TermKind<'tcx> { + pub fn kind(self) -> TermKind<'tcx> { let ptr = unsafe { self.ptr.map_addr(|addr| NonZero::new_unchecked(addr.get() & !TAG_MASK)) }; // SAFETY: use of `Interned::new_unchecked` here is ok because these @@ -609,7 +609,7 @@ impl<'tcx> Term<'tcx> { } pub fn as_type(&self) -> Option> { - if let TermKind::Ty(ty) = self.unpack() { Some(ty) } else { None } + if let TermKind::Ty(ty) = self.kind() { Some(ty) } else { None } } pub fn expect_type(&self) -> Ty<'tcx> { @@ -617,7 +617,7 @@ impl<'tcx> Term<'tcx> { } pub fn as_const(&self) -> Option> { - if let TermKind::Const(c) = self.unpack() { Some(c) } else { None } + if let TermKind::Const(c) = self.kind() { Some(c) } else { None } } pub fn expect_const(&self) -> Const<'tcx> { @@ -625,14 +625,14 @@ impl<'tcx> Term<'tcx> { } pub fn into_arg(self) -> GenericArg<'tcx> { - match self.unpack() { + match self.kind() { TermKind::Ty(ty) => ty.into(), TermKind::Const(c) => c.into(), } } pub fn to_alias_term(self) -> Option> { - match self.unpack() { + match self.kind() { TermKind::Ty(ty) => match *ty.kind() { ty::Alias(_kind, alias_ty) => Some(alias_ty.into()), _ => None, @@ -645,7 +645,7 @@ impl<'tcx> Term<'tcx> { } pub fn is_infer(&self) -> bool { - match self.unpack() { + match self.kind() { TermKind::Ty(ty) => ty.is_ty_var(), TermKind::Const(ct) => ct.is_ct_infer(), } diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index 9445a18ad76b..2b024b7b6cbb 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -120,7 +120,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { } } - match self.map.get(&r.into()).map(|k| k.unpack()) { + match self.map.get(&r.into()).map(|arg| arg.kind()) { Some(GenericArgKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {u:?}"), None if self.do_not_error => self.tcx.lifetimes.re_static, @@ -162,7 +162,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { ty::Param(param) => { // Look it up in the generic parameters list. - match self.map.get(&ty.into()).map(|k| k.unpack()) { + match self.map.get(&ty.into()).map(|arg| arg.kind()) { // Found it in the generic parameters list; replace with the parameter from the // opaque type. Some(GenericArgKind::Type(t1)) => t1, @@ -195,7 +195,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { match ct.kind() { ty::ConstKind::Param(..) => { // Look it up in the generic parameters list. - match self.map.get(&ct.into()).map(|k| k.unpack()) { + match self.map.get(&ct.into()).map(|arg| arg.kind()) { // Found it in the generic parameters list, replace with the parameter from the // opaque type. Some(GenericArgKind::Const(c1)) => c1, diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index ecd6132b3ef3..3858778bfc8f 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -68,6 +68,7 @@ trivially_parameterized_over_tcx! { ty::AsyncDestructor, ty::AssocItemContainer, ty::Asyncness, + ty::AnonConstKind, ty::DeducedParamAttrs, ty::Destructor, ty::Generics, diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 551d816941b6..bc2ac42b6b1f 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -238,6 +238,8 @@ impl<'tcx> Clause<'tcx> { } } +impl<'tcx> rustc_type_ir::inherent::Clauses> for ty::Clauses<'tcx> {} + #[extension(pub trait ExistentialPredicateStableCmpExt<'tcx>)] impl<'tcx> ExistentialPredicate<'tcx> { /// Compares via an ordering that will not change if modules are reordered or other changes are diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6fd6aff0e2b2..877bea095f96 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1239,7 +1239,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name())); - match term.unpack() { + match term.kind() { TermKind::Ty(ty) => p!(print(ty)), TermKind::Const(c) => p!(print(c)), }; @@ -3386,7 +3386,7 @@ define_print_and_forward_display! { } ty::Term<'tcx> { - match self.unpack() { + match self.kind() { ty::TermKind::Ty(ty) => p!(print(ty)), ty::TermKind::Const(c) => p!(print(c)), } @@ -3401,7 +3401,7 @@ define_print_and_forward_display! { } GenericArg<'tcx> { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(lt) => p!(print(lt)), GenericArgKind::Type(ty) => p!(print(ty)), GenericArgKind::Const(ct) => p!(print(ct)), diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 6ad4e5276b25..dc1d60f3d43c 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -169,7 +169,7 @@ impl<'tcx> Relate> for ty::GenericArg<'tcx> { a: ty::GenericArg<'tcx>, b: ty::GenericArg<'tcx>, ) -> RelateResult<'tcx, ty::GenericArg<'tcx>> { - match (a.unpack(), b.unpack()) { + match (a.kind(), b.kind()) { (ty::GenericArgKind::Lifetime(a_lt), ty::GenericArgKind::Lifetime(b_lt)) => { Ok(relation.relate(a_lt, b_lt)?.into()) } @@ -190,7 +190,7 @@ impl<'tcx> Relate> for ty::Term<'tcx> { a: Self, b: Self, ) -> RelateResult<'tcx, Self> { - Ok(match (a.unpack(), b.unpack()) { + Ok(match (a.kind(), b.kind()) { (ty::TermKind::Ty(a), ty::TermKind::Ty(b)) => relation.relate(a, b)?.into(), (ty::TermKind::Const(a), ty::TermKind::Const(b)) => relation.relate(a, b)?.into(), _ => return Err(TypeError::Mismatch), diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 58f7bc75054b..000ba7b6fa79 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -202,7 +202,7 @@ impl fmt::Debug for ty::Placeholder { impl<'tcx> fmt::Debug for GenericArg<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.unpack() { + match self.kind() { GenericArgKind::Lifetime(lt) => lt.fmt(f), GenericArgKind::Type(ty) => ty.fmt(f), GenericArgKind::Const(ct) => ct.fmt(f), @@ -326,7 +326,7 @@ impl<'tcx, T: Lift>> Lift> for Option { impl<'a, 'tcx> Lift> for Term<'a> { type Lifted = ty::Term<'tcx>; fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { - match self.unpack() { + match self.kind() { TermKind::Ty(ty) => tcx.lift(ty).map(Into::into), TermKind::Const(c) => tcx.lift(c).map(Into::into), } @@ -570,6 +570,19 @@ impl<'tcx> TypeFoldable> for ty::Clause<'tcx> { } } +impl<'tcx> TypeFoldable> for ty::Clauses<'tcx> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_clauses(self) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_clauses(self) + } +} + impl<'tcx> TypeVisitable> for ty::Predicate<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { visitor.visit_predicate(*self) @@ -615,6 +628,19 @@ impl<'tcx> TypeSuperVisitable> for ty::Clauses<'tcx> { } } +impl<'tcx> TypeSuperFoldable> for ty::Clauses<'tcx> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + ty::util::try_fold_list(self, folder, |tcx, v| tcx.mk_clauses(v)) + } + + fn super_fold_with>>(self, folder: &mut F) -> Self { + ty::util::fold_list(self, folder, |tcx, v| tcx.mk_clauses(v)) + } +} + impl<'tcx> TypeFoldable> for ty::Const<'tcx> { fn try_fold_with>>( self, @@ -775,7 +801,6 @@ macro_rules! list_fold { } list_fold! { - ty::Clauses<'tcx> : mk_clauses, &'tcx ty::List> : mk_poly_existential_predicates, &'tcx ty::List> : mk_place_elems, &'tcx ty::List> : mk_patterns, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ab1f3d6099fc..404674c359ed 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1882,10 +1882,8 @@ impl<'tcx> Ty<'tcx> { // Needs normalization or revealing to determine, so no is the safe answer. ty::Alias(..) => false, - ty::Param(..) | ty::Infer(..) | ty::Error(..) => false, - - ty::Bound(..) | ty::Placeholder(..) => { - bug!("`is_trivially_pure_clone_copy` applied to unexpected type: {:?}", self); + ty::Param(..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(..) => { + false } } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index c6a45f846869..cc3887079d81 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -777,8 +777,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { return false; } - iter::zip(user_args.args, BoundVar::ZERO..).all(|(kind, cvar)| { - match kind.unpack() { + iter::zip(user_args.args, BoundVar::ZERO..).all(|(arg, cvar)| { + match arg.kind() { GenericArgKind::Type(ty) => match ty.kind() { ty::Bound(debruijn, b) => { // We only allow a `ty::INNERMOST` index in generic parameters. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 9676aa404482..461d92f80063 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -26,7 +26,7 @@ use crate::query::Providers; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, - TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, fold_regions, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, }; #[derive(Copy, Clone, Debug)] @@ -516,8 +516,8 @@ impl<'tcx> TyCtxt<'tcx> { let item_args = ty::GenericArgs::identity_for_item(self, def.did()); let result = iter::zip(item_args, impl_args) - .filter(|&(_, k)| { - match k.unpack() { + .filter(|&(_, arg)| { + match arg.kind() { GenericArgKind::Lifetime(region) => match region.kind() { ty::ReEarlyParam(ebr) => { !impl_generics.region_param(ebr, self).pure_wrt_drop @@ -554,7 +554,7 @@ impl<'tcx> TyCtxt<'tcx> { let mut seen = GrowableBitSet::default(); let mut seen_late = FxHashSet::default(); for arg in args { - match arg.unpack() { + match arg.kind() { GenericArgKind::Lifetime(lt) => match (ignore_regions, lt.kind()) { (CheckRegions::FromFunction, ty::ReBound(di, reg)) => { if !seen_late.insert((di, reg)) { @@ -737,40 +737,6 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Return the set of types that should be taken into account when checking - /// trait bounds on a coroutine's internal state. This properly replaces - /// `ReErased` with new existential bound lifetimes. - pub fn coroutine_hidden_types( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List>>> { - let coroutine_layout = self.mir_coroutine_witnesses(def_id); - let mut vars = vec![]; - let bound_tys = self.mk_type_list_from_iter( - coroutine_layout - .as_ref() - .map_or_else(|| [].iter(), |l| l.field_tys.iter()) - .filter(|decl| !decl.ignore_for_traits) - .map(|decl| { - let ty = fold_regions(self, decl.ty, |re, debruijn| { - assert_eq!(re, self.lifetimes.re_erased); - let var = ty::BoundVar::from_usize(vars.len()); - vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)); - ty::Region::new_bound( - self, - debruijn, - ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }, - ) - }); - ty - }), - ); - ty::EarlyBinder::bind(ty::Binder::bind_with_vars( - bound_tys, - self.mk_bound_variable_kinds(&vars), - )) - } - /// Expands the given impl trait type, stopping if the type is recursive. #[instrument(skip(self), level = "debug", ret)] pub fn try_expand_impl_trait_type( diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 6c9e0e7c0eb8..74b6a840a2e7 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -110,7 +110,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let ptr_align = tcx.data_layout.pointer_align.abi; let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap(); - let mut vtable = Allocation::new(vtable_size, ptr_align, AllocInit::Uninit); + let mut vtable = Allocation::new(vtable_size, ptr_align, AllocInit::Uninit, ()); // No need to do any alignment checks on the memory accesses below, because we know the // allocation is correctly aligned as we created it above. Also we're only offsetting by diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 494ee33fd8b9..9825b947fe09 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -323,9 +323,9 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { fn parse_place_inner(&self, expr_id: ExprId) -> PResult<(Place<'tcx>, PlaceTy<'tcx>)> { let (parent, proj) = parse_by_kind!(self, expr_id, expr, "place", @call(mir_field, args) => { - let (parent, ty) = self.parse_place_inner(args[0])?; + let (parent, place_ty) = self.parse_place_inner(args[0])?; let field = FieldIdx::from_u32(self.parse_integer_literal(args[1])? as u32); - let field_ty = ty.field_ty(self.tcx, field); + let field_ty = PlaceTy::field_ty(self.tcx, place_ty.ty, place_ty.variant_index, field); let proj = PlaceElem::Field(field, field_ty); let place = parent.project_deeper(&[proj], self.tcx); return Ok((place, PlaceTy::from_ty(field_ty))); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 64d092e03545..eb8e98ec3644 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -121,14 +121,14 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> let value = match (lit, lit_ty.kind()) { (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { let s = s.as_str(); - let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); + let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes(), ()); let allocation = tcx.mk_const_alloc(allocation); ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } } (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); + let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8], ()); let allocation = tcx.mk_const_alloc(allocation); ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } } @@ -138,7 +138,7 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> } (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); + let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8], ()); let allocation = tcx.mk_const_alloc(allocation); ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index fbe530811567..f8c64d7d13ed 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -101,12 +101,12 @@ fn convert_to_hir_projections_and_truncate_for_capture( variant = Some(*idx); continue; } + ProjectionElem::UnwrapUnsafeBinder(_) => HirProjectionKind::UnwrapUnsafeBinder, // These do not affect anything, they just make sure we know the right type. ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue, ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::UnwrapUnsafeBinder(_) => { + | ProjectionElem::Subslice { .. } => { // We don't capture array-access projections. // We can stop here as arrays are captured completely. break; @@ -240,6 +240,9 @@ fn strip_prefix<'tcx>( HirProjectionKind::OpaqueCast => { assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..))); } + HirProjectionKind::UnwrapUnsafeBinder => { + assert_matches!(iter.next(), Some(ProjectionElem::UnwrapUnsafeBinder(..))); + } HirProjectionKind::Index | HirProjectionKind::Subslice => { bug!("unexpected projection kind: {:?}", projection); } diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 9cf051a8760b..3d5f6f4cf451 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -66,8 +66,7 @@ pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { } }; - // this must run before MIR dump, because - // "not all control paths return a value" is reported here. + // Checking liveness after building the THIR ensures there were no typeck errors. // // maybe move the check to a MIR pass? tcx.ensure_ok().check_liveness(def); @@ -451,10 +450,6 @@ fn construct_fn<'tcx>( let span = tcx.def_span(fn_def); let fn_id = tcx.local_def_id_to_hir_id(fn_def); - // The representation of thir for `-Zunpretty=thir-tree` relies on - // the entry expression being the last element of `thir.exprs`. - assert_eq!(expr.as_usize(), thir.exprs.len() - 1); - // Figure out what primary body this item has. let body = tcx.hir_body_owned_by(fn_def); let span_with_body = tcx.hir_span_with_body(fn_id); @@ -563,7 +558,7 @@ fn construct_const<'a, 'tcx>( // Figure out what primary body this item has. let (span, const_ty_span) = match tcx.hir_node(hir_id) { Node::Item(hir::Item { - kind: hir::ItemKind::Static(_, ty, _, _) | hir::ItemKind::Const(_, ty, _, _), + kind: hir::ItemKind::Static(_, _, ty, _) | hir::ItemKind::Const(_, _, ty, _), span, .. }) diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 2a30777e98c4..67988f1fcbc2 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -209,7 +209,7 @@ const ROOT_NODE: DropIdx = DropIdx::ZERO; #[derive(Debug)] struct DropTree { /// Nodes in the drop tree, containing drop data and a link to the next node. - drops: IndexVec, + drop_nodes: IndexVec, /// Map for finding the index of an existing node, given its contents. existing_drops_map: FxHashMap, /// Edges into the `DropTree` that need to be added once it's lowered. @@ -230,7 +230,6 @@ struct DropNode { struct DropNodeKey { next: DropIdx, local: Local, - kind: DropKind, } impl Scope { @@ -278,8 +277,8 @@ impl DropTree { let fake_source_info = SourceInfo::outermost(DUMMY_SP); let fake_data = DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage }; - let drops = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]); - Self { drops, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() } + let drop_nodes = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]); + Self { drop_nodes, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() } } /// Adds a node to the drop tree, consisting of drop data and the index of @@ -288,12 +287,12 @@ impl DropTree { /// If there is already an equivalent node in the tree, nothing is added, and /// that node's index is returned. Otherwise, the new node's index is returned. fn add_drop(&mut self, data: DropData, next: DropIdx) -> DropIdx { - let drops = &mut self.drops; + let drop_nodes = &mut self.drop_nodes; *self .existing_drops_map - .entry(DropNodeKey { next, local: data.local, kind: data.kind }) + .entry(DropNodeKey { next, local: data.local }) // Create a new node, and also add its index to the map. - .or_insert_with(|| drops.push(DropNode { data, next })) + .or_insert_with(|| drop_nodes.push(DropNode { data, next })) } /// Registers `from` as an entry point to this drop tree, at `to`. @@ -301,7 +300,7 @@ impl DropTree { /// During [`Self::build_mir`], `from` will be linked to the corresponding /// block within the drop tree. fn add_entry_point(&mut self, from: BasicBlock, to: DropIdx) { - debug_assert!(to < self.drops.next_index()); + debug_assert!(to < self.drop_nodes.next_index()); self.entry_points.push((to, from)); } @@ -341,10 +340,10 @@ impl DropTree { Own, } - let mut blocks = IndexVec::from_elem(None, &self.drops); + let mut blocks = IndexVec::from_elem(None, &self.drop_nodes); blocks[ROOT_NODE] = root_node; - let mut needs_block = IndexVec::from_elem(Block::None, &self.drops); + let mut needs_block = IndexVec::from_elem(Block::None, &self.drop_nodes); if root_node.is_some() { // In some cases (such as drops for `continue`) the root node // already has a block. In this case, make sure that we don't @@ -356,7 +355,7 @@ impl DropTree { let entry_points = &mut self.entry_points; entry_points.sort(); - for (drop_idx, drop_node) in self.drops.iter_enumerated().rev() { + for (drop_idx, drop_node) in self.drop_nodes.iter_enumerated().rev() { if entry_points.last().is_some_and(|entry_point| entry_point.0 == drop_idx) { let block = *blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg)); needs_block[drop_idx] = Block::Own; @@ -396,7 +395,7 @@ impl DropTree { cfg: &mut CFG<'tcx>, blocks: &IndexSlice>, ) { - for (drop_idx, drop_node) in self.drops.iter_enumerated().rev() { + for (drop_idx, drop_node) in self.drop_nodes.iter_enumerated().rev() { let Some(block) = blocks[drop_idx] else { continue }; match drop_node.data.kind { DropKind::Value => { @@ -726,11 +725,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { drops }; - let drop_idx = self.scopes.scopes[scope_index + 1..] - .iter() - .flat_map(|scope| &scope.drops) - .fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx)); - + let mut drop_idx = ROOT_NODE; + for scope in &self.scopes.scopes[scope_index + 1..] { + for drop in &scope.drops { + drop_idx = drops.add_drop(*drop, drop_idx); + } + } drops.add_entry_point(block, drop_idx); // `build_drop_trees` doesn't have access to our source_info, so we @@ -829,9 +829,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // `unwind_to` should drop the value that we're about to // schedule. If dropping this value panics, then we continue // with the *next* value on the unwind path. - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, + drop_data.local + ); + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.kind, + drop_data.kind + ); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; let mut unwind_entry_point = unwind_to; @@ -1551,14 +1557,14 @@ where // // We adjust this BEFORE we create the drop (e.g., `drops[n]`) // because `drops[n]` should unwind to `drops[n-1]`. - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.local, drop_data.local); + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; if let Some(idx) = dropline_to { - debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); - debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); - dropline_to = Some(coroutine_drops.drops[idx].next); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drop_nodes[idx].next); } // If the operand has been moved, and we are not on an unwind @@ -1598,9 +1604,12 @@ where // cases we emit things ALSO on the unwind path, so we need to adjust // `unwind_to` in that case. if storage_dead_on_unwind { - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, + drop_data.local + ); + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; } // If the operand has been moved, and we are not on an unwind @@ -1629,14 +1638,17 @@ where // the storage-dead has completed, we need to adjust the `unwind_to` pointer // so that any future drops we emit will not register storage-dead. if storage_dead_on_unwind { - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, + drop_data.local + ); + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; } if let Some(idx) = dropline_to { - debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); - debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); - dropline_to = Some(coroutine_drops.drops[idx].next); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drop_nodes[idx].next); } // Only temps and vars need their storage dead. assert!(local.index() > arg_count); @@ -1663,10 +1675,10 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { let is_coroutine = self.coroutine.is_some(); // Link the exit drop tree to unwind drop tree. - if drops.drops.iter().any(|drop_node| drop_node.data.kind == DropKind::Value) { + if drops.drop_nodes.iter().any(|drop_node| drop_node.data.kind == DropKind::Value) { let unwind_target = self.diverge_cleanup_target(else_scope, span); let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1); - for (drop_idx, drop_node) in drops.drops.iter_enumerated().skip(1) { + for (drop_idx, drop_node) in drops.drop_nodes.iter_enumerated().skip(1) { match drop_node.data.kind { DropKind::Storage | DropKind::ForLint => { if is_coroutine { @@ -1695,35 +1707,29 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { } // Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops if is_coroutine - && drops.drops.iter().any(|DropNode { data, next: _ }| { + && drops.drop_nodes.iter().any(|DropNode { data, next: _ }| { data.kind == DropKind::Value && self.is_async_drop(data.local) }) { let dropline_target = self.diverge_dropline_target(else_scope, span); let mut dropline_indices = IndexVec::from_elem_n(dropline_target, 1); - for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) { + for (drop_idx, drop_data) in drops.drop_nodes.iter_enumerated().skip(1) { + let coroutine_drop = self + .scopes + .coroutine_drops + .add_drop(drop_data.data, dropline_indices[drop_data.next]); match drop_data.data.kind { - DropKind::Storage | DropKind::ForLint => { - let coroutine_drop = self - .scopes - .coroutine_drops - .add_drop(drop_data.data, dropline_indices[drop_data.next]); - dropline_indices.push(coroutine_drop); - } + DropKind::Storage | DropKind::ForLint => {} DropKind::Value => { - let coroutine_drop = self - .scopes - .coroutine_drops - .add_drop(drop_data.data, dropline_indices[drop_data.next]); if self.is_async_drop(drop_data.data.local) { self.scopes.coroutine_drops.add_entry_point( blocks[drop_idx].unwrap(), dropline_indices[drop_data.next], ); } - dropline_indices.push(coroutine_drop); } } + dropline_indices.push(coroutine_drop); } } blocks[ROOT_NODE].map(BasicBlock::unit) @@ -1769,11 +1775,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { // prevent drop elaboration from creating drop flags that would have // to be captured by the coroutine. I'm not sure how important this // optimization is, but it is here. - for (drop_idx, drop_node) in drops.drops.iter_enumerated() { + for (drop_idx, drop_node) in drops.drop_nodes.iter_enumerated() { if let DropKind::Value = drop_node.data.kind && let Some(bb) = blocks[drop_idx] { - debug_assert!(drop_node.next < drops.drops.next_index()); + debug_assert!(drop_node.next < drops.drop_nodes.next_index()); drops.entry_points.push((drop_node.next, bb)); } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index fde23413972b..226dc920a496 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1220,6 +1220,9 @@ impl<'tcx> ThirBuildCx<'tcx> { HirProjectionKind::OpaqueCast => { ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) } } + HirProjectionKind::UnwrapUnsafeBinder => ExprKind::PlaceUnwrapUnsafeBinder { + source: self.thir.exprs.push(captured_place_expr), + }, HirProjectionKind::Index | HirProjectionKind::Subslice => { // We don't capture these projections, so we can ignore them here continue; diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 2f593b9a0a74..8c817605847a 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -27,8 +27,8 @@ pub(crate) fn thir_body( if let Some(reported) = cx.typeck_results.tainted_by_errors { return Err(reported); } - let expr = cx.mirror_expr(body.value); + // Lower the params before the body's expression so errors from params are shown first. let owner_id = tcx.local_def_id_to_hir_id(owner_def); if let Some(fn_decl) = tcx.hir_fn_decl_by_hir_id(owner_id) { let closure_env_param = cx.closure_env_param(owner_def, owner_id); @@ -48,6 +48,7 @@ pub(crate) fn thir_body( } } + let expr = cx.mirror_expr(body.value); Ok((tcx.alloc_steal_thir(cx.thir), expr)) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index b7d203e3cd78..84a0190a7fa1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -131,7 +131,7 @@ impl<'tcx> ConstToPat<'tcx> { .dcx() .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); for arg in uv.args { - if let ty::GenericArgKind::Type(ty) = arg.unpack() + if let ty::GenericArgKind::Type(ty) = arg.kind() && let ty::Param(param_ty) = ty.kind() { let def_id = self.tcx.hir_enclosing_body_owner(self.id); @@ -382,6 +382,9 @@ fn extend_type_not_partial_eq<'tcx>( fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { match ty.kind() { ty::Dynamic(..) => return ControlFlow::Break(()), + // Unsafe binders never implement `PartialEq`, so avoid walking into them + // which would require instantiating its binder with placeholders too. + ty::UnsafeBinder(..) => return ControlFlow::Break(()), ty::FnPtr(..) => return ControlFlow::Continue(()), ty::Adt(def, _args) => { let ty_def_id = def.did(); diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 37248941e2c4..db9547a481fb 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -8,10 +8,10 @@ use rustc_span::def_id::LocalDefId; /// Create a THIR tree for debugging. pub fn thir_tree(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String { match super::cx::thir_body(tcx, owner_def) { - Ok((thir, _)) => { + Ok((thir, expr)) => { let thir = thir.steal(); let mut printer = ThirPrinter::new(&thir); - printer.print(); + printer.print(expr); printer.into_buffer() } Err(_) => "error".into(), @@ -58,7 +58,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { } } - fn print(&mut self) { + fn print(&mut self, body_expr: ExprId) { print_indented!(self, "params: [", 0); for param in self.thir.params.iter() { self.print_param(param, 1); @@ -66,8 +66,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "]", 0); print_indented!(self, "body:", 0); - let expr = ExprId::from_usize(self.thir.exprs.len() - 1); - self.print_expr(expr, 1); + self.print_expr(body_expr, 1); } fn into_buffer(self) -> String { diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 9e4b4534dcc5..a7d0b3acbe49 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -10,7 +10,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_const_eval = { path = "../rustc_const_eval" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index a1264471a2df..ae3062f07de9 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -78,10 +78,4 @@ mir_transform_unconditional_recursion = function cannot return without recursing mir_transform_unconditional_recursion_call_site_label = recursive call site -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 - mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored -mir_transform_unnecessary_transmute = unnecessary transmute diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs index 83c3cda5a505..14d9532894fd 100644 --- a/compiler/rustc_mir_transform/src/check_inline.rs +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -1,7 +1,7 @@ //! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its //! definition alone (irrespective of any specific caller). -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::{Body, TerminatorKind}; diff --git a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs deleted file mode 100644 index daddb5dedbcf..000000000000 --- a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs +++ /dev/null @@ -1,77 +0,0 @@ -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_raw_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/check_unnecessary_transmutes.rs b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs deleted file mode 100644 index 8da17a056e31..000000000000 --- a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs +++ /dev/null @@ -1,124 +0,0 @@ -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; -use rustc_middle::ty::*; -use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES; -use rustc_span::source_map::Spanned; -use rustc_span::{Span, sym}; - -use crate::errors::UnnecessaryTransmute as Error; - -/// Check for transmutes that overlap with stdlib methods. -/// For example, transmuting `[u8; 4]` to `u32`. -pub(super) struct CheckUnnecessaryTransmutes; - -impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes { - fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - let mut checker = UnnecessaryTransmuteChecker { body, tcx }; - checker.visit_body(body); - } -} - -struct UnnecessaryTransmuteChecker<'a, 'tcx> { - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> { - fn is_unnecessary_transmute( - &self, - function: &Operand<'tcx>, - arg: String, - span: Span, - ) -> Option { - let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); - let [input] = fn_sig.inputs() else { return None }; - - let err = |sugg| Error { span, sugg, help: None }; - - Some(match (input.kind(), fn_sig.output().kind()) { - // dont check the length; transmute does that for us. - // [u8; _] => primitive - (Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error { - sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()), - help: Some( - "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order", - ), - span, - }, - // primitive => [u8; _] - (Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error { - sugg: format!("{input}::to_ne_bytes({arg})"), - help: Some( - "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order", - ), - span, - }, - // char → u32 - (Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")), - // char (→ u32) → i32 - (Char, Int(IntTy::I32)) => err(format!("u32::from({arg}).cast_signed()")), - // u32 → char - (Uint(UintTy::U32), Char) => Error { - sugg: format!("char::from_u32_unchecked({arg})"), - help: Some("consider `char::from_u32(…).unwrap()`"), - span, - }, - // i32 → char - (Int(IntTy::I32), Char) => Error { - sugg: format!("char::from_u32_unchecked(i32::cast_unsigned({arg}))"), - help: Some("consider `char::from_u32(i32::cast_unsigned(…)).unwrap()`"), - span, - }, - // uNN → iNN - (Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())), - // iNN → uNN - (Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())), - // fNN → xsize - (Float(ty), Uint(UintTy::Usize)) => { - err(format!("{}::to_bits({arg}) as usize", ty.name_str())) - } - (Float(ty), Int(IntTy::Isize)) => { - err(format!("{}::to_bits({arg}) as isize", ty.name_str())) - } - // fNN (→ uNN) → iNN - (Float(ty), Int(..)) => err(format!("{}::to_bits({arg}).cast_signed()", ty.name_str())), - // fNN → uNN - (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())), - // xsize → fNN - (Uint(UintTy::Usize) | Int(IntTy::Isize), Float(ty)) => { - err(format!("{}::from_bits({arg} as _)", ty.name_str(),)) - } - // iNN (→ uNN) → fNN - (Int(int_ty), Float(ty)) => err(format!( - "{}::from_bits({}::cast_unsigned({arg}))", - ty.name_str(), - int_ty.name_str() - )), - // uNN → fNN - (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())), - // bool → { x8 } - (Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())), - // u8 → bool - (Uint(_), Bool) => err(format!("({arg} == 1)")), - _ => return None, - }) - } -} - -impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, '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, args, .. } = &terminator.kind - && let [Spanned { span: arg, .. }] = **args - && let Some((func_def_id, _)) = func.const_fn_def() - && self.tcx.is_intrinsic(func_def_id, sym::transmute) - && let span = self.body.source_info(location).span - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg) - && let Some(lint) = self.is_unnecessary_transmute(func, snippet, span) - && let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes) - { - self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint); - } - } -} diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 4f45d9588a89..727d4a126d21 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::visit::Visitor; diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 73a58160a6aa..211e2a92f73d 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -251,7 +251,29 @@ where span_bug!(span, "invalid `AsyncDrop` impl_source: {:?}", impl_source); } }; - let drop_fn_def_id = tcx.associated_item_def_ids(drop_trait)[0]; + // impl_item_refs may be empty if drop fn is not implemented in 'impl AsyncDrop for ...' + // (#140974). + // Such code will report error, so just generate sync drop here and return + let Some(drop_fn_def_id) = + tcx.associated_item_def_ids(drop_trait).into_iter().nth(0).copied() + else { + tcx.dcx().span_delayed_bug( + self.elaborator.body().span, + "AsyncDrop type without correct `async fn drop(...)`.", + ); + self.elaborator.patch().patch_terminator( + pin_obj_bb, + TerminatorKind::Drop { + place, + target: succ, + unwind: unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }, + ); + return pin_obj_bb; + }; let drop_fn = Ty::new_fn_def(tcx, drop_fn_def_id, trait_args); let sig = drop_fn.fn_sig(tcx); let sig = tcx.instantiate_bound_regions_with_erased(sig); @@ -318,15 +340,20 @@ where bug!(); }; let obj_ptr_ty = Ty::new_mut_ptr(tcx, drop_ty); - let obj_ptr_place = Place::from(self.new_temp(obj_ptr_ty)); let unwrap_ty = adt_def.non_enum_variant().fields[FieldIdx::ZERO].ty(tcx, adt_args); - let addr = Rvalue::RawPtr( - RawPtrKind::Mut, - pin_obj_place.project_deeper( - &[ProjectionElem::Field(FieldIdx::ZERO, unwrap_ty), ProjectionElem::Deref], - tcx, - ), - ); + let obj_ref_place = Place::from(self.new_temp(unwrap_ty)); + call_statements.push(self.assign( + obj_ref_place, + Rvalue::Use(Operand::Copy(tcx.mk_place_field( + pin_obj_place, + FieldIdx::ZERO, + unwrap_ty, + ))), + )); + + let obj_ptr_place = Place::from(self.new_temp(obj_ptr_ty)); + + let addr = Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_deref(obj_ref_place)); call_statements.push(self.assign(obj_ptr_place, addr)); obj_ptr_place }; @@ -1251,6 +1278,23 @@ where } ty::Slice(ety) => self.drop_loop_trio_for_slice(*ety), + ty::UnsafeBinder(_) => { + // Unsafe binders may elaborate drops if their inner type isn't copy. + // This is enforced in typeck, so this should never happen. + self.tcx().dcx().span_delayed_bug( + self.source_info.span, + "open drop for unsafe binder shouldn't be encountered", + ); + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Unreachable, + }), + is_cleanup: self.unwind.is_cleanup(), + }) + } + _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty), } } diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 5b03a4987ed7..cffa0183fa7a 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -158,33 +158,6 @@ pub(crate) struct MustNotSuspendReason { pub reason: String, } -pub(crate) struct UnnecessaryTransmute { - pub span: Span, - pub sugg: String, - pub help: Option<&'static str>, -} - -// Needed for def_path_str -impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute { - fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { - diag.primary_message(fluent::mir_transform_unnecessary_transmute); - diag.span_suggestion( - self.span, - "replace this with", - self.sugg, - lint::Applicability::MachineApplicable, - ); - self.help.map(|help| diag.help(help)); - } -} - -#[derive(LintDiagnostic)] -#[diag(mir_transform_undefined_transmute)] -#[note] -#[note(mir_transform_note2)] -#[help] -pub(crate) struct UndefinedTransmute; - #[derive(Diagnostic)] #[diag(mir_transform_force_inline)] #[note] diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 209e818e9e32..a91d46ec406e 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -638,6 +638,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { place: PlaceRef<'tcx>, value: VnIndex, proj: PlaceElem<'tcx>, + from_non_ssa_index: &mut bool, ) -> Option { let proj = match proj { ProjectionElem::Deref => { @@ -682,6 +683,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { + *from_non_ssa_index |= self.locals[idx].is_none(); return Some(*inner); } let idx = self.locals[idx]?; @@ -774,6 +776,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Invariant: `value` holds the value up-to the `index`th projection excluded. let mut value = self.locals[place.local]?; + let mut from_non_ssa_index = false; for (index, proj) in place.projection.iter().enumerate() { if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer) @@ -791,7 +794,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let base = PlaceRef { local: place.local, projection: &place.projection[..index] }; - value = self.project(base, value, proj)?; + value = self.project(base, value, proj, &mut from_non_ssa_index)?; } if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) @@ -804,6 +807,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } if let Some(new_local) = self.try_as_local(value, location) { place_ref = PlaceRef { local: new_local, projection: &[] }; + } else if from_non_ssa_index { + // If access to non-SSA locals is unavoidable, bail out. + return None; } if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 9785c039d539..f48dba9663a5 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -5,7 +5,7 @@ use std::iter; use std::ops::{Range, RangeFrom}; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_attr_parsing::{InlineAttr, OptimizeAttr}; +use rustc_attr_data_structures::{InlineAttr, OptimizeAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::Idx; diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 47cb478fe33e..1a91d6bd7da9 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -241,6 +241,7 @@ impl EnumSizeOpt { data, tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi, Mutability::Not, + (), ); let alloc = tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc)); Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc))) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 10dbb3437dcb..d26e44687157 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -123,8 +123,6 @@ declare_passes! { mod check_const_item_mutation : CheckConstItemMutation; mod check_null : CheckNull; mod check_packed_ref : CheckPackedRef; - mod check_undefined_transmutes : CheckUndefinedTransmutes; - mod check_unnecessary_transmutes: CheckUnnecessaryTransmutes; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck : CleanupPostBorrowck; @@ -390,8 +388,6 @@ 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), - &Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, &Lint(sanity_check::SanityCheck), diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 9c21bcfc0d26..52f4c39c09bb 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -262,6 +262,52 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }); terminator.kind = TerminatorKind::Goto { target }; } + sym::slice_get_unchecked => { + let target = target.unwrap(); + let Ok([ptrish, index]) = take_array(args) else { + span_bug!( + terminator.source_info.span, + "Wrong number of arguments for {intrinsic:?}", + ); + }; + + let place = ptrish.node.place().unwrap(); + assert!(!place.is_indirect()); + let updated_place = place.project_deeper( + &[ + ProjectionElem::Deref, + ProjectionElem::Index( + index.node.place().unwrap().as_local().unwrap(), + ), + ], + tcx, + ); + + let ret_ty = generic_args.type_at(0); + let rvalue = match *ret_ty.kind() { + ty::RawPtr(_, Mutability::Not) => { + Rvalue::RawPtr(RawPtrKind::Const, updated_place) + } + ty::RawPtr(_, Mutability::Mut) => { + Rvalue::RawPtr(RawPtrKind::Mut, updated_place) + } + ty::Ref(region, _, Mutability::Not) => { + Rvalue::Ref(region, BorrowKind::Shared, updated_place) + } + ty::Ref(region, _, Mutability::Mut) => Rvalue::Ref( + region, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + updated_place, + ), + _ => bug!("Unknown return type {ret_ty:?}"), + }; + + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new((*destination, rvalue))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } sym::transmute | sym::transmute_unchecked => { let dst_ty = destination.ty(local_decls, tcx).ty; let Ok([arg]) = take_array(args) else { diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 8c0c30968990..5e511f1a418b 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -284,12 +284,14 @@ fn can_cast( let v = match src_layout.ty.kind() { ty::Uint(_) => from_scalar.to_uint(src_layout.size), ty::Int(_) => from_scalar.to_int(src_layout.size) as u128, - _ => unreachable!("invalid int"), + // We can also transform the values of other integer representations (such as char), + // although this may not be practical in real-world scenarios. + _ => return false, }; let size = match *cast_ty.kind() { ty::Int(t) => Integer::from_int_ty(&tcx, t).size(), ty::Uint(t) => Integer::from_uint_ty(&tcx, t).size(), - _ => unreachable!("invalid int"), + _ => return false, }; let v = size.truncate(v); let cast_scalar = ScalarInt::try_from_uint(v, size).unwrap(); diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 7976b65aae7b..fbc8ee9b06c0 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -6,7 +6,7 @@ use rustc_middle::mir::{ BasicBlock, BasicBlockData, Body, Local, LocalDecl, MirSource, Operand, Place, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt}; use super::*; use crate::patch::MirPatch; @@ -121,9 +121,10 @@ pub(super) fn build_async_drop_shim<'tcx>( parent_args.as_coroutine().resume_ty(), ))); body.phase = MirPhase::Runtime(RuntimePhase::Initial); - if !needs_async_drop { + if !needs_async_drop || drop_ty.references_error() { // Returning noop body for types without `need async drop` - // (or sync Drop in case of !`need async drop` && `need drop`) + // (or sync Drop in case of !`need async drop` && `need drop`). + // And also for error types. return body; } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index f8d1629b0e26..c8aa7588d03a 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1,7 +1,7 @@ //! Validates the MIR to ensure that invariants are upheld. use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_index::IndexVec; diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 36b76d261de6..063fc8f1c749 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index c6a81e60b2b5..1ee977a5457e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -208,7 +208,7 @@ use std::cell::OnceCell; use std::path::PathBuf; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{MTLock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -1483,7 +1483,7 @@ impl<'v> RootCollector<'_, 'v> { // But even just declaring them must collect the items they refer to // unless their generics require monomorphization. - if !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx) + if !self.tcx.generics_of(id.owner_id).own_requires_monomorphization() && let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) { collect_const_value(self.tcx, val, self.output); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index c3a415297940..b4169a060d4d 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -100,6 +100,7 @@ use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -845,8 +846,7 @@ fn mono_item_visibility<'tcx>( return if is_generic && (always_export_generics || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline - == rustc_attr_parsing::InlineAttr::Never)) + && tcx.codegen_fn_attrs(def_id).inline == InlineAttr::Never)) { // If it is an upstream monomorphization and we export generics, we must make // it available to downstream crates. @@ -859,8 +859,7 @@ fn mono_item_visibility<'tcx>( if is_generic { if always_export_generics - || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline == rustc_attr_parsing::InlineAttr::Never) + || (can_export_generics && tcx.codegen_fn_attrs(def_id).inline == InlineAttr::Never) { if tcx.is_unreachable_local_definition(def_id) { // This instance cannot be used from another crate. diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index bbb4a162027d..a87ae5284b1a 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -4,12 +4,22 @@ use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack}; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; use rustc_type_ir::{ - self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike, - Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self as ty, Canonical, CanonicalTyVarKind, CanonicalVarKind, Flags, InferCtxtLike, Interner, + TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use crate::delegate::SolverDelegate; +/// Does this have infer/placeholder/param, free regions or ReErased? +const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( + TypeFlags::HAS_INFER.bits() + | TypeFlags::HAS_PLACEHOLDER.bits() + | TypeFlags::HAS_PARAM.bits() + | TypeFlags::HAS_FREE_REGIONS.bits() + | TypeFlags::HAS_RE_ERASED.bits(), +) +.unwrap(); + /// Whether we're canonicalizing a query input or the query response. /// /// When canonicalizing an input we're in the context of the caller @@ -50,7 +60,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { // Mutable fields. variables: &'a mut Vec, - primitive_var_infos: Vec>, + var_kinds: Vec>, variable_lookup_table: HashMap, binder_index: ty::DebruijnIndex, @@ -73,15 +83,19 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables, variable_lookup_table: Default::default(), - primitive_var_infos: Vec::new(), + var_kinds: Vec::new(), binder_index: ty::INNERMOST, cache: Default::default(), }; - let value = value.fold_with(&mut canonicalizer); - assert!(!value.has_infer(), "unexpected infer in {value:?}"); - assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); + let value = if value.has_type_flags(NEEDS_CANONICAL) { + value.fold_with(&mut canonicalizer) + } else { + value + }; + debug_assert!(!value.has_infer(), "unexpected infer in {value:?}"); + debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); let (max_universe, variables) = canonicalizer.finalize(); Canonical { max_universe, variables, value } } @@ -106,12 +120,19 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables, variable_lookup_table: Default::default(), - primitive_var_infos: Vec::new(), + var_kinds: Vec::new(), binder_index: ty::INNERMOST, cache: Default::default(), }; - let param_env = input.goal.param_env.fold_with(&mut env_canonicalizer); + + let param_env = input.goal.param_env; + let param_env = if param_env.has_type_flags(NEEDS_CANONICAL) { + param_env.fold_with(&mut env_canonicalizer) + } else { + param_env + }; + debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST); // Then canonicalize the rest of the input without keeping `'static` // while *mostly* reusing the canonicalizer from above. @@ -123,7 +144,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // We're able to reuse the `variable_lookup_table` as whether or not // it already contains an entry for `'static` does not matter. variable_lookup_table: env_canonicalizer.variable_lookup_table, - primitive_var_infos: env_canonicalizer.primitive_var_infos, + var_kinds: env_canonicalizer.var_kinds, binder_index: ty::INNERMOST, // We do not reuse the cache as it may contain entries whose canonicalized @@ -134,14 +155,26 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { cache: Default::default(), }; - let predicate = input.goal.predicate.fold_with(&mut rest_canonicalizer); + let predicate = input.goal.predicate; + let predicate = if predicate.has_type_flags(NEEDS_CANONICAL) { + predicate.fold_with(&mut rest_canonicalizer) + } else { + predicate + }; let goal = Goal { param_env, predicate }; + + let predefined_opaques_in_body = input.predefined_opaques_in_body; let predefined_opaques_in_body = - input.predefined_opaques_in_body.fold_with(&mut rest_canonicalizer); + if input.predefined_opaques_in_body.has_type_flags(NEEDS_CANONICAL) { + predefined_opaques_in_body.fold_with(&mut rest_canonicalizer) + } else { + predefined_opaques_in_body + }; + let value = QueryInput { goal, predefined_opaques_in_body }; - assert!(!value.has_infer(), "unexpected infer in {value:?}"); - assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); + debug_assert!(!value.has_infer(), "unexpected infer in {value:?}"); + debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); let (max_universe, variables) = rest_canonicalizer.finalize(); Canonical { max_universe, variables, value } } @@ -149,7 +182,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { fn get_or_insert_bound_var( &mut self, arg: impl Into, - canonical_var_info: CanonicalVarInfo, + kind: CanonicalVarKind, ) -> ty::BoundVar { // FIXME: 16 is made up and arbitrary. We should look at some // perf data here. @@ -162,14 +195,14 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { *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); + self.var_kinds.push(kind); 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); + self.var_kinds.push(kind); var }) }; @@ -177,8 +210,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { ty::BoundVar::from(idx) } - fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) { - let mut var_infos = self.primitive_var_infos; + fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) { + let mut var_kinds = self.var_kinds; // See the rustc-dev-guide section about how we deal with universes // during canonicalization in the new solver. match self.canonicalize_mode { @@ -192,25 +225,25 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // information for placeholders and inference variables created inside // of the query. CanonicalizeMode::Response { max_input_universe } => { - for var in var_infos.iter_mut() { + for var in var_kinds.iter_mut() { let uv = var.universe(); let new_uv = ty::UniverseIndex::from( uv.index().saturating_sub(max_input_universe.index()), ); *var = var.with_updated_universe(new_uv); } - let max_universe = var_infos + let max_universe = var_kinds .iter() - .map(|info| info.universe()) + .map(|kind| kind.universe()) .max() .unwrap_or(ty::UniverseIndex::ROOT); - let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos); - return (max_universe, var_infos); + let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds); + return (max_universe, var_kinds); } } - // Given a `var_infos` with existentials `En` and universals `Un` in + // Given a `var_kinds` with existentials `En` and universals `Un` in // universes `n`, this algorithm compresses them in place so that: // // - the new universe indices are as small as possible @@ -219,12 +252,12 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // 2. put a placeholder in the same universe as an existential which cannot name it // // Let's walk through an example: - // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0 - // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1 - // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2 - // - var_infos: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5 - // - 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: - + // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0 + // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1 + // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2 + // - var_kinds: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5 + // - var_kinds: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6 + // - var_kinds: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: - // // 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. @@ -232,7 +265,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let mut existential_in_new_uv = None; let mut next_orig_uv = Some(ty::UniverseIndex::ROOT); while let Some(orig_uv) = next_orig_uv.take() { - let mut update_uv = |var: &mut CanonicalVarInfo, orig_uv, is_existential| { + let mut update_uv = |var: &mut CanonicalVarKind, orig_uv, is_existential| { let uv = var.universe(); match uv.cmp(&orig_uv) { Ordering::Less => (), // Already updated @@ -284,7 +317,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // Whenever we compress the universe of a placeholder, no existential with // an already compressed universe can name that placeholder. for is_existential in [false, true] { - for var in var_infos.iter_mut() { + for var in var_kinds.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() { @@ -298,26 +331,26 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // We put all regions into a separate universe. let mut first_region = true; - for var in var_infos.iter_mut() { + for var in var_kinds.iter_mut() { if var.is_region() { if first_region { first_region = false; curr_compressed_uv = curr_compressed_uv.next_universe(); } - assert!(var.is_existential()); + debug_assert!(var.is_existential()); *var = var.with_updated_universe(curr_compressed_uv); } } - let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos); - (curr_compressed_uv, var_infos) + let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds); + (curr_compressed_uv, var_kinds) } fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { - assert_eq!( + debug_assert_eq!( self.delegate.opportunistic_resolve_ty_var(vid), t, "ty vid should have been resolved fully before canonicalization" @@ -330,7 +363,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { )) } ty::IntVar(vid) => { - assert_eq!( + debug_assert_eq!( self.delegate.opportunistic_resolve_int_var(vid), t, "ty vid should have been resolved fully before canonicalization" @@ -338,7 +371,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { CanonicalVarKind::Ty(CanonicalTyVarKind::Int) } ty::FloatVar(vid) => { - assert_eq!( + debug_assert_eq!( self.delegate.opportunistic_resolve_float_var(vid), t, "ty vid should have been resolved fully before canonicalization" @@ -387,11 +420,15 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::Alias(_, _) | ty::Bound(_, _) | ty::Error(_) => { - return ensure_sufficient_stack(|| t.super_fold_with(self)); + return if t.has_type_flags(NEEDS_CANONICAL) { + ensure_sufficient_stack(|| t.super_fold_with(self)) + } else { + t + }; } }; - let var = self.get_or_insert_bound_var(t, CanonicalVarInfo { kind }); + let var = self.get_or_insert_bound_var(t, kind); Ty::new_anon_bound(self.cx(), self.binder_index, var) } @@ -459,7 +496,7 @@ impl, I: Interner> TypeFolder for Canonicaliz }, ty::ReVar(vid) => { - assert_eq!( + debug_assert_eq!( self.delegate.opportunistic_resolve_lt_var(vid), r, "region vid should have been resolved fully before canonicalization" @@ -475,7 +512,7 @@ impl, I: Interner> TypeFolder for Canonicaliz } }; - let var = self.get_or_insert_bound_var(r, CanonicalVarInfo { kind }); + let var = self.get_or_insert_bound_var(r, kind); Region::new_anon_bound(self.cx(), self.binder_index, var) } @@ -485,7 +522,8 @@ impl, I: Interner> TypeFolder for Canonicaliz ty } else { let res = self.cached_fold_ty(t); - assert!(self.cache.insert((self.binder_index, t), res).is_none()); + let old = self.cache.insert((self.binder_index, t), res); + assert_eq!(old, None); res } } @@ -494,7 +532,7 @@ impl, I: Interner> TypeFolder for Canonicaliz let kind = match c.kind() { ty::ConstKind::Infer(i) => match i { ty::InferConst::Var(vid) => { - assert_eq!( + debug_assert_eq!( self.delegate.opportunistic_resolve_ct_var(vid), c, "const vid should have been resolved fully before canonicalization" @@ -522,11 +560,28 @@ impl, I: Interner> TypeFolder for Canonicaliz | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) - | ty::ConstKind::Expr(_) => return c.super_fold_with(self), + | ty::ConstKind::Expr(_) => { + return if c.has_type_flags(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c }; + } }; - let var = self.get_or_insert_bound_var(c, CanonicalVarInfo { kind }); + let var = self.get_or_insert_bound_var(c, kind); Const::new_anon_bound(self.cx(), self.binder_index, var) } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.flags().intersects(NEEDS_CANONICAL) { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + match self.canonicalize_mode { + CanonicalizeMode::Input { keep_static: true } + | CanonicalizeMode::Response { max_input_universe: _ } => {} + CanonicalizeMode::Input { keep_static: false } => { + panic!("erasing 'static in env") + } + } + if c.flags().intersects(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c } + } } diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 90a7c2e9f787..7b932010d498 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -17,6 +17,12 @@ pub trait SolverDelegate: Deref + Sized { where V: TypeFoldable; + fn compute_goal_fast_path( + &self, + goal: Goal::Predicate>, + span: ::Span, + ) -> Option; + fn fresh_var_for_kind_with_span( &self, arg: ::GenericArg, @@ -53,7 +59,7 @@ pub trait SolverDelegate: Deref + Sized { fn instantiate_canonical_var_with_infer( &self, - cv_info: ty::CanonicalVarInfo, + kind: ty::CanonicalVarKind, span: ::Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ::GenericArg; diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 992c5ddf504e..c3c57eccd6ef 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -11,7 +11,7 @@ use crate::delegate::SolverDelegate; // EAGER RESOLUTION /// Resolves ty, region, and const vars to their inferred values or their root vars. -pub struct EagerResolver<'a, D, I = ::Interner> +struct EagerResolver<'a, D, I = ::Interner> where D: SolverDelegate, I: Interner, @@ -22,8 +22,20 @@ where cache: DelayedMap, } +pub fn eager_resolve_vars>( + delegate: &D, + value: T, +) -> T { + if value.has_infer() { + let mut folder = EagerResolver::new(delegate); + value.fold_with(&mut folder) + } else { + value + } +} + impl<'a, D: SolverDelegate> EagerResolver<'a, D> { - pub fn new(delegate: &'a D) -> Self { + fn new(delegate: &'a D) -> Self { EagerResolver { delegate, cache: Default::default() } } } @@ -86,4 +98,12 @@ impl, I: Interner> TypeFolder for EagerResolv } } } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_infer() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_infer() { c.super_fold_with(self) } else { c } + } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 2a2b462a36cb..4a2a7761f7f1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -83,7 +83,7 @@ where .cx() .coroutine_hidden_types(def_id) .instantiate(cx, args) - .map_bound(|tys| tys.to_vec())), + .map_bound(|bound| bound.types.to_vec())), ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])), @@ -249,7 +249,7 @@ where .cx() .coroutine_hidden_types(def_id) .instantiate(ecx.cx(), args) - .map_bound(|tys| tys.to_vec())), + .map_bound(|bound| bound.types.to_vec())), } } @@ -327,7 +327,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable>( + pub(super) fn canonicalize_goal( &self, - goal: Goal, - ) -> (Vec, CanonicalInput) { + goal: Goal, + ) -> (Vec, CanonicalInput) { // We only care about one entry per `OpaqueTypeKey` here, // so we only canonicalize the lookup table and ignore // duplicate entries. let opaque_types = self.delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = - (goal, opaque_types).fold_with(&mut EagerResolver::new(self.delegate)); + let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); let mut orig_values = Default::default(); let canonical = Canonicalizer::canonicalize_input( @@ -129,7 +129,12 @@ where if goals.is_empty() { assert!(matches!(goals_certainty, Certainty::Yes)); } - (Certainty::Yes, NestedNormalizationGoals(goals)) + ( + Certainty::Yes, + NestedNormalizationGoals( + goals.into_iter().map(|(s, g, _)| (s, g)).collect(), + ), + ) } _ => { let certainty = shallow_certainty.and(goals_certainty); @@ -156,12 +161,14 @@ where let external_constraints = self.compute_external_query_constraints(certainty, normalization_nested_goals); - let (var_values, mut external_constraints) = (self.var_values, external_constraints) - .fold_with(&mut EagerResolver::new(self.delegate)); - // Remove any trivial region constraints once we've resolved regions - external_constraints - .region_constraints - .retain(|outlives| outlives.0.as_region().is_none_or(|re| re != outlives.1)); + let (var_values, mut external_constraints) = + eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); + + // Remove any trivial or duplicated region constraints once we've resolved regions + let mut unique = HashSet::default(); + external_constraints.region_constraints.retain(|outlives| { + outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) + }); let canonical = Canonicalizer::canonicalize_response( self.delegate, @@ -269,7 +276,7 @@ where pub(super) fn instantiate_and_apply_query_response( &mut self, param_env: I::ParamEnv, - original_values: Vec, + original_values: &[I::GenericArg], response: CanonicalResponse, ) -> (NestedNormalizationGoals, Certainty) { let instantiation = Self::compute_query_response_instantiation_values( @@ -357,15 +364,15 @@ where } let var_values = delegate.cx().mk_args_from_iter( - response.variables.iter().enumerate().map(|(index, info)| { - if info.universe() != ty::UniverseIndex::ROOT { + response.variables.iter().enumerate().map(|(index, var_kind)| { + if var_kind.universe() != ty::UniverseIndex::ROOT { // A variable from inside a binder of the query. While ideally these shouldn't // exist at all (see the FIXME at the start of this method), we have to deal with // them for now. - delegate.instantiate_canonical_var_with_infer(info, span, |idx| { + delegate.instantiate_canonical_var_with_infer(var_kind, span, |idx| { prev_universe + idx.index() }) - } else if info.is_existential() { + } else if var_kind.is_existential() { // As an optimization we sometimes avoid creating a new inference variable here. // // All new inference variables we create start out in the current universe of the caller. @@ -376,12 +383,13 @@ where if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { v } else { - delegate.instantiate_canonical_var_with_infer(info, span, |_| prev_universe) + delegate + .instantiate_canonical_var_with_infer(var_kind, span, |_| prev_universe) } } else { // For placeholders which were already part of the input, we simply map this // universal bound variable back the placeholder of the input. - original_values[info.expect_placeholder_index()] + original_values[var_kind.expect_placeholder_index()] } }), ); @@ -465,7 +473,7 @@ where { let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; let state = inspect::State { var_values, data }; - let state = state.fold_with(&mut EagerResolver::new(delegate)); + let state = eager_resolve_vars(delegate, state); Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state) } 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 fc5dad9a3edf..38d7ff576a5e 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 @@ -22,8 +22,9 @@ use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::{ - CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, GoalSource, - HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, + CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalEvaluationKind, + GoalSource, GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, + QueryResult, }; pub(super) mod canonical; @@ -91,7 +92,7 @@ where /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. - variables: I::CanonicalVars, + variables: I::CanonicalVarKinds, /// What kind of goal we're currently computing, see the enum definition /// for more info. @@ -115,7 +116,7 @@ where pub(super) search_graph: &'a mut SearchGraph, - nested_goals: Vec<(GoalSource, Goal)>, + nested_goals: Vec<(GoalSource, Goal, Option>)>, pub(super) origin_span: I::Span, @@ -147,8 +148,9 @@ pub trait SolverDelegateEvalExt: SolverDelegate { goal: Goal::Predicate>, generate_proof_tree: GenerateProofTree, span: ::Span, + stalled_on: Option>, ) -> ( - Result<(HasChanged, Certainty), NoSolution>, + Result, NoSolution>, Option>, ); @@ -171,8 +173,12 @@ pub trait SolverDelegateEvalExt: SolverDelegate { &self, goal: Goal::Predicate>, generate_proof_tree: GenerateProofTree, + stalled_on: Option>, ) -> ( - Result<(NestedNormalizationGoals, HasChanged, Certainty), NoSolution>, + Result< + (NestedNormalizationGoals, GoalEvaluation), + NoSolution, + >, Option>, ); } @@ -188,9 +194,10 @@ where goal: Goal, generate_proof_tree: GenerateProofTree, span: I::Span, - ) -> (Result<(HasChanged, Certainty), NoSolution>, Option>) { + stalled_on: Option>, + ) -> (Result, NoSolution>, Option>) { EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, span, |ecx| { - ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) + ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, stalled_on) }) } @@ -201,7 +208,7 @@ where ) -> bool { self.probe(|| { EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, I::Span::dummy(), |ecx| { - ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) + ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, None) }) .0 }) @@ -213,8 +220,9 @@ where &self, goal: Goal, generate_proof_tree: GenerateProofTree, + stalled_on: Option>, ) -> ( - Result<(NestedNormalizationGoals, HasChanged, Certainty), NoSolution>, + Result<(NestedNormalizationGoals, GoalEvaluation), NoSolution>, Option>, ) { EvalCtxt::enter_root( @@ -222,7 +230,9 @@ where self.cx().recursion_limit(), generate_proof_tree, I::Span::dummy(), - |ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal), + |ecx| { + ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal, stalled_on) + }, ) } } @@ -447,11 +457,12 @@ where goal_evaluation_kind: GoalEvaluationKind, source: GoalSource, goal: Goal, - ) -> Result<(HasChanged, Certainty), NoSolution> { - let (normalization_nested_goals, has_changed, certainty) = - self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?; + stalled_on: Option>, + ) -> Result, NoSolution> { + let (normalization_nested_goals, goal_evaluation) = + self.evaluate_goal_raw(goal_evaluation_kind, source, goal, stalled_on)?; assert!(normalization_nested_goals.is_empty()); - Ok((has_changed, certainty)) + Ok(goal_evaluation) } /// Recursively evaluates `goal`, returning the nested goals in case @@ -466,7 +477,29 @@ where goal_evaluation_kind: GoalEvaluationKind, source: GoalSource, goal: Goal, - ) -> Result<(NestedNormalizationGoals, HasChanged, Certainty), NoSolution> { + stalled_on: Option>, + ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolution> { + // If we have run this goal before, and it was stalled, check that any of the goal's + // args have changed. Otherwise, we don't need to re-run the goal because it'll remain + // stalled, since it'll canonicalize the same way and evaluation is pure. + if let Some(stalled_on) = stalled_on { + if !stalled_on.stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) + && !self + .delegate + .opaque_types_storage_num_entries() + .needs_reevaluation(stalled_on.num_opaques) + { + return Ok(( + NestedNormalizationGoals::empty(), + GoalEvaluation { + certainty: Certainty::Maybe(stalled_on.stalled_cause), + has_changed: HasChanged::No, + stalled_on: Some(stalled_on), + }, + )); + } + } + let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); @@ -489,7 +522,7 @@ where if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; let (normalization_nested_goals, certainty) = - self.instantiate_and_apply_query_response(goal.param_env, orig_values, response); + self.instantiate_and_apply_query_response(goal.param_env, &orig_values, response); self.inspect.goal_evaluation(goal_evaluation); // FIXME: We previously had an assert here that checked that recomputing @@ -502,7 +535,42 @@ where // Once we have decided on how to handle trait-system-refactor-initiative#75, // we should re-add an assert here. - Ok((normalization_nested_goals, has_changed, certainty)) + let stalled_on = match certainty { + Certainty::Yes => None, + Certainty::Maybe(stalled_cause) => match has_changed { + // FIXME: We could recompute a *new* set of stalled variables by walking + // through the orig values, resolving, and computing the root vars of anything + // that is not resolved. Only when *these* have changed is it meaningful + // to recompute this goal. + HasChanged::Yes => None, + HasChanged::No => { + // Remove the unconstrained RHS arg, which is expected to have changed. + let mut stalled_vars = orig_values; + if let Some(normalizes_to) = goal.predicate.as_normalizes_to() { + let normalizes_to = normalizes_to.skip_binder(); + let rhs_arg: I::GenericArg = normalizes_to.term.into(); + let idx = stalled_vars + .iter() + .rposition(|arg| *arg == rhs_arg) + .expect("expected unconstrained arg"); + stalled_vars.swap_remove(idx); + } + + Some(GoalStalledOn { + num_opaques: canonical_goal + .canonical + .value + .predefined_opaques_in_body + .opaque_types + .len(), + stalled_vars, + stalled_cause, + }) + } + }, + }; + + Ok((normalization_nested_goals, GoalEvaluation { certainty, has_changed, stalled_on })) } fn compute_goal(&mut self, goal: Goal) -> QueryResult { @@ -602,7 +670,18 @@ where let cx = self.cx(); // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); - for (source, goal) in mem::take(&mut self.nested_goals) { + for (source, goal, stalled_on) in mem::take(&mut self.nested_goals) { + if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) { + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal, None)); + unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); + } + } + continue; + } + // We treat normalizes-to goals specially here. In each iteration we take the // RHS of the projection, replace it with a fresh inference variable, and only // after evaluating that goal do we equate the fresh inference variable with the @@ -622,11 +701,18 @@ where let unconstrained_goal = goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs }); - let (NestedNormalizationGoals(nested_goals), _, certainty) = - self.evaluate_goal_raw(GoalEvaluationKind::Nested, source, unconstrained_goal)?; + let ( + NestedNormalizationGoals(nested_goals), + GoalEvaluation { certainty, stalled_on, has_changed: _ }, + ) = self.evaluate_goal_raw( + GoalEvaluationKind::Nested, + source, + unconstrained_goal, + stalled_on, + )?; // Add the nested goals from normalization to our own nested goals. trace!(?nested_goals); - self.nested_goals.extend(nested_goals); + self.nested_goals.extend(nested_goals.into_iter().map(|(s, g)| (s, g, None))); // Finally, equate the goal's RHS with the unconstrained var. // @@ -652,6 +738,8 @@ where // looking at the "has changed" return from evaluate_goal, // because we expect the `unconstrained_rhs` part of the predicate // to have changed -- that means we actually normalized successfully! + // FIXME: Do we need to eagerly resolve here? Or should we check + // if the cache key has any changed vars? let with_resolved_vars = self.resolve_vars_if_possible(goal); if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { unchanged_certainty = None; @@ -660,13 +748,13 @@ where match certainty { Certainty::Yes => {} Certainty::Maybe(_) => { - self.nested_goals.push((source, with_resolved_vars)); + self.nested_goals.push((source, with_resolved_vars, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } } } else { - let (has_changed, certainty) = - self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; + let GoalEvaluation { certainty, has_changed, stalled_on } = + self.evaluate_goal(GoalEvaluationKind::Nested, source, goal, stalled_on)?; if has_changed == HasChanged::Yes { unchanged_certainty = None; } @@ -674,7 +762,7 @@ where match certainty { Certainty::Yes => {} Certainty::Maybe(_) => { - self.nested_goals.push((source, goal)); + self.nested_goals.push((source, goal, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } } @@ -698,7 +786,7 @@ where goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); - self.nested_goals.push((source, goal)); + self.nested_goals.push((source, goal, None)); } #[instrument(level = "trace", skip(self, goals))] @@ -732,8 +820,8 @@ where /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`. /// If `kind` is an integer inference variable this will still return a ty infer var. - pub(super) fn next_term_infer_of_kind(&mut self, kind: I::Term) -> I::Term { - match kind.kind() { + pub(super) fn next_term_infer_of_kind(&mut self, term: I::Term) -> I::Term { + match term.kind() { ty::TermKind::Ty(_) => self.next_ty_infer().into(), ty::TermKind::Const(_) => self.next_const_infer().into(), } @@ -840,6 +928,22 @@ where } } } + + fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result { + if p.has_non_region_infer() || p.has_placeholders() { + p.super_visit_with(self) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_clauses(&mut self, c: I::Clauses) -> Self::Result { + if c.has_non_region_infer() || c.has_placeholders() { + c.super_visit_with(self) + } else { + ControlFlow::Continue(()) + } + } } let mut visitor = ContainsTermOrNotNameable { @@ -1016,7 +1120,7 @@ where } pub(super) fn register_region_outlives(&self, a: I::Region, b: I::Region) { - // `b : a` ==> `a <= b` + // `'a: 'b` ==> `'b <= 'a` self.delegate.sub_regions(b, a, self.origin_span); } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 8173146e2fe2..a7a984181d7f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -21,6 +21,7 @@ mod project_goals; mod search_graph; mod trait_goals; +use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; use rustc_type_ir::{self as ty, Interner, TypingMode}; @@ -354,7 +355,7 @@ where fn response_no_constraints_raw( cx: I, max_universe: ty::UniverseIndex, - variables: I::CanonicalVars, + variables: I::CanonicalVarKinds, certainty: Certainty, ) -> CanonicalResponse { ty::Canonical { @@ -369,3 +370,21 @@ fn response_no_constraints_raw( }, } } + +/// The result of evaluating a goal. +pub struct GoalEvaluation { + pub certainty: Certainty, + pub has_changed: HasChanged, + /// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed + /// before rerunning it. + pub stalled_on: Option>, +} + +/// The conditions that must change for a goal to warrant +#[derive_where(Clone, Debug; I: Interner)] +pub struct GoalStalledOn { + pub num_opaques: usize, + pub stalled_vars: Vec, + /// The cause that will be returned on subsequent evaluations if this goal remains stalled. + pub stalled_cause: MaybeCause, +} diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 966d5422fbb7..7c4e1dc2c12e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -9,7 +9,7 @@ use rustc_type_ir::{ self as ty, Interner, Movability, TraitPredicate, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, }; -use tracing::{instrument, trace}; +use tracing::{debug, instrument, trace}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; @@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - NoSolution, ParamEnvSource, QueryResult, + NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints, }; impl assembly::GoalKind for TraitPredicate @@ -1253,6 +1253,45 @@ where D: SolverDelegate, I: Interner, { + /// FIXME(#57893): For backwards compatability with the old trait solver implementation, + /// we need to handle overlap between builtin and user-written impls for trait objects. + /// + /// This overlap is unsound in general and something which we intend to fix separately. + /// To avoid blocking the stabilization of the trait solver, we add this hack to avoid + /// breakage in cases which are *mostly fine*™. Importantly, this preference is strictly + /// weaker than the old behavior. + /// + /// We only prefer builtin over user-written impls if there are no inference constraints. + /// Importantly, we also only prefer the builtin impls for trait goals, and not during + /// normalization. This means the only case where this special-case results in exploitable + /// unsoundness should be lifetime dependent user-written impls. + pub(super) fn unsound_prefer_builtin_dyn_impl(&mut self, candidates: &mut Vec>) { + match self.typing_mode() { + TypingMode::Coherence => return, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => {} + } + + if candidates + .iter() + .find(|c| { + matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Object(_))) + }) + .is_some_and(|c| has_only_region_constraints(c.result)) + { + candidates.retain(|c| { + if matches!(c.source, CandidateSource::Impl(_)) { + debug!(?c, "unsoundly dropping impl in favor of builtin dyn-candidate"); + false + } else { + true + } + }); + } + } + #[instrument(level = "debug", skip(self), ret)] pub(super) fn merge_trait_candidates( &mut self, @@ -1313,6 +1352,7 @@ where } self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); + self.unsound_prefer_builtin_dyn_impl(&mut candidates); // If there are *only* global where bounds, then make sure to return that this // is still reported as being proven-via the param-env so that rigid projections diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index a6919afef12c..1f221b4bf78c 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -8,6 +8,30 @@ parse_array_brackets_instead_of_braces = this is a block expression, not an arra parse_array_index_offset_of = array indexing not supported in offset_of +parse_asm_expected_comma = expected token: `,` + .label = expected `,` + +parse_asm_expected_other = expected operand, {$is_inline_asm -> + [false] options + *[true] clobber_abi, options + }, or additional template string + +parse_asm_expected_register_class_or_explicit_register = expected register class or explicit register + +parse_asm_expected_string_literal = expected string literal + .label = not a string literal + +parse_asm_non_abi = at least one abi must be provided as an argument to `clobber_abi` + +parse_asm_requires_template = requires at least a template string argument + +parse_asm_sym_no_path = expected a path for argument to `sym` + +parse_asm_underscore_input = _ cannot be used for input operands + +parse_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 + parse_assignment_else_not_allowed = ... else {"{"} ... {"}"} is not allowed parse_associated_static_item_not_allowed = associated `static` items are not allowed diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 31a48b22cfee..2dba568a258a 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3525,3 +3525,73 @@ pub(crate) struct MoveSelfModifier { pub insertion_span: Span, pub modifier: String, } + +#[derive(Diagnostic)] +#[diag(parse_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(parse_asm_underscore_input)] +pub(crate) struct AsmUnderscoreInput { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_sym_no_path)] +pub(crate) struct AsmSymNoPath { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_requires_template)] +pub(crate) struct AsmRequiresTemplate { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_comma)] +pub(crate) struct AsmExpectedComma { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_other)] +pub(crate) struct AsmExpectedOther { + #[primary_span] + #[label(parse_asm_expected_other)] + pub(crate) span: Span, + pub(crate) is_inline_asm: bool, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_non_abi)] +pub(crate) struct NonABI { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_string_literal)] +pub(crate) struct AsmExpectedStringLiteral { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_register_class_or_explicit_register)] +pub(crate) struct ExpectedRegisterClassOrExplicitRegister { + #[primary_span] + pub(crate) span: Span, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 78c5742414b8..2845bbed1c0e 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -4,7 +4,7 @@ use diagnostics::make_unclosed_delims_error; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::util::unicode::contains_text_flow_control_chars; +use rustc_ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_control_chars}; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey}; use rustc_lexer::{ @@ -14,7 +14,7 @@ use rustc_literal_escaper::{EscapeError, Mode, unescape_mixed, unescape_unicode} use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, - TEXT_DIRECTION_CODEPOINT_IN_COMMENT, + TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TEXT_DIRECTION_CODEPOINT_IN_LITERAL, }; use rustc_session::parse::ParseSess; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; @@ -174,6 +174,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // Opening delimiter of the length 3 is not included into the symbol. let content_start = start + BytePos(3); let content = self.str_from(content_start); + self.lint_doc_comment_unicode_text_flow(start, content); self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style) } rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => { @@ -193,6 +194,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let content_start = start + BytePos(3); let content_end = self.pos - BytePos(if terminated { 2 } else { 0 }); let content = self.str_from_to(content_start, content_end); + self.lint_doc_comment_unicode_text_flow(start, content); self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style) } rustc_lexer::TokenKind::Frontmatter { has_invalid_preceding_whitespace, invalid_infostring } => { @@ -287,6 +289,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } else { None }; + self.lint_literal_unicode_text_flow(symbol, kind, self.mk_sp(start, self.pos), "literal"); token::Literal(token::Lit { kind, symbol, suffix }) } rustc_lexer::TokenKind::Lifetime { starts_with_number } => { @@ -481,6 +484,88 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } } + fn lint_doc_comment_unicode_text_flow(&mut self, start: BytePos, content: &str) { + if contains_text_flow_control_chars(content) { + self.report_text_direction_codepoint( + content, + self.mk_sp(start, self.pos), + 0, + false, + "doc comment", + ); + } + } + + fn lint_literal_unicode_text_flow( + &mut self, + text: Symbol, + lit_kind: token::LitKind, + span: Span, + label: &'static str, + ) { + if !contains_text_flow_control_chars(text.as_str()) { + return; + } + let (padding, point_at_inner_spans) = match lit_kind { + // account for `"` or `'` + token::LitKind::Str | token::LitKind::Char => (1, true), + // account for `c"` + token::LitKind::CStr => (2, true), + // account for `r###"` + token::LitKind::StrRaw(n) => (n as u32 + 2, true), + // account for `cr###"` + token::LitKind::CStrRaw(n) => (n as u32 + 3, true), + // suppress bad literals. + token::LitKind::Err(_) => return, + // Be conservative just in case new literals do support these. + _ => (0, false), + }; + self.report_text_direction_codepoint( + text.as_str(), + span, + padding, + point_at_inner_spans, + label, + ); + } + + fn report_text_direction_codepoint( + &self, + text: &str, + span: Span, + padding: u32, + point_at_inner_spans: bool, + label: &str, + ) { + // Obtain the `Span`s for each of the forbidden chars. + let spans: Vec<_> = text + .char_indices() + .filter_map(|(i, c)| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { + let lo = span.lo() + BytePos(i as u32 + padding); + (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + }) + }) + .collect(); + + let count = spans.len(); + let labels = point_at_inner_spans.then_some(spans.clone()); + + self.psess.buffer_lint( + TEXT_DIRECTION_CODEPOINT_IN_LITERAL, + span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::HiddenUnicodeCodepoints { + label: label.to_string(), + count, + span_label: span, + labels, + escape: point_at_inner_spans && !spans.is_empty(), + spans, + }, + ); + } + fn validate_frontmatter( &self, start: BytePos, diff --git a/compiler/rustc_parse/src/parser/asm.rs b/compiler/rustc_parse/src/parser/asm.rs new file mode 100644 index 000000000000..d4d0612a3179 --- /dev/null +++ b/compiler/rustc_parse/src/parser/asm.rs @@ -0,0 +1,385 @@ +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, AsmMacro}; +use rustc_span::{Span, Symbol, kw}; + +use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos}; +use crate::{PResult, Parser, errors, exp, token}; + +/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise +/// not validated at all. +pub struct AsmArg { + pub kind: AsmArgKind, + pub attributes: AsmAttrVec, + pub span: Span, +} + +pub enum AsmArgKind { + Template(P), + Operand(Option, ast::InlineAsmOperand), + Options(Vec), + ClobberAbi(Vec<(Symbol, Span)>), +} + +pub struct AsmOption { + pub symbol: Symbol, + pub span: Span, + // A bitset, with only the bit for this option's symbol set. + pub options: ast::InlineAsmOptions, + // Used when suggesting to remove an option. + pub span_with_comma: Span, +} + +/// A parsed list of attributes that is not attached to any item. +/// Used to check whether `asm!` arguments are configured out. +pub struct AsmAttrVec(pub ast::AttrVec); + +impl AsmAttrVec { + fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> { + let attrs = p.parse_outer_attributes()?; + + p.collect_tokens(None, attrs, ForceCollect::No, |_, attrs| { + Ok((Self(attrs), Trailing::No, UsePreAttrPos::No)) + }) + } +} +impl ast::HasAttrs for AsmAttrVec { + // Follows `ast::Expr`. + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + + fn attrs(&self) -> &[rustc_ast::Attribute] { + &self.0 + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) { + f(&mut self.0) + } +} + +impl ast::HasTokens for AsmAttrVec { + fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> { + None + } + + fn tokens_mut(&mut self) -> Option<&mut Option> { + None + } +} + +/// Used for better error messages when operand types are used that are not +/// supported by the current macro (e.g. `in` or `out` for `global_asm!`) +/// +/// returns +/// +/// - `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>, + exp: ExpKeywordPair, + asm_macro: AsmMacro, +) -> PResult<'a, bool> { + if matches!(asm_macro, AsmMacro::Asm) { + Ok(p.eat_keyword(exp)) + } else { + let span = p.token.span; + if p.eat_keyword_noexpect(exp.kw) { + // in gets printed as `r#in` otherwise + let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() }; + Err(p.dcx().create_err(errors::AsmUnsupportedOperand { + span, + symbol, + macro_name: asm_macro.macro_name(), + })) + } else { + Ok(false) + } + } +} + +fn parse_asm_operand<'a>( + p: &mut Parser<'a>, + asm_macro: AsmMacro, +) -> PResult<'a, Option> { + let dcx = p.dcx(); + + Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + ast::InlineAsmOperand::In { reg, expr } + } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: false } + } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: true } + } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: false } + } + } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: true } + } + } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } + } else if p.eat_keyword(exp!(Const)) { + let anon_const = p.parse_expr_anon_const()?; + ast::InlineAsmOperand::Const { anon_const } + } else if p.eat_keyword(exp!(Sym)) { + let expr = p.parse_expr()?; + let ast::ExprKind::Path(qself, path) = &expr.kind else { + let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); + return Err(err); + }; + let sym = + ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; + ast::InlineAsmOperand::Sym { sym } + } else { + return Ok(None); + })) +} + +// Public for rustfmt. +pub fn parse_asm_args<'a>( + p: &mut Parser<'a>, + sp: Span, + asm_macro: AsmMacro, +) -> PResult<'a, Vec> { + let dcx = p.dcx(); + + if p.token == token::Eof { + return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); + } + + let mut args = Vec::new(); + + let attributes = AsmAttrVec::parse(p)?; + let first_template = p.parse_expr()?; + args.push(AsmArg { + span: first_template.span, + kind: AsmArgKind::Template(first_template), + attributes, + }); + + let mut allow_templates = true; + + while p.token != token::Eof { + if !p.eat(exp!(Comma)) { + if allow_templates { + // After a template string, we always expect *only* a comma... + return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span })); + } else { + // ...after that delegate to `expect` to also include the other expected tokens. + return Err(p.expect(exp!(Comma)).err().unwrap()); + } + } + + // Accept trailing commas. + if p.token == token::Eof { + break; + } + + let attributes = AsmAttrVec::parse(p)?; + let span_start = p.token.span; + + // Parse `clobber_abi`. + if p.eat_keyword(exp!(ClobberAbi)) { + allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + span: span_start.to(p.prev_token.span), + attributes, + }); + + continue; + } + + // Parse `options`. + if p.eat_keyword(exp!(Options)) { + allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::Options(parse_options(p, asm_macro)?), + span: span_start.to(p.prev_token.span), + attributes, + }); + + continue; + } + + // Parse operand names. + let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { + let (ident, _) = p.token.ident().unwrap(); + p.bump(); + p.expect(exp!(Eq))?; + allow_templates = false; + Some(ident.name) + } else { + None + }; + + if let Some(op) = parse_asm_operand(p, asm_macro)? { + allow_templates = false; + + args.push(AsmArg { + span: span_start.to(p.prev_token.span), + kind: AsmArgKind::Operand(name, op), + attributes, + }); + } else if allow_templates { + let template = p.parse_expr()?; + // If it can't possibly expand to a string, provide diagnostics here to include other + // things it could have been. + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } + } + + args.push(AsmArg { + span: template.span, + kind: AsmArgKind::Template(template), + attributes, + }); + } else { + p.unexpected_any()? + } + } + + Ok(args) +} + +fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { + p.expect(exp!(OpenParen))?; + + let mut asm_options = Vec::new(); + + while !p.eat(exp!(CloseParen)) { + const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ + (exp!(Pure), ast::InlineAsmOptions::PURE), + (exp!(Nomem), ast::InlineAsmOptions::NOMEM), + (exp!(Readonly), ast::InlineAsmOptions::READONLY), + (exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS), + (exp!(Noreturn), ast::InlineAsmOptions::NORETURN), + (exp!(Nostack), ast::InlineAsmOptions::NOSTACK), + (exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND), + (exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX), + (exp!(Raw), ast::InlineAsmOptions::RAW), + ]; + + 'blk: { + for (exp, options) in OPTIONS { + // Gives a more accurate list of expected next tokens. + let kw_matched = if asm_macro.is_supported_option(options) { + p.eat_keyword(exp) + } else { + p.eat_keyword_noexpect(exp.kw) + }; + + if kw_matched { + let span = p.prev_token.span; + let span_with_comma = + if p.token == token::Comma { span.to(p.token.span) } else { span }; + + asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); + break 'blk; + } + } + + return p.unexpected_any(); + } + + // Allow trailing commas. + if p.eat(exp!(CloseParen)) { + break; + } + p.expect(exp!(Comma))?; + } + + Ok(asm_options) +} + +fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { + p.expect(exp!(OpenParen))?; + + if p.eat(exp!(CloseParen)) { + return Err(p.dcx().create_err(errors::NonABI { span: p.token.span })); + } + + let mut new_abis = Vec::new(); + while !p.eat(exp!(CloseParen)) { + match p.parse_str_lit() { + Ok(str_lit) => { + new_abis.push((str_lit.symbol_unescaped, str_lit.span)); + } + Err(opt_lit) => { + let span = opt_lit.map_or(p.token.span, |lit| lit.span); + return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span })); + } + }; + + // Allow trailing commas + if p.eat(exp!(CloseParen)) { + break; + } + p.expect(exp!(Comma))?; + } + + Ok(new_abis) +} + +fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { + p.expect(exp!(OpenParen))?; + let result = match p.token.uninterpolate().kind { + token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), + token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { + ast::InlineAsmRegOrRegClass::Reg(symbol) + } + _ => { + return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister { + span: p.token.span, + })); + } + }; + p.bump(); + p.expect(exp!(CloseParen))?; + Ok(result) +} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2a7910a6af4d..1a44f4af8a62 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3311,26 +3311,44 @@ impl<'a> Parser<'a> { let sm = this.psess.source_map(); if let Ok(expr_lines) = sm.span_to_lines(expr_span) && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span) - && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col && expr_lines.lines.len() == 2 { - // We check whether there's any trailing code in the parse span, - // if there isn't, we very likely have the following: - // - // X | &Y => "y" - // | -- - missing comma - // | | - // | arrow_span - // X | &X => "x" - // | - ^^ self.token.span - // | | - // | parsed until here as `"y" & X` - err.span_suggestion_short( - arm_start_span.shrink_to_hi(), - "missing a comma here to end this `match` arm", - ",", - Applicability::MachineApplicable, - ); + if arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col { + // We check whether there's any trailing code in the parse span, + // if there isn't, we very likely have the following: + // + // X | &Y => "y" + // | -- - missing comma + // | | + // | arrow_span + // X | &X => "x" + // | - ^^ self.token.span + // | | + // | parsed until here as `"y" & X` + err.span_suggestion_short( + arm_start_span.shrink_to_hi(), + "missing a comma here to end this `match` arm", + ",", + Applicability::MachineApplicable, + ); + } else if arm_start_lines.lines[0].end_col + rustc_span::CharPos(1) + == expr_lines.lines[0].end_col + { + // similar to the above, but we may typo a `.` or `/` at the end of the line + let comma_span = arm_start_span + .shrink_to_hi() + .with_hi(arm_start_span.hi() + rustc_span::BytePos(1)); + if let Ok(res) = sm.span_to_snippet(comma_span) + && (res == "." || res == "/") + { + err.span_suggestion_short( + comma_span, + "you might have meant to write a `,` to end this `match` arm", + ",", + Applicability::MachineApplicable, + ); + } + } } } else { err.span_label( diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index babc55ccc0f9..c7b0eb11e5a0 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1577,7 +1577,7 @@ impl<'a> Parser<'a> { }; let enum_definition = EnumDef { variants: variants.into_iter().flatten().collect() }; - Ok(ItemKind::Enum(ident, enum_definition, generics)) + Ok(ItemKind::Enum(ident, generics, enum_definition)) } fn parse_enum_variant(&mut self, span: Span) -> PResult<'a, Option> { @@ -1732,7 +1732,7 @@ impl<'a> Parser<'a> { return Err(self.dcx().create_err(err)); }; - Ok(ItemKind::Struct(ident, vdata, generics)) + Ok(ItemKind::Struct(ident, generics, vdata)) } /// Parses `union Foo { ... }`. @@ -1764,7 +1764,7 @@ impl<'a> Parser<'a> { return Err(err); }; - Ok(ItemKind::Union(ident, vdata, generics)) + Ok(ItemKind::Union(ident, generics, vdata)) } /// This function parses the fields of record structs: diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 968376678f3b..b2e902513672 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1,3 +1,4 @@ +pub mod asm; pub mod attr; mod attr_wrapper; mod diagnostics; diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 396ded96bde1..ccc3410674b4 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -515,8 +515,8 @@ impl<'a> Parser<'a> { fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> { let prev = self.prev_token.span; let sp = self.token.span; - let mut e = self.dcx().struct_span_err(sp, msg); - self.label_expected_raw_ref(&mut e); + let mut err = self.dcx().struct_span_err(sp, msg); + self.label_expected_raw_ref(&mut err); let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon @@ -558,20 +558,19 @@ impl<'a> Parser<'a> { stmt.span }; self.suggest_fixes_misparsed_for_loop_head( - &mut e, + &mut err, prev.between(sp), stmt_span, &stmt.kind, ); } Err(e) => { - self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); - e.cancel(); + e.delay_as_bug(); } _ => {} } - e.span_label(sp, "expected `{`"); - e + err.span_label(sp, "expected `{`"); + err } fn suggest_fixes_misparsed_for_loop_head( diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index ba81ef3103bd..b91674890765 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -9,7 +9,7 @@ rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c68f8df49fc7..0777d442b594 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; -use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr}; +use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; @@ -35,7 +35,7 @@ use rustc_session::lint::builtin::{ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; -use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, kw, sym}; +use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; @@ -255,7 +255,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_generic_attr(hir_id, attr, target, Target::Fn); self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) } - [sym::autodiff, ..] => { + [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } [sym::coroutine, ..] => { @@ -802,7 +802,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct => { if let Some(ItemLike::Item(hir::Item { - kind: hir::ItemKind::Struct(_, hir::VariantData::Struct { fields, .. }, _), + kind: hir::ItemKind::Struct(_, _, hir::VariantData::Struct { fields, .. }), .. })) = item && !fields.is_empty() @@ -936,7 +936,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); let attr_str = &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); - if doc_alias == kw::Empty { + if doc_alias == sym::empty { tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str }); return; } @@ -1068,7 +1068,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } let doc_keyword = match meta.value_str() { - Some(value) if value != kw::Empty => value, + Some(value) if value != sym::empty => value, _ => return self.doc_attr_str_error(meta, "keyword"), }; @@ -1130,12 +1130,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; }; match item.kind { - ItemKind::Enum(_, _, generics) | ItemKind::Struct(_, _, generics) + ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _) if generics.params.len() != 0 => {} ItemKind::Trait(_, _, _, generics, _, items) if generics.params.len() != 0 || items.iter().any(|item| matches!(item.kind, AssocItemKind::Type)) => {} - ItemKind::TyAlias(_, _, generics) if generics.params.len() != 0 => {} + ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {} _ => { self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() }); } @@ -2792,7 +2792,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { } fn is_c_like_enum(item: &Item<'_>) -> bool { - if let ItemKind::Enum(_, ref def, _) = item.kind { + if let ItemKind::Enum(_, _, ref def) = item.kind { for variant in def.variants { match variant.data { hir::VariantData::Unit(..) => { /* continue */ } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 0060e726a8e0..6b82252f32c2 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -8,12 +8,13 @@ use std::mem; use hir::ItemKind; use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::UnordSet; use rustc_errors::MultiSpan; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatKind, QPath, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -44,15 +45,20 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::Def(def_kind, def_id) = path.res - && def_id.is_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) - { - tcx.visibility(def_id).is_public() - } else { - true +/// Returns the local def id of the ADT if the given ty refers to a local one. +fn local_adt_def_of_ty<'tcx>(ty: &hir::Ty<'tcx>) -> Option { + match ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => { + if let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() + && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) + { + Some(local_def_id) + } else { + None + } + } + _ => None, } } @@ -78,7 +84,7 @@ struct MarkSymbolVisitor<'tcx> { // maps from ADTs to ignored derived traits (e.g. Debug and Clone) // and the span of their respective impl (i.e., part of the derive // macro) - ignored_derived_traits: LocalDefIdMap>, + ignored_derived_traits: LocalDefIdMap>, } impl<'tcx> MarkSymbolVisitor<'tcx> { @@ -360,7 +366,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let Some(fn_sig) = self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) - && let TyKind::Path(hir::QPath::Resolved(_, path)) = + && let TyKind::Path(QPath::Resolved(_, path)) = self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind && let Res::Def(def_kind, did) = path.res { @@ -388,7 +394,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.ignored_derived_traits .entry(adt_def_id) .or_default() - .push((trait_of, impl_of)); + .insert((trait_of, impl_of)); } return true; } @@ -420,51 +426,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(..) => { - for &impl_def_id in self.tcx.local_trait_impls(item.owner_id.def_id) { - if let ItemKind::Impl(impl_ref) = self.tcx.hir_expect_item(impl_def_id).kind - { - // skip items - // mark dependent traits live - intravisit::walk_generics(self, impl_ref.generics); - // mark dependent parameters live - intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); + hir::ItemKind::Trait(.., trait_item_refs) => { + // mark assoc ty live if the trait is live + for trait_item in trait_item_refs { + if matches!(trait_item.kind, hir::AssocItemKind::Type) { + self.check_def_id(trait_item.id.owner_id.to_def_id()); } } - intravisit::walk_item(self, item) } _ => intravisit::walk_item(self, item), }, Node::TraitItem(trait_item) => { - // mark corresponding ImplTerm live + // mark the trait live let trait_item_id = trait_item.owner_id.to_def_id(); if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) { - // mark the trait live self.check_def_id(trait_id); - - for impl_id in self.tcx.all_impls(trait_id) { - if let Some(local_impl_id) = impl_id.as_local() - && let ItemKind::Impl(impl_ref) = - self.tcx.hir_expect_item(local_impl_id).kind - { - if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) - && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - { - // skip methods of private ty, - // they would be solved in `solve_rest_impl_items` - continue; - } - - // mark self_ty live - intravisit::walk_unambig_ty(self, impl_ref.self_ty); - if let Some(&impl_item_id) = - self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id) - { - self.check_def_id(impl_item_id); - } - } - } } intravisit::walk_trait_item(self, trait_item); } @@ -508,48 +485,58 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } - fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) { - let mut ready; - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) - }); - - while !ready.is_empty() { - self.worklist = - ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); - self.mark_live_symbols(); - - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) - }); + /// Returns whether `local_def_id` is potentially alive or not. + /// `local_def_id` points to an impl or an impl item, + /// both impl and impl item that may be passed to this function are of a trait, + /// and added into the unsolved_items during `create_and_seed_worklist` + fn check_impl_or_impl_item_live( + &mut self, + impl_id: hir::ItemId, + local_def_id: LocalDefId, + ) -> bool { + if self.should_ignore_item(local_def_id.to_def_id()) { + return false; } - } - fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir_item(impl_id).expect_impl().self_ty.kind - && let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) - { - if self.tcx.visibility(impl_item_id).is_public() { - // for the public method, we don't know the trait item is used or not, - // so we mark the method live if the self is used - return self.live_symbols.contains(&local_def_id); - } + let trait_def_id = match self.tcx.def_kind(local_def_id) { + // assoc impl items of traits are live if the corresponding trait items are live + DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id, + // impl items are live if the corresponding traits are live + DefKind::Impl { of_trait: true } => self + .tcx + .impl_trait_ref(impl_id.owner_id.def_id) + .and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)), + _ => None, + }; - if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id - && let Some(local_id) = trait_item_id.as_local() + if let Some(trait_def_id) = trait_def_id { + if let Some(trait_def_id) = trait_def_id.as_local() + && !self.live_symbols.contains(&trait_def_id) { - // for the private method, we can know the trait item is used or not, - // so we mark the method live if the self is used and the trait item is used - return self.live_symbols.contains(&local_id) - && self.live_symbols.contains(&local_def_id); + return false; + } + + // FIXME: legacy logic to check whether the function may construct `Self`, + // this can be removed after supporting marking ADTs appearing in patterns + // as live, then we can check private impls of public traits directly + if let Some(fn_sig) = + self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) + && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) + && self.tcx.visibility(trait_def_id).is_public() + { + return true; } } - false + + // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used. + if let Some(local_def_id) = + local_adt_def_of_ty(self.tcx.hir_item(impl_id).expect_impl().self_ty) + && !self.live_symbols.contains(&local_def_id) + { + return false; + } + + true } } @@ -584,7 +571,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { - hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { + hir::ExprKind::Path(ref qpath @ QPath::TypeRelative(..)) => { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); self.handle_res(res); } @@ -707,7 +694,7 @@ fn has_allow_dead_code_or_lang_attr( // #[used], #[no_mangle], #[export_name], etc also keeps the item alive // forcefully, e.g., for placing it in a specific section. cg_attrs.contains_extern_indicator() - || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) + || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) } } @@ -738,7 +725,7 @@ fn check_item<'tcx>( tcx: TyCtxt<'tcx>, worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, struct_constructors: &mut LocalDefIdMap, - unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>, + unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>, id: hir::ItemId, ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id); @@ -749,7 +736,7 @@ fn check_item<'tcx>( match tcx.def_kind(id.owner_id) { DefKind::Enum => { let item = tcx.hir_item(id); - if let hir::ItemKind::Enum(_, ref enum_def, _) = item.kind { + if let hir::ItemKind::Enum(_, _, ref enum_def) = item.kind { if let Some(comes_from_allow) = allow_dead_code { worklist.extend( enum_def.variants.iter().map(|variant| (variant.def_id, comes_from_allow)), @@ -764,47 +751,39 @@ fn check_item<'tcx>( } } DefKind::Impl { of_trait } => { - // get DefIds from another query - let local_def_ids = tcx - .associated_item_def_ids(id.owner_id) - .iter() - .filter_map(|def_id| def_id.as_local()); + if let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id) + { + worklist.push((id.owner_id.def_id, comes_from_allow)); + } else if of_trait { + unsolved_items.push((id, id.owner_id.def_id)); + } - let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir_item(id).expect_impl().self_ty); + for def_id in tcx.associated_item_def_ids(id.owner_id) { + let local_def_id = def_id.expect_local(); - // And we access the Map here to get HirId from LocalDefId - for local_def_id in local_def_ids { - // check the function may construct Self - let mut may_construct_self = false; - if let Some(fn_sig) = - tcx.hir_fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id)) - { - may_construct_self = - matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None); - } - - // for trait impl blocks, - // mark the method live if the self_ty is public, - // or the method is public and may construct self - if of_trait - && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) - || tcx.visibility(local_def_id).is_public() - && (ty_is_pub || may_construct_self)) - { - worklist.push((local_def_id, ComesFromAllowExpect::No)); - } else if let Some(comes_from_allow) = - has_allow_dead_code_or_lang_attr(tcx, local_def_id) + if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); } else if of_trait { - // private method || public method not constructs self - unsolved_impl_items.push((id, local_def_id)); + // FIXME: This condition can be removed + // if we support dead check for assoc consts and tys. + if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) { + worklist.push((local_def_id, ComesFromAllowExpect::No)); + } else { + // We only care about associated items of traits, + // because they cannot be visited directly, + // so we later mark them as live if their corresponding traits + // or trait items and self types are both live, + // but inherent associated items can be visited and marked directly. + unsolved_items.push((id, local_def_id)); + } } } } DefKind::Struct => { let item = tcx.hir_item(id); - if let hir::ItemKind::Struct(_, ref variant_data, _) = item.kind + if let hir::ItemKind::Struct(_, _, ref variant_data) = item.kind && let Some(ctor_def_id) = variant_data.ctor_def_id() { struct_constructors.insert(ctor_def_id, item.owner_id.def_id); @@ -892,8 +871,8 @@ fn create_and_seed_worklist( fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), -) -> (LocalDefIdSet, LocalDefIdMap>) { - let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx); +) -> (LocalDefIdSet, LocalDefIdMap>) { + let (worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -907,7 +886,22 @@ fn live_symbols_and_ignored_derived_traits( ignored_derived_traits: Default::default(), }; symbol_visitor.mark_live_symbols(); - symbol_visitor.solve_rest_impl_items(unsolved_impl_items); + let mut items_to_check; + (items_to_check, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id) + }); + + while !items_to_check.is_empty() { + symbol_visitor.worklist = + items_to_check.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); + symbol_visitor.mark_live_symbols(); + + (items_to_check, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id) + }); + } (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) } @@ -921,7 +915,7 @@ struct DeadItem { struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, live_symbols: &'tcx LocalDefIdSet, - ignored_derived_traits: &'tcx LocalDefIdMap>, + ignored_derived_traits: &'tcx LocalDefIdMap>, } enum ShouldWarnAboutField { @@ -1061,7 +1055,7 @@ impl<'tcx> DeadVisitor<'tcx> { let tuple_fields = if let Some(parent_id) = parent_item && let node = tcx.hir_node_by_def_id(parent_id) && let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, hir::VariantData::Tuple(fields, _, _), _), + kind: hir::ItemKind::Struct(_, _, hir::VariantData::Tuple(fields, _, _)), .. }) = node { @@ -1188,19 +1182,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); - // if we have diagnosed the trait, do not diagnose unused methods - if matches!(def_kind, DefKind::Impl { .. }) + // Only diagnose unused assoc items in inherient impl and used trait, + // for unused assoc items in impls of trait, + // we have diagnosed them in the trait if they are unused, + // for unused assoc items in unused trait, + // we have diagnosed the unused trait. + if matches!(def_kind, DefKind::Impl { of_trait: false }) || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused methods in traits - if matches!(def_kind, DefKind::Impl { of_trait: true }) - && tcx.def_kind(def_id) == DefKind::AssocFn - || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn - { - continue; - } - if let Some(local_def_id) = def_id.as_local() && !visitor.is_live_code(local_def_id) { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 718154481722..6852153a2e76 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -426,10 +426,16 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_fn(self, fk, fd, b, id) } - fn visit_use(&mut self, p: &'v hir::UsePath<'v>, hir_id: HirId) { + fn visit_use(&mut self, p: &'v hir::UsePath<'v>, _hir_id: HirId) { // This is `visit_use`, but the type is `Path` so record it that way. self.record("Path", None, p); - hir_visit::walk_use(self, p, hir_id) + // Don't call `hir_visit::walk_use(self, p, hir_id)`: it calls + // `visit_path` up to three times, once for each namespace result in + // `p.res`, by building temporary `Path`s that are not part of the real + // HIR, which causes `p` to be double- or triple-counted. Instead just + // walk the path internals (i.e. the segments) directly. + let hir::Path { span: _, res: _, segments } = *p; + ast_visit::walk_list!(self, visit_path_segment, segments); } fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) { diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 7353c1ead5a7..b3e6ee9512c8 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -4,7 +4,7 @@ //! but are not declared in one single location (unlike lang features), which means we need to //! collect them instead. -use rustc_attr_parsing::{AttributeKind, StabilityLevel, StableSince}; +use rustc_attr_data_structures::{AttributeKind, StabilityLevel, StableSince}; use rustc_hir::Attribute; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 4e9b7fd44d4b..763d9fda8049 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -122,7 +122,6 @@ enum LiveNodeKind { VarDefNode(Span, HirId), ClosureNode, ExitNode, - ErrNode, } fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { @@ -133,7 +132,6 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { VarDefNode(s, _) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)), ClosureNode => "Closure node".to_owned(), ExitNode => "Exit node".to_owned(), - ErrNode => "Error node".to_owned(), } } @@ -492,6 +490,9 @@ struct Liveness<'a, 'tcx> { impl<'a, 'tcx> Liveness<'a, 'tcx> { fn new(ir: &'a mut IrMaps<'tcx>, body_owner: LocalDefId) -> Liveness<'a, 'tcx> { let typeck_results = ir.tcx.typeck(body_owner); + // Liveness linting runs after building the THIR. We make several assumptions based on + // typeck succeeding, e.g. that breaks and continues are well-formed. + assert!(typeck_results.tainted_by_errors.is_none()); // FIXME(#132279): we're in a body here. let typing_env = ty::TypingEnv::non_body_analysis(ir.tcx, body_owner); let closure_min_captures = typeck_results.closure_min_captures.get(&body_owner); @@ -976,8 +977,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Now that we know the label we're going to, // look it up in the continue loop nodes table self.cont_ln.get(&sc).cloned().unwrap_or_else(|| { - self.ir.tcx.dcx().span_delayed_bug(expr.span, "continue to unknown label"); - self.ir.add_live_node(ErrNode) + // Liveness linting happens after building the THIR. Bad labels should already + // have been caught. + span_bug!(expr.span, "continue to unknown label"); }) } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index f0e8fa986fea..7e15267a953b 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -427,7 +427,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. - || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED) + || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index d7baad69c786..45e26c8999a9 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -5,9 +5,9 @@ use std::mem::replace; use std::num::NonZero; use rustc_ast_lowering::stability::extern_abi_stability; -use rustc_attr_parsing::{ - self as attr, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, Stability, - StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr, +use rustc_attr_data_structures::{ + self as attrs, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, + Stability, StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr, }; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; @@ -121,7 +121,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs); - let depr = attr::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span)); + let depr = attrs::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span)); let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect); let mut is_deprecated = false; @@ -174,9 +174,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } // # Regular and body stability - let stab = attr::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span)); + let stab = attrs::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span)); let body_stab = - attr::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability); + attrs::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability); if let Some((depr, span)) = &depr && depr.is_since_rustc_version() @@ -206,7 +206,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // this is *almost surely* an accident. if let ( &Some(DeprecatedSince::RustcVersion(dep_since)), - &attr::StabilityLevel::Stable { since: stab_since, .. }, + &attrs::StabilityLevel::Stable { since: stab_since, .. }, ) = (&depr.as_ref().map(|(d, _)| d.since), &stab.level) { match stab_since { @@ -263,7 +263,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // # Const stability - let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span)); + let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span)); // If the current node is a function with const stability attributes (directly given or // implied), check if the function/method is const or the parent impl block is const. @@ -430,7 +430,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { kind = AnnotationKind::DeprecationProhibited; const_stab_inherit = InheritConstStability::Yes; } - hir::ItemKind::Struct(_, ref sd, _) => { + hir::ItemKind::Struct(_, _, ref sd) => { if let Some(ctor_def_id) = sd.ctor_def_id() { self.annotate( ctor_def_id, @@ -713,7 +713,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { // by default and are unable to be used. if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { let stability = Stability { - level: attr::StabilityLevel::Unstable { + level: attrs::StabilityLevel::Unstable { reason: UnstableReason::Default, issue: NonZero::new(27812), is_soft: false, @@ -796,17 +796,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir_attrs(item.hir_id()); - let stab = attr::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span)); + let stab = attrs::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span)); // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem - let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); + let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 if let Some(( - Stability { level: attr::StabilityLevel::Unstable { .. }, .. }, + Stability { level: attrs::StabilityLevel::Unstable { .. }, .. }, span, )) = stab { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index a89d01dcbbe4..46ced7500ea2 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -1,6 +1,5 @@ use std::fmt; use std::iter::once; -use std::ops::ControlFlow; use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx}; use rustc_arena::DroplessArena; @@ -12,8 +11,7 @@ use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, VariantDef, + self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -137,22 +135,11 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// Returns the hidden type corresponding to this key if the body under analysis is allowed to /// know it. fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option> { - if let Some(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key.def_id) { - let ty = ty::EarlyBinder::bind(hidden_ty.ty).instantiate(self.tcx, key.args); - if ty.visit_with(&mut RecursiveOpaque { def_id: key.def_id.into() }).is_continue() { - Some(ty) - } else { - // HACK: We skip revealing opaque types which recursively expand - // to themselves. This is because we may infer hidden types like - // `Opaque = Opaque>` or `Opaque = Opaque<(T,)>` - // in hir typeck. - None - } - } else { - None - } + self.typeck_results + .concrete_opaque_types + .get(&key.def_id) + .map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args)) } - // This can take a non-revealed `Ty` because it reveals opaques itself. pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { !ty.inhabited_predicate(self.tcx).apply_revealing_opaque( @@ -262,12 +249,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)), _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"), }, - Slice(slice) => match *ty.kind() { - ty::Slice(ty) | ty::Array(ty, _) => { + Slice(slice) => match ty.builtin_index() { + Some(ty) => { let arity = slice.arity(); reveal_and_alloc(cx, (0..arity).map(|_| ty)) } - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), + None => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())), Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) @@ -1177,20 +1164,3 @@ fn detect_mixed_deref_pat_ctors<'p, 'tcx>( } Ok(()) } - -struct RecursiveOpaque { - def_id: DefId, -} -impl<'tcx> TypeVisitor> for RecursiveOpaque { - type Result = ControlFlow<()>; - - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - if let ty::Alias(ty::Opaque, alias_ty) = t.kind() { - if alias_ty.def_id == self.def_id { - return ControlFlow::Break(()); - } - } - - if t.has_opaque_types() { t.super_visit_with(self) } else { ControlFlow::Continue(()) } - } -} diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index 242c67d732af..109a7bf49fe6 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9c2921cd5b22..963f4c77d809 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -20,7 +20,6 @@ use errors::{ }; use rustc_ast::MacroDef; use rustc_ast::visit::{VisitorResult, try_visit}; -use rustc_attr_parsing::AttributeKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; use rustc_errors::{MultiSpan, listify}; @@ -40,7 +39,7 @@ use rustc_session::lint; use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; -use {rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_attr_data_structures as attrs, rustc_hir as hir}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -497,7 +496,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.tcx.hir_attrs(hir_id); - if attr::find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) + if attrs::find_attr!(attrs, attrs::AttributeKind::MacroTransparency(x) => *x) .unwrap_or(Transparency::fallback(md.macro_rules)) != Transparency::Opaque { @@ -600,8 +599,8 @@ impl<'tcx> EmbargoVisitor<'tcx> { DefKind::Struct | DefKind::Union => { // While structs and unions have type privacy, their fields do not. let item = self.tcx.hir_expect_item(def_id); - if let hir::ItemKind::Struct(_, ref struct_def, _) - | hir::ItemKind::Union(_, ref struct_def, _) = item.kind + if let hir::ItemKind::Struct(_, _, ref struct_def) + | hir::ItemKind::Union(_, _, ref struct_def) = item.kind { for field in struct_def.fields() { let field_vis = self.tcx.local_visibility(field.def_id); @@ -726,7 +725,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } } } - hir::ItemKind::Enum(_, ref def, _) => { + hir::ItemKind::Enum(_, _, ref def) => { if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates(); } @@ -764,8 +763,8 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } } } - hir::ItemKind::Struct(_, ref struct_def, _) - | hir::ItemKind::Union(_, ref struct_def, _) => { + hir::ItemKind::Struct(_, _, ref struct_def) + | hir::ItemKind::Union(_, _, ref struct_def) => { if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates(); for field in struct_def.fields() { @@ -869,7 +868,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TestReachabilityVisitor<'a, 'tcx> { self.effective_visibility_diagnostic(item.owner_id.def_id); match item.kind { - hir::ItemKind::Enum(_, ref def, _) => { + hir::ItemKind::Enum(_, _, ref def) => { for variant in def.variants.iter() { self.effective_visibility_diagnostic(variant.def_id); if let Some(ctor_def_id) = variant.data.ctor_def_id() { @@ -880,7 +879,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TestReachabilityVisitor<'a, 'tcx> { } } } - hir::ItemKind::Struct(_, ref def, _) | hir::ItemKind::Union(_, ref def, _) => { + hir::ItemKind::Struct(_, _, ref def) | hir::ItemKind::Union(_, _, ref def) => { if let Some(ctor_def_id) = def.ctor_def_id() { self.effective_visibility_diagnostic(ctor_def_id); } @@ -1652,7 +1651,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { } DefKind::Enum => { let item = tcx.hir_item(id); - if let hir::ItemKind::Enum(_, ref def, _) = item.kind { + if let hir::ItemKind::Enum(_, _, ref def) = item.kind { self.check_unnameable(item.owner_id.def_id, effective_vis); self.check(item.owner_id.def_id, item_visibility, effective_vis) @@ -1690,8 +1689,8 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { // Subitems of structs and unions have their own publicity. DefKind::Struct | DefKind::Union => { let item = tcx.hir_item(id); - if let hir::ItemKind::Struct(_, ref struct_def, _) - | hir::ItemKind::Union(_, ref struct_def, _) = item.kind + if let hir::ItemKind::Struct(_, _, ref struct_def) + | hir::ItemKind::Union(_, _, ref struct_def) = item.kind { self.check_unnameable(item.owner_id.def_id, effective_vis); self.check(item.owner_id.def_id, item_visibility, effective_vis) diff --git a/compiler/rustc_proc_macro/Cargo.toml b/compiler/rustc_proc_macro/Cargo.toml new file mode 100644 index 000000000000..4a7c0d78ede8 --- /dev/null +++ b/compiler/rustc_proc_macro/Cargo.toml @@ -0,0 +1,21 @@ +# We need to use a separate crate including library/proc_macro as opposed to a +# direct path dependency on library/proc_macro because doing the latter will +# cause two copies of libproc_macro.rlib to end up in the sysroot, breaking +# proc-macro crates. In addition it confuses the workspace_members function of +# bootstrap. + +[package] +name = "rustc_proc_macro" +version = "0.0.0" +edition = "2024" + +[lib] +path = "../../library/proc_macro/src/lib.rs" +test = false +doctest = false + +[dependencies] +rustc-literal-escaper = "0.0.2" + +[features] +rustc-dep-of-std = [] diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 3ae56cef2c42..d4217e0aa549 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -1433,6 +1433,8 @@ fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepN && let Some(nodes) = &data.current.nodes_in_current_session { // Try to find it among the nodes allocated so far in this session + // This is OK, there's only ever one node result possible so this is deterministic. + #[allow(rustc::potential_query_instability)] if let Some((node, _)) = nodes.lock().iter().find(|&(_, index)| *index == dep_node_index) { dep_node = Some(*node); } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index f1b609a3ca90..79b99c52d0c0 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -784,6 +784,8 @@ impl EncoderState { ) { if let Some(record_stats) = &self.stats { let record_stats = record_stats.lock(); + // `stats` is sorted below so we can allow this lint here. + #[allow(rustc::potential_query_instability)] let mut stats: Vec<_> = record_stats.values().collect(); stats.sort_by_key(|s| -(s.node_counter as i64)); diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index eba7378b475e..d36cb6f0e5b1 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,5 +1,5 @@ // tidy-alphabetical-start -#![allow(rustc::potential_query_instability, internal_features)] +#![allow(internal_features)] #![feature(assert_matches)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 6321abc5087f..1e79bd461d2a 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -510,6 +510,10 @@ pub fn break_query_cycles( registry: &rayon_core::Registry, ) { let mut wakelist = Vec::new(); + // It is OK per the comments: + // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 + // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392 + #[allow(rustc::potential_query_instability)] let mut jobs: Vec = query_map.keys().cloned().collect(); let mut found_cycle = false; diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 3460c53782f3..c30ed781f35f 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -823,7 +823,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } // These items live in both the type and value namespaces. - ItemKind::Struct(ident, ref vdata, _) => { + ItemKind::Struct(ident, _, ref vdata) => { self.build_reduced_graph_for_struct_variant( vdata.fields(), ident, @@ -874,7 +874,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } } - ItemKind::Union(ident, ref vdata, _) => { + ItemKind::Union(ident, _, ref vdata) => { self.build_reduced_graph_for_struct_variant( vdata.fields(), ident, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index e97233e97ce5..0579e91c0d6e 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -154,6 +154,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { extern_crate.id, span, BuiltinLintDiag::UnusedExternCrate { + span: extern_crate.span, removal_span: extern_crate.span_with_attributes, }, ); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 13dfb59f27fc..25485be56226 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -162,8 +162,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { self.with_parent(def_id, |this| { this.with_impl_trait(ImplTraitContext::Existential, |this| { match i.kind { - ItemKind::Struct(_, ref struct_def, _) - | ItemKind::Union(_, ref struct_def, _) => { + ItemKind::Struct(_, _, ref struct_def) + | ItemKind::Union(_, _, ref struct_def) => { // If this is a unit or tuple-like struct, register the constructor. if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(struct_def) { this.create_def( diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d09750fa281b..201b1c0a493b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_attr_data_structures::{self as attr, Stability}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{ @@ -2623,7 +2623,53 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; for &StrippedCfgItem { parent_module, ident, ref cfg } in symbols { - if parent_module != module || ident.name != *segment { + if ident.name != *segment { + continue; + } + + fn comes_from_same_module_for_glob( + r: &Resolver<'_, '_>, + parent_module: DefId, + module: DefId, + visited: &mut FxHashMap, + ) -> bool { + if let Some(&cached) = visited.get(&parent_module) { + // this branch is prevent from being called recursively infinity, + // because there has some cycles in globs imports, + // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` + return cached; + } + visited.insert(parent_module, false); + let res = r.module_map.get(&parent_module).is_some_and(|m| { + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() + { + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) + { + return true; + } + } + } + false + }); + visited.insert(parent_module, res); + res + } + + let comes_from_same_module = parent_module == module + || comes_from_same_module_for_glob( + self, + parent_module, + module, + &mut Default::default(), + ); + if !comes_from_same_module { continue; } diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index a5ca4565d7b4..5de80de3f8d3 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -252,7 +252,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> self.current_private_vis = prev_private_vis; } - ast::ItemKind::Enum(_, EnumDef { ref variants }, _) => { + ast::ItemKind::Enum(_, _, EnumDef { ref variants }) => { self.set_bindings_effective_visibilities(def_id); for variant in variants { let variant_def_id = self.r.local_def_id(variant.id); @@ -262,7 +262,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> } } - ast::ItemKind::Struct(_, ref def, _) | ast::ItemKind::Union(_, ref def, _) => { + ast::ItemKind::Struct(_, _, ref def) | ast::ItemKind::Union(_, _, ref def) => { for field in def.fields() { self.update_field(self.r.local_def_id(field.id), def_id); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1b682d0cf8ae..744e99c86e1a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -415,7 +415,7 @@ pub(crate) enum AliasPossibility { } #[derive(Copy, Clone, Debug)] -pub(crate) enum PathSource<'a> { +pub(crate) enum PathSource<'a, 'c> { /// Type paths `Path`. Type, /// Trait paths in bounds or impls. @@ -429,7 +429,10 @@ pub(crate) enum PathSource<'a> { /// Paths in tuple struct patterns `Path(..)`. TupleStruct(Span, &'a [Span]), /// `m::A::B` in `::B::C`. - TraitItem(Namespace), + /// + /// Second field holds the "cause" of this one, i.e. the context within + /// which the trait item is resolved. Used for diagnostics. + TraitItem(Namespace, &'c PathSource<'a, 'c>), /// Paths in delegation item Delegation, /// An arg in a `use<'a, N>` precise-capturing bound. @@ -440,7 +443,7 @@ pub(crate) enum PathSource<'a> { DefineOpaques, } -impl<'a> PathSource<'a> { +impl<'a> PathSource<'a, '_> { fn namespace(self) -> Namespace { match self { PathSource::Type @@ -452,7 +455,7 @@ impl<'a> PathSource<'a> { | PathSource::TupleStruct(..) | PathSource::Delegation | PathSource::ReturnTypeNotation => ValueNS, - PathSource::TraitItem(ns) => ns, + PathSource::TraitItem(ns, _) => ns, PathSource::PreciseCapturingArg(ns) => ns, } } @@ -480,8 +483,9 @@ impl<'a> PathSource<'a> { PathSource::Trait(_) => "trait", PathSource::Pat => "unit struct, unit variant or constant", PathSource::Struct => "struct, variant or union type", - PathSource::TupleStruct(..) => "tuple struct or tuple variant", - PathSource::TraitItem(ns) => match ns { + PathSource::TraitItem(ValueNS, PathSource::TupleStruct(..)) + | PathSource::TupleStruct(..) => "tuple struct or tuple variant", + PathSource::TraitItem(ns, _) => match ns { TypeNS => "associated type", ValueNS => "method or associated constant", MacroNS => bug!("associated macro"), @@ -585,7 +589,7 @@ impl<'a> PathSource<'a> { ) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } ), - PathSource::TraitItem(ns) => match res { + PathSource::TraitItem(ns, _) => match res { Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, _ => false, @@ -934,8 +938,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r ) } TyKind::UnsafeBinder(unsafe_binder) => { - // FIXME(unsafe_binder): Better span - let span = ty.span; + let span = ty.span.shrink_to_lo().to(unsafe_binder.inner_ty.span.shrink_to_lo()); self.with_generic_param_rib( &unsafe_binder.generic_params, RibKind::Normal, @@ -2008,7 +2011,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, partial_res: PartialRes, path: &[Segment], - source: PathSource<'_>, + source: PathSource<'_, '_>, path_span: Span, ) { let proj_start = path.len() - partial_res.unresolved_segments(); @@ -2694,9 +2697,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_define_opaques(define_opaque); } - ItemKind::Enum(_, _, ref generics) - | ItemKind::Struct(_, _, ref generics) - | ItemKind::Union(_, _, ref generics) => { + ItemKind::Enum(_, ref generics, _) + | ItemKind::Struct(_, ref generics, _) + | ItemKind::Union(_, ref generics, _) => { self.resolve_adt(item, generics); } @@ -3901,7 +3904,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // We walk the pattern before declaring the pattern's inner bindings, // so that we avoid resolving a literal expression to a binding defined // by the pattern. - visit::walk_pat(self, pat); + // NB: `Self::visit_pat` must be used rather than `visit::walk_pat` to avoid resolving guard + // patterns' guard expressions multiple times (#141265). + self.visit_pat(pat); self.resolve_pattern_inner(pat, pat_src, bindings); // This has to happen *after* we determine which pat_idents are variants: self.check_consistent_bindings(pat); @@ -4205,7 +4210,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { id: NodeId, qself: &Option>, path: &Path, - source: PathSource<'ast>, + source: PathSource<'ast, '_>, ) { self.smart_resolve_path_fragment( qself, @@ -4222,7 +4227,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, qself: &Option>, path: &[Segment], - source: PathSource<'ast>, + source: PathSource<'ast, '_>, finalize: Finalize, record_partial_res: RecordPartialRes, parent_qself: Option<&QSelf>, @@ -4403,6 +4408,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { path_span, source.defer_to_typeck(), finalize, + source, ) { Ok(Some(partial_res)) if let Some(res) = partial_res.full_res() => { // if we also have an associated type that matches the ident, stash a suggestion @@ -4525,12 +4531,13 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { span: Span, defer_to_typeck: bool, finalize: Finalize, + source: PathSource<'ast, '_>, ) -> Result, Spanned>> { let mut fin_res = None; for (i, &ns) in [primary_ns, TypeNS, ValueNS].iter().enumerate() { if i == 0 || ns != primary_ns { - match self.resolve_qpath(qself, path, ns, finalize)? { + match self.resolve_qpath(qself, path, ns, finalize, source)? { Some(partial_res) if partial_res.unresolved_segments() == 0 || defer_to_typeck => { @@ -4567,6 +4574,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { path: &[Segment], ns: Namespace, finalize: Finalize, + source: PathSource<'ast, '_>, ) -> Result, Spanned>> { debug!( "resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})", @@ -4614,7 +4622,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let partial_res = self.smart_resolve_path_fragment( &None, &path[..=qself.position], - PathSource::TraitItem(ns), + PathSource::TraitItem(ns, &source), Finalize::with_root_span(finalize.node_id, finalize.path_span, qself.path_span), RecordPartialRes::No, Some(&qself), @@ -5241,9 +5249,9 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { ItemKind::TyAlias(box TyAlias { generics, .. }) | ItemKind::Const(box ConstItem { generics, .. }) | ItemKind::Fn(box Fn { generics, .. }) - | ItemKind::Enum(_, _, generics) - | ItemKind::Struct(_, _, generics) - | ItemKind::Union(_, _, generics) + | ItemKind::Enum(_, generics, _) + | ItemKind::Struct(_, generics, _) + | ItemKind::Union(_, generics, _) | ItemKind::Impl(box Impl { generics, .. }) | ItemKind::Trait(box Trait { generics, .. }) | ItemKind::TraitAlias(_, generics, _) => { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index b538be34f31f..97a45fcf2330 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -11,6 +11,7 @@ use rustc_ast::{ Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind, }; use rustc_ast_pretty::pprust::where_bound_predicate_to_string; +use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -39,7 +40,7 @@ use crate::late::{ }; use crate::ty::fast_reject::SimplifiedType; use crate::{ - Module, ModuleKind, ModuleOrUniformRoot, PathResult, PathSource, Segment, errors, + Module, ModuleKind, ModuleOrUniformRoot, PathResult, PathSource, Resolver, Segment, errors, path_names_to_string, }; @@ -174,7 +175,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, path: &[Segment], span: Span, - source: PathSource<'_>, + source: PathSource<'_, '_>, res: Option, ) -> BaseError { // Make the base error. @@ -420,7 +421,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path: &[Segment], following_seg: Option<&Segment>, span: Span, - source: PathSource<'_>, + source: PathSource<'_, '_>, res: Option, qself: Option<&QSelf>, ) -> (Diag<'tcx>, Vec) { @@ -477,6 +478,19 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return (err, Vec::new()); } + if let Some((did, item)) = self.lookup_doc_alias_name(path, source.namespace()) { + let item_name = item.name; + let suggestion_name = self.r.tcx.item_name(did); + err.span_suggestion( + item.span, + format!("`{suggestion_name}` has a name defined in the doc alias attribute as `{item_name}`"), + suggestion_name, + Applicability::MaybeIncorrect + ); + + return (err, Vec::new()); + }; + let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed( &mut err, source, @@ -525,12 +539,12 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path: &[Segment], following_seg: Option<&Segment>, span: Span, - source: PathSource<'_>, + source: PathSource<'_, '_>, res: Option, qself: Option<&QSelf>, ) { if let Some(Res::Def(DefKind::AssocFn, _)) = res - && let PathSource::TraitItem(TypeNS) = source + && let PathSource::TraitItem(TypeNS, _) = source && let None = following_seg && let Some(qself) = qself && let TyKind::Path(None, ty_path) = &qself.ty.kind @@ -636,7 +650,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn try_lookup_name_relaxed( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -751,12 +765,24 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { match candidate { AssocSuggestion::Field(field_span) => { if self_is_available { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "you might have meant to use the available field", - format!("{pre}self."), - Applicability::MachineApplicable, - ); + let source_map = self.r.tcx.sess.source_map(); + // check if the field is used in a format string, such as `"{x}"` + let field_is_format_named_arg = source_map + .span_to_source(span, |s, start, _| { + Ok(s.get(start - 1..start) == Some("{")) + }); + if let Ok(true) = field_is_format_named_arg { + err.help( + format!("you might have meant to use the available field in a format string: `\"{{}}\", self.{}`", segment.ident.name), + ); + } else { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "you might have meant to use the available field", + format!("{pre}self."), + Applicability::MaybeIncorrect, + ); + } } else { err.span_label(field_span, "a field by that name exists in `Self`"); } @@ -852,10 +878,69 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { (false, suggested_candidates, candidates) } + fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> { + let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| { + for resolution in r.resolutions(m).borrow().values() { + let Some(did) = + resolution.borrow().binding.and_then(|binding| binding.res().opt_def_id()) + else { + continue; + }; + if did.is_local() { + // We don't record the doc alias name in the local crate + // because the people who write doc alias are usually not + // confused by them. + continue; + } + if is_doc_alias_attrs_contain_symbol(r.tcx.get_attrs(did, sym::doc), item_name) { + return Some(did); + } + } + None + }; + + if path.len() == 1 { + for rib in self.ribs[ns].iter().rev() { + let item = path[0].ident; + if let RibKind::Module(module) = rib.kind + && let Some(did) = find_doc_alias_name(self.r, module, item.name) + { + return Some((did, item)); + } + } + } else { + // Finds to the last resolved module item in the path + // and searches doc aliases within that module. + // + // Example: For the path `a::b::last_resolved::not_exist::c::d`, + // we will try to find any item has doc aliases named `not_exist` + // in `last_resolved` module. + // + // - Use `skip(1)` because the final segment must remain unresolved. + for (idx, seg) in path.iter().enumerate().rev().skip(1) { + let Some(id) = seg.id else { + continue; + }; + let Some(res) = self.r.partial_res_map.get(&id) else { + continue; + }; + if let Res::Def(DefKind::Mod, module) = res.expect_full_res() + && let Some(module) = self.r.get_module(module) + && let item = path[idx + 1].ident + && let Some(did) = find_doc_alias_name(self.r, module, item.name) + { + return Some((did, item)); + } + break; + } + } + None + } + fn suggest_trait_and_bounds( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, res: Option, span: Span, base_error: &BaseError, @@ -932,7 +1017,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_typo( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -978,7 +1063,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_shadowed( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -1011,7 +1096,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn err_code_special_cases( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, path: &[Segment], span: Span, ) { @@ -1056,7 +1141,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_self_ty( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, path: &[Segment], span: Span, ) -> bool { @@ -1079,7 +1164,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_self_value( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, path: &[Segment], span: Span, ) -> bool { @@ -1247,7 +1332,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, res: Option, span: Span, ) { @@ -1276,7 +1361,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, err: &mut Diag<'_>, res: Option, - source: PathSource<'_>, + source: PathSource<'_, '_>, ) { let PathSource::TupleStruct(_, _) = source else { return }; let Some(Res::Def(DefKind::Fn, _)) = res else { return }; @@ -1288,7 +1373,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, err: &mut Diag<'_>, res: Option, - source: PathSource<'_>, + source: PathSource<'_, '_>, span: Span, ) { let PathSource::Trait(_) = source else { return }; @@ -1337,7 +1422,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_pattern_match_with_let( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, span: Span, ) -> bool { if let PathSource::Expr(_) = source @@ -1363,10 +1448,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn get_single_associated_item( &mut self, path: &[Segment], - source: &PathSource<'_>, + source: &PathSource<'_, '_>, filter_fn: &impl Fn(Res) -> bool, ) -> Option { - if let crate::PathSource::TraitItem(_) = source { + if let crate::PathSource::TraitItem(_, _) = source { let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(mod_path, None, None) @@ -1471,7 +1556,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { /// Check if the source is call expression and the first argument is `self`. If true, /// return the span of whole call and the span for all arguments expect the first one (`self`). - fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option)> { + fn call_has_self_arg(&self, source: PathSource<'_, '_>) -> Option<(Span, Option)> { let mut has_self_arg = None; if let PathSource::Expr(Some(parent)) = source && let ExprKind::Call(_, args) = &parent.kind @@ -1529,7 +1614,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, err: &mut Diag<'_>, span: Span, - source: PathSource<'_>, + source: PathSource<'_, '_>, path: &[Segment], res: Res, path_str: &str, @@ -1581,7 +1666,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } }; - let find_span = |source: &PathSource<'_>, err: &mut Diag<'_>| { + let find_span = |source: &PathSource<'_, '_>, err: &mut Diag<'_>| { match source { PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. })) | PathSource::TupleStruct(span, _) => { @@ -1965,8 +2050,86 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { err.span_label(span, fallback_label.to_string()); err.note("can't use `Self` as a constructor, you must use the implemented struct"); } - (Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => { + ( + Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), + PathSource::TraitItem(ValueNS, PathSource::TupleStruct(whole, args)), + ) => { + err.note("can't use a type alias as tuple pattern"); + + let mut suggestion = Vec::new(); + + if let &&[first, ..] = args + && let &&[.., last] = args + { + suggestion.extend([ + // "0: " has to be included here so that the fix is machine applicable. + // + // If this would only add " { " and then the code below add "0: ", + // rustfix would crash, because end of this suggestion is the same as start + // of the suggestion below. Thus, we have to merge these... + (span.between(first), " { 0: ".to_owned()), + (last.between(whole.shrink_to_hi()), " }".to_owned()), + ]); + + suggestion.extend( + args.iter() + .enumerate() + .skip(1) // See above + .map(|(index, &arg)| (arg.shrink_to_lo(), format!("{index}: "))), + ) + } else { + suggestion.push((span.between(whole.shrink_to_hi()), " {}".to_owned())); + } + + err.multipart_suggestion( + "use struct pattern instead", + suggestion, + Applicability::MachineApplicable, + ); + } + ( + Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), + PathSource::TraitItem( + ValueNS, + PathSource::Expr(Some(ast::Expr { + span: whole, + kind: ast::ExprKind::Call(_, args), + .. + })), + ), + ) => { err.note("can't use a type alias as a constructor"); + + let mut suggestion = Vec::new(); + + if let [first, ..] = &**args + && let [.., last] = &**args + { + suggestion.extend([ + // "0: " has to be included here so that the fix is machine applicable. + // + // If this would only add " { " and then the code below add "0: ", + // rustfix would crash, because end of this suggestion is the same as start + // of the suggestion below. Thus, we have to merge these... + (span.between(first.span), " { 0: ".to_owned()), + (last.span.between(whole.shrink_to_hi()), " }".to_owned()), + ]); + + suggestion.extend( + args.iter() + .enumerate() + .skip(1) // See above + .map(|(index, arg)| (arg.span.shrink_to_lo(), format!("{index}: "))), + ) + } else { + suggestion.push((span.between(whole.shrink_to_hi()), " {}".to_owned())); + } + + err.multipart_suggestion( + "use struct expression instead", + suggestion, + Applicability::MachineApplicable, + ); } _ => return false, } @@ -2536,7 +2699,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_using_enum_variant( &mut self, err: &mut Diag<'_>, - source: PathSource<'_>, + source: PathSource<'_, '_>, def_id: DefId, span: Span, ) { @@ -2714,7 +2877,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { pub(crate) fn suggest_adding_generic_parameter( &self, path: &[Segment], - source: PathSource<'_>, + source: PathSource<'_, '_>, ) -> Option<(Span, &'static str, String, Applicability)> { let (ident, span) = match path { [segment] diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c58f84805720..ee905065b966 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, Crate, NodeId, attr}; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::StabilityLevel; +use rustc_attr_data_structures::StabilityLevel; use rustc_data_structures::intern::Interned; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index a32fe6990160..fa839d2748d8 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -12,10 +12,14 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordSet; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; -use rustc_span::{DUMMY_SP, InnerSpan, Span, Symbol, kw, sym}; +use rustc_span::source_map::SourceMap; +use rustc_span::{DUMMY_SP, InnerSpan, Span, Symbol, sym}; use thin_vec::ThinVec; use tracing::{debug, trace}; +#[cfg(test)] +mod tests; + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DocFragmentKind { /// A doc fragment created from a `///` or `//!` doc comment. @@ -157,7 +161,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) { }; for fragment in docs { - if fragment.doc == kw::Empty { + if fragment.doc == sym::empty { continue; } @@ -177,7 +181,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) { /// /// Note: remove the trailing newline where appropriate pub fn add_doc_fragment(out: &mut String, frag: &DocFragment) { - if frag.doc == kw::Empty { + if frag.doc == sym::empty { out.push('\n'); return; } @@ -514,20 +518,40 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option { /// This method does not always work, because markdown bytes don't necessarily match source bytes, /// like if escapes are used in the string. In this case, it returns `None`. /// -/// This method will return `Some` only if: +/// `markdown` is typically the entire documentation for an item, +/// after combining fragments. +/// +/// This method will return `Some` only if one of the following is true: /// /// - The doc is made entirely from sugared doc comments, which cannot contain escapes -/// - The doc is entirely from a single doc fragment, with a string literal, exactly equal +/// - The doc is entirely from a single doc fragment with a string literal exactly equal to `markdown`. /// - The doc comes from `include_str!` +/// - The doc includes exactly one substring matching `markdown[md_range]` which is contained in a single doc fragment. +/// +/// This function is defined in the compiler so it can be used by +/// both `rustdoc` and `clippy`. pub fn source_span_for_markdown_range( tcx: TyCtxt<'_>, markdown: &str, md_range: &Range, fragments: &[DocFragment], ) -> Option { + let map = tcx.sess.source_map(); + source_span_for_markdown_range_inner(map, markdown, md_range, fragments) +} + +// inner function used for unit testing +pub fn source_span_for_markdown_range_inner( + map: &SourceMap, + markdown: &str, + md_range: &Range, + fragments: &[DocFragment], +) -> Option { + use rustc_span::BytePos; + if let &[fragment] = &fragments && fragment.kind == DocFragmentKind::RawDoc - && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(fragment.span) + && let Ok(snippet) = map.span_to_snippet(fragment.span) && snippet.trim_end() == markdown.trim_end() && let Ok(md_range_lo) = u32::try_from(md_range.start) && let Ok(md_range_hi) = u32::try_from(md_range.end) @@ -544,10 +568,49 @@ pub fn source_span_for_markdown_range( let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc); if !is_all_sugared_doc { + // This case ignores the markdown outside of the range so that it can + // work in cases where the markdown is made from several different + // doc fragments, but the target range does not span across multiple + // fragments. + let mut match_data = None; + let pat = &markdown[md_range.clone()]; + // This heirustic doesn't make sense with a zero-sized range. + if pat.is_empty() { + return None; + } + for (i, fragment) in fragments.iter().enumerate() { + if let Ok(snippet) = map.span_to_snippet(fragment.span) + && let Some(match_start) = snippet.find(pat) + { + // If there is either a match in a previous fragment, or + // multiple matches in this fragment, there is ambiguity. + // the snippet cannot be zero-sized, because it matches + // the pattern, which is checked to not be zero sized. + if match_data.is_none() + && !snippet.as_bytes()[match_start + 1..] + .windows(pat.len()) + .any(|s| s == pat.as_bytes()) + { + match_data = Some((i, match_start)); + } else { + // Heirustic produced ambiguity, return nothing. + return None; + } + } + } + if let Some((i, match_start)) = match_data { + let sp = fragments[i].span; + // we need to calculate the span start, + // then use that in our calulations for the span end + let lo = sp.lo() + BytePos(match_start as u32); + return Some( + sp.with_lo(lo).with_hi(lo + BytePos((md_range.end - md_range.start) as u32)), + ); + } return None; } - let snippet = tcx.sess.source_map().span_to_snippet(span_of_fragments(fragments)?).ok()?; + let snippet = map.span_to_snippet(span_of_fragments(fragments)?).ok()?; let starting_line = markdown[..md_range.start].matches('\n').count(); let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count(); diff --git a/compiler/rustc_resolve/src/rustdoc/tests.rs b/compiler/rustc_resolve/src/rustdoc/tests.rs new file mode 100644 index 000000000000..221ac907e7c9 --- /dev/null +++ b/compiler/rustc_resolve/src/rustdoc/tests.rs @@ -0,0 +1,50 @@ +use std::path::PathBuf; + +use rustc_span::source_map::{FilePathMapping, SourceMap}; +use rustc_span::symbol::sym; +use rustc_span::{BytePos, Span}; + +use super::{DocFragment, DocFragmentKind, source_span_for_markdown_range_inner}; + +#[test] +fn single_backtick() { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("foo.rs").into(), r#"#[doc = "`"] fn foo() {}"#.to_string()); + let span = source_span_for_markdown_range_inner( + &sm, + "`", + &(0..1), + &[DocFragment { + span: Span::with_root_ctxt(BytePos(8), BytePos(11)), + item_id: None, + kind: DocFragmentKind::RawDoc, + doc: sym::empty, // unused placeholder + indent: 0, + }], + ) + .unwrap(); + assert_eq!(span.lo(), BytePos(9)); + assert_eq!(span.hi(), BytePos(10)); +} + +#[test] +fn utf8() { + // regression test for https://github.com/rust-lang/rust/issues/141665 + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("foo.rs").into(), r#"#[doc = "⚠"] fn foo() {}"#.to_string()); + let span = source_span_for_markdown_range_inner( + &sm, + "⚠", + &(0..3), + &[DocFragment { + span: Span::with_root_ctxt(BytePos(8), BytePos(14)), + item_id: None, + kind: DocFragmentKind::RawDoc, + doc: sym::empty, // unused placeholder + indent: 0, + }], + ) + .unwrap(); + assert_eq!(span.lo(), BytePos(9)); + assert_eq!(span.hi(), BytePos(12)); +} 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 47831f2f4180..9d1d3412f8d2 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 @@ -79,7 +79,7 @@ fn encode_args<'tcx>( s.push('I'); let def_generics = tcx.generics_of(for_def); for (n, arg) in args.iter().enumerate() { - match arg.unpack() { + match arg.kind() { GenericArgKind::Lifetime(region) => { s.push_str(&encode_region(region, dict)); } @@ -245,7 +245,7 @@ fn encode_predicate<'tcx>( let name = encode_ty_name(tcx, projection.def_id); let _ = write!(s, "u{}{}", name.len(), name); s.push_str(&encode_args(tcx, projection.args, projection.def_id, true, dict, options)); - match projection.term.unpack() { + match projection.term.kind() { TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)), TermKind::Const(c) => s.push_str(&encode_const( tcx, diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 207ba5157bdb..0e711890e076 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -82,9 +82,7 @@ fn current_dll_path() -> Result { let fname_ptr = info.dli_fname.as_ptr(); #[cfg(not(target_os = "cygwin"))] let fname_ptr = { - if info.dli_fname.is_null() { - return Err("dladdr returned null pointer".into()); - } + assert!(!info.dli_fname.is_null(), "dli_fname cannot be null"); info.dli_fname }; let bytes = CStr::from_ptr(fname_ptr).to_bytes(); @@ -174,6 +172,11 @@ fn current_dll_path() -> Result { Ok(OsString::from_wide(&filename).into()) } +#[cfg(target_os = "wasi")] +fn current_dll_path() -> Result { + Err("current_dll_path is not supported on WASI".to_string()) +} + pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> { let target = crate::config::host_tuple(); let mut sysroot_candidates: SmallVec<[PathBuf; 2]> = smallvec![get_or_default_sysroot()]; diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 9cb89634c52e..a6d31ac4e13d 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -48,6 +48,7 @@ pub(crate) fn try_new_allocation<'tcx>( size, layout.align.abi, AllocInit::Uninit, + (), ); allocation .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar) @@ -65,6 +66,7 @@ pub(crate) fn try_new_allocation<'tcx>( layout.size, layout.align.abi, AllocInit::Uninit, + (), ); allocation .write_scalar( diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 8bcac4c4678e..b0c9dba78a65 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -100,7 +100,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> { stable_mir::ty::ExistentialProjection { def_id: tables.trait_def(*def_id), generic_args: args.stable(tables), - term: term.unpack().stable(tables), + term: term.kind().stable(tables), } } } @@ -158,7 +158,7 @@ impl<'tcx> Stable<'tcx> for ty::FieldDef { impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { type T = stable_mir::ty::GenericArgs; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { - GenericArgs(self.iter().map(|arg| arg.unpack().stable(tables)).collect()) + GenericArgs(self.iter().map(|arg| arg.kind().stable(tables)).collect()) } } @@ -604,8 +604,8 @@ impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> { PredicateKind::NormalizesTo(_pred) => unimplemented!(), PredicateKind::AliasRelate(a, b, alias_relation_direction) => { stable_mir::ty::PredicateKind::AliasRelate( - a.unpack().stable(tables), - b.unpack().stable(tables), + a.kind().stable(tables), + b.kind().stable(tables), alias_relation_direction.stable(tables), ) } @@ -640,7 +640,7 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { ty.stable(tables), ), ClauseKind::WellFormed(term) => { - stable_mir::ty::ClauseKind::WellFormed(term.unpack().stable(tables)) + stable_mir::ty::ClauseKind::WellFormed(term.kind().stable(tables)) } ClauseKind::ConstEvaluatable(const_) => { stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables)) @@ -726,7 +726,7 @@ impl<'tcx> Stable<'tcx> for ty::ProjectionPredicate<'tcx> { let ty::ProjectionPredicate { projection_term, term } = self; stable_mir::ty::ProjectionPredicate { projection_term: projection_term.stable(tables), - term: term.unpack().stable(tables), + term: term.kind().stable(tables), } } } diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index 6384fa06c21b..c32593a6d95a 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -29,7 +29,7 @@ pub(crate) fn analyze_source_file(src: &str) -> (Vec, Vec { fn analyze_source_file_dispatch( src: &str, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 22ca8accf9fd..906462a0d229 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -20,7 +20,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] -#![feature(cfg_match)] +#![feature(cfg_select)] #![feature(core_io_borrowed_buf)] #![feature(hash_set_entry)] #![feature(if_let_guard)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index efae6250b072..970faf2997c5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -34,17 +34,8 @@ symbols! { // unnamed method parameters, crate root module, error recovery etc. // Matching predicates: `is_special`/`is_reserved` // - // Notes about `kw::Empty`: - // - Its use can blur the lines between "empty symbol" and "no symbol". - // Using `Option` is preferable, where possible, because that - // is unambiguous. - // - For dummy symbols that are never used and absolutely must be - // present, it's better to use `sym::dummy` than `kw::Empty`, because - // it's clearer that it's intended as a dummy value, and more likely - // to be detected if it accidentally does get used. // tidy-alphabetical-start DollarCrate: "$crate", - Empty: "", PathRoot: "{{root}}", Underscore: "_", // tidy-alphabetical-end @@ -253,6 +244,7 @@ symbols! { FnMut, FnOnce, Formatter, + Forward, From, FromIterator, FromResidual, @@ -348,6 +340,7 @@ symbols! { Result, ResumeTy, Return, + Reverse, Right, Rust, RustaceansAreAwesome, @@ -398,7 +391,6 @@ symbols! { Wrapping, Yield, _DECLS, - _Self, __D, __H, __S, @@ -475,6 +467,7 @@ symbols! { as_ref, as_str, asm, + asm_cfg, asm_const, asm_experimental_arch, asm_experimental_reg, @@ -522,6 +515,7 @@ symbols! { async_iterator_poll_next, async_trait_bounds, atomic, + atomic_load, atomic_mod, atomics, att_syntax, @@ -531,7 +525,8 @@ symbols! { audit_that, augmented_assignments, auto_traits, - autodiff, + autodiff_forward, + autodiff_reverse, automatically_derived, avx, avx10_target_feature, @@ -864,7 +859,7 @@ symbols! { drop_types_in_const, dropck_eyepatch, dropck_parametricity, - dummy: "", // use this instead of `kw::Empty` for symbols that won't be used + dummy: "", // use this instead of `sym::empty` for symbols that won't be used dummy_cgu_name, dylib, dyn_compatible_for_dispatch, @@ -883,6 +878,14 @@ symbols! { emit_enum_variant_arg, emit_struct, emit_struct_field, + // Notes about `sym::empty`: + // - It should only be used when it genuinely means "empty symbol". Use + // `Option` when "no symbol" is a possibility. + // - For dummy symbols that are never used and absolutely must be + // present, it's better to use `sym::dummy` than `sym::empty`, because + // it's clearer that it's intended as a dummy value, and more likely + // to be detected if it accidentally does get used. + empty: "", emscripten_wasm_eh, enable, encode, @@ -1997,6 +2000,7 @@ symbols! { slice, slice_from_raw_parts, slice_from_raw_parts_mut, + slice_get_unchecked, slice_into_vec, slice_iter, slice_len_fn, @@ -2072,6 +2076,9 @@ symbols! { sym, sync, synthetic, + sys_mutex_lock, + sys_mutex_try_lock, + sys_mutex_unlock, t32, target, target_abi, @@ -2362,7 +2369,7 @@ impl Ident { #[inline] /// Constructs a new identifier from a symbol and a span. pub fn new(name: Symbol, span: Span) -> Ident { - debug_assert_ne!(name, kw::Empty); + debug_assert_ne!(name, sym::empty); Ident { name, span } } @@ -2584,7 +2591,7 @@ impl Symbol { } pub fn is_empty(self) -> bool { - self == kw::Empty + self == sym::empty } /// This method is supposed to be used in error messages, so it's expected to be @@ -2592,7 +2599,8 @@ impl Symbol { /// (`token_to_string`, `Ident::to_string`), except that symbols don't keep the rawness flag /// or edition, so we have to guess the rawness using the global edition. pub fn to_ident_string(self) -> String { - Ident::with_dummy_span(self).to_string() + // Avoid creating an empty identifier, because that asserts in debug builds. + if self == sym::empty { String::new() } else { Ident::with_dummy_span(self).to_string() } } } @@ -2772,7 +2780,7 @@ impl Symbol { /// Returns `true` if this symbol can be a raw identifier. pub fn can_be_raw(self) -> bool { - self != kw::Empty && self != kw::Underscore && !self.is_path_segment_keyword() + self != sym::empty && self != kw::Underscore && !self.is_path_segment_keyword() } /// Was this symbol predefined in the compiler's `symbols!` macro @@ -2822,7 +2830,7 @@ impl Ident { /// Whether this would be the identifier for a tuple field like `self.0`, as /// opposed to a named field like `self.thing`. pub fn is_numeric(self) -> bool { - !self.name.is_empty() && self.as_str().bytes().all(|b| b.is_ascii_digit()) + self.as_str().bytes().all(|b| b.is_ascii_digit()) } } diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 770401fc8cfe..956c996326bf 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -147,7 +147,7 @@ impl<'tcx> AbiHashStable<'tcx> for ty::FnSig<'tcx> { impl<'tcx> AbiHashStable<'tcx> for ty::GenericArg<'tcx> { fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { - self.unpack().abi_hash(tcx, hasher); + self.kind().abi_hash(tcx, hasher); } } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index db102abda7fa..12d1de463136 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -386,7 +386,7 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { print_prefix(self)?; let args = - args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_))); if args.clone().next().is_some() { self.generic_delimiters(|cx| cx.comma_sep(args)) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 4a99ce09b39a..49a5e20d7cf8 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -20,7 +20,7 @@ use rustc_middle::ty::{ self, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, UintTy, }; -use rustc_span::kw; +use rustc_span::sym; pub(super) fn mangle<'tcx>( tcx: TyCtxt<'tcx>, @@ -646,7 +646,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { let name = cx.tcx.associated_item(projection.def_id).name(); cx.push("p"); cx.push_ident(name.as_str()); - match projection.term.unpack() { + match projection.term.kind() { ty::TermKind::Ty(ty) => ty.print(cx), ty::TermKind::Const(c) => c.print(cx), }?; @@ -902,7 +902,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { print_prefix, ns, disambiguated_data.disambiguator as u64, - name.unwrap_or(kw::Empty).as_str(), + name.unwrap_or(sym::empty).as_str(), ) } @@ -912,11 +912,11 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { args: &[GenericArg<'tcx>], ) -> Result<(), PrintError> { // Don't print any regions if they're all erased. - let print_regions = args.iter().any(|arg| match arg.unpack() { + let print_regions = args.iter().any(|arg| match arg.kind() { GenericArgKind::Lifetime(r) => !r.is_erased(), _ => false, }); - let args = args.iter().cloned().filter(|arg| match arg.unpack() { + let args = args.iter().cloned().filter(|arg| match arg.kind() { GenericArgKind::Lifetime(_) => print_regions, _ => true, }); @@ -928,7 +928,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { self.push("I"); print_prefix(self)?; for arg in args { - match arg.unpack() { + match arg.kind() { GenericArgKind::Lifetime(lt) => { lt.print(self)?; } diff --git a/compiler/rustc_target/src/spec/base/cygwin.rs b/compiler/rustc_target/src/spec/base/cygwin.rs index 819d1d68a71d..d6ae0a905bf6 100644 --- a/compiler/rustc_target/src/spec/base/cygwin.rs +++ b/compiler/rustc_target/src/spec/base/cygwin.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use crate::spec::{ - BinaryFormat, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions, cvs, + BinaryFormat, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions, TlsModel, + cvs, }; pub(crate) fn opts() -> TargetOptions { @@ -44,6 +45,8 @@ pub(crate) fn opts() -> TargetOptions { eh_frame_header: false, debuginfo_kind: DebuginfoKind::Dwarf, supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + tls_model: TlsModel::Emulated, + has_thread_local: true, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs index d3e0a32c8b8f..6587abb2ba7e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(128), // FIXME: The leak sanitizer currently fails the tests, see #88132. supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD, + supports_xray: true, ..opts }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs index 87c07cd31090..4b75a6e5dbf6 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs @@ -1,6 +1,6 @@ use rustc_abi::Endian; -use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -16,6 +16,10 @@ pub(crate) fn target() -> Target { arch: "aarch64".into(), options: TargetOptions { features: "+v8a,+outline-atomics".into(), + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, mcount: "\u{1}_mcount".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs index e785069c78a1..2a16d1de3b51 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs @@ -1,6 +1,6 @@ use rustc_abi::Endian; -use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_gnu::opts(); @@ -20,6 +20,10 @@ pub(crate) fn target() -> Target { options: TargetOptions { abi: "ilp32".into(), features: "+v8a,+outline-atomics".into(), + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, stack_probes: StackProbeType::Inline, mcount: "\u{1}_mcount".into(), endian: Endian::Big, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs index 41c25393e129..d8651f305fe9 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs @@ -1,4 +1,6 @@ -use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{ + FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base, +}; // See https://developer.android.com/ndk/guides/abis.html#arm64-v8a // for target ABI requirements. @@ -20,6 +22,10 @@ pub(crate) fn target() -> Target { // As documented in https://developer.android.com/ndk/guides/cpu-features.html // the neon (ASIMD) and FP must exist on all android aarch64 targets. features: "+v8a,+neon,+fp-armv8".into(), + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::CFI | SanitizerSet::HWADDRESS diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs index a8b133d19bb0..eee668cc67ed 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs @@ -1,4 +1,4 @@ -use crate::spec::{Target, TargetMetadata, base}; +use crate::spec::{FramePointer, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::windows_gnullvm::opts(); @@ -6,6 +6,12 @@ pub(crate) fn target() -> Target { base.features = "+v8a,+neon,+fp-armv8".into(); base.linker = Some("aarch64-w64-mingw32-clang".into()); + // Microsoft recommends enabling frame pointers on Arm64 Windows. + // From https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#integer-registers + // "The frame pointer (x29) is required for compatibility with fast stack walking used by ETW + // and other services. It must point to the previous {x29, x30} pair on the stack." + base.frame_pointer = FramePointer::NonLeaf; + Target { llvm_target: "aarch64-pc-windows-gnu".into(), metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs index c6be2c20ea23..4220d74dfc89 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs @@ -1,4 +1,6 @@ -use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{ + FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base, +}; pub(crate) fn target() -> Target { Target { @@ -14,6 +16,10 @@ pub(crate) fn target() -> Target { arch: "aarch64".into(), options: TargetOptions { features: "+v8a,+outline-atomics".into(), + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, mcount: "\u{1}_mcount".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs index 166bb1ed2151..a22c12896773 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs @@ -1,4 +1,4 @@ -use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -15,6 +15,10 @@ pub(crate) fn target() -> Target { options: TargetOptions { abi: "ilp32".into(), features: "+v8a,+outline-atomics".into(), + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, mcount: "\u{1}_mcount".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs index 58ba06e124c7..58daaa036750 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs @@ -1,4 +1,6 @@ -use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{ + FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base, +}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); @@ -26,6 +28,12 @@ pub(crate) fn target() -> Target { pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), - options: TargetOptions { mcount: "\u{1}_mcount".into(), ..base }, + options: TargetOptions { + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, + mcount: "\u{1}_mcount".into(), ..base + }, } } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs index f2994b1232e6..51cdebf22db2 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs @@ -1,4 +1,6 @@ -use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{ + FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base, +}; pub(crate) fn target() -> Target { let mut base = base::linux_ohos::opts(); @@ -16,6 +18,10 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, mcount: "\u{1}_mcount".into(), stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::ADDRESS diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs index 2f868e38f1a5..64c170547805 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::THREAD, + supports_xray: true, ..opts }, } diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 99b04ac27200..576c9bd6b57f 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -980,14 +980,16 @@ impl Target { // the use of soft-float, so all we can do here is some crude hacks. match &*self.abi { "softfloat" => { - // This is not fully correct, LLVM actually doesn't let us enforce the softfloat - // ABI properly... see . - // FIXME: should we forbid "neon" here? But that would be a breaking change. - NOTHING + // LLVM will use float registers when `fp-armv8` is available, e.g. for + // calls to built-ins. The only way to ensure a consistent softfloat ABI + // on aarch64 is to never enable `fp-armv8`, so we enforce that. + // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the + // feature we have to mark as incompatible. + FeatureConstraints { required: &[], incompatible: &["neon"] } } _ => { // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. - // These are Rust feature names and we use "neon" to control both of them. + // `FeatureConstraints` uses Rust feature names, hence only "neon" shows up. FeatureConstraints { required: &["neon"], incompatible: &[] } } } diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 00922c6038ee..9b949a0a7954 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -337,6 +337,8 @@ trait_selection_rustc_on_unimplemented_expected_one_predicate_in_not = expected .label = unexpected quantity of predicates here trait_selection_rustc_on_unimplemented_invalid_flag = invalid flag in `on`-clause .label = expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}` +trait_selection_rustc_on_unimplemented_invalid_name = invalid name in `on`-clause + .label = expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}` trait_selection_rustc_on_unimplemented_invalid_predicate = this predicate is invalid .label = expected one of `any`, `all` or `not` here, not `{$invalid_pred}` trait_selection_rustc_on_unimplemented_missing_value = this attribute must have a value diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index fdd547448f00..8e2137da6552 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -738,7 +738,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { value.push_normal(", "); } - match arg.unpack() { + match arg.kind() { ty::GenericArgKind::Lifetime(lt) => { let s = lt.to_string(); value.push_normal(if s.is_empty() { "'_" } else { &s }); @@ -1166,7 +1166,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) { self.push_comma(&mut values.0, &mut values.1, i); - match arg1.unpack() { + match arg1.kind() { // At one point we'd like to elide all lifetimes here, they are // irrelevant for all diagnostics that use this output. // @@ -1509,7 +1509,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } let (is_simple_error, exp_found) = match values { ValuePairs::Terms(ExpectedFound { expected, found }) => { - match (expected.unpack(), found.unpack()) { + match (expected.kind(), found.kind()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { let is_simple_err = expected.is_simple_text() && found.is_simple_text(); @@ -2026,7 +2026,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } LetVisitor { span }.visit_body(body).break_value() } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(_, ty, _, _), .. }) => { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(_, _, ty, _), .. }) => { Some(&ty.peel_refs().kind) } _ => None, @@ -2156,7 +2156,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return None; } - Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) { + Some(match (exp_found.expected.kind(), exp_found.found.kind()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { let (mut exp, mut fnd) = self.cmp(expected, found); // Use the terminal width as the basis to determine when to compress the printed diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index cb1c9c753690..bfef3340b323 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -215,7 +215,7 @@ impl<'a, 'tcx> TypeFolder> for ClosureEraser<'a, 'tcx> { // `_` because then we'd end up with `Vec<_, _>`, instead of // `Vec<_>`. arg - } else if let GenericArgKind::Type(_) = arg.unpack() { + } else if let GenericArgKind::Type(_) = arg.kind() { // We don't replace lifetime or const params, only type params. self.new_infer().into() } else { @@ -347,7 +347,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { highlight: ty::print::RegionHighlightMode<'tcx>, ) -> InferenceDiagnosticsData { let tcx = self.tcx; - match term.unpack() { + match term.kind() { TermKind::Ty(ty) => { if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { let var_origin = self.infcx.type_var_origin(ty_vid); @@ -568,7 +568,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return arg; } - match arg.unpack() { + match arg.kind() { GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(), GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(), @@ -803,7 +803,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } impl<'tcx> CostCtxt<'tcx> { fn arg_cost(self, arg: GenericArg<'tcx>) -> usize { - match arg.unpack() { + match arg.kind() { GenericArgKind::Lifetime(_) => 0, // erased GenericArgKind::Type(ty) => self.ty_cost(ty), GenericArgKind::Const(_) => 3, // some non-zero value @@ -898,7 +898,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { return true; } - match (arg.unpack(), self.target.unpack()) { + match (arg.kind(), self.target.kind()) { (GenericArgKind::Type(inner_ty), TermKind::Ty(target_ty)) => { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { @@ -929,7 +929,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { if self.generic_arg_is_target(inner) { return true; } - match inner.unpack() { + match inner.kind() { GenericArgKind::Lifetime(_) => {} GenericArgKind::Type(ty) => { if matches!( 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 cdbb92f4c7ba..3804c13acce8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -664,8 +664,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let Some(found) = exp_found.found.args.get(1) else { return; }; - let expected = expected.unpack(); - let found = found.unpack(); + let expected = expected.kind(); + let found = found.kind(); // 3. Extract the tuple type from Fn trait and suggest the change. if let GenericArgKind::Type(expected) = expected && let GenericArgKind::Type(found) = found 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 970160ba212a..9b5e421e0e48 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 @@ -841,16 +841,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return None; }; - let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() { + let (closure_def_id, found_args, has_self_borrows) = match *self_ty.kind() { ty::Closure(def_id, args) => { - (def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None) + (def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), false) } ty::CoroutineClosure(def_id, args) => ( def_id, args.as_coroutine_closure() .coroutine_closure_sig() .map_bound(|sig| sig.tupled_inputs_ty), - Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()), + !args.as_coroutine_closure().tupled_upvars_ty().is_ty_var() + && args.as_coroutine_closure().has_self_borrows(), ), _ => return None, }; @@ -884,10 +885,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // If the closure has captures, then perhaps the reason that the trait // is unimplemented is because async closures don't implement `Fn`/`FnMut` // if they have captures. - if let Some(by_ref_captures) = by_ref_captures - && let ty::FnPtr(sig_tys, _) = by_ref_captures.kind() - && !sig_tys.skip_binder().output().is_unit() - { + if has_self_borrows && expected_kind != ty::ClosureKind::FnOnce { let mut err = self.dcx().create_err(AsyncClosureNotFn { span: self.tcx.def_span(closure_def_id), kind: expected_kind.as_str(), @@ -1503,11 +1501,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return None; }; - let Ok(Some(ImplSource::UserDefined(impl_data))) = SelectionContext::new(self) - .poly_select(&obligation.with( - self.tcx, - predicate.kind().rebind(proj.projection_term.trait_ref(self.tcx)), - )) + let trait_ref = self.enter_forall_and_leak_universe( + predicate.kind().rebind(proj.projection_term.trait_ref(self.tcx)), + ); + let Ok(Some(ImplSource::UserDefined(impl_data))) = + SelectionContext::new(self).select(&obligation.with(self.tcx, trait_ref)) else { return None; }; @@ -2448,7 +2446,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if let ty::Adt(def, args) = self_ty.kind() && let [arg] = &args[..] - && let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::GenericArgKind::Type(ty) = arg.kind() && let ty::Adt(inner_def, _) = ty.kind() && inner_def == def { 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 d5ee6e2123a1..37968386e9ae 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 @@ -429,7 +429,19 @@ impl<'tcx> OnUnimplementedDirective { .next() .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?; - match OnUnimplementedCondition::parse(cond) { + let generics: Vec = tcx + .generics_of(item_def_id) + .own_params + .iter() + .filter_map(|param| { + if matches!(param.kind, GenericParamDefKind::Lifetime) { + None + } else { + Some(param.name) + } + }) + .collect(); + match OnUnimplementedCondition::parse(cond, &generics) { Ok(condition) => Some(condition), Err(e) => return Err(tcx.dcx().emit_err(e)), } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs index 13753761f092..e8ea9f2d23eb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs @@ -26,9 +26,12 @@ impl OnUnimplementedCondition { }) } - pub(crate) fn parse(input: &MetaItemInner) -> Result { + pub(crate) fn parse( + input: &MetaItemInner, + generics: &[Symbol], + ) -> Result { let span = input.span(); - let pred = Predicate::parse(input)?; + let pred = Predicate::parse(input, generics)?; Ok(OnUnimplementedCondition { span, pred }) } } @@ -52,7 +55,7 @@ enum Predicate { } impl Predicate { - fn parse(input: &MetaItemInner) -> Result { + fn parse(input: &MetaItemInner, generics: &[Symbol]) -> Result { let meta_item = match input { MetaItemInner::MetaItem(meta_item) => meta_item, MetaItemInner::Lit(lit) => { @@ -69,10 +72,10 @@ impl Predicate { match meta_item.kind { MetaItemKind::List(ref mis) => match predicate.name { - sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis)?)), - sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis)?)), + sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis, generics)?)), + sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis, generics)?)), sym::not => match &**mis { - [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one)?))), + [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one, generics)?))), [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot { span: first.span().to(last.span()), }), @@ -83,7 +86,7 @@ impl Predicate { } }, MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => { - let name = Name::parse(predicate); + let name = Name::parse(predicate, generics)?; let value = FilterFormatString::parse(symbol); let kv = NameValue { name, value }; Ok(Predicate::Match(kv)) @@ -95,8 +98,11 @@ impl Predicate { } } - fn parse_sequence(sequence: &[MetaItemInner]) -> Result, InvalidOnClause> { - sequence.iter().map(Predicate::parse).collect() + fn parse_sequence( + sequence: &[MetaItemInner], + generics: &[Symbol], + ) -> Result, InvalidOnClause> { + sequence.iter().map(|item| Predicate::parse(item, generics)).collect() } fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { @@ -156,14 +162,13 @@ enum Name { } impl Name { - fn parse(Ident { name, .. }: Ident) -> Self { + fn parse(Ident { name, span }: Ident, generics: &[Symbol]) -> Result { match name { - sym::_Self | kw::SelfUpper => Name::SelfUpper, - sym::from_desugaring => Name::FromDesugaring, - sym::cause => Name::Cause, - // FIXME(mejrs) Perhaps we should start checking that - // this actually is a valid generic parameter? - generic => Name::GenericArg(generic), + kw::SelfUpper => Ok(Name::SelfUpper), + sym::from_desugaring => Ok(Name::FromDesugaring), + sym::cause => Ok(Name::Cause), + generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)), + invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }), } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs index ce170f820e12..7c1dfc1728f0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -2,8 +2,8 @@ use std::fmt; use std::ops::Range; use errors::*; -use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::TraitRefPrintSugared; +use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, @@ -232,48 +232,16 @@ fn parse_arg<'tcx>( ) -> FormatArg { let (Ctx::RustcOnUnimplemented { tcx, trait_def_id } | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx; - let trait_name = tcx.item_ident(*trait_def_id); - let generics = tcx.generics_of(trait_def_id); + let span = slice_span(input_span, arg.position_span.clone()); match arg.position { // Something like "hello {name}" Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) { - // accepted, but deprecated - (Ctx::RustcOnUnimplemented { .. }, sym::_Self) => { - warnings - .push(FormatWarning::FutureIncompat { span, help: String::from("use {Self}") }); - FormatArg::SelfUpper - } - ( - Ctx::RustcOnUnimplemented { .. }, - sym::from_desugaring - | sym::crate_local - | sym::direct - | sym::cause - | sym::float - | sym::integer_ - | sym::integral, - ) => { - warnings.push(FormatWarning::FutureIncompat { - span, - help: String::from("don't use this in a format string"), - }); - FormatArg::AsIs(String::new()) - } - // Only `#[rustc_on_unimplemented]` can use these (Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, (Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, (Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, - // `{ThisTraitsName}`. Some attrs in std use this, but I'd like to change it to the more general `{This}` - // because that'll be simpler to parse and extend in the future - (Ctx::RustcOnUnimplemented { .. }, name) if name == trait_name.name => { - warnings - .push(FormatWarning::FutureIncompat { span, help: String::from("use {This}") }); - FormatArg::This - } - // Any attribute can use these ( Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, @@ -282,7 +250,10 @@ fn parse_arg<'tcx>( ( Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, generic_param, - ) if generics.own_params.iter().any(|param| param.name == generic_param) => { + ) if tcx.generics_of(trait_def_id).own_params.iter().any(|param| { + !matches!(param.kind, GenericParamDefKind::Lifetime) && param.name == generic_param + }) => + { FormatArg::GenericParam { generic_param } } @@ -375,39 +346,4 @@ pub mod errors { #[diag(trait_selection_missing_options_for_on_unimplemented_attr)] #[help] pub struct MissingOptionsForOnUnimplementedAttr; - - #[derive(LintDiagnostic)] - #[diag(trait_selection_ignored_diagnostic_option)] - pub struct IgnoredDiagnosticOption { - pub option_name: &'static str, - #[label] - pub span: Span, - #[label(trait_selection_other_label)] - pub prev_span: Span, - } - - impl IgnoredDiagnosticOption { - pub fn maybe_emit_warning<'tcx>( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - new: Option, - old: Option, - option_name: &'static str, - ) { - if let (Some(new_item), Some(old_item)) = (new, old) { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - new_item, - IgnoredDiagnosticOption { - span: new_item, - prev_span: old_item, - option_name, - }, - ); - } - } - } - } } 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 8801397b7754..c4f1f7d712a7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -351,14 +351,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { hir::Node::Item(hir::Item { kind: - hir::ItemKind::Struct(_, _, generics) - | hir::ItemKind::Enum(_, _, generics) - | hir::ItemKind::Union(_, _, generics) + hir::ItemKind::Struct(_, generics, _) + | hir::ItemKind::Enum(_, generics, _) + | hir::ItemKind::Union(_, generics, _) | hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn { generics, .. } - | hir::ItemKind::TyAlias(_, _, generics) - | hir::ItemKind::Const(_, _, generics, _) + | hir::ItemKind::TyAlias(_, generics, _) + | hir::ItemKind::Const(_, generics, _, _) | hir::ItemKind::TraitAlias(_, generics, _), .. }) @@ -411,14 +411,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { hir::Node::Item(hir::Item { kind: - hir::ItemKind::Struct(_, _, generics) - | hir::ItemKind::Enum(_, _, generics) - | hir::ItemKind::Union(_, _, generics) + hir::ItemKind::Struct(_, generics, _) + | hir::ItemKind::Enum(_, generics, _) + | hir::ItemKind::Union(_, generics, _) | hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn { generics, .. } - | hir::ItemKind::TyAlias(_, _, generics) - | hir::ItemKind::Const(_, _, generics, _) + | hir::ItemKind::TyAlias(_, generics, _) + | hir::ItemKind::Const(_, generics, _, _) | hir::ItemKind::TraitAlias(_, generics, _), .. }) if !param_ty => { @@ -2124,16 +2124,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { accessed through a specific `impl`", self.tcx.def_kind_descr(assoc_item.as_def_kind(), item_def_id) )); - err.span_suggestion( - span, - "use the fully qualified path to an implementation", - format!( - "::{}", - self.tcx.def_path_str(trait_ref), - assoc_item.name() - ), - Applicability::HasPlaceholders, - ); + + if !assoc_item.is_impl_trait_in_trait() { + err.span_suggestion( + span, + "use the fully qualified path to an implementation", + format!( + "::{}", + self.tcx.def_path_str(trait_ref), + assoc_item.name() + ), + Applicability::HasPlaceholders, + ); + } } } } diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 8ab4d795c459..779c861637a4 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -72,6 +72,13 @@ pub enum InvalidOnClause { span: Span, invalid_flag: Symbol, }, + #[diag(trait_selection_rustc_on_unimplemented_invalid_name, code = E0232)] + InvalidName { + #[primary_span] + #[label] + span: Span, + invalid_name: Symbol, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 332204a0c5f0..d5bde9192d5e 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -81,7 +81,7 @@ pub fn check_opaque_type_parameter_valid<'tcx>( } for (i, arg) in opaque_type_key.iter_captured_args(tcx) { - let arg_is_param = match arg.unpack() { + let arg_is_param = match arg.kind() { GenericArgKind::Lifetime(lt) => match defining_scope_kind { DefiningScopeKind::HirTypeck => continue, DefiningScopeKind::MirBorrowck => { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 3601c2cba9b5..e92e37b87384 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -1,19 +1,22 @@ use std::ops::Deref; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::LangItem; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ - Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues, + Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues, }; -use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::Goal; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, TypingMode}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode, +}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; -use crate::traits::{EvaluateConstErr, specialization_graph}; +use crate::traits::{EvaluateConstErr, ObligationCause, specialization_graph}; #[repr(transparent)] pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); @@ -55,12 +58,73 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< (SolverDelegate(infcx), value, vars) } + fn compute_goal_fast_path( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + span: Span, + ) -> Option { + if let Some(trait_pred) = goal.predicate.as_trait_clause() { + if trait_pred.polarity() == ty::PredicatePolarity::Positive { + match self.0.tcx.as_lang_item(trait_pred.def_id()) { + Some(LangItem::Sized) + if self + .resolve_vars_if_possible(trait_pred.self_ty().skip_binder()) + .is_trivially_sized(self.0.tcx) => + { + return Some(Certainty::Yes); + } + Some(LangItem::Copy | LangItem::Clone) => { + let self_ty = + self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder()); + // Unlike `Sized` traits, which always prefer the built-in impl, + // `Copy`/`Clone` may be shadowed by a param-env candidate which + // could force a lifetime error or guide inference. While that's + // not generally desirable, it is observable, so for now let's + // ignore this fast path for types that have regions or infer. + if !self_ty + .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER) + && self_ty.is_trivially_pure_clone_copy() + { + return Some(Certainty::Yes); + } + } + _ => {} + } + } + } + + let pred = goal.predicate.kind(); + match pred.no_bound_vars()? { + ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => { + Some(Certainty::Yes) + } + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => { + self.0.sub_regions( + SubregionOrigin::RelateRegionParamBound(span, None), + outlives.1, + outlives.0, + ); + Some(Certainty::Yes) + } + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => { + self.0.register_type_outlives_constraint( + outlives.0, + outlives.1, + &ObligationCause::dummy_with_span(span), + ); + + Some(Certainty::Yes) + } + _ => None, + } + } + fn fresh_var_for_kind_with_span( &self, arg: ty::GenericArg<'tcx>, span: Span, ) -> ty::GenericArg<'tcx> { - match arg.unpack() { + match arg.kind() { ty::GenericArgKind::Lifetime(_) => { self.next_region_var(RegionVariableOrigin::MiscVariable(span)).into() } @@ -142,11 +206,11 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn instantiate_canonical_var_with_infer( &self, - cv_info: CanonicalVarInfo<'tcx>, + kind: CanonicalVarKind<'tcx>, span: Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ty::GenericArg<'tcx> { - self.0.instantiate_canonical_var(span, cv_info, universe_map) + self.0.instantiate_canonical_var(span, kind, universe_map) } fn add_item_bounds_for_hidden_type( diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 3e1cdac84dfd..ed99c678a4d2 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -12,8 +12,12 @@ use rustc_infer::traits::{ use rustc_middle::ty::{ self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode, }; -use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _}; +use rustc_next_trait_solver::delegate::SolverDelegate as _; +use rustc_next_trait_solver::solve::{ + GenerateProofTree, GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt as _, +}; use rustc_span::Span; +use thin_vec::ThinVec; use tracing::instrument; use self::derive_errors::*; @@ -24,6 +28,10 @@ use crate::traits::{FulfillmentError, ScrubbedTraitError}; mod derive_errors; +// FIXME: Do we need to use a `ThinVec` here? +type PendingObligations<'tcx> = + ThinVec<(PredicateObligation<'tcx>, Option>>)>; + /// A trait engine using the new trait solver. /// /// This is mostly identical to how `evaluate_all` works inside of the @@ -53,13 +61,17 @@ struct ObligationStorage<'tcx> { /// We cannot eagerly return these as error so we instead store them here /// to avoid recomputing them each time `select_where_possible` is called. /// This also allows us to return the correct `FulfillmentError` for them. - overflowed: PredicateObligations<'tcx>, - pending: PredicateObligations<'tcx>, + overflowed: Vec>, + pending: PendingObligations<'tcx>, } impl<'tcx> ObligationStorage<'tcx> { - fn register(&mut self, obligation: PredicateObligation<'tcx>) { - self.pending.push(obligation); + fn register( + &mut self, + obligation: PredicateObligation<'tcx>, + stalled_on: Option>>, + ) { + self.pending.push((obligation, stalled_on)); } fn has_pending_obligations(&self) -> bool { @@ -67,7 +79,8 @@ impl<'tcx> ObligationStorage<'tcx> { } fn clone_pending(&self) -> PredicateObligations<'tcx> { - let mut obligations = self.pending.clone(); + let mut obligations: PredicateObligations<'tcx> = + self.pending.iter().map(|(o, _)| o.clone()).collect(); obligations.extend(self.overflowed.iter().cloned()); obligations } @@ -75,8 +88,9 @@ impl<'tcx> ObligationStorage<'tcx> { fn drain_pending( &mut self, cond: impl Fn(&PredicateObligation<'tcx>) -> bool, - ) -> PredicateObligations<'tcx> { - let (unstalled, pending) = mem::take(&mut self.pending).into_iter().partition(cond); + ) -> PendingObligations<'tcx> { + let (unstalled, pending) = + mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o)); self.pending = pending; unstalled } @@ -89,13 +103,21 @@ impl<'tcx> ObligationStorage<'tcx> { // we were to do another step of `select_where_possible`, which goals would // change. // FIXME: is merged, this can be removed. - self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| { - let goal = o.as_goal(); - let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span) - .0; - matches!(result, Ok((HasChanged::Yes, _))) - })); + self.overflowed.extend( + ExtractIf::new(&mut self.pending, |(o, stalled_on)| { + let goal = o.as_goal(); + let result = <&SolverDelegate<'tcx>>::from(infcx) + .evaluate_root_goal( + goal, + GenerateProofTree::No, + o.cause.span, + stalled_on.take(), + ) + .0; + matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. })) + }) + .map(|(o, _)| o), + ); }) } } @@ -118,11 +140,11 @@ impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> { &self, infcx: &InferCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, - result: &Result<(HasChanged, Certainty), NoSolution>, + result: &Result>, NoSolution>, ) { if let Some(inspector) = infcx.obligation_inspector.get() { let result = match result { - Ok((_, c)) => Ok(*c), + Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty), Err(NoSolution) => Err(NoSolution), }; (inspector)(infcx, &obligation, result); @@ -141,14 +163,14 @@ where obligation: PredicateObligation<'tcx>, ) { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); - self.obligations.register(obligation); + self.obligations.register(obligation, None); } fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { self.obligations .pending .drain(..) - .map(|obligation| NextSolverError::Ambiguity(obligation)) + .map(|(obligation, _)| NextSolverError::Ambiguity(obligation)) .chain( self.obligations .overflowed @@ -163,8 +185,8 @@ where assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); loop { - let mut has_changed = false; - for mut obligation in self.obligations.drain_pending(|_| true) { + let mut any_changed = false; + for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) { if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { self.obligations.on_fulfillment_overflow(infcx); // Only return true errors that we have accumulated while processing. @@ -172,11 +194,29 @@ where } let goal = obligation.as_goal(); - let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) + let delegate = <&SolverDelegate<'tcx>>::from(infcx); + if let Some(certainty) = + delegate.compute_goal_fast_path(goal, obligation.cause.span) + { + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.obligations.register(obligation, None); + } + } + continue; + } + + let result = delegate + .evaluate_root_goal( + goal, + GenerateProofTree::No, + obligation.cause.span, + stalled_on, + ) .0; self.inspect_evaluated_obligation(infcx, &obligation, &result); - let (changed, certainty) = match result { + let GoalEvaluation { certainty, has_changed, stalled_on } = match result { Ok(result) => result, Err(NoSolution) => { errors.push(E::from_solver_error( @@ -187,7 +227,7 @@ where } }; - if changed == HasChanged::Yes { + if has_changed == HasChanged::Yes { // We increment the recursion depth here to track the number of times // this goal has resulted in inference progress. This doesn't precisely // model the way that we track recursion depth in the old solver due @@ -195,16 +235,16 @@ where // approximation and should only result in fulfillment overflow in // pathological cases. obligation.recursion_depth += 1; - has_changed = true; + any_changed = true; } match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => self.obligations.register(obligation), + Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), } } - if !has_changed { + if !any_changed { break; } } @@ -238,20 +278,24 @@ where return Default::default(); } - self.obligations.drain_pending(|obl| { - infcx.probe(|_| { - infcx - .visit_proof_tree( - obl.as_goal(), - &mut StalledOnCoroutines { - stalled_generators, - span: obl.cause.span, - cache: Default::default(), - }, - ) - .is_break() + self.obligations + .drain_pending(|obl| { + infcx.probe(|_| { + infcx + .visit_proof_tree( + obl.as_goal(), + &mut StalledOnCoroutines { + stalled_generators, + span: obl.cause.span, + cache: Default::default(), + }, + ) + .is_break() + }) }) - }) + .into_iter() + .map(|(o, _)| o) + .collect() } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index f64cd5ffebe3..1c9d69da3228 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -11,7 +11,9 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; +use rustc_next_trait_solver::solve::{ + GenerateProofTree, GoalEvaluation, SolverDelegateEvalExt as _, +}; use tracing::{instrument, trace}; use crate::solve::delegate::SolverDelegate; @@ -93,19 +95,21 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( root_obligation.as_goal(), GenerateProofTree::No, root_obligation.cause.span, + None, ) .0 { - Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { + Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { (FulfillmentErrorCode::Ambiguity { overflow: None }, true) } - Ok(( - _, - Certainty::Maybe(MaybeCause::Overflow { - suggest_increasing_limit, - keep_constraints: _, - }), - )) => ( + Ok(GoalEvaluation { + certainty: + Certainty::Maybe(MaybeCause::Overflow { + suggest_increasing_limit, + keep_constraints: _, + }), + .. + }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, // Don't look into overflows because we treat overflows weirdly anyways. // We discard the inference constraints from overflowing goals, so @@ -115,7 +119,7 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( // FIXME: We should probably just look into overflows here. false, ), - Ok((_, Certainty::Yes)) => { + Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { bug!( "did not expect successful goal when collecting ambiguity errors for `{:?}`", infcx.resolve_vars_if_possible(root_obligation.predicate), diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 9795655e8422..1193a9059ca9 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -15,9 +15,9 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; -use rustc_middle::ty::{TyCtxt, TypeFoldable, VisitorResult, try_visit}; +use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; use rustc_middle::{bug, ty}; -use rustc_next_trait_solver::resolve::EagerResolver; +use rustc_next_trait_solver::resolve::eager_resolve_vars; use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _}; use rustc_span::{DUMMY_SP, Span}; @@ -187,8 +187,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { let _ = term_hack.constrain(infcx, span, param_env); } - let opt_impl_args = - opt_impl_args.map(|impl_args| impl_args.fold_with(&mut EagerResolver::new(infcx))); + let opt_impl_args = opt_impl_args.map(|impl_args| eager_resolve_vars(infcx, impl_args)); let goals = instantiated_goals .into_iter() @@ -207,7 +206,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { let infcx = self.goal.infcx; match goal.predicate.kind().no_bound_vars() { Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { - let unconstrained_term = match term.unpack() { + let unconstrained_term = match term.kind() { ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(), ty::TermKind::Const(_) => infcx.next_const_var(span).into(), }; @@ -219,8 +218,8 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { // building their proof tree, the expected term was unconstrained, but when // instantiating the candidate it is already constrained to the result of another // candidate. - let proof_tree = - infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1); + let proof_tree = infcx + .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes, None).1); InspectGoal::new( infcx, self.goal.depth + 1, @@ -236,7 +235,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { // constraints, we get an ICE if we already applied the constraints // from the chosen candidate. let proof_tree = infcx - .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span).1) + .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span, None).1) .unwrap(); InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) } @@ -392,7 +391,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { infcx, depth, orig_values, - goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)), + goal: eager_resolve_vars(infcx, uncanonicalized_goal), result, evaluation_kind: evaluation.kind, normalizes_to_term_hack, @@ -442,6 +441,7 @@ impl<'tcx> InferCtxt<'tcx> { goal, GenerateProofTree::Yes, visitor.span(), + None, ); let proof_tree = proof_tree.unwrap(); visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index 4fdaf740287b..21812c8017da 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -5,7 +5,7 @@ use rustc_infer::traits::solve::inspect::ProbeKind; use rustc_infer::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_infer::traits::{ BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, Obligation, ObligationCause, - PolyTraitObligation, Selection, SelectionError, SelectionResult, + Selection, SelectionError, SelectionResult, TraitObligation, }; use rustc_macros::extension; use rustc_middle::{bug, span_bug}; @@ -15,9 +15,10 @@ use crate::solve::inspect::{self, ProofTreeInferCtxtExt}; #[extension(pub trait InferCtxtSelectExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { + /// Do not use this directly. This is called from [`crate::traits::SelectionContext::select`]. fn select_in_new_trait_solver( &self, - obligation: &PolyTraitObligation<'tcx>, + obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { assert!(self.next_trait_solver()); diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 02521c9453d9..3ae908ec16b8 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -726,7 +726,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { } ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(binder)) => { let binder = bound_predicate.rebind(binder); - selcx.infcx.region_outlives_predicate(&dummy_cause, binder) + selcx.infcx.enter_forall(binder, |pred| { + selcx.infcx.register_region_outlives_constraint(pred, &dummy_cause); + }); } ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(binder)) => { let binder = bound_predicate.rebind(binder); @@ -735,14 +737,14 @@ impl<'tcx> AutoTraitFinder<'tcx> { binder.map_bound_ref(|pred| pred.0).no_bound_vars(), ) { (None, Some(t_a)) => { - selcx.infcx.register_region_obligation_with_cause( + selcx.infcx.register_type_outlives_constraint( t_a, selcx.infcx.tcx.lifetimes.re_static, &dummy_cause, ); } (Some(ty::OutlivesPredicate(t_a, r_b)), _) => { - selcx.infcx.register_region_obligation_with_cause( + selcx.infcx.register_type_outlives_constraint( t_a, r_b, &dummy_cause, diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 8d6e6b4a6516..18f28d72f6f8 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -188,6 +188,20 @@ where .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + /// Computes the least-upper-bound, or mutual supertype, of two values. + pub fn lub>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> Result> { + self.infcx + .at(cause, param_env) + .lub(expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + #[must_use] pub fn select_where_possible(&self) -> Vec { self.engine.borrow_mut().select_where_possible(self.infcx) diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 34c3c905bd97..951dfb879aed 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -428,7 +428,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => { if infcx.considering_regions { - infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); + infcx.register_region_outlives_constraint(data, &obligation.cause); } ProcessResult::Changed(Default::default()) @@ -439,7 +439,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { r_b, ))) => { if infcx.considering_regions { - infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause); + infcx.register_type_outlives_constraint(t_a, r_b, &obligation.cause); } ProcessResult::Changed(Default::default()) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 31b075db04b9..999ef97683ca 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -221,7 +221,7 @@ fn pred_known_to_hold_modulo_regions<'tcx>( if result.must_apply_modulo_regions() { true - } else if result.may_apply() { + } else if result.may_apply() && !infcx.next_trait_solver() { // Sometimes obligations are ambiguous because the recursive evaluator // is not smart enough, so we fall back to fulfillment when we're not certain // that an obligation holds or not. Even still, we must make sure that @@ -340,7 +340,7 @@ pub fn normalize_param_env_or_error<'tcx>( let mut predicates: Vec<_> = util::elaborate( tcx, unnormalized_env.caller_bounds().into_iter().map(|predicate| { - if tcx.features().generic_const_exprs() { + if tcx.features().generic_const_exprs() || tcx.next_trait_solver_globally() { return predicate; } @@ -405,8 +405,6 @@ pub fn normalize_param_env_or_error<'tcx>( // compatibility. Eventually when lazy norm is implemented this can just be removed. // We do not normalize types here as there is no backwards compatibility requirement // for us to do so. - // - // FIXME(-Znext-solver): remove this hack since we have deferred projection equality predicate.fold_with(&mut ConstNormalizer(tcx)) }), ) @@ -542,10 +540,13 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Placeholder(_) | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), ty::ConstKind::Unevaluated(uv) => { + let opt_anon_const_kind = + (tcx.def_kind(uv.def) == DefKind::AnonConst).then(|| tcx.anon_const_kind(uv.def)); + // Postpone evaluation of constants that depend on generic parameters or // inference variables. // - // We use `TypingMode::PostAnalysis` here which is not *technically* correct + // We use `TypingMode::PostAnalysis` here which is not *technically* correct // to be revealing opaque types here as borrowcheck has not run yet. However, // CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). @@ -553,65 +554,95 @@ pub fn try_evaluate_const<'tcx>( // // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself // instead of having this logic here - let (args, typing_env) = if tcx.features().generic_const_exprs() - && uv.has_non_region_infer() - { - // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause - // inference variables and generic parameters to show up in `ty::Const` even though the anon const - // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. - match tcx.thir_abstract_const(uv.def) { - Ok(Some(ct)) => { - let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args)); - if let Err(e) = ct.error_reported() { - return Err(EvaluateConstErr::EvaluationFailure(e)); - } else if ct.has_non_region_infer() || ct.has_non_region_param() { - // If the anon const *does* actually use generic parameters or inference variables from - // the generic arguments provided for it, then we should *not* attempt to evaluate it. - return Err(EvaluateConstErr::HasGenericsOrInfers); - } else { - let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args); - let typing_env = infcx - .typing_env(tcx.erase_regions(param_env)) - .with_post_analysis_normalized(tcx); - (args, typing_env) + let (args, typing_env) = match opt_anon_const_kind { + // We handle `generic_const_exprs` separately as reasonable ways of handling constants in the type system + // completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason + // about if you have to consider gce whatsoever. + Some(ty::AnonConstKind::GCE) => { + if uv.has_non_region_infer() || uv.has_non_region_param() { + // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause + // inference variables and generic parameters to show up in `ty::Const` even though the anon const + // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. + match tcx.thir_abstract_const(uv.def) { + Ok(Some(ct)) => { + let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args)); + if let Err(e) = ct.error_reported() { + return Err(EvaluateConstErr::EvaluationFailure(e)); + } else if ct.has_non_region_infer() || ct.has_non_region_param() { + // If the anon const *does* actually use generic parameters or inference variables from + // the generic arguments provided for it, then we should *not* attempt to evaluate it. + return Err(EvaluateConstErr::HasGenericsOrInfers); + } else { + let args = + replace_param_and_infer_args_with_placeholder(tcx, uv.args); + let typing_env = infcx + .typing_env(tcx.erase_regions(param_env)) + .with_post_analysis_normalized(tcx); + (args, typing_env) + } + } + Err(_) | Ok(None) => { + let args = GenericArgs::identity_for_item(tcx, uv.def); + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); + (args, typing_env) + } } - } - Err(_) | Ok(None) => { - let args = GenericArgs::identity_for_item(tcx, uv.def); - let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); - (args, typing_env) + } else { + let typing_env = infcx + .typing_env(tcx.erase_regions(param_env)) + .with_post_analysis_normalized(tcx); + (uv.args, typing_env) } } - } else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() { - // FIXME: remove this when `const_evaluatable_unchecked` is a hard error. - // - // Diagnostics will sometimes replace the identity args of anon consts in - // array repeat expr counts with inference variables so we have to handle this - // even though it is not something we should ever actually encounter. - // - // Array repeat expr counts are allowed to syntactically use generic parameters - // but must not actually depend on them in order to evalaute successfully. This means - // that it is actually fine to evalaute them in their own environment rather than with - // the actually provided generic arguments. - tcx.dcx().delayed_bug( - "Encountered anon const with inference variable args but no error reported", - ); + Some(ty::AnonConstKind::RepeatExprCount) => { + if uv.has_non_region_infer() { + // Diagnostics will sometimes replace the identity args of anon consts in + // array repeat expr counts with inference variables so we have to handle this + // even though it is not something we should ever actually encounter. + // + // Array repeat expr counts are allowed to syntactically use generic parameters + // but must not actually depend on them in order to evalaute successfully. This means + // that it is actually fine to evalaute them in their own environment rather than with + // the actually provided generic arguments. + tcx.dcx().delayed_bug("AnonConst with infer args but no error reported"); + } - let args = GenericArgs::identity_for_item(tcx, uv.def); - let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); - (args, typing_env) - } else { - // FIXME: This codepath is reachable under `associated_const_equality` and in the - // future will be reachable by `min_generic_const_args`. We should handle inference - // variables and generic parameters properly instead of doing nothing. - let typing_env = infcx - .typing_env(tcx.erase_regions(param_env)) - .with_post_analysis_normalized(tcx); - (uv.args, typing_env) + // The generic args of repeat expr counts under `min_const_generics` are not supposed to + // affect evaluation of the constant as this would make it a "truly" generic const arg. + // To prevent this we discard all the generic arguments and evalaute with identity args + // and in its own environment instead of the current environment we are normalizing in. + let args = GenericArgs::identity_for_item(tcx, uv.def); + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); + + (args, typing_env) + } + _ => { + // We are only dealing with "truly" generic/uninferred constants here: + // - GCEConsts have been handled separately + // - Repeat expr count back compat consts have also been handled separately + // So we are free to simply defer evaluation here. + // + // FIXME: This assumes that `args` are normalized which is not necessarily true + // + // Const patterns are converted to type system constants before being + // evaluated. However, we don't care about them here as pattern evaluation + // logic does not go through type system normalization. If it did this would + // be a backwards compatibility problem as we do not enforce "syntactic" non- + // usage of generic parameters like we do here. + if uv.args.has_non_region_param() || uv.args.has_non_region_infer() { + return Err(EvaluateConstErr::HasGenericsOrInfers); + } + + let typing_env = infcx + .typing_env(tcx.erase_regions(param_env)) + .with_post_analysis_normalized(tcx); + (uv.args, typing_env) + } }; - let uv = ty::UnevaluatedConst::new(uv.def, args); + let uv = ty::UnevaluatedConst::new(uv.def, args); let erased_uv = tcx.erase_regions(uv); + use rustc_middle::mir::interpret::ErrorHandled; match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) { Ok(Ok(val)) => Ok(ty::Const::new_value( diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 88a0c402702e..eb6d5c8a60a2 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -299,12 +299,21 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { ); } + // We don't replace bound vars in the generic arguments of the free alias with + // placeholders. This doesn't cause any issues as instantiating parameters with + // bound variables is special-cased to rewrite the debruijn index to be higher + // whenever we fold through a binder. + // + // However, we do replace any escaping bound vars in the resulting goals with + // placeholders as the trait solver does not expect to encounter escaping bound + // vars in obligations. + // + // FIXME(lazy_type_alias): Check how much this actually matters for perf before + // stabilization. This is a bit weird and generally not how we handle binders in + // the compiler so ideally we'd do the same boundvar->placeholder->boundvar dance + // that other kinds of normalization do. let infcx = self.selcx.infcx; self.obligations.extend( - // FIXME(BoxyUwU): - // FIXME(lazy_type_alias): - // It seems suspicious to instantiate the predicates with arguments that might be bound vars, - // we might wind up instantiating one of these bound vars underneath a hrtb. infcx.tcx.predicates_of(free.def_id).instantiate_own(infcx.tcx, free.args).map( |(mut predicate, span)| { if free.has_escaping_bound_vars() { diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 68983ef80fa4..59d3ac21387f 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -9,7 +9,7 @@ use rustc_span::def_id::LocalDefId; use tracing::instrument; use crate::infer::InferCtxt; -use crate::traits::{ObligationCause, ObligationCtxt}; +use crate::traits::ObligationCause; /// Implied bounds are region relationships that we deduce /// automatically. The idea is that (e.g.) a caller must check that a @@ -79,24 +79,9 @@ fn implied_outlives_bounds<'a, 'tcx>( if !constraints.is_empty() { let QueryRegionConstraints { outlives } = constraints; - // Instantiation may have produced new inference variables and constraints on those - // variables. Process these constraints. - let ocx = ObligationCtxt::new(infcx); let cause = ObligationCause::misc(span, body_id); - for &constraint in &outlives { - ocx.register_obligation(infcx.query_outlives_constraint_to_obligation( - constraint, - cause.clone(), - param_env, - )); - } - - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.dcx().span_bug( - span, - "implied_outlives_bounds failed to solve obligations from instantiation", - ); + for &(predicate, _) in &outlives { + infcx.register_outlives_constraint(predicate, &cause); } }; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index ca58da5ca6d5..ed0f34b5aa91 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -378,6 +378,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>( term: projected_term, obligations: mut projected_obligations, })) => { + debug!("opt_normalize_projection_type: progress"); // if projection succeeded, then what we get out of this // is also non-normalized (consider: it was derived from // an impl, where-clause etc) and hence we must @@ -408,6 +409,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>( Ok(Some(result.value)) } Ok(Projected::NoProgress(projected_ty)) => { + debug!("opt_normalize_projection_type: no progress"); let result = Normalized { value: projected_ty, obligations: PredicateObligations::new() }; infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); @@ -621,8 +623,17 @@ struct Progress<'tcx> { } impl<'tcx> Progress<'tcx> { - fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { - Progress { term: Ty::new_error(tcx, guar).into(), obligations: PredicateObligations::new() } + fn error_for_term( + tcx: TyCtxt<'tcx>, + alias_term: ty::AliasTerm<'tcx>, + guar: ErrorGuaranteed, + ) -> Self { + let err_term = if alias_term.kind(tcx).is_type() { + Ty::new_error(tcx, guar).into() + } else { + ty::Const::new_error(tcx, guar).into() + }; + Progress { term: err_term, obligations: PredicateObligations::new() } } fn with_addl_obligations(mut self, mut obligations: PredicateObligations<'tcx>) -> Self { @@ -650,7 +661,11 @@ fn project<'cx, 'tcx>( } if let Err(guar) = obligation.predicate.error_reported() { - return Ok(Projected::Progress(Progress::error(selcx.tcx(), guar))); + return Ok(Projected::Progress(Progress::error_for_term( + selcx.tcx(), + obligation.predicate, + guar, + ))); } let mut candidates = ProjectionCandidateSet::None; @@ -1965,7 +1980,13 @@ fn confirm_impl_candidate<'cx, 'tcx>( let param_env = obligation.param_env; let assoc_term = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) { Ok(assoc_term) => assoc_term, - Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))), + Err(guar) => { + return Ok(Projected::Progress(Progress::error_for_term( + tcx, + obligation.predicate, + guar, + ))); + } }; // This means that the impl is missing a definition for the diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index d9b57f0c67d1..e294f7839aac 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,6 +1,6 @@ use std::ops::ControlFlow; -use rustc_infer::infer::RegionObligation; +use rustc_infer::infer::TypeOutlivesConstraint; use rustc_infer::infer::canonical::CanonicalQueryInput; use rustc_infer::traits::query::OutlivesBound; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; @@ -141,7 +141,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( && !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat && ty.visit_with(&mut ContainsBevyParamSet { tcx: ocx.infcx.tcx }).is_break() { - for RegionObligation { sup_type, sub_region, .. } in + for TypeOutlivesConstraint { sup_type, sub_region, .. } in ocx.infcx.take_registered_region_obligations() { let mut components = smallvec![]; diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 10a2ba049d85..7d869b27445c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -65,71 +65,92 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let def_id = obligation.predicate.def_id(); let tcx = self.tcx(); - if tcx.is_lang_item(def_id, LangItem::Copy) { - debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); + let lang_item = tcx.as_lang_item(def_id); + match lang_item { + Some(LangItem::Copy | LangItem::Clone) => { + debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); - // User-defined copy impls are permitted, but only for - // structs and enums. - self.assemble_candidates_from_impls(obligation, &mut candidates); + // User-defined copy impls are permitted, but only for + // structs and enums. + self.assemble_candidates_from_impls(obligation, &mut candidates); - // For other types, we'll use the builtin rules. - let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::DiscriminantKind) { - // `DiscriminantKind` is automatically implemented for every type. - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if tcx.is_lang_item(def_id, LangItem::PointeeTrait) { - // `Pointee` is automatically implemented for every type. - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if tcx.is_lang_item(def_id, LangItem::Sized) { - self.assemble_builtin_sized_candidate(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Unsize) { - self.assemble_candidates_for_unsizing(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Destruct) { - self.assemble_const_destruct_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::TransmuteTrait) { - // User-defined transmutability impls are permitted. - self.assemble_candidates_from_impls(obligation, &mut candidates); - self.assemble_candidates_for_transmutability(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Tuple) { - self.assemble_candidate_for_tuple(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { - self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::BikeshedGuaranteedNoDrop) { - self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( - obligation, - &mut candidates, - ); - } else { - if tcx.is_lang_item(def_id, LangItem::Clone) { - // Same builtin conditions as `Copy`, i.e., every type which has builtin support - // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` - // types have builtin support for `Clone`. - let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); + // For other types, we'll use the builtin rules. + let copy_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); } - - if tcx.is_lang_item(def_id, LangItem::Coroutine) { - self.assemble_coroutine_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Future) { - self.assemble_future_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Iterator) { - self.assemble_iterator_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::FusedIterator) { - self.assemble_fused_iterator_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::AsyncIterator) { - self.assemble_async_iterator_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::AsyncFnKindHelper) { - self.assemble_async_fn_kind_helper_candidates(obligation, &mut candidates); + Some(LangItem::DiscriminantKind) => { + // `DiscriminantKind` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); } + Some(LangItem::PointeeTrait) => { + // `Pointee` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + Some(LangItem::Sized) => { + self.assemble_builtin_sized_candidate(obligation, &mut candidates); + } + Some(LangItem::Unsize) => { + self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } + Some(LangItem::Destruct) => { + self.assemble_const_destruct_candidates(obligation, &mut candidates); + } + Some(LangItem::TransmuteTrait) => { + // User-defined transmutability impls are permitted. + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_for_transmutability(obligation, &mut candidates); + } + Some(LangItem::Tuple) => { + self.assemble_candidate_for_tuple(obligation, &mut candidates); + } + Some(LangItem::FnPtrTrait) => { + self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); + } + Some(LangItem::BikeshedGuaranteedNoDrop) => { + self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + obligation, + &mut candidates, + ); + } + _ => { + // We re-match here for traits that can have both builtin impls and user written impls. + // After the builtin impls we need to also add user written impls, which we do not want to + // do in general because just checking if there are any is expensive. + match lang_item { + Some(LangItem::Coroutine) => { + self.assemble_coroutine_candidates(obligation, &mut candidates); + } + Some(LangItem::Future) => { + self.assemble_future_candidates(obligation, &mut candidates); + } + Some(LangItem::Iterator) => { + self.assemble_iterator_candidates(obligation, &mut candidates); + } + Some(LangItem::FusedIterator) => { + self.assemble_fused_iterator_candidates(obligation, &mut candidates); + } + Some(LangItem::AsyncIterator) => { + self.assemble_async_iterator_candidates(obligation, &mut candidates); + } + Some(LangItem::AsyncFnKindHelper) => { + self.assemble_async_fn_kind_helper_candidates( + obligation, + &mut candidates, + ); + } + Some(LangItem::AsyncFn | LangItem::AsyncFnMut | LangItem::AsyncFnOnce) => { + self.assemble_async_closure_candidates(obligation, &mut candidates); + } + Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => { + self.assemble_closure_candidates(obligation, &mut candidates); + self.assemble_fn_pointer_candidates(obligation, &mut candidates); + } + _ => {} + } - // FIXME: Put these into `else if` blocks above, since they're built-in. - self.assemble_closure_candidates(obligation, &mut candidates); - self.assemble_async_closure_candidates(obligation, &mut candidates); - self.assemble_fn_pointer_candidates(obligation, &mut candidates); - - self.assemble_candidates_from_impls(obligation, &mut candidates); - self.assemble_candidates_from_object_ty(obligation, &mut candidates); + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_from_object_ty(obligation, &mut candidates); + } } self.assemble_candidates_from_projected_tys(obligation, &mut candidates); @@ -360,9 +381,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else { - return; - }; + let kind = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); // Okay to skip binder because the args on closure types never // touch bound regions, they just capture the in-scope @@ -424,11 +443,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let Some(goal_kind) = - self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()) - else { - return; - }; + let goal_kind = + self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); match *obligation.self_ty().skip_binder().kind() { ty::CoroutineClosure(_, args) => { @@ -501,11 +517,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - // We provide impl of all fn traits for fn pointers. - if !self.tcx().is_fn_trait(obligation.predicate.def_id()) { - return; - } - // Keep this function in sync with extract_tupled_inputs_and_output_from_callable // until the old solver (and thus this function) is removed. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 94190cd3ae33..c9169127e0b9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -251,16 +251,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); let obligations = if has_nested { let trait_def = obligation.predicate.def_id(); - let conditions = if tcx.is_lang_item(trait_def, LangItem::Sized) { - self.sized_conditions(obligation) - } else if tcx.is_lang_item(trait_def, LangItem::Copy) { - self.copy_clone_conditions(obligation) - } else if tcx.is_lang_item(trait_def, LangItem::Clone) { - self.copy_clone_conditions(obligation) - } else if tcx.is_lang_item(trait_def, LangItem::FusedIterator) { - self.fused_iterator_conditions(obligation) - } else { - bug!("unexpected builtin trait {:?}", trait_def) + let conditions = match tcx.as_lang_item(trait_def) { + Some(LangItem::Sized) => self.sized_conditions(obligation), + Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation), + Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation), + other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), }; let BuiltinImplConditions::Where(types) = conditions else { bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 44a76f6e0832..3a2f9e8ca179 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -265,9 +265,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { - if self.infcx.next_trait_solver() { - return self.infcx.select_in_new_trait_solver(obligation); - } + assert!(!self.infcx.next_trait_solver()); let candidate = match self.select_from_obligation(obligation) { Err(SelectionError::Overflow(OverflowError::Canonical)) => { @@ -299,6 +297,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { + if self.infcx.next_trait_solver() { + return self.infcx.select_in_new_trait_solver(obligation); + } + self.poly_select(&Obligation { cause: obligation.cause.clone(), param_env: obligation.param_env, @@ -1782,7 +1784,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !generics.is_own_empty() && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { p.has_non_region_infer() - && match p.unpack() { + && match p.kind() { ty::GenericArgKind::Const(ct) => { self.infcx.shallow_resolve_const(ct) != ct } @@ -2866,7 +2868,7 @@ fn rebind_coroutine_witness_types<'tcx>( let shifted_coroutine_types = tcx.shift_bound_var_indices(bound_vars.len(), bound_coroutine_types.skip_binder()); ty::Binder::bind_with_vars( - ty::EarlyBinder::bind(shifted_coroutine_types.to_vec()).instantiate(tcx, args), + ty::EarlyBinder::bind(shifted_coroutine_types.types.to_vec()).instantiate(tcx, args), tcx.mk_bound_variable_kinds_from_iter( bound_vars.iter().chain(bound_coroutine_types.bound_vars()), ), diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index e6d5d336b8d5..3f7413454047 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -39,7 +39,7 @@ impl<'tcx> At<'_, 'tcx> { return Ok(term); } - let new_infer = match term.unpack() { + let new_infer = match term.kind() { ty::TermKind::Ty(_) => self.infcx.next_ty_var(self.cause.span).into(), ty::TermKind::Const(_) => self.infcx.next_const_var(self.cause.span).into(), }; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 08d3b92e9b5e..3018dad8e091 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -37,7 +37,7 @@ pub fn obligations<'tcx>( span: Span, ) -> Option> { // Handle the "cycle" case (see comment above) by bailing out if necessary. - let term = match term.unpack() { + let term = match term.kind() { TermKind::Ty(ty) => { match ty.kind() { ty::Infer(ty::TyVar(_)) => { diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs new file mode 100644 index 000000000000..447e13126ccd --- /dev/null +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -0,0 +1,37 @@ +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, TyCtxt, fold_regions}; + +/// Return the set of types that should be taken into account when checking +/// trait bounds on a coroutine's internal state. This properly replaces +/// `ReErased` with new existential bound lifetimes. +pub(crate) fn coroutine_hidden_types<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { + let coroutine_layout = tcx.mir_coroutine_witnesses(def_id); + let mut vars = vec![]; + let bound_tys = tcx.mk_type_list_from_iter( + coroutine_layout + .as_ref() + .map_or_else(|| [].iter(), |l| l.field_tys.iter()) + .filter(|decl| !decl.ignore_for_traits) + .map(|decl| { + let ty = fold_regions(tcx, decl.ty, |re, debruijn| { + assert_eq!(re, tcx.lifetimes.re_erased); + let var = ty::BoundVar::from_usize(vars.len()); + vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)); + ty::Region::new_bound( + tcx, + debruijn, + ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }, + ) + }); + ty + }), + ); + + ty::EarlyBinder::bind(ty::Binder::bind_with_vars( + ty::CoroutineWitnessTypes { types: bound_tys }, + tcx.mk_bound_variable_kinds(&vars), + )) +} diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index 697c83918031..32d8c3f58e08 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -5,6 +5,7 @@ // tidy-alphabetical-end mod codegen; +mod coroutine_witnesses; mod dropck_outlives; mod evaluate_obligation; mod implied_outlives_bounds; @@ -24,4 +25,5 @@ pub fn provide(p: &mut Providers) { normalize_erasing_regions::provide(p); type_op::provide(p); p.codegen_select_candidate = codegen::codegen_select_candidate; + p.coroutine_hidden_types = coroutine_witnesses::coroutine_hidden_types; } diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 841f602d985c..3b4482146d4f 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -321,7 +321,10 @@ fn opaque_types_defined_by<'tcx>( collector.collect_taits_declared_in_body(); } // Closures and coroutines are type checked with their parent - DefKind::Closure | DefKind::InlineConst => { + // Note that we also support `SyntheticCoroutineBody` since we create + // a MIR body for the def kind, and some MIR passes (like promotion) + // may require doing analysis using its typing env. + DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => { collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); } DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} @@ -343,8 +346,7 @@ fn opaque_types_defined_by<'tcx>( | DefKind::ForeignMod | DefKind::Field | DefKind::LifetimeParam - | DefKind::Impl { .. } - | DefKind::SyntheticCoroutineBody => { + | DefKind::Impl { .. } => { span_bug!( tcx.def_span(item), "`opaque_types_defined_by` not defined for {} `{item:?}`", diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 98b1550e1a3d..33d334092ba9 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -74,7 +74,7 @@ fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representab // but the type parameters may cause a cycle with an upstream type let params_in_repr = tcx.params_in_repr(adt.did()); for (i, arg) in args.iter().enumerate() { - if let ty::GenericArgKind::Type(ty) = arg.unpack() { + if let ty::GenericArgKind::Type(ty) = arg.kind() { if params_in_repr.contains(i as u32) { rtry!(representability_ty(tcx, ty)); } @@ -104,7 +104,7 @@ fn params_in_repr_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, params_in_repr: &mut ty::Adt(adt, args) => { let inner_params_in_repr = tcx.params_in_repr(adt.did()); for (i, arg) in args.iter().enumerate() { - if let ty::GenericArgKind::Type(ty) = arg.unpack() { + if let ty::GenericArgKind::Type(ty) = arg.kind() { if inner_params_in_repr.contains(i as u32) { params_in_repr_ty(tcx, ty, params_in_repr); } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 0c49ddff39bc..330aaa25d135 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -274,7 +274,7 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSe let def = tcx.adt_def(def_id); let num_params = tcx.generics_of(def_id).count(); - let maybe_unsizing_param_idx = |arg: ty::GenericArg<'tcx>| match arg.unpack() { + let maybe_unsizing_param_idx = |arg: ty::GenericArg<'tcx>| match arg.kind() { ty::GenericArgKind::Type(ty) => match ty.kind() { ty::Param(p) => Some(p.index), _ => None, diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 000cf1e1fd8b..55c0a3bba9f2 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -676,7 +676,7 @@ impl<'a, I: Interner> TypeFolder for ArgFolder<'a, I> { // the specialized routine `ty::replace_late_regions()`. match r.kind() { ty::ReEarlyParam(data) => { - let rk = self.args.get(data.index() as usize).map(|k| k.kind()); + let rk = self.args.get(data.index() as usize).map(|arg| arg.kind()); match rk { Some(ty::GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), Some(other) => self.region_param_expected(data, r, other), @@ -711,12 +711,20 @@ impl<'a, I: Interner> TypeFolder for ArgFolder<'a, I> { c.super_fold_with(self) } } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_param() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_param() { c.super_fold_with(self) } else { c } + } } impl<'a, I: Interner> ArgFolder<'a, I> { fn ty_for_param(&self, p: I::ParamTy, source_ty: I::Ty) -> I::Ty { // Look up the type in the args. It really should be in there. - let opt_ty = self.args.get(p.index() as usize).map(|k| k.kind()); + let opt_ty = self.args.get(p.index() as usize).map(|arg| arg.kind()); let ty = match opt_ty { Some(ty::GenericArgKind::Type(ty)) => ty, Some(kind) => self.type_param_expected(p, source_ty, kind), @@ -753,7 +761,7 @@ impl<'a, I: Interner> ArgFolder<'a, I> { fn const_for_param(&self, p: I::ParamConst, source_ct: I::Const) -> I::Const { // Look up the const in the args. It really should be in there. - let opt_ct = self.args.get(p.index() as usize).map(|k| k.kind()); + let opt_ct = self.args.get(p.index() as usize).map(|arg| arg.kind()); let ct = match opt_ct { Some(ty::GenericArgKind::Const(ct)) => ct, Some(kind) => self.const_param_expected(p, source_ct, kind), diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 67b67df4b281..2b1b0617cef6 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -41,7 +41,7 @@ pub struct CanonicalQueryInput { pub struct Canonical { pub value: V, pub max_universe: UniverseIndex, - pub variables: I::CanonicalVars, + pub variables: I::CanonicalVarKinds, } impl Canonical { @@ -89,63 +89,6 @@ impl fmt::Display for Canonical { /// a copy of the canonical value in some other inference context, /// with fresh inference variables replacing the canonical values. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -pub struct CanonicalVarInfo { - pub kind: CanonicalVarKind, -} - -impl CanonicalVarInfo { - pub fn universe(self) -> UniverseIndex { - self.kind.universe() - } - - #[must_use] - pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarInfo { - CanonicalVarInfo { kind: self.kind.with_updated_universe(ui) } - } - - pub fn is_existential(&self) -> bool { - match self.kind { - CanonicalVarKind::Ty(_) => true, - CanonicalVarKind::PlaceholderTy(_) => false, - CanonicalVarKind::Region(_) => true, - CanonicalVarKind::PlaceholderRegion(..) => false, - CanonicalVarKind::Const(_) => true, - CanonicalVarKind::PlaceholderConst(_) => false, - } - } - - pub fn is_region(&self) -> bool { - match self.kind { - CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, - CanonicalVarKind::Ty(_) - | CanonicalVarKind::PlaceholderTy(_) - | CanonicalVarKind::Const(_) - | CanonicalVarKind::PlaceholderConst(_) => false, - } - } - - pub fn expect_placeholder_index(self) -> usize { - match self.kind { - CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => { - panic!("expected placeholder: {self:?}") - } - - CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(), - CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(), - CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(), - } - } -} - -/// Describes the "kind" of the canonical variable. This is a "kind" -/// in the type-theory sense of the term -- i.e., a "meta" type system -/// that analyzes type-like values. -#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -214,6 +157,39 @@ impl CanonicalVarKind { } } } + + pub fn is_existential(self) -> bool { + match self { + CanonicalVarKind::Ty(_) => true, + CanonicalVarKind::PlaceholderTy(_) => false, + CanonicalVarKind::Region(_) => true, + CanonicalVarKind::PlaceholderRegion(..) => false, + CanonicalVarKind::Const(_) => true, + CanonicalVarKind::PlaceholderConst(_) => false, + } + } + + pub fn is_region(self) -> bool { + match self { + CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, + CanonicalVarKind::Ty(_) + | CanonicalVarKind::PlaceholderTy(_) + | CanonicalVarKind::Const(_) + | CanonicalVarKind::PlaceholderConst(_) => false, + } + } + + pub fn expect_placeholder_index(self) -> usize { + match self { + CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => { + panic!("expected placeholder: {self:?}") + } + + CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(), + CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(), + CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(), + } + } } /// Rust actually has more than one category of type variables; @@ -306,11 +282,11 @@ impl CanonicalVarValues { // Given a list of canonical variables, construct a set of values which are // the identity response. - pub fn make_identity(cx: I, infos: I::CanonicalVars) -> CanonicalVarValues { + pub fn make_identity(cx: I, infos: I::CanonicalVarKinds) -> CanonicalVarValues { CanonicalVarValues { var_values: cx.mk_args_from_iter(infos.iter().enumerate().map( - |(i, info)| -> I::GenericArg { - match info.kind { + |(i, kind)| -> I::GenericArg { + match kind { CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => { Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) .into() diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 7ed0f92b6398..37cc2baa402a 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -479,8 +479,8 @@ impl FlagComputation { } fn add_args(&mut self, args: &[I::GenericArg]) { - for kind in args { - match kind.kind() { + for arg in args { + match arg.kind() { ty::GenericArgKind::Type(ty) => self.add_ty(ty), ty::GenericArgKind::Lifetime(lt) => self.add_region(lt), ty::GenericArgKind::Const(ct) => self.add_const(ct), diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index ce1188070ca7..a5eb8699e5fc 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -152,6 +152,10 @@ pub trait TypeFolder: Sized { fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { p.super_fold_with(self) } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + c.super_fold_with(self) + } } /// This trait is implemented for every folding traversal. There is a fold @@ -190,6 +194,10 @@ pub trait FallibleTypeFolder: Sized { fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { p.try_super_fold_with(self) } + + fn try_fold_clauses(&mut self, c: I::Clauses) -> Result { + c.try_super_fold_with(self) + } } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index c14907621173..2bc12d0a23bf 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -1,11 +1,10 @@ -use std::fmt::Debug; - use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::fold::TypeFoldable; +use crate::inherent::*; use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; use crate::{self as ty, Interner}; @@ -168,6 +167,8 @@ pub trait InferCtxtLike: Sized { vid: ty::RegionVid, ) -> ::Region; + fn is_changed_arg(&self, arg: ::GenericArg) -> bool; + fn next_region_infer(&self) -> ::Region; fn next_ty_infer(&self) -> ::Ty; fn next_const_infer(&self) -> ::Const; @@ -248,7 +249,7 @@ pub trait InferCtxtLike: Sized { span: ::Span, ); - type OpaqueTypeStorageEntries: Debug + Copy + Default; + type OpaqueTypeStorageEntries: OpaqueTypeStorageEntries; fn opaque_types_storage_num_entries(&self) -> Self::OpaqueTypeStorageEntries; fn clone_opaque_types_lookup_table( &self, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index ee4a8096462a..fa88bcb891a9 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -297,6 +297,7 @@ pub trait GenericArg>: + From + From + From + + From { fn as_term(&self) -> Option { match self.kind() { @@ -510,6 +511,18 @@ pub trait Clause>: fn instantiate_supertrait(self, cx: I, trait_ref: ty::Binder>) -> Self; } +pub trait Clauses>: + Copy + + Debug + + Hash + + Eq + + TypeSuperVisitable + + TypeSuperFoldable + + Flags + + SliceLike +{ +} + /// Common capabilities of placeholder kinds pub trait PlaceholderLike: Copy + Debug + Hash + Eq { fn universe(self) -> ty::UniverseIndex; @@ -596,6 +609,13 @@ pub trait Span: Copy + Debug + Hash + Eq + TypeFoldable { fn dummy() -> Self; } +pub trait OpaqueTypeStorageEntries: Debug + Copy + Default { + /// Whether the number of opaques has changed in a way that necessitates + /// reevaluating a goal. For now, this is only when the number of non-duplicated + /// entries changed. + fn needs_reevaluation(self, canonicalized: usize) -> bool; +} + pub trait SliceLike: Sized + Copy { type Item: Copy; type IntoIter: Iterator + DoubleEndedIterator; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 0fd2d9f3ad38..a9917192144f 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -12,7 +12,7 @@ use crate::ir_print::IrPrint; use crate::lang_items::TraitSolverLangItem; use crate::relate::Relate; use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult}; -use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; +use crate::visit::{Flags, TypeVisitable}; use crate::{self as ty, search_graph}; #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] @@ -64,13 +64,16 @@ pub trait Interner: + TypeVisitable + SliceLike; - type CanonicalVars: Copy + type CanonicalVarKinds: Copy + Debug + Hash + Eq - + SliceLike> + + SliceLike> + Default; - fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars; + fn mk_canonical_var_kinds( + self, + kinds: &[ty::CanonicalVarKind], + ) -> Self::CanonicalVarKinds; type ExternalConstraints: Copy + Debug @@ -143,7 +146,7 @@ pub trait Interner: type ParamEnv: ParamEnv; type Predicate: Predicate; type Clause: Clause; - type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; + type Clauses: Clauses; fn with_global_cache(self, f: impl FnOnce(&mut search_graph::GlobalCache) -> R) -> R; @@ -210,7 +213,7 @@ pub trait Interner: fn coroutine_hidden_types( self, def_id: Self::DefId, - ) -> ty::EarlyBinder>; + ) -> ty::EarlyBinder>>; fn fn_sig( self, diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index eae3213ead0d..1b5d04e6025f 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -193,7 +193,7 @@ impl fmt::Debug for RegionKind { ReVar(vid) => write!(f, "{vid:?}"), - RePlaceholder(placeholder) => write!(f, "{placeholder:?}"), + RePlaceholder(placeholder) => write!(f, "'{placeholder:?}"), // Use `'{erased}` as the output instead of `'erased` so that its more obviously distinct from // a `ReEarlyParam` named `'erased`. Technically that would print as `'erased/#IDX` so this is diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index cf2e4284d10d..0cd98b5aa53b 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1163,3 +1163,13 @@ pub struct FnHeader { pub safety: I::Safety, pub abi: I::Abi, } + +#[derive_where(Clone, Copy, Debug, PartialEq, Eq, Hash; I: Interner)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +pub struct CoroutineWitnessTypes { + pub types: I::Tys, +} diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index ccb84e259112..fc3864dd5ae6 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -120,8 +120,8 @@ pub trait TypeVisitor: Sized { p.super_visit_with(self) } - fn visit_clauses(&mut self, p: I::Clauses) -> Self::Result { - p.super_visit_with(self) + fn visit_clauses(&mut self, c: I::Clauses) -> Self::Result { + c.super_visit_with(self) } fn visit_error(&mut self, _guar: I::ErrorGuaranteed) -> Self::Result { diff --git a/library/Cargo.lock b/library/Cargo.lock index 02018057ed53..0c75977ee798 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.159" +version = "0.1.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "448068da8f2326b2a0472353cb401dd8795a89c007ef30fff90f50706e862e72" +checksum = "6376049cfa92c0aa8b9ac95fae22184b981c658208d4ed8a1dc553cd83612895" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 9d0d957226d2..365c9dc00dfc 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -16,7 +16,7 @@ bench = false [dependencies] core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.159", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.160", features = ['rustc-dep-of-std'] } [features] compiler-builtins-mem = ['compiler_builtins/mem'] diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 5ca32ed741af..52b98291ff93 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1151,7 +1151,7 @@ impl BTreeMap { K: Ord, F: FnMut(&K, &mut V) -> bool, { - self.extract_if(|k, v| !f(k, v)).for_each(drop); + self.extract_if(.., |k, v| !f(k, v)).for_each(drop); } /// Moves all elements from `other` into `self`, leaving `other` empty. @@ -1397,11 +1397,13 @@ impl BTreeMap { } } - /// Creates an iterator that visits all elements (key-value pairs) in - /// ascending key order and uses a closure to determine if an element should - /// be removed. If the closure returns `true`, the element is removed from - /// the map and yielded. If the closure returns `false`, or panics, the - /// element remains in the map and will not be yielded. + /// Creates an iterator that visits elements (key-value pairs) in the specified range in + /// ascending key order and uses a closure to determine if an element + /// should be removed. + /// + /// If the closure returns `true`, the element is removed from the map and + /// yielded. If the closure returns `false`, or panics, the element remains + /// in the map and will not be yielded. /// /// The iterator also lets you mutate the value of each element in the /// closure, regardless of whether you choose to keep or remove it. @@ -1421,33 +1423,42 @@ impl BTreeMap { /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); - /// let evens: BTreeMap<_, _> = map.extract_if(|k, _v| k % 2 == 0).collect(); + /// let evens: BTreeMap<_, _> = map.extract_if(.., |k, _v| k % 2 == 0).collect(); /// let odds = map; /// assert_eq!(evens.keys().copied().collect::>(), [0, 2, 4, 6]); /// assert_eq!(odds.keys().copied().collect::>(), [1, 3, 5, 7]); + /// + /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); + /// let low: BTreeMap<_, _> = map.extract_if(0..4, |_k, _v| true).collect(); + /// let high = map; + /// assert_eq!(low.keys().copied().collect::>(), [0, 1, 2, 3]); + /// assert_eq!(high.keys().copied().collect::>(), [4, 5, 6, 7]); /// ``` #[unstable(feature = "btree_extract_if", issue = "70530")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F, A> + pub fn extract_if(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, R, F, A> where K: Ord, + R: RangeBounds, F: FnMut(&K, &mut V) -> bool, { - let (inner, alloc) = self.extract_if_inner(); + let (inner, alloc) = self.extract_if_inner(range); ExtractIf { pred, inner, alloc } } - pub(super) fn extract_if_inner(&mut self) -> (ExtractIfInner<'_, K, V>, A) + pub(super) fn extract_if_inner(&mut self, range: R) -> (ExtractIfInner<'_, K, V, R>, A) where K: Ord, + R: RangeBounds, { if let Some(root) = self.root.as_mut() { let (root, dormant_root) = DormantMutRef::new(root); - let front = root.borrow_mut().first_leaf_edge(); + let first = root.borrow_mut().lower_bound(SearchBound::from_range(range.start_bound())); ( ExtractIfInner { length: &mut self.length, dormant_root: Some(dormant_root), - cur_leaf_edge: Some(front), + cur_leaf_edge: Some(first), + range, }, (*self.alloc).clone(), ) @@ -1457,6 +1468,7 @@ impl BTreeMap { length: &mut self.length, dormant_root: None, cur_leaf_edge: None, + range, }, (*self.alloc).clone(), ) @@ -1915,18 +1927,19 @@ pub struct ExtractIf< 'a, K, V, + R, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, > { pred: F, - inner: ExtractIfInner<'a, K, V>, + inner: ExtractIfInner<'a, K, V, R>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } /// Most of the implementation of ExtractIf are generic over the type /// of the predicate, thus also serving for BTreeSet::ExtractIf. -pub(super) struct ExtractIfInner<'a, K, V> { +pub(super) struct ExtractIfInner<'a, K, V, R> { /// Reference to the length field in the borrowed map, updated live. length: &'a mut usize, /// Buried reference to the root field in the borrowed map. @@ -1936,10 +1949,13 @@ pub(super) struct ExtractIfInner<'a, K, V> { /// Empty if the map has no root, if iteration went beyond the last leaf edge, /// or if a panic occurred in the predicate. cur_leaf_edge: Option, K, V, marker::Leaf>, marker::Edge>>, + /// Range over which iteration was requested. We don't need the left side, but we + /// can't extract the right side without requiring K: Clone. + range: R, } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl fmt::Debug for ExtractIf<'_, K, V, F, A> +impl fmt::Debug for ExtractIf<'_, K, V, R, F, A> where K: fmt::Debug, V: fmt::Debug, @@ -1951,8 +1967,10 @@ where } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl Iterator for ExtractIf<'_, K, V, F, A> +impl Iterator for ExtractIf<'_, K, V, R, F, A> where + K: PartialOrd, + R: RangeBounds, F: FnMut(&K, &mut V) -> bool, { type Item = (K, V); @@ -1966,7 +1984,7 @@ where } } -impl<'a, K, V> ExtractIfInner<'a, K, V> { +impl<'a, K, V, R> ExtractIfInner<'a, K, V, R> { /// Allow Debug implementations to predict the next element. pub(super) fn peek(&self) -> Option<(&K, &V)> { let edge = self.cur_leaf_edge.as_ref()?; @@ -1976,10 +1994,22 @@ impl<'a, K, V> ExtractIfInner<'a, K, V> { /// Implementation of a typical `ExtractIf::next` method, given the predicate. pub(super) fn next(&mut self, pred: &mut F, alloc: A) -> Option<(K, V)> where + K: PartialOrd, + R: RangeBounds, F: FnMut(&K, &mut V) -> bool, { while let Ok(mut kv) = self.cur_leaf_edge.take()?.next_kv() { let (k, v) = kv.kv_mut(); + + // On creation, we navigated directly to the left bound, so we need only check the + // right bound here to decide whether to stop. + match self.range.end_bound() { + Bound::Included(ref end) if (*k).le(end) => (), + Bound::Excluded(ref end) if (*k).lt(end) => (), + Bound::Unbounded => (), + _ => return None, + } + if pred(k, v) { *self.length -= 1; let (kv, pos) = kv.remove_kv_tracking( @@ -2011,7 +2041,13 @@ impl<'a, K, V> ExtractIfInner<'a, K, V> { } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +impl FusedIterator for ExtractIf<'_, K, V, R, F> +where + K: PartialOrd, + R: RangeBounds, + F: FnMut(&K, &mut V) -> bool, +{ +} #[stable(feature = "btree_range", since = "1.17.0")] impl<'a, K, V> Iterator for Range<'a, K, V> { diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 5975134382e7..79879d31d3df 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -944,7 +944,7 @@ mod test_extract_if { #[test] fn empty() { let mut map: BTreeMap = BTreeMap::new(); - map.extract_if(|_, _| unreachable!("there's nothing to decide on")).for_each(drop); + map.extract_if(.., |_, _| unreachable!("there's nothing to decide on")).for_each(drop); assert_eq!(map.height(), None); map.check(); } @@ -954,7 +954,7 @@ mod test_extract_if { fn consumed_keeping_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - assert!(map.extract_if(|_, _| false).eq(iter::empty())); + assert!(map.extract_if(.., |_, _| false).eq(iter::empty())); map.check(); } @@ -963,18 +963,42 @@ mod test_extract_if { fn consumed_removing_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs.clone()); - assert!(map.extract_if(|_, _| true).eq(pairs)); + assert!(map.extract_if(.., |_, _| true).eq(pairs)); assert!(map.is_empty()); map.check(); } + #[test] + fn consumed_removing_some() { + let pairs = (0..3).map(|i| (i, i)); + let map = BTreeMap::from_iter(pairs); + for x in 0..3 { + for y in 0..3 { + let mut map = map.clone(); + assert!(map.extract_if(x..y, |_, _| true).eq((x..y).map(|i| (i, i)))); + for i in 0..3 { + assert_ne!(map.contains_key(&i), (x..y).contains(&i)); + } + } + } + for x in 0..3 { + for y in 0..2 { + let mut map = map.clone(); + assert!(map.extract_if(x..=y, |_, _| true).eq((x..=y).map(|i| (i, i)))); + for i in 0..3 { + assert_ne!(map.contains_key(&i), (x..=y).contains(&i)); + } + } + } + } + // Explicitly consumes the iterator and modifies values through it. #[test] fn mutating_and_keeping() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); assert!( - map.extract_if(|_, v| { + map.extract_if(.., |_, v| { *v += 6; false }) @@ -991,7 +1015,7 @@ mod test_extract_if { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); assert!( - map.extract_if(|_, v| { + map.extract_if(.., |_, v| { *v += 6; true }) @@ -1005,7 +1029,7 @@ mod test_extract_if { fn underfull_keeping_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| false).for_each(drop); + map.extract_if(.., |_, _| false).for_each(drop); assert!(map.keys().copied().eq(0..3)); map.check(); } @@ -1015,7 +1039,7 @@ mod test_extract_if { let pairs = (0..3).map(|i| (i, i)); for doomed in 0..3 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), 2); map.check(); } @@ -1026,7 +1050,7 @@ mod test_extract_if { let pairs = (0..3).map(|i| (i, i)); for sacred in 0..3 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1036,7 +1060,7 @@ mod test_extract_if { fn underfull_removing_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1045,7 +1069,7 @@ mod test_extract_if { fn height_0_keeping_all() { let pairs = (0..node::CAPACITY).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| false).for_each(drop); + map.extract_if(.., |_, _| false).for_each(drop); assert!(map.keys().copied().eq(0..node::CAPACITY)); map.check(); } @@ -1055,7 +1079,7 @@ mod test_extract_if { let pairs = (0..node::CAPACITY).map(|i| (i, i)); for doomed in 0..node::CAPACITY { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), node::CAPACITY - 1); map.check(); } @@ -1066,7 +1090,7 @@ mod test_extract_if { let pairs = (0..node::CAPACITY).map(|i| (i, i)); for sacred in 0..node::CAPACITY { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1076,7 +1100,7 @@ mod test_extract_if { fn height_0_removing_all() { let pairs = (0..node::CAPACITY).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1084,7 +1108,7 @@ mod test_extract_if { #[test] fn height_0_keeping_half() { let mut map = BTreeMap::from_iter((0..16).map(|i| (i, i))); - assert_eq!(map.extract_if(|i, _| *i % 2 == 0).count(), 8); + assert_eq!(map.extract_if(.., |i, _| *i % 2 == 0).count(), 8); assert_eq!(map.len(), 8); map.check(); } @@ -1093,7 +1117,7 @@ mod test_extract_if { fn height_1_removing_all() { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1103,7 +1127,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); for doomed in 0..MIN_INSERTS_HEIGHT_1 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1); map.check(); } @@ -1114,7 +1138,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); for sacred in 0..MIN_INSERTS_HEIGHT_1 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1125,7 +1149,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i == doomed).for_each(drop); + map.extract_if(.., |i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); map.check(); } @@ -1136,7 +1160,7 @@ mod test_extract_if { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { let mut map = BTreeMap::from_iter(pairs.clone()); - map.extract_if(|i, _| *i != sacred).for_each(drop); + map.extract_if(.., |i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1146,7 +1170,7 @@ mod test_extract_if { fn height_2_removing_all() { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.extract_if(|_, _| true).for_each(drop); + map.extract_if(.., |_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1162,7 +1186,7 @@ mod test_extract_if { map.insert(b.spawn(Panic::InDrop), ()); map.insert(c.spawn(Panic::Never), ()); - catch_unwind(move || map.extract_if(|dummy, _| dummy.query(true)).for_each(drop)) + catch_unwind(move || map.extract_if(.., |dummy, _| dummy.query(true)).for_each(drop)) .unwrap_err(); assert_eq!(a.queried(), 1); @@ -1185,7 +1209,7 @@ mod test_extract_if { map.insert(c.spawn(Panic::InQuery), ()); catch_unwind(AssertUnwindSafe(|| { - map.extract_if(|dummy, _| dummy.query(true)).for_each(drop) + map.extract_if(.., |dummy, _| dummy.query(true)).for_each(drop) })) .unwrap_err(); @@ -1214,7 +1238,7 @@ mod test_extract_if { map.insert(c.spawn(Panic::InQuery), ()); { - let mut it = map.extract_if(|dummy, _| dummy.query(true)); + let mut it = map.extract_if(.., |dummy, _| dummy.query(true)); catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); // Iterator behavior after a panic is explicitly unspecified, // so this is just the current implementation: @@ -1658,7 +1682,7 @@ fn assert_sync() { } fn extract_if(v: &mut BTreeMap) -> impl Sync + '_ { - v.extract_if(|_, _| false) + v.extract_if(.., |_, _| false) } fn iter(v: &BTreeMap) -> impl Sync + '_ { @@ -1727,7 +1751,7 @@ fn assert_send() { } fn extract_if(v: &mut BTreeMap) -> impl Send + '_ { - v.extract_if(|_, _| false) + v.extract_if(.., |_, _| false) } fn iter(v: &BTreeMap) -> impl Send + '_ { diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 343934680b87..780bd8b0dd14 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1109,7 +1109,7 @@ impl BTreeSet { T: Ord, F: FnMut(&T) -> bool, { - self.extract_if(|v| !f(v)).for_each(drop); + self.extract_if(.., |v| !f(v)).for_each(drop); } /// Moves all elements from `other` into `self`, leaving `other` empty. @@ -1187,7 +1187,7 @@ impl BTreeSet { BTreeSet { map: self.map.split_off(value) } } - /// Creates an iterator that visits all elements in ascending order and + /// Creates an iterator that visits elements in the specified range in ascending order and /// uses a closure to determine if an element should be removed. /// /// If the closure returns `true`, the element is removed from the set and @@ -1208,18 +1208,25 @@ impl BTreeSet { /// use std::collections::BTreeSet; /// /// let mut set: BTreeSet = (0..8).collect(); - /// let evens: BTreeSet<_> = set.extract_if(|v| v % 2 == 0).collect(); + /// let evens: BTreeSet<_> = set.extract_if(.., |v| v % 2 == 0).collect(); /// let odds = set; /// assert_eq!(evens.into_iter().collect::>(), vec![0, 2, 4, 6]); /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 7]); + /// + /// let mut map: BTreeSet = (0..8).collect(); + /// let low: BTreeSet<_> = map.extract_if(0..4, |_v| true).collect(); + /// let high = map; + /// assert_eq!(low.into_iter().collect::>(), [0, 1, 2, 3]); + /// assert_eq!(high.into_iter().collect::>(), [4, 5, 6, 7]); /// ``` #[unstable(feature = "btree_extract_if", issue = "70530")] - pub fn extract_if<'a, F>(&'a mut self, pred: F) -> ExtractIf<'a, T, F, A> + pub fn extract_if<'a, F, R>(&'a mut self, range: R, pred: F) -> ExtractIf<'a, T, R, F, A> where T: Ord, + R: RangeBounds, F: 'a + FnMut(&T) -> bool, { - let (inner, alloc) = self.map.extract_if_inner(); + let (inner, alloc) = self.map.extract_if_inner(range); ExtractIf { pred, inner, alloc } } @@ -1554,17 +1561,18 @@ impl<'a, T, A: Allocator + Clone> IntoIterator for &'a BTreeSet { pub struct ExtractIf< 'a, T, + R, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, > { pred: F, - inner: super::map::ExtractIfInner<'a, T, SetValZST>, + inner: super::map::ExtractIfInner<'a, T, SetValZST, R>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl fmt::Debug for ExtractIf<'_, T, F, A> +impl fmt::Debug for ExtractIf<'_, T, R, F, A> where T: fmt::Debug, A: Allocator + Clone, @@ -1577,8 +1585,10 @@ where } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl<'a, T, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, F, A> +impl<'a, T, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, R, F, A> where + T: PartialOrd, + R: RangeBounds, F: 'a + FnMut(&T) -> bool, { type Item = T; @@ -1595,7 +1605,13 @@ where } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl FusedIterator for ExtractIf<'_, T, F, A> where F: FnMut(&T) -> bool {} +impl FusedIterator for ExtractIf<'_, T, R, F, A> +where + T: PartialOrd, + R: RangeBounds, + F: FnMut(&T) -> bool, +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl Extend for BTreeSet { diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index d538ef707eb9..85c9a98c461d 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -368,8 +368,8 @@ fn test_extract_if() { let mut x = BTreeSet::from([1]); let mut y = BTreeSet::from([1]); - x.extract_if(|_| true).for_each(drop); - y.extract_if(|_| false).for_each(drop); + x.extract_if(.., |_| true).for_each(drop); + y.extract_if(.., |_| false).for_each(drop); assert_eq!(x.len(), 0); assert_eq!(y.len(), 1); } @@ -385,7 +385,7 @@ fn test_extract_if_drop_panic_leak() { set.insert(b.spawn(Panic::InDrop)); set.insert(c.spawn(Panic::Never)); - catch_unwind(move || set.extract_if(|dummy| dummy.query(true)).for_each(drop)).ok(); + catch_unwind(move || set.extract_if(.., |dummy| dummy.query(true)).for_each(drop)).ok(); assert_eq!(a.queried(), 1); assert_eq!(b.queried(), 1); @@ -406,7 +406,7 @@ fn test_extract_if_pred_panic_leak() { set.insert(b.spawn(Panic::InQuery)); set.insert(c.spawn(Panic::InQuery)); - catch_unwind(AssertUnwindSafe(|| set.extract_if(|dummy| dummy.query(true)).for_each(drop))) + catch_unwind(AssertUnwindSafe(|| set.extract_if(.., |dummy| dummy.query(true)).for_each(drop))) .ok(); assert_eq!(a.queried(), 1); @@ -605,7 +605,7 @@ fn assert_sync() { } fn extract_if(v: &mut BTreeSet) -> impl Sync + '_ { - v.extract_if(|_| false) + v.extract_if(.., |_| false) } fn difference(v: &BTreeSet) -> impl Sync + '_ { @@ -644,7 +644,7 @@ fn assert_send() { } fn extract_if(v: &mut BTreeSet) -> impl Send + '_ { - v.extract_if(|_| false) + v.extract_if(.., |_| false) } fn difference(v: &BTreeSet) -> impl Send + '_ { diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 00e2805d11f6..d03c1969b5b7 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1124,20 +1124,20 @@ impl LinkedList { /// Creates an iterator which uses a closure to determine if an element should be removed. /// - /// If the closure returns true, then the element is removed and yielded. - /// If the closure returns false, the element will remain in the list and will not be yielded - /// by the iterator. + /// If the closure returns `true`, the element is removed from the list and + /// yielded. If the closure returns `false`, or panics, the element remains + /// in the list and will not be yielded. /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. /// Use `extract_if().for_each(drop)` if you do not need the returned iterator. /// - /// Note that `extract_if` lets you mutate every element in the filter closure, regardless of - /// whether you choose to keep or remove it. + /// The iterator also lets you mutate the value of each element in the + /// closure, regardless of whether you choose to keep or remove it. /// /// # Examples /// - /// Splitting a list into evens and odds, reusing the original list: + /// Splitting a list into even and odd values, reusing the original list: /// /// ``` /// use std::collections::LinkedList; diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 712f38a76c01..08b1828ff000 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1312,6 +1312,8 @@ impl VecDeque { /// /// If [`make_contiguous`] was previously called, all elements of the /// deque will be in the first slice and the second slice will be empty. + /// Otherwise, the exact split point depends on implementation details + /// and is not guaranteed. /// /// [`make_contiguous`]: VecDeque::make_contiguous /// @@ -1326,12 +1328,18 @@ impl VecDeque { /// deque.push_back(1); /// deque.push_back(2); /// - /// assert_eq!(deque.as_slices(), (&[0, 1, 2][..], &[][..])); + /// let expected = [0, 1, 2]; + /// let (front, back) = deque.as_slices(); + /// assert_eq!(&expected[..front.len()], front); + /// assert_eq!(&expected[front.len()..], back); /// /// deque.push_front(10); /// deque.push_front(9); /// - /// assert_eq!(deque.as_slices(), (&[9, 10][..], &[0, 1, 2][..])); + /// let expected = [9, 10, 0, 1, 2]; + /// let (front, back) = deque.as_slices(); + /// assert_eq!(&expected[..front.len()], front); + /// assert_eq!(&expected[front.len()..], back); /// ``` #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] @@ -1347,6 +1355,8 @@ impl VecDeque { /// /// If [`make_contiguous`] was previously called, all elements of the /// deque will be in the first slice and the second slice will be empty. + /// Otherwise, the exact split point depends on implementation details + /// and is not guaranteed. /// /// [`make_contiguous`]: VecDeque::make_contiguous /// @@ -1363,9 +1373,22 @@ impl VecDeque { /// deque.push_front(10); /// deque.push_front(9); /// - /// deque.as_mut_slices().0[0] = 42; - /// deque.as_mut_slices().1[0] = 24; - /// assert_eq!(deque.as_slices(), (&[42, 10][..], &[24, 1][..])); + /// // Since the split point is not guaranteed, we may need to update + /// // either slice. + /// let mut update_nth = |index: usize, val: u32| { + /// let (front, back) = deque.as_mut_slices(); + /// if index > front.len() - 1 { + /// back[index - front.len()] = val; + /// } else { + /// front[index] = val; + /// } + /// }; + /// + /// update_nth(0, 42); + /// update_nth(2, 24); + /// + /// let v: Vec<_> = deque.into(); + /// assert_eq!(v, [42, 10, 24, 1]); /// ``` #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 7c5d22e1ee9b..b49b3f41a767 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -69,7 +69,7 @@ use crate::boxed::Box; use crate::vec::Vec; impl [T] { - /// Sorts the slice, preserving initial order of equal elements. + /// Sorts the slice in ascending order, preserving initial order of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. @@ -137,7 +137,8 @@ impl [T] { stable_sort(self, T::lt); } - /// Sorts the slice with a comparison function, preserving initial order of equal elements. + /// Sorts the slice in ascending order with a comparison function, preserving initial order of + /// equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. @@ -197,7 +198,8 @@ impl [T] { stable_sort(self, |a, b| compare(a, b) == Less); } - /// Sorts the slice with a key extraction function, preserving initial order of equal elements. + /// Sorts the slice in ascending order with a key extraction function, preserving initial order + /// of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). @@ -252,7 +254,8 @@ impl [T] { stable_sort(self, |a, b| f(a).lt(&f(b))); } - /// Sorts the slice with a key extraction function, preserving initial order of equal elements. + /// Sorts the slice in ascending order with a key extraction function, preserving initial order + /// of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* /// log(*n*)) worst-case, where the key function is *O*(*m*). diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 24c5d4c92f71..f1b1734b8b2d 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -234,7 +234,7 @@ impl str { #[stable(feature = "str_box_extras", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline] - pub fn into_boxed_bytes(self: Box) -> Box<[u8]> { + pub fn into_boxed_bytes(self: Box) -> Box<[u8]> { self.into() } @@ -320,6 +320,7 @@ impl str { /// ``` #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] + #[doc(alias = "replace_first")] #[must_use = "this returns the replaced string as a new allocation, \ without modifying the original"] #[stable(feature = "str_replacen", since = "1.16.0")] @@ -501,7 +502,7 @@ impl str { #[rustc_allow_incoherent_impl] #[must_use = "`self` will be dropped if the result is not used"] #[inline] - pub fn into_string(self: Box) -> String { + pub fn into_string(self: Box) -> String { let slice = Box::<[u8]>::from(self); unsafe { String::from_utf8_unchecked(slice.into_vec()) } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 59879f23d785..ce7321544b6b 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3648,11 +3648,11 @@ impl Vec { Splice { drain: self.drain(range), replace_with: replace_with.into_iter() } } - /// Creates an iterator which uses a closure to determine if element in the range should be removed. + /// Creates an iterator which uses a closure to determine if an element in the range should be removed. /// - /// If the closure returns true, then the element is removed and yielded. - /// If the closure returns false, the element will remain in the vector and will not be yielded - /// by the iterator. + /// If the closure returns `true`, the element is removed from the vector + /// and yielded. If the closure returns `false`, or panics, the element + /// remains in the vector and will not be yielded. /// /// Only elements that fall in the provided range are considered for extraction, but any elements /// after the range will still have to be moved if any element has been extracted. @@ -3692,8 +3692,8 @@ impl Vec { /// But `extract_if` is easier to use. `extract_if` is also more efficient, /// because it can backshift the elements of the array in bulk. /// - /// Note that `extract_if` also lets you mutate the elements passed to the filter closure, - /// regardless of whether you choose to keep or remove them. + /// The iterator also lets you mutate the value of each element in the + /// closure, regardless of whether you choose to keep or remove it. /// /// # Panics /// @@ -3701,7 +3701,7 @@ impl Vec { /// /// # Examples /// - /// Splitting an array into evens and odds, reusing the original allocation: + /// Splitting a vector into even and odd values, reusing the original vector: /// /// ``` /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]; diff --git a/library/alloctests/benches/btree/map.rs b/library/alloctests/benches/btree/map.rs index 20f02dc3a968..778065fd9657 100644 --- a/library/alloctests/benches/btree/map.rs +++ b/library/alloctests/benches/btree/map.rs @@ -386,7 +386,7 @@ pub fn clone_slim_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_slim_100_and_drain_all(b: &mut Bencher) { let src = slim_map(100); - b.iter(|| src.clone().extract_if(|_, _| true).count()) + b.iter(|| src.clone().extract_if(.., |_, _| true).count()) } #[bench] @@ -394,7 +394,7 @@ pub fn clone_slim_100_and_drain_half(b: &mut Bencher) { let src = slim_map(100); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 100 / 2); assert_eq!(map.len(), 100 / 2); }) } @@ -457,7 +457,7 @@ pub fn clone_slim_10k_and_clear(b: &mut Bencher) { #[bench] pub fn clone_slim_10k_and_drain_all(b: &mut Bencher) { let src = slim_map(10_000); - b.iter(|| src.clone().extract_if(|_, _| true).count()) + b.iter(|| src.clone().extract_if(.., |_, _| true).count()) } #[bench] @@ -465,7 +465,7 @@ pub fn clone_slim_10k_and_drain_half(b: &mut Bencher) { let src = slim_map(10_000); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 10_000 / 2); assert_eq!(map.len(), 10_000 / 2); }) } @@ -528,7 +528,7 @@ pub fn clone_fat_val_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_fat_val_100_and_drain_all(b: &mut Bencher) { let src = fat_val_map(100); - b.iter(|| src.clone().extract_if(|_, _| true).count()) + b.iter(|| src.clone().extract_if(.., |_, _| true).count()) } #[bench] @@ -536,7 +536,7 @@ pub fn clone_fat_val_100_and_drain_half(b: &mut Bencher) { let src = fat_val_map(100); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.extract_if(.., |i, _| i % 2 == 0).count(), 100 / 2); assert_eq!(map.len(), 100 / 2); }) } diff --git a/library/alloctests/benches/btree/set.rs b/library/alloctests/benches/btree/set.rs index 5aa395b4d52a..027c86a89a51 100644 --- a/library/alloctests/benches/btree/set.rs +++ b/library/alloctests/benches/btree/set.rs @@ -69,7 +69,7 @@ pub fn clone_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_100_and_drain_all(b: &mut Bencher) { let src = slim_set(100); - b.iter(|| src.clone().extract_if(|_| true).count()) + b.iter(|| src.clone().extract_if(.., |_| true).count()) } #[bench] @@ -77,7 +77,7 @@ pub fn clone_100_and_drain_half(b: &mut Bencher) { let src = slim_set(100); b.iter(|| { let mut set = src.clone(); - assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 100 / 2); + assert_eq!(set.extract_if(.., |i| i % 2 == 0).count(), 100 / 2); assert_eq!(set.len(), 100 / 2); }) } @@ -140,7 +140,7 @@ pub fn clone_10k_and_clear(b: &mut Bencher) { #[bench] pub fn clone_10k_and_drain_all(b: &mut Bencher) { let src = slim_set(10_000); - b.iter(|| src.clone().extract_if(|_| true).count()) + b.iter(|| src.clone().extract_if(.., |_| true).count()) } #[bench] @@ -148,7 +148,7 @@ pub fn clone_10k_and_drain_half(b: &mut Bencher) { let src = slim_set(10_000); b.iter(|| { let mut set = src.clone(); - assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(set.extract_if(.., |i| i % 2 == 0).count(), 10_000 / 2); assert_eq!(set.len(), 10_000 / 2); }) } diff --git a/library/alloctests/tests/autotraits.rs b/library/alloctests/tests/autotraits.rs index 6b82deeac8ac..ad0a10385969 100644 --- a/library/alloctests/tests/autotraits.rs +++ b/library/alloctests/tests/autotraits.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + fn require_sync(_: T) {} fn require_send_sync(_: T) {} @@ -55,7 +57,13 @@ fn test_btree_map() { require_send_sync(async { let _v = None::< - alloc::collections::btree_map::ExtractIf<'_, &u32, &u32, fn(&&u32, &mut &u32) -> bool>, + alloc::collections::btree_map::ExtractIf< + '_, + &u32, + &u32, + Range, + fn(&&u32, &mut &u32) -> bool, + >, >; async {}.await; }); @@ -144,7 +152,9 @@ fn test_btree_set() { }); require_send_sync(async { - let _v = None:: bool>>; + let _v = None::< + alloc::collections::btree_set::ExtractIf<'_, &u32, Range, fn(&&u32) -> bool>, + >; async {}.await; }); diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index e8a03aadc339..380f67f91f94 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -126,6 +126,7 @@ impl Layout { #[rustc_const_stable(feature = "const_alloc_layout_unchecked", since = "1.36.0")] #[must_use] #[inline] + #[track_caller] pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { assert_unsafe_precondition!( check_library_ub, diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 4476e3f79238..221ca91e005f 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -588,7 +588,7 @@ impl [T; N] { /// Returns a mutable slice containing the entire array. Equivalent to /// `&mut s[..]`. #[stable(feature = "array_as_slice", since = "1.57.0")] - #[rustc_const_unstable(feature = "const_array_as_mut_slice", issue = "133333")] + #[rustc_const_stable(feature = "const_array_as_mut_slice", since = "CURRENT_RUSTC_VERSION")] pub const fn as_mut_slice(&mut self) -> &mut [T] { self } diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 48de4f17b1b3..0b72b4780f16 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -503,6 +503,7 @@ impl AsciiChar { /// something useful. It might be tightened before stabilization.) #[unstable(feature = "ascii_char", issue = "110998")] #[inline] + #[track_caller] pub const unsafe fn digit_unchecked(d: u8) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index fd17f92f7be0..78cd89fefae7 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -22,6 +22,7 @@ pub(super) const fn from_u32(i: u32) -> Option { #[inline] #[must_use] #[allow(unnecessary_transmutes)] +#[track_caller] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index c237ac84cf40..2c0662c96290 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -38,7 +38,16 @@ mod uninit; -/// A common trait for the ability to explicitly duplicate an object. +/// A common trait that allows explicit creation of a duplicate value. +/// +/// Calling [`clone`] always produces a new value. +/// However, for types that are references to other data (such as smart pointers or references), +/// the new value may still point to the same underlying data, rather than duplicating it. +/// See [`Clone::clone`] for more details. +/// +/// This distinction is especially important when using `#[derive(Clone)]` on structs containing +/// smart pointers like `Arc>` - the cloned struct will share mutable state with the +/// original. /// /// Differs from [`Copy`] in that [`Copy`] is implicit and an inexpensive bit-wise copy, while /// `Clone` is always explicit and may or may not be expensive. In order to enforce @@ -147,7 +156,16 @@ mod uninit; #[rustc_diagnostic_item = "Clone"] #[rustc_trivial_field_reads] pub trait Clone: Sized { - /// Returns a copy of the value. + /// Returns a duplicate of the value. + /// + /// Note that what "duplicate" means varies by type: + /// - For most types, this creates a deep, independent copy + /// - For reference types like `&T`, this creates another reference to the same value + /// - For smart pointers like [`Arc`] or [`Rc`], this increments the reference count + /// but still points to the same underlying data + /// + /// [`Arc`]: ../../std/sync/struct.Arc.html + /// [`Rc`]: ../../std/rc/struct.Rc.html /// /// # Examples /// @@ -157,6 +175,23 @@ pub trait Clone: Sized { /// /// assert_eq!("Hello", hello.clone()); /// ``` + /// + /// Example with a reference-counted type: + /// + /// ``` + /// use std::sync::{Arc, Mutex}; + /// + /// let data = Arc::new(Mutex::new(vec![1, 2, 3])); + /// let data_clone = data.clone(); // Creates another Arc pointing to the same Mutex + /// + /// { + /// let mut lock = data.lock().unwrap(); + /// lock.push(4); + /// } + /// + /// // Changes are visible through the clone because they share the same underlying data + /// assert_eq!(*data_clone.lock().unwrap(), vec![1, 2, 3, 4]); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "cloning is often expensive and is not expected to have side effects"] // Clone::clone is special because the compiler generates MIR to implement it for some types. diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index c542a28beb87..d86dc24fb579 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -575,7 +575,7 @@ pub trait Into: Sized { #[rustc_diagnostic_item = "From"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented(on( - all(_Self = "&str", T = "alloc::string::String"), + all(Self = "&str", T = "alloc::string::String"), note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix", ))] #[doc(search_unbox)] diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index ac07c645c019..595cc1fe025e 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -207,7 +207,7 @@ impl CStr { /// * `ptr` must be [valid] for reads of bytes up to and including the nul terminator. /// This means in particular: /// - /// * The entire memory range of this `CStr` must be contained within a single allocated object! + /// * The entire memory range of this `CStr` must be contained within a single allocation! /// * `ptr` must be non-null even for a zero-length cstr. /// /// * The memory referenced by the returned `CStr` must not be mutated for @@ -511,13 +511,8 @@ impl CStr { /// # Examples /// /// ``` - /// use std::ffi::CStr; - /// - /// let cstr = CStr::from_bytes_with_nul(b"foo\0").unwrap(); - /// assert_eq!(cstr.count_bytes(), 3); - /// - /// let cstr = CStr::from_bytes_with_nul(b"\0").unwrap(); - /// assert_eq!(cstr.count_bytes(), 0); + /// assert_eq!(c"foo".count_bytes(), 3); + /// assert_eq!(c"".count_bytes(), 0); /// ``` #[inline] #[must_use] @@ -533,19 +528,8 @@ impl CStr { /// # Examples /// /// ``` - /// use std::ffi::CStr; - /// # use std::ffi::FromBytesWithNulError; - /// - /// # fn main() { test().unwrap(); } - /// # fn test() -> Result<(), FromBytesWithNulError> { - /// let cstr = CStr::from_bytes_with_nul(b"foo\0")?; - /// assert!(!cstr.is_empty()); - /// - /// let empty_cstr = CStr::from_bytes_with_nul(b"\0")?; - /// assert!(empty_cstr.is_empty()); + /// assert!(!c"foo".is_empty()); /// assert!(c"".is_empty()); - /// # Ok(()) - /// # } /// ``` #[inline] #[stable(feature = "cstr_is_empty", since = "1.71.0")] @@ -569,10 +553,7 @@ impl CStr { /// # Examples /// /// ``` - /// use std::ffi::CStr; - /// - /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); - /// assert_eq!(cstr.to_bytes(), b"foo"); + /// assert_eq!(c"foo".to_bytes(), b"foo"); /// ``` #[inline] #[must_use = "this returns the result of the operation, \ @@ -598,10 +579,7 @@ impl CStr { /// # Examples /// /// ``` - /// use std::ffi::CStr; - /// - /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); - /// assert_eq!(cstr.to_bytes_with_nul(), b"foo\0"); + /// assert_eq!(c"foo".to_bytes_with_nul(), b"foo\0"); /// ``` #[inline] #[must_use = "this returns the result of the operation, \ @@ -623,10 +601,8 @@ impl CStr { /// /// ``` /// #![feature(cstr_bytes)] - /// use std::ffi::CStr; /// - /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); - /// assert!(cstr.bytes().eq(*b"foo")); + /// assert!(c"foo".bytes().eq(*b"foo")); /// ``` #[inline] #[unstable(feature = "cstr_bytes", issue = "112115")] @@ -645,10 +621,7 @@ impl CStr { /// # Examples /// /// ``` - /// use std::ffi::CStr; - /// - /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); - /// assert_eq!(cstr.to_str(), Ok("foo")); + /// assert_eq!(c"foo".to_str(), Ok("foo")); /// ``` #[stable(feature = "cstr_to_str", since = "1.4.0")] #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] @@ -659,6 +632,30 @@ impl CStr { // instead of doing it afterwards. str::from_utf8(self.to_bytes()) } + + /// Returns an object that implements [`Display`] for safely printing a [`CStr`] that may + /// contain non-Unicode data. + /// + /// Behaves as if `self` were first lossily converted to a `str`, with invalid UTF-8 presented + /// as the Unicode replacement character: �. + /// + /// [`Display`]: fmt::Display + /// + /// # Examples + /// + /// ``` + /// #![feature(cstr_display)] + /// + /// let cstr = c"Hello, world!"; + /// println!("{}", cstr.display()); + /// ``` + #[unstable(feature = "cstr_display", issue = "139984")] + #[must_use = "this does not display the `CStr`; \ + it returns an object that can be displayed"] + #[inline] + pub fn display(&self) -> impl fmt::Display { + crate::bstr::ByteStr::from_bytes(self.to_bytes()) + } } // `.to_bytes()` representations are compared instead of the inner `[c_char]`s, diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 288d0df0d05b..0bc98e2ea864 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -28,7 +28,7 @@ pub mod c_str; issue = "44930", reason = "the `c_variadic` feature has not been properly tested on all supported platforms" )] -pub use self::va_list::{VaList, VaListImpl}; +pub use self::va_list::{VaArgSafe, VaList, VaListImpl}; #[unstable( feature = "c_variadic", diff --git a/library/core/src/ffi/primitives.rs b/library/core/src/ffi/primitives.rs index 351bf9f83147..fa23cf33af43 100644 --- a/library/core/src/ffi/primitives.rs +++ b/library/core/src/ffi/primitives.rs @@ -35,7 +35,7 @@ type_alias! { "c_float.md", c_float = f32; } type_alias! { "c_double.md", c_double = f64; } mod c_char_definition { - crate::cfg_match! { + crate::cfg_select! { // These are the targets on which c_char is unsigned. Usually the // signedness is the same for all target_os values on a given architecture // but there are some exceptions (see isSignedCharDefault() in clang). @@ -133,7 +133,7 @@ mod c_char_definition { } mod c_long_definition { - crate::cfg_match! { + crate::cfg_select! { any( all(target_pointer_width = "64", not(windows)), // wasm32 Linux ABI uses 64-bit long @@ -172,7 +172,7 @@ pub type c_ptrdiff_t = isize; pub type c_ssize_t = isize; mod c_int_definition { - crate::cfg_match! { + crate::cfg_select! { any(target_arch = "avr", target_arch = "msp430") => { pub(super) type c_int = i16; pub(super) type c_uint = u16; diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 1ad8038cbf6e..8f7c090bc1ba 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -5,148 +5,120 @@ use crate::ffi::c_void; #[allow(unused_imports)] use crate::fmt; -use crate::marker::PhantomData; +use crate::marker::{PhantomData, PhantomInvariantLifetime}; use crate::ops::{Deref, DerefMut}; -/// Basic implementation of a `va_list`. // The name is WIP, using `VaListImpl` for now. -#[cfg(any( +// +// Most targets explicitly specify the layout of `va_list`, this layout is matched here. +crate::cfg_select! { all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "xtensa"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -#[repr(transparent)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - ptr: *mut c_void, + target_arch = "aarch64", + not(target_vendor = "apple"), + not(target_os = "uefi"), + not(windows), + ) => { + /// AArch64 ABI implementation of a `va_list`. See the + /// [AArch64 Procedure Call Standard] for more details. + /// + /// [AArch64 Procedure Call Standard]: + /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf + #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[derive(Debug)] + #[lang = "va_list"] + pub struct VaListImpl<'f> { + stack: *mut c_void, + gr_top: *mut c_void, + vr_top: *mut c_void, + gr_offs: i32, + vr_offs: i32, + _marker: PhantomInvariantLifetime<'f>, + } + } + all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => { + /// PowerPC ABI implementation of a `va_list`. + #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[derive(Debug)] + #[lang = "va_list"] + pub struct VaListImpl<'f> { + gpr: u8, + fpr: u8, + reserved: u16, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomInvariantLifetime<'f>, + } + } + target_arch = "s390x" => { + /// s390x ABI implementation of a `va_list`. + #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[derive(Debug)] + #[lang = "va_list"] + pub struct VaListImpl<'f> { + gpr: i64, + fpr: i64, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomInvariantLifetime<'f>, + } + } + all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => { + /// x86_64 ABI implementation of a `va_list`. + #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 + #[derive(Debug)] + #[lang = "va_list"] + pub struct VaListImpl<'f> { + gp_offset: i32, + fp_offset: i32, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomInvariantLifetime<'f>, + } + } + target_arch = "xtensa" => { + /// Xtensa ABI implementation of a `va_list`. + #[repr(C)] + #[derive(Debug)] + #[lang = "va_list"] + pub struct VaListImpl<'f> { + stk: *mut i32, + reg: *mut i32, + ndx: i32, + _marker: PhantomInvariantLifetime<'f>, + } + } - // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to - // the region of the function it's defined in - _marker: PhantomData<&'f mut &'f c_void>, -} + // The fallback implementation, used for: + // + // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599) + // - windows + // - uefi + // - any other target for which we don't specify the `VaListImpl` above + // + // In this implementation the `va_list` type is just an alias for an opaque pointer. + // That pointer is probably just the next variadic argument on the caller's stack. + _ => { + /// Basic implementation of a `va_list`. + #[repr(transparent)] + #[lang = "va_list"] + pub struct VaListImpl<'f> { + ptr: *mut c_void, -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "xtensa"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -impl<'f> fmt::Debug for VaListImpl<'f> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "va_list* {:p}", self.ptr) + // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to + // the region of the function it's defined in + _marker: PhantomInvariantLifetime<'f>, + } + + impl<'f> fmt::Debug for VaListImpl<'f> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "va_list* {:p}", self.ptr) + } + } } } -/// AArch64 ABI implementation of a `va_list`. See the -/// [AArch64 Procedure Call Standard] for more details. -/// -/// [AArch64 Procedure Call Standard]: -/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf -#[cfg(all( - target_arch = "aarch64", - not(target_vendor = "apple"), - not(target_os = "uefi"), - not(windows), -))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - stack: *mut c_void, - gr_top: *mut c_void, - vr_top: *mut c_void, - gr_offs: i32, - vr_offs: i32, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// PowerPC ABI implementation of a `va_list`. -#[cfg(all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gpr: u8, - fpr: u8, - reserved: u16, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// s390x ABI implementation of a `va_list`. -#[cfg(target_arch = "s390x")] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gpr: i64, - fpr: i64, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// x86_64 ABI implementation of a `va_list`. -#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gp_offset: i32, - fp_offset: i32, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// Xtensa ABI implementation of a `va_list`. -#[cfg(target_arch = "xtensa")] -#[repr(C)] -#[derive(Debug)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - stk: *mut i32, - reg: *mut i32, - ndx: i32, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// A wrapper for a `va_list` -#[repr(transparent)] -#[derive(Debug)] -pub struct VaList<'a, 'f: 'a> { - #[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - target_arch = "xtensa", - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, - ))] - inner: VaListImpl<'f>, - - #[cfg(all( +crate::cfg_select! { + all( any( target_arch = "aarch64", target_arch = "powerpc", @@ -158,52 +130,41 @@ pub struct VaList<'a, 'f: 'a> { not(target_family = "wasm"), not(target_os = "uefi"), not(windows), - ))] - inner: &'a mut VaListImpl<'f>, + ) => { + /// A wrapper for a `va_list` + #[repr(transparent)] + #[derive(Debug)] + pub struct VaList<'a, 'f: 'a> { + inner: &'a mut VaListImpl<'f>, + _marker: PhantomData<&'a mut VaListImpl<'f>>, + } - _marker: PhantomData<&'a mut VaListImpl<'f>>, -} -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - target_arch = "xtensa", - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -impl<'f> VaListImpl<'f> { - /// Converts a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } + impl<'f> VaListImpl<'f> { + /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: self, _marker: PhantomData } + } + } } -} -#[cfg(all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "xtensa", - target_arch = "x86_64" - ), - not(target_arch = "xtensa"), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), -))] -impl<'f> VaListImpl<'f> { - /// Converts a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: self, _marker: PhantomData } + _ => { + /// A wrapper for a `va_list` + #[repr(transparent)] + #[derive(Debug)] + pub struct VaList<'a, 'f: 'a> { + inner: VaListImpl<'f>, + _marker: PhantomData<&'a mut VaListImpl<'f>>, + } + + impl<'f> VaListImpl<'f> { + /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } + } + } } } @@ -223,39 +184,57 @@ impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { } } -// The VaArgSafe trait needs to be used in public interfaces, however, the trait -// itself must not be allowed to be used outside this module. Allowing users to -// implement the trait for a new type (thereby allowing the va_arg intrinsic to -// be used on a new type) is likely to cause undefined behavior. -// -// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface -// but also ensure it cannot be used elsewhere, the trait needs to be public -// within a private module. Once RFC 2145 has been implemented look into -// improving this. -mod sealed_trait { - /// Trait which permits the allowed types to be used with [super::VaListImpl::arg]. - pub unsafe trait VaArgSafe {} +mod sealed { + pub trait Sealed {} + + impl Sealed for i32 {} + impl Sealed for i64 {} + impl Sealed for isize {} + + impl Sealed for u32 {} + impl Sealed for u64 {} + impl Sealed for usize {} + + impl Sealed for f64 {} + + impl Sealed for *mut T {} + impl Sealed for *const T {} } -macro_rules! impl_va_arg_safe { - ($($t:ty),+) => { - $( - unsafe impl sealed_trait::VaArgSafe for $t {} - )+ - } -} +/// Trait which permits the allowed types to be used with [`VaListImpl::arg`]. +/// +/// # Safety +/// +/// This trait must only be implemented for types that C passes as varargs without implicit promotion. +/// +/// In C varargs, integers smaller than [`c_int`] and floats smaller than [`c_double`] +/// are implicitly promoted to [`c_int`] and [`c_double`] respectively. Implementing this trait for +/// types that are subject to this promotion rule is invalid. +/// +/// [`c_int`]: core::ffi::c_int +/// [`c_double`]: core::ffi::c_double +pub unsafe trait VaArgSafe: sealed::Sealed {} -impl_va_arg_safe! {i8, i16, i32, i64, usize} -impl_va_arg_safe! {u8, u16, u32, u64, isize} -impl_va_arg_safe! {f64} +// i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`. +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for isize {} -unsafe impl sealed_trait::VaArgSafe for *mut T {} -unsafe impl sealed_trait::VaArgSafe for *const T {} +// u8 and u16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`. +unsafe impl VaArgSafe for u32 {} +unsafe impl VaArgSafe for u64 {} +unsafe impl VaArgSafe for usize {} + +// f32 is implicitly promoted to c_double in C, and cannot implement `VaArgSafe`. +unsafe impl VaArgSafe for f64 {} + +unsafe impl VaArgSafe for *mut T {} +unsafe impl VaArgSafe for *const T {} impl<'f> VaListImpl<'f> { /// Advance to the next arg. #[inline] - pub unsafe fn arg(&mut self) -> T { + pub unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } } @@ -317,4 +296,4 @@ unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); /// argument `ap` points to. #[rustc_intrinsic] #[rustc_nounwind] -unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; +unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 4f7f8a5b84dd..5978cb660f6b 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -856,10 +856,10 @@ impl Display for Arguments<'_> { on( crate_local, label = "`{Self}` cannot be formatted using `{{:?}}`", - note = "add `#[derive(Debug)]` to `{Self}` or manually `impl {Debug} for {Self}`" + note = "add `#[derive(Debug)]` to `{Self}` or manually `impl {This} for {Self}`" ), - message = "`{Self}` doesn't implement `{Debug}`", - label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`" + message = "`{Self}` doesn't implement `{This}`", + label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{This}`" )] #[doc(alias = "{:?}")] #[rustc_diagnostic_item = "Debug"] @@ -969,12 +969,12 @@ pub use macros::Debug; /// ``` #[rustc_on_unimplemented( on( - any(_Self = "std::path::Path", _Self = "std::path::PathBuf"), + any(Self = "std::path::Path", Self = "std::path::PathBuf"), label = "`{Self}` cannot be formatted with the default formatter; call `.display()` on it", note = "call `.display()` or `.to_string_lossy()` to safely print paths, \ as they may contain non-Unicode data" ), - message = "`{Self}` doesn't implement `{Display}`", + message = "`{Self}` doesn't implement `{This}`", label = "`{Self}` cannot be formatted with the default formatter", note = "in format strings you may be able to use `{{:?}}` (or {{:#?}} for pretty-print) instead" )] diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 6eefb3046893..696d323c66d2 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -98,7 +98,7 @@ use crate::{intrinsics, ub_checks}; #[inline] #[stable(feature = "unreachable", since = "1.27.0")] #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] pub const unsafe fn unreachable_unchecked() -> ! { ub_checks::assert_unsafe_precondition!( check_language_ub, @@ -231,7 +231,7 @@ pub const unsafe fn assert_unchecked(cond: bool) { /// /// # Examples /// -/// ``` +/// ```ignore-wasm /// use std::sync::atomic::{AtomicBool, Ordering}; /// use std::sync::Arc; /// use std::{hint, thread}; diff --git a/library/core/src/intrinsics/bounds.rs b/library/core/src/intrinsics/bounds.rs new file mode 100644 index 000000000000..046e191212cc --- /dev/null +++ b/library/core/src/intrinsics/bounds.rs @@ -0,0 +1,39 @@ +//! Various traits used to restrict intrinsics to not-completely-wrong types. + +/// Types with a built-in dereference operator in runtime MIR, +/// aka references and raw pointers. +/// +/// # Safety +/// Must actually *be* such a type. +pub unsafe trait BuiltinDeref: Sized { + type Pointee: ?Sized; +} + +unsafe impl BuiltinDeref for &mut T { + type Pointee = T; +} +unsafe impl BuiltinDeref for &T { + type Pointee = T; +} +unsafe impl BuiltinDeref for *mut T { + type Pointee = T; +} +unsafe impl BuiltinDeref for *const T { + type Pointee = T; +} + +pub trait ChangePointee: BuiltinDeref { + type Output; +} +impl<'a, T: ?Sized + 'a, U: ?Sized + 'a> ChangePointee for &'a mut T { + type Output = &'a mut U; +} +impl<'a, T: ?Sized + 'a, U: ?Sized + 'a> ChangePointee for &'a T { + type Output = &'a U; +} +impl ChangePointee for *mut T { + type Output = *mut U; +} +impl ChangePointee for *const T { + type Output = *const U; +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index effdc3c63eea..d147cf889cc0 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1,7 +1,10 @@ //! Compiler intrinsics. //! -//! The corresponding definitions are in . -//! The corresponding const implementations are in . +//! These are the imports making intrinsics available to Rust code. The actual implementations live in the compiler. +//! Some of these intrinsics are lowered to MIR in . +//! The remaining intrinsics are implemented for the LLVM backend in +//! and , +//! and for const evaluation in . //! //! # Const intrinsics //! @@ -20,28 +23,14 @@ //! //! The volatile intrinsics provide operations intended to act on I/O //! memory, which are guaranteed to not be reordered by the compiler -//! across other volatile intrinsics. See the LLVM documentation on -//! [[volatile]]. -//! -//! [volatile]: https://llvm.org/docs/LangRef.html#volatile-memory-accesses +//! across other volatile intrinsics. See [`read_volatile`][ptr::read_volatile] +//! and [`write_volatile`][ptr::write_volatile]. //! //! # Atomics //! //! The atomic intrinsics provide common atomic operations on machine -//! words, with multiple possible memory orderings. They obey the same -//! semantics as C++11. See the LLVM documentation on [[atomics]]. -//! -//! [atomics]: https://llvm.org/docs/Atomics.html -//! -//! A quick refresher on memory ordering: -//! -//! * Acquire - a barrier for acquiring a lock. Subsequent reads and writes -//! take place after the barrier. -//! * Release - a barrier for releasing a lock. Preceding reads and writes -//! take place before the barrier. -//! * Sequentially consistent - sequentially consistent operations are -//! guaranteed to happen in order. This is the standard mode for working -//! with atomic types and is equivalent to Java's `volatile`. +//! words, with multiple possible memory orderings. See the +//! [atomic types][atomic] docs for details. //! //! # Unwinding //! @@ -61,10 +50,10 @@ )] #![allow(missing_docs)] -use crate::marker::{DiscriminantKind, Tuple}; -use crate::mem::SizedTypeProperties; -use crate::{ptr, ub_checks}; +use crate::marker::{ConstParamTy, DiscriminantKind, Tuple}; +use crate::ptr; +mod bounds; pub mod fallback; pub mod mir; pub mod simd; @@ -74,6 +63,20 @@ pub mod simd; #[cfg(all(target_has_atomic = "8", target_has_atomic = "32", target_has_atomic = "ptr"))] use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; +/// A type for atomic ordering parameters for intrinsics. This is a separate type from +/// `atomic::Ordering` so that we can make it `ConstParamTy` and fix the values used here without a +/// risk of leaking that to stable code. +#[derive(Debug, ConstParamTy, PartialEq, Eq)] +pub enum AtomicOrdering { + // These values must match the compiler's `AtomicOrdering` defined in + // `rustc_middle/src/ty/consts/int.rs`! + Relaxed = 0, + Release = 1, + Acquire = 2, + AcqRel = 3, + SeqCst = 4, +} + // N.B., these intrinsics take raw pointers because they mutate aliased // memory, which is not valid for either `&` or `&mut`. @@ -403,6 +406,15 @@ pub unsafe fn atomic_cxchgweak_seqcst_acquire(dst: *mut T, old: T, src: #[rustc_nounwind] pub unsafe fn atomic_cxchgweak_seqcst_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); +/// Loads the current value of the pointer. +/// `T` must be an integer or pointer type. +/// +/// The stabilized version of this intrinsic is available on the +/// [`atomic`] types via the `load` method. For example, [`AtomicBool::load`]. +#[rustc_intrinsic] +#[rustc_nounwind] +#[cfg(not(bootstrap))] +pub unsafe fn atomic_load(src: *const T) -> T; /// Loads the current value of the pointer. /// `T` must be an integer or pointer type. /// @@ -411,6 +423,7 @@ pub unsafe fn atomic_cxchgweak_seqcst_seqcst(dst: *mut T, old: T, src: /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::load`]. #[rustc_intrinsic] #[rustc_nounwind] +#[cfg(bootstrap)] pub unsafe fn atomic_load_seqcst(src: *const T) -> T; /// Loads the current value of the pointer. /// `T` must be an integer or pointer type. @@ -420,6 +433,7 @@ pub unsafe fn atomic_load_seqcst(src: *const T) -> T; /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::load`]. #[rustc_intrinsic] #[rustc_nounwind] +#[cfg(bootstrap)] pub unsafe fn atomic_load_acquire(src: *const T) -> T; /// Loads the current value of the pointer. /// `T` must be an integer or pointer type. @@ -429,6 +443,7 @@ pub unsafe fn atomic_load_acquire(src: *const T) -> T; /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::load`]. #[rustc_intrinsic] #[rustc_nounwind] +#[cfg(bootstrap)] pub unsafe fn atomic_load_relaxed(src: *const T) -> T; /// Stores the value at the specified memory location. @@ -1708,7 +1723,7 @@ pub const fn needs_drop() -> bool; /// # Safety /// /// If the computed offset is non-zero, then both the starting and resulting pointer must be -/// either in bounds or at the end of an allocated object. If either pointer is out +/// either in bounds or at the end of an allocation. If either pointer is out /// of bounds or arithmetic overflow occurs then this operation is undefined behavior. /// /// The stabilized version of this intrinsic is [`pointer::offset`]. @@ -1716,7 +1731,7 @@ pub const fn needs_drop() -> bool; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn offset(dst: Ptr, offset: Delta) -> Ptr; +pub const unsafe fn offset(dst: Ptr, offset: Delta) -> Ptr; /// Calculates the offset from a pointer, potentially wrapping. /// @@ -1737,6 +1752,33 @@ pub const unsafe fn offset(dst: Ptr, offset: Delta) -> Ptr; #[rustc_intrinsic] pub const unsafe fn arith_offset(dst: *const T, offset: isize) -> *const T; +/// Projects to the `index`-th element of `slice_ptr`, as the same kind of pointer +/// as the slice was provided -- so `&mut [T] → &mut T`, `&[T] → &T`, +/// `*mut [T] → *mut T`, or `*const [T] → *const T` -- without a bounds check. +/// +/// This is exposed via `::get(_unchecked)(_mut)`, +/// and isn't intended to be used elsewhere. +/// +/// Expands in MIR to `{&, &mut, &raw const, &raw mut} (*slice_ptr)[index]`, +/// depending on the types involved, so no backend support is needed. +/// +/// # Safety +/// +/// - `index < PtrMetadata(slice_ptr)`, so the indexing is in-bounds for the slice +/// - the resulting offsetting is in-bounds of the allocated object, which is +/// always the case for references, but needs to be upheld manually for pointers +#[cfg(not(bootstrap))] +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn slice_get_unchecked< + ItemPtr: bounds::ChangePointee<[T], Pointee = T, Output = SlicePtr>, + SlicePtr, + T, +>( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + /// Masks out bits of the pointer according to a mask. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2198,28 +2240,28 @@ pub unsafe fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; /// [`f16::floor`](../../std/primitive.f16.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf16(x: f16) -> f16; +pub const unsafe fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::floor`](../../std/primitive.f32.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf32(x: f32) -> f32; +pub const unsafe fn floorf32(x: f32) -> f32; /// Returns the largest integer less than or equal to an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::floor`](../../std/primitive.f64.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf64(x: f64) -> f64; +pub const unsafe fn floorf64(x: f64) -> f64; /// Returns the largest integer less than or equal to an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::floor`](../../std/primitive.f128.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf128(x: f128) -> f128; +pub const unsafe fn floorf128(x: f128) -> f128; /// Returns the smallest integer greater than or equal to an `f16`. /// @@ -2227,28 +2269,28 @@ pub unsafe fn floorf128(x: f128) -> f128; /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf16(x: f16) -> f16; +pub const unsafe fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::ceil`](../../std/primitive.f32.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf32(x: f32) -> f32; +pub const unsafe fn ceilf32(x: f32) -> f32; /// Returns the smallest integer greater than or equal to an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf64(x: f64) -> f64; +pub const unsafe fn ceilf64(x: f64) -> f64; /// Returns the smallest integer greater than or equal to an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf128(x: f128) -> f128; +pub const unsafe fn ceilf128(x: f128) -> f128; /// Returns the integer part of an `f16`. /// @@ -2256,28 +2298,28 @@ pub unsafe fn ceilf128(x: f128) -> f128; /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf16(x: f16) -> f16; +pub const unsafe fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::trunc`](../../std/primitive.f32.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf32(x: f32) -> f32; +pub const unsafe fn truncf32(x: f32) -> f32; /// Returns the integer part of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf64(x: f64) -> f64; +pub const unsafe fn truncf64(x: f64) -> f64; /// Returns the integer part of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf128(x: f128) -> f128; +pub const unsafe fn truncf128(x: f128) -> f128; /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number with an even /// least significant digit. @@ -2286,7 +2328,7 @@ pub unsafe fn truncf128(x: f128) -> f128; /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -pub fn round_ties_even_f16(x: f16) -> f16; +pub const fn round_ties_even_f16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number with an even /// least significant digit. @@ -2295,13 +2337,7 @@ pub fn round_ties_even_f16(x: f16) -> f16; /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -pub fn round_ties_even_f32(x: f32) -> f32; - -/// Provided for compatibility with stdarch. DO NOT USE. -#[inline(always)] -pub unsafe fn rintf32(x: f32) -> f32 { - round_ties_even_f32(x) -} +pub const fn round_ties_even_f32(x: f32) -> f32; /// Returns the nearest integer to an `f64`. Rounds half-way cases to the number with an even /// least significant digit. @@ -2310,13 +2346,7 @@ pub unsafe fn rintf32(x: f32) -> f32 { /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -pub fn round_ties_even_f64(x: f64) -> f64; - -/// Provided for compatibility with stdarch. DO NOT USE. -#[inline(always)] -pub unsafe fn rintf64(x: f64) -> f64 { - round_ties_even_f64(x) -} +pub const fn round_ties_even_f64(x: f64) -> f64; /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number with an even /// least significant digit. @@ -2325,7 +2355,7 @@ pub unsafe fn rintf64(x: f64) -> f64 { /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -pub fn round_ties_even_f128(x: f128) -> f128; +pub const fn round_ties_even_f128(x: f128) -> f128; /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. /// @@ -2333,28 +2363,28 @@ pub fn round_ties_even_f128(x: f128) -> f128; /// [`f16::round`](../../std/primitive.f16.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf16(x: f16) -> f16; +pub const unsafe fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is /// [`f32::round`](../../std/primitive.f32.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf32(x: f32) -> f32; +pub const unsafe fn roundf32(x: f32) -> f32; /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is /// [`f64::round`](../../std/primitive.f64.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf64(x: f64) -> f64; +pub const unsafe fn roundf64(x: f64) -> f64; /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is /// [`f128::round`](../../std/primitive.f128.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf128(x: f128) -> f128; +pub const unsafe fn roundf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -2635,7 +2665,7 @@ pub const fn three_way_compare(lhs: T, rhss: T) -> crate::cmp::Ordering #[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")] #[rustc_nounwind] #[rustc_intrinsic] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] #[miri::intrinsic_fallback_is_spec] // the fallbacks all `assume` to tell Miri pub const unsafe fn disjoint_bitor(a: T, b: T) -> T { // SAFETY: same preconditions as this function. @@ -3317,7 +3347,7 @@ pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { /// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(ub_checks)` means that /// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the /// user has UB checks disabled, the checks will still get optimized out. This intrinsic is -/// primarily used by [`ub_checks::assert_unsafe_precondition`]. +/// primarily used by [`crate::ub_checks::assert_unsafe_precondition`]. #[rustc_intrinsic_const_stable_indirect] // just for UB checks #[inline(always)] #[rustc_intrinsic] @@ -3573,18 +3603,9 @@ pub const fn type_id() -> u128; #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const fn aggregate_raw_ptr, D, M>(data: D, meta: M) -> P; - -#[unstable(feature = "core_intrinsics", issue = "none")] -pub trait AggregateRawPtr { - type Metadata: Copy; -} -impl AggregateRawPtr<*const T> for *const P { - type Metadata =

::Metadata; -} -impl AggregateRawPtr<*mut T> for *mut P { - type Metadata =

::Metadata; -} +pub const fn aggregate_raw_ptr(data: D, meta: M) -> P +where +

+ /// + /// This function always resolves `..` to the "lexical" parent. + /// That is "a/b/../c" will always resolve to `a/c` which can change the meaning of the path. + /// In particular, `a/c` and `a/b/../c` are distinct on many systems because `b` may be a symbolic link, so its parent isn’t `a`. + /// + ///
+ /// + /// [`path::absolute`](absolute) is an alternative that preserves `..`. + /// Or [`Path::canonicalize`] can be used to resolve any `..` by querying the filesystem. + #[unstable(feature = "normalize_lexically", issue = "134694")] + pub fn normalize_lexically(&self) -> Result { + let mut lexical = PathBuf::new(); + let mut iter = self.components().peekable(); + + // Find the root, if any, and add it to the lexical path. + // Here we treat the Windows path "C:\" as a single "root" even though + // `components` splits it into two: (Prefix, RootDir). + let root = match iter.peek() { + Some(Component::ParentDir) => return Err(NormalizeError), + Some(p @ Component::RootDir) | Some(p @ Component::CurDir) => { + lexical.push(p); + iter.next(); + lexical.as_os_str().len() + } + Some(Component::Prefix(prefix)) => { + lexical.push(prefix.as_os_str()); + iter.next(); + if let Some(p @ Component::RootDir) = iter.peek() { + lexical.push(p); + iter.next(); + } + lexical.as_os_str().len() + } + None => return Ok(PathBuf::new()), + Some(Component::Normal(_)) => 0, + }; + + for component in iter { + match component { + Component::RootDir => unreachable!(), + Component::Prefix(_) => return Err(NormalizeError), + Component::CurDir => continue, + Component::ParentDir => { + // It's an error if ParentDir causes us to go above the "root". + if lexical.as_os_str().len() == root { + return Err(NormalizeError); + } else { + lexical.pop(); + } + } + Component::Normal(path) => lexical.push(path), + } + } + Ok(lexical) + } + /// Reads a symbolic link, returning the file that the link points to. /// /// This is an alias to [`fs::read_link`]. @@ -3163,7 +3246,7 @@ impl Path { /// allocating. #[stable(feature = "into_boxed_path", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] - pub fn into_path_buf(self: Box) -> PathBuf { + pub fn into_path_buf(self: Box) -> PathBuf { let rw = Box::into_raw(self) as *mut OsStr; let inner = unsafe { Box::from_raw(rw) }; PathBuf { inner: OsString::from(inner) } @@ -3502,6 +3585,15 @@ impl Error for StripPrefixError { } } +#[unstable(feature = "normalize_lexically", issue = "134694")] +impl fmt::Display for NormalizeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("parent reference `..` points outside of base directory") + } +} +#[unstable(feature = "normalize_lexically", issue = "134694")] +impl Error for NormalizeError {} + /// Makes the path absolute without accessing the filesystem. /// /// If the path is relative, the current directory is used as the base directory. diff --git a/library/std/src/process.rs b/library/std/src/process.rs index df6b9a6e563c..373584d0117c 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1348,7 +1348,7 @@ impl Output { /// /// ``` /// #![feature(exit_status_error)] - /// # #[cfg(unix)] { + /// # #[cfg(all(unix, not(target_os = "android")))] { /// use std::process::Command; /// assert!(Command::new("false").output().unwrap().exit_ok().is_err()); /// # } @@ -1695,7 +1695,7 @@ impl From for Stdio { /// # Ok(()) /// # } /// # - /// # if cfg!(unix) { + /// # if cfg!(all(unix, not(target_os = "android"))) { /// # test().unwrap(); /// # } /// ``` @@ -1724,7 +1724,7 @@ impl From for Stdio { /// # Ok(()) /// # } /// # - /// # if cfg!(unix) { + /// # if cfg!(all(unix, not(target_os = "android"))) { /// # test().unwrap(); /// # } /// ``` @@ -1907,7 +1907,7 @@ impl crate::sealed::Sealed for ExitStatusError {} /// /// ``` /// #![feature(exit_status_error)] -/// # if cfg!(unix) { +/// # if cfg!(all(unix, not(target_os = "android"))) { /// use std::process::{Command, ExitStatusError}; /// /// fn run(cmd: &str) -> Result<(), ExitStatusError> { @@ -1950,7 +1950,7 @@ impl ExitStatusError { /// /// ``` /// #![feature(exit_status_error)] - /// # #[cfg(unix)] { + /// # #[cfg(all(unix, not(target_os = "android")))] { /// use std::process::Command; /// /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err(); @@ -1975,7 +1975,7 @@ impl ExitStatusError { /// ``` /// #![feature(exit_status_error)] /// - /// # if cfg!(unix) { + /// # if cfg!(all(unix, not(target_os = "android"))) { /// use std::num::NonZero; /// use std::process::Command; /// @@ -2532,7 +2532,7 @@ pub fn id() -> u32 { #[rustc_on_unimplemented(on( cause = "MainFunctionType", message = "`main` has invalid return type `{Self}`", - label = "`main` can only return types that implement `{Termination}`" + label = "`main` can only return types that implement `{This}`" ))] pub trait Termination { /// Is called to get the representation of the value as status code. diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index 3fcfb85cf2aa..050f26b097a0 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -575,7 +575,7 @@ impl Channel { // After this point `head.block` is not modified again and it will be deallocated if it's // non-null. The `Drop` code of the channel, which runs after this function, also attempts // to deallocate `head.block` if it's non-null. Therefore this function must maintain the - // invariant that if a deallocation of head.block is attemped then it must also be set to + // invariant that if a deallocation of head.block is attempted then it must also be set to // NULL. Failing to do so will lead to the Drop code attempting a double free. For this // reason both reads above do an atomic swap instead of a simple atomic load. diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 1c29c619edc3..30325be685c3 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -608,6 +608,17 @@ impl Mutex { let data = self.data.get_mut(); poison::map_result(self.poison.borrow(), |()| data) } + + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the mutex is dropped. + #[unstable(feature = "mutex_data_ptr", issue = "140368")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } } #[stable(feature = "mutex_from", since = "1.24.0")] diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 6976c0a64e23..a060e2ea57a7 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -634,6 +634,17 @@ impl RwLock { let data = self.data.get_mut(); poison::map_result(self.poison.borrow(), |()| data) } + + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the lock is dropped. + #[unstable(feature = "rwlock_data_ptr", issue = "140368")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 24539d4e8303..727252f03a24 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -136,7 +136,7 @@ cfg_if!( // we only ever read from the tid if `tls_addr` matches the current // TLS address. In that case, either the tid has been set by // the current thread, or by a thread that has terminated before - // the current thread was created. In either case, no further + // the current thread's `tls_addr` was allocated. In either case, no further // synchronization is needed (as per ) tls_addr: Atomic, tid: UnsafeCell, @@ -154,8 +154,12 @@ cfg_if!( // NOTE: This assumes that `owner` is the ID of the current // thread, and may spuriously return `false` if that's not the case. fn contains(&self, owner: ThreadId) -> bool { + // We must call `tls_addr()` *before* doing the load to ensure that if we reuse an + // earlier thread's address, the `tls_addr.load()` below happens-after everything + // that thread did. + let tls_addr = tls_addr(); // SAFETY: See the comments in the struct definition. - self.tls_addr.load(Ordering::Relaxed) == tls_addr() + self.tls_addr.load(Ordering::Relaxed) == tls_addr && unsafe { *self.tid.get() } == owner.as_u64().get() } @@ -345,6 +349,17 @@ impl ReentrantLock { } } + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads through it are + /// properly synchronized to avoid data races, and that it is not read + /// through after the lock is dropped. + #[unstable(feature = "reentrant_lock_data_ptr", issue = "140368")] + pub fn data_ptr(&self) -> *const T { + &raw const self.data + } + unsafe fn increment_lock_count(&self) -> Option<()> { unsafe { *self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?; diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 9039fd00f5d6..d01a572ac733 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -415,10 +415,7 @@ impl File { match result { Ok(_) => Ok(()), - Err(err) - if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) - || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => - { + Err(err) if err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => { Err(TryLockError::WouldBlock) } Err(err) => Err(TryLockError::Error(err)), @@ -440,10 +437,7 @@ impl File { match result { Ok(_) => Ok(()), - Err(err) - if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) - || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => - { + Err(err) if err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => { Err(TryLockError::WouldBlock) } Err(err) => Err(TryLockError::Error(err)), diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index da2174396266..46d67c8e5101 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -4,11 +4,14 @@ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::unsupported; use crate::time::Duration; -pub struct TcpStream(!); +mod tcp; +pub(crate) mod tcp4; + +pub struct TcpStream(#[expect(dead_code)] tcp::Tcp); impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + tcp::Tcp::connect(addr?).map(Self) } pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { @@ -16,105 +19,105 @@ impl TcpStream { } pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 + unsupported() } pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 + unsupported() } pub fn read_timeout(&self) -> io::Result> { - self.0 + unsupported() } pub fn write_timeout(&self) -> io::Result> { - self.0 + unsupported() } pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 + unsupported() } pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 + unsupported() } pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 + unsupported() } pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 + unsupported() } pub fn is_read_vectored(&self) -> bool { - self.0 + false } pub fn write(&self, _: &[u8]) -> io::Result { - self.0 + unsupported() } pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 + unsupported() } pub fn is_write_vectored(&self) -> bool { - self.0 + false } pub fn peer_addr(&self) -> io::Result { - self.0 + unsupported() } pub fn socket_addr(&self) -> io::Result { - self.0 + unsupported() } pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 + unsupported() } pub fn duplicate(&self) -> io::Result { - self.0 + unsupported() } pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 + unsupported() } pub fn linger(&self) -> io::Result> { - self.0 + unsupported() } pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 + unsupported() } pub fn nodelay(&self) -> io::Result { - self.0 + unsupported() } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 + unsupported() } pub fn ttl(&self) -> io::Result { - self.0 + unsupported() } pub fn take_error(&self) -> io::Result> { - self.0 + unsupported() } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 + unsupported() } } impl fmt::Debug for TcpStream { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + todo!() } } diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs new file mode 100644 index 000000000000..f87accdc41de --- /dev/null +++ b/library/std/src/sys/net/connection/uefi/tcp.rs @@ -0,0 +1,21 @@ +use super::tcp4; +use crate::io; +use crate::net::SocketAddr; + +pub(crate) enum Tcp { + V4(#[expect(dead_code)] tcp4::Tcp4), +} + +impl Tcp { + pub(crate) fn connect(addr: &SocketAddr) -> io::Result { + match addr { + SocketAddr::V4(x) => { + let temp = tcp4::Tcp4::new()?; + temp.configure(true, Some(x), None)?; + temp.connect()?; + Ok(Tcp::V4(temp)) + } + SocketAddr::V6(_) => todo!(), + } + } +} diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs new file mode 100644 index 000000000000..f7ca373b52b5 --- /dev/null +++ b/library/std/src/sys/net/connection/uefi/tcp4.rs @@ -0,0 +1,118 @@ +use r_efi::efi::{self, Status}; +use r_efi::protocols::tcp4; + +use crate::io; +use crate::net::SocketAddrV4; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sys::pal::helpers; + +const TYPE_OF_SERVICE: u8 = 8; +const TIME_TO_LIVE: u8 = 255; + +pub(crate) struct Tcp4 { + protocol: NonNull, + flag: AtomicBool, + #[expect(dead_code)] + service_binding: helpers::ServiceProtocol, +} + +const DEFAULT_ADDR: efi::Ipv4Address = efi::Ipv4Address { addr: [0u8; 4] }; + +impl Tcp4 { + pub(crate) fn new() -> io::Result { + let service_binding = helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?; + let protocol = helpers::open_protocol(service_binding.child_handle(), tcp4::PROTOCOL_GUID)?; + + Ok(Self { service_binding, protocol, flag: AtomicBool::new(false) }) + } + + pub(crate) fn configure( + &self, + active: bool, + remote_address: Option<&SocketAddrV4>, + station_address: Option<&SocketAddrV4>, + ) -> io::Result<()> { + let protocol = self.protocol.as_ptr(); + + let (remote_address, remote_port) = if let Some(x) = remote_address { + (helpers::ipv4_to_r_efi(*x.ip()), x.port()) + } else { + (DEFAULT_ADDR, 0) + }; + + // FIXME: Remove when passive connections with proper subnet handling are added + assert!(station_address.is_none()); + let use_default_address = efi::Boolean::TRUE; + let (station_address, station_port) = (DEFAULT_ADDR, 0); + let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(0, 0, 0, 0)); + + let mut config_data = tcp4::ConfigData { + type_of_service: TYPE_OF_SERVICE, + time_to_live: TIME_TO_LIVE, + access_point: tcp4::AccessPoint { + use_default_address, + remote_address, + remote_port, + active_flag: active.into(), + station_address, + station_port, + subnet_mask, + }, + control_option: crate::ptr::null_mut(), + }; + + let r = unsafe { ((*protocol).configure)(protocol, &mut config_data) }; + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + pub(crate) fn connect(&self) -> io::Result<()> { + let evt = unsafe { self.create_evt() }?; + let completion_token = + tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; + + let protocol = self.protocol.as_ptr(); + let mut conn_token = tcp4::ConnectionToken { completion_token }; + + let r = unsafe { ((*protocol).connect)(protocol, &mut conn_token) }; + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + self.wait_for_flag(); + + if completion_token.status.is_error() { + Err(io::Error::from_raw_os_error(completion_token.status.as_usize())) + } else { + Ok(()) + } + } + + unsafe fn create_evt(&self) -> io::Result { + self.flag.store(false, Ordering::Relaxed); + helpers::OwnedEvent::new( + efi::EVT_NOTIFY_SIGNAL, + efi::TPL_CALLBACK, + Some(toggle_atomic_flag), + Some(unsafe { NonNull::new_unchecked(self.flag.as_ptr().cast()) }), + ) + } + + fn wait_for_flag(&self) { + while !self.flag.load(Ordering::Relaxed) { + let _ = self.poll(); + } + } + + fn poll(&self) -> io::Result<()> { + let protocol = self.protocol.as_ptr(); + let r = unsafe { ((*protocol).poll)(protocol) }; + + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } +} + +extern "efiapi" fn toggle_atomic_flag(_: r_efi::efi::Event, ctx: *mut crate::ffi::c_void) { + let flag = unsafe { AtomicBool::from_ptr(ctx.cast()) }; + flag.store(true, Ordering::Relaxed); +} diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 5174ea65d0cd..892bd2e3de65 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -215,9 +215,9 @@ impl Buf { /// The slice must be valid for the platform encoding (as described in /// [`Slice::from_encoded_bytes_unchecked`]). /// - /// This bypasses the WTF-8 surrogate joining, so `self` must not end with a - /// leading surrogate half and `other` must not start with with a trailing - /// surrogate half. + /// This bypasses the WTF-8 surrogate joining, so either `self` must not + /// end with a leading surrogate half, or `other` must not start with a + /// trailing surrogate half. #[inline] pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 6ee3e0a8b662..e47263348dbd 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -653,7 +653,6 @@ pub(crate) struct ServiceProtocol { } impl ServiceProtocol { - #[expect(dead_code)] pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result { let handles = locate_handles(service_guid)?; @@ -670,7 +669,6 @@ impl ServiceProtocol { Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found")) } - #[expect(dead_code)] pub(crate) fn child_handle(&self) -> NonNull { self.child_handle } @@ -732,6 +730,10 @@ impl OwnedEvent { } } + pub(crate) fn as_ptr(&self) -> efi::Event { + self.0.as_ptr() + } + pub(crate) fn into_raw(self) -> *mut crate::ffi::c_void { let r = self.0.as_ptr(); crate::mem::forget(self); @@ -755,3 +757,7 @@ impl Drop for OwnedEvent { } } } + +pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Address { + efi::Ipv4Address { addr: addr.octets() } +} diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index a9c2510e6d45..b6777b76668d 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -1,8 +1,10 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_char, c_int, gid_t, pid_t, uid_t}; +use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_int, gid_t, pid_t, uid_t}; +pub use self::cstring_array::CStringArray; +use self::cstring_array::CStringIter; use crate::collections::BTreeMap; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::os::unix::prelude::*; @@ -14,7 +16,9 @@ use crate::sys::fs::OpenOptions; use crate::sys::pipe::{self, AnonPipe}; use crate::sys::process::env::{CommandEnv, CommandEnvs}; use crate::sys_common::{FromInner, IntoInner}; -use crate::{fmt, io, ptr}; +use crate::{fmt, io}; + +mod cstring_array; cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { @@ -77,17 +81,12 @@ cfg_if::cfg_if! { pub struct Command { program: CString, - args: Vec, - /// Exactly what will be passed to `execvp`. - /// - /// First element is a pointer to `program`, followed by pointers to - /// `args`, followed by a `null`. Be careful when modifying `program` or - /// `args` to properly update this as well. - argv: Argv, + args: CStringArray, env: CommandEnv, program_kind: ProgramKind, cwd: Option, + chroot: Option, uid: Option, gid: Option, saw_nul: bool, @@ -101,14 +100,6 @@ pub struct Command { pgroup: Option, } -// Create a new type for argv, so that we can make it `Send` and `Sync` -struct Argv(Vec<*const c_char>); - -// It is safe to make `Argv` `Send` and `Sync`, because it contains -// pointers to memory owned by `Command.args` -unsafe impl Send for Argv {} -unsafe impl Sync for Argv {} - // passed back to std::process with the pipes connected to the child, if any // were requested pub struct StdioPipes { @@ -170,42 +161,19 @@ impl ProgramKind { } impl Command { - #[cfg(not(target_os = "linux"))] pub fn new(program: &OsStr) -> Command { let mut saw_nul = false; let program_kind = ProgramKind::new(program.as_ref()); let program = os2c(program, &mut saw_nul); + let mut args = CStringArray::with_capacity(1); + args.push(program.clone()); Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], program, - program_kind, - env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul, - closures: Vec::new(), - groups: None, - stdin: None, - stdout: None, - stderr: None, - pgroup: None, - } - } - - #[cfg(target_os = "linux")] - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program_kind = ProgramKind::new(program.as_ref()); - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - program_kind, + args, env: Default::default(), + program_kind, cwd: None, + chroot: None, uid: None, gid: None, saw_nul, @@ -214,6 +182,7 @@ impl Command { stdin: None, stdout: None, stderr: None, + #[cfg(target_os = "linux")] create_pidfd: false, pgroup: None, } @@ -222,20 +191,11 @@ impl Command { pub fn set_arg_0(&mut self, arg: &OsStr) { // Set a new arg0 let arg = os2c(arg, &mut self.saw_nul); - debug_assert!(self.argv.0.len() > 1); - self.argv.0[0] = arg.as_ptr(); - self.args[0] = arg; + self.args.write(0, arg); } pub fn arg(&mut self, arg: &OsStr) { - // Overwrite the trailing null pointer in `argv` and then add a new null - // pointer. let arg = os2c(arg, &mut self.saw_nul); - self.argv.0[self.args.len()] = arg.as_ptr(); - self.argv.0.push(ptr::null()); - - // Also make sure we keep track of the owned value to schedule a - // destructor for this memory. self.args.push(arg); } @@ -254,6 +214,12 @@ impl Command { pub fn pgroup(&mut self, pgroup: pid_t) { self.pgroup = Some(pgroup); } + pub fn chroot(&mut self, dir: &Path) { + self.chroot = Some(os2c(dir.as_os_str(), &mut self.saw_nul)); + if self.cwd.is_none() { + self.cwd(&OsStr::new("/")); + } + } #[cfg(target_os = "linux")] pub fn create_pidfd(&mut self, val: bool) { @@ -286,6 +252,8 @@ impl Command { pub fn get_args(&self) -> CommandArgs<'_> { let mut iter = self.args.iter(); + // argv[0] contains the program name, but we are only interested in the + // arguments so skip it. iter.next(); CommandArgs { iter } } @@ -298,12 +266,12 @@ impl Command { self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes()))) } - pub fn get_argv(&self) -> &Vec<*const c_char> { - &self.argv.0 + pub fn get_argv(&self) -> &CStringArray { + &self.args } pub fn get_program_cstr(&self) -> &CStr { - &*self.program + &self.program } #[allow(dead_code)] @@ -326,6 +294,10 @@ impl Command { pub fn get_pgroup(&self) -> Option { self.pgroup } + #[allow(dead_code)] + pub fn get_chroot(&self) -> Option<&CStr> { + self.chroot.as_deref() + } pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { &mut self.closures @@ -392,32 +364,6 @@ fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { }) } -// Helper type to manage ownership of the strings within a C-style array. -pub struct CStringArray { - items: Vec, - ptrs: Vec<*const c_char>, -} - -impl CStringArray { - pub fn with_capacity(capacity: usize) -> Self { - let mut result = CStringArray { - items: Vec::with_capacity(capacity), - ptrs: Vec::with_capacity(capacity + 1), - }; - result.ptrs.push(ptr::null()); - result - } - pub fn push(&mut self, item: CString) { - let l = self.ptrs.len(); - self.ptrs[l - 1] = item.as_ptr(); - self.ptrs.push(ptr::null()); - self.items.push(item); - } - pub fn as_ptr(&self) -> *const *const c_char { - self.ptrs.as_ptr() - } -} - fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStringArray { let mut result = CStringArray::with_capacity(env.len()); for (mut k, v) in env { @@ -606,14 +552,16 @@ impl fmt::Debug for Command { write!(f, "{}={value:?} ", key.to_string_lossy())?; } } - if self.program != self.args[0] { + + if *self.program != self.args[0] { write!(f, "[{:?}] ", self.program)?; } - write!(f, "{:?}", self.args[0])?; + write!(f, "{:?}", &self.args[0])?; - for arg in &self.args[1..] { + for arg in self.get_args() { write!(f, " {:?}", arg)?; } + Ok(()) } } @@ -645,14 +593,16 @@ impl From for ExitCode { } pub struct CommandArgs<'a> { - iter: crate::slice::Iter<'a, CString>, + iter: CStringIter<'a>, } impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { - self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes())) + self.iter.next().map(|cs| OsStr::from_bytes(cs.to_bytes())) } + fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } @@ -662,6 +612,7 @@ impl<'a> ExactSizeIterator for CommandArgs<'a> { fn len(&self) -> usize { self.iter.len() } + fn is_empty(&self) -> bool { self.iter.is_empty() } diff --git a/library/std/src/sys/process/unix/common/cstring_array.rs b/library/std/src/sys/process/unix/common/cstring_array.rs new file mode 100644 index 000000000000..1c840a85df9b --- /dev/null +++ b/library/std/src/sys/process/unix/common/cstring_array.rs @@ -0,0 +1,115 @@ +use crate::ffi::{CStr, CString, c_char}; +use crate::ops::Index; +use crate::{fmt, mem, ptr}; + +/// Helper type to manage ownership of the strings within a C-style array. +/// +/// This type manages an array of C-string pointers terminated by a null +/// pointer. The pointer to the array (as returned by `as_ptr`) can be used as +/// a value of `argv` or `environ`. +pub struct CStringArray { + ptrs: Vec<*const c_char>, +} + +impl CStringArray { + /// Creates a new `CStringArray` with enough capacity to hold `capacity` + /// strings. + pub fn with_capacity(capacity: usize) -> Self { + let mut result = CStringArray { ptrs: Vec::with_capacity(capacity + 1) }; + result.ptrs.push(ptr::null()); + result + } + + /// Replace the string at position `index`. + pub fn write(&mut self, index: usize, item: CString) { + let argc = self.ptrs.len() - 1; + let ptr = &mut self.ptrs[..argc][index]; + let old = mem::replace(ptr, item.into_raw()); + // SAFETY: + // `CStringArray` owns all of its strings, and they were all transformed + // into pointers using `CString::into_raw`. Also, this is not the null + // pointer since the indexing above would have failed. + drop(unsafe { CString::from_raw(old.cast_mut()) }); + } + + /// Push an additional string to the array. + pub fn push(&mut self, item: CString) { + let argc = self.ptrs.len() - 1; + // Replace the null pointer at the end of the array... + self.ptrs[argc] = item.into_raw(); + // ... and recreate it to restore the data structure invariant. + self.ptrs.push(ptr::null()); + } + + /// Returns a pointer to the C-string array managed by this type. + pub fn as_ptr(&self) -> *const *const c_char { + self.ptrs.as_ptr() + } + + /// Returns an iterator over all `CStr`s contained in this array. + pub fn iter(&self) -> CStringIter<'_> { + CStringIter { iter: self.ptrs[..self.ptrs.len() - 1].iter() } + } +} + +impl Index for CStringArray { + type Output = CStr; + fn index(&self, index: usize) -> &CStr { + let ptr = self.ptrs[..self.ptrs.len() - 1][index]; + // SAFETY: + // `CStringArray` owns all of its strings. Also, this is not the null + // pointer since the indexing above would have failed. + unsafe { CStr::from_ptr(ptr) } + } +} + +impl fmt::Debug for CStringArray { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +// SAFETY: `CStringArray` is basically just a `Vec` +unsafe impl Send for CStringArray {} +// SAFETY: `CStringArray` is basically just a `Vec` +unsafe impl Sync for CStringArray {} + +impl Drop for CStringArray { + fn drop(&mut self) { + // SAFETY: + // `CStringArray` owns all of its strings, and they were all transformed + // into pointers using `CString::into_raw`. + self.ptrs[..self.ptrs.len() - 1] + .iter() + .for_each(|&p| drop(unsafe { CString::from_raw(p.cast_mut()) })) + } +} + +/// An iterator over all `CStr`s contained in a `CStringArray`. +#[derive(Clone)] +pub struct CStringIter<'a> { + iter: crate::slice::Iter<'a, *const c_char>, +} + +impl<'a> Iterator for CStringIter<'a> { + type Item = &'a CStr; + fn next(&mut self) -> Option<&'a CStr> { + // SAFETY: + // `CStringArray` owns all of its strings. Also, this is not the null + // pointer since the last element is excluded when creating `iter`. + self.iter.next().map(|&p| unsafe { CStr::from_ptr(p) }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CStringIter<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 1b3bd2de265d..4f595ac9a1c5 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -323,6 +323,15 @@ impl Command { cvt(libc::setuid(u as uid_t))?; } } + if let Some(chroot) = self.get_chroot() { + #[cfg(not(target_os = "fuchsia"))] + cvt(libc::chroot(chroot.as_ptr()))?; + #[cfg(target_os = "fuchsia")] + return Err(io::const_error!( + io::ErrorKind::Unsupported, + "chroot not supported by fuchsia" + )); + } if let Some(cwd) = self.get_cwd() { cvt(libc::chdir(cwd.as_ptr()))?; } @@ -447,6 +456,7 @@ impl Command { || (self.env_saw_path() && !self.program_is_path()) || !self.get_closures().is_empty() || self.get_groups().is_some() + || self.get_chroot().is_some() { return Ok(None); } diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index fab3b36ebf3f..f33b4a375da8 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -27,6 +27,12 @@ impl Command { "nul byte found in provided data", )); } + if self.get_chroot().is_some() { + return Err(io::const_error!( + ErrorKind::Unsupported, + "chroot not supported by vxworks", + )); + } let (ours, theirs) = self.setup_io(default, needs_stdin)?; let mut p = Process { pid: 0, status: None }; diff --git a/library/std/src/sys/sync/mutex/futex.rs b/library/std/src/sys/sync/mutex/futex.rs index ce9b2daa5f80..70e2ea9f6058 100644 --- a/library/std/src/sys/sync/mutex/futex.rs +++ b/library/std/src/sys/sync/mutex/futex.rs @@ -19,11 +19,15 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_try_lock")] pub fn try_lock(&self) -> bool { self.futex.compare_exchange(UNLOCKED, LOCKED, Acquire, Relaxed).is_ok() } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_lock")] pub fn lock(&self) { if self.futex.compare_exchange(UNLOCKED, LOCKED, Acquire, Relaxed).is_err() { self.lock_contended(); @@ -80,6 +84,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_unlock")] pub unsafe fn unlock(&self) { if self.futex.swap(UNLOCKED, Release) == CONTENDED { // We only wake up one thread. When that thread locks the mutex, it diff --git a/library/std/src/sys/sync/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs index 75b4b9c6dad9..a7a3b47d0ec6 100644 --- a/library/std/src/sys/sync/mutex/pthread.rs +++ b/library/std/src/sys/sync/mutex/pthread.rs @@ -6,7 +6,7 @@ use crate::sys::pal::sync as pal; use crate::sys::sync::OnceBox; pub struct Mutex { - pub pal: OnceBox, + pub(in crate::sys::sync) pal: OnceBox, } impl Mutex { @@ -28,6 +28,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_lock")] pub fn lock(&self) { // SAFETY: we call `init` above, therefore reentrant locking is safe. // In `drop` we ensure that the mutex is not destroyed while locked. @@ -35,6 +37,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_unlock")] pub unsafe fn unlock(&self) { // SAFETY: the mutex can only be locked if it is already initialized // and we observed this initialization since we observed the locking. @@ -42,6 +46,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_try_lock")] pub fn try_lock(&self) -> bool { // SAFETY: we call `init` above, therefore reentrant locking is safe. // In `drop` we ensure that the mutex is not destroyed while locked. diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs index 59581e6f281e..f91471419c1c 100644 --- a/library/std/src/sys/thread_local/guard/key.rs +++ b/library/std/src/sys/thread_local/guard/key.rs @@ -32,7 +32,7 @@ pub fn enable() { /// 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 +/// however. This is done by deferring the execution of a TLS destructor to /// the next round of destruction inside the TLS destructors. #[cfg(not(target_thread_local))] pub fn enable() { @@ -46,7 +46,7 @@ pub fn enable() { 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, + // TLS destruction. If there is no further round, there will be leaks, // but that's okay, `thread_cleanup` is not guaranteed to be called. unsafe { set(CLEANUP.force(), RUN) } } else { diff --git a/library/std/src/sys/thread_local/native/lazy.rs b/library/std/src/sys/thread_local/native/lazy.rs index 51294285ba01..b556dd9aa25e 100644 --- a/library/std/src/sys/thread_local/native/lazy.rs +++ b/library/std/src/sys/thread_local/native/lazy.rs @@ -1,9 +1,9 @@ -use crate::cell::UnsafeCell; -use crate::hint::unreachable_unchecked; +use crate::cell::{Cell, UnsafeCell}; +use crate::mem::MaybeUninit; use crate::ptr; use crate::sys::thread_local::{abort_on_dtor_unwind, destructors}; -pub unsafe trait DestroyedState: Sized { +pub unsafe trait DestroyedState: Sized + Copy { fn register_dtor(s: &Storage); } @@ -19,15 +19,17 @@ unsafe impl DestroyedState for () { } } -enum State { - Initial, - Alive(T), +#[derive(Copy, Clone)] +enum State { + Uninitialized, + Alive, Destroyed(D), } #[allow(missing_debug_implementations)] pub struct Storage { - state: UnsafeCell>, + state: Cell>, + value: UnsafeCell>, } impl Storage @@ -35,7 +37,10 @@ where D: DestroyedState, { pub const fn new() -> Storage { - Storage { state: UnsafeCell::new(State::Initial) } + Storage { + state: Cell::new(State::Uninitialized), + value: UnsafeCell::new(MaybeUninit::uninit()), + } } /// Gets a pointer to the TLS value, potentially initializing it with the @@ -49,35 +54,49 @@ where /// The `self` reference must remain valid until the TLS destructor is run. #[inline] pub unsafe fn get_or_init(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { - let state = unsafe { &*self.state.get() }; - match state { - State::Alive(v) => v, - State::Destroyed(_) => ptr::null(), - State::Initial => unsafe { self.initialize(i, f) }, + if let State::Alive = self.state.get() { + self.value.get().cast() + } else { + unsafe { self.get_or_init_slow(i, f) } } } + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. #[cold] - unsafe fn initialize(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { - // Perform initialization + unsafe fn get_or_init_slow( + &self, + i: Option<&mut Option>, + f: impl FnOnce() -> T, + ) -> *const T { + match self.state.get() { + State::Uninitialized => {} + State::Alive => return self.value.get().cast(), + State::Destroyed(_) => return ptr::null(), + } let v = i.and_then(Option::take).unwrap_or_else(f); - let old = unsafe { self.state.get().replace(State::Alive(v)) }; - match old { + // SAFETY: we cannot be inside a `LocalKey::with` scope, as the initializer + // has already returned and the next scope only starts after we return + // the pointer. Therefore, there can be no references to the old value, + // even if it was initialized. Thus because we are !Sync we have exclusive + // access to self.value and may replace it. + let mut old_value = unsafe { self.value.get().replace(MaybeUninit::new(v)) }; + match self.state.replace(State::Alive) { // If the variable is not being recursively initialized, register // the destructor. This might be a noop if the value does not need // destruction. - State::Initial => D::register_dtor(self), - // Else, drop the old value. This might be changed to a panic. - val => drop(val), + State::Uninitialized => D::register_dtor(self), + + // Recursive initialization, we only need to drop the old value + // as we've already registered the destructor. + State::Alive => unsafe { old_value.assume_init_drop() }, + + State::Destroyed(_) => unreachable!(), } - // SAFETY: the state was just set to `Alive` - unsafe { - let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() }; - v - } + self.value.get().cast() } } @@ -92,9 +111,12 @@ unsafe extern "C" fn destroy(ptr: *mut u8) { // Print a nice abort message if a panic occurs. abort_on_dtor_unwind(|| { let storage = unsafe { &*(ptr as *const Storage) }; - // Update the state before running the destructor as it may attempt to - // access the variable. - let val = unsafe { storage.state.get().replace(State::Destroyed(())) }; - drop(val); + if let State::Alive = storage.state.replace(State::Destroyed(())) { + // SAFETY: we ensured the state was Alive so the value was initialized. + // We also updated the state to Destroyed to prevent the destructor + // from accessing the thread-local variable, as this would violate + // the exclusive access provided by &mut T in Drop::drop. + unsafe { (*storage.value.get()).assume_init_drop() } + } }) } diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 978402b6fdae..781855a2d14a 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -3,7 +3,8 @@ path_add_extension, path_file_prefix, maybe_uninit_slice, - os_string_pathbuf_leak + os_string_pathbuf_leak, + normalize_lexically )] use std::clone::CloneToUninit; @@ -1976,3 +1977,87 @@ fn clone_to_uninit() { unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b).cast()) }; assert_eq!(a, &*b); } + +// Test: Only separators (e.g., "/" or "\\") +// This test checks how Path handles a string that consists only of path separators. +// It should recognize the root and not treat it as a normal component. +#[test] +fn test_only_separators() { + let path = Path::new("/////"); + assert!(path.has_root()); + assert_eq!(path.iter().count(), 1); + assert_eq!(path.parent(), None); +} + +// Test: Non-ASCII/Unicode +// This test verifies that Path can handle Unicode and non-ASCII characters in the path. +// It ensures that such paths are not rejected or misinterpreted. +#[test] +fn test_non_ascii_unicode() { + let path = Path::new("/tmp/❤/🚀/file.txt"); + assert!(path.to_str().is_some()); + assert_eq!(path.file_name(), Some(OsStr::new("file.txt"))); +} + +// Test: Embedded newlines +// This test verifies that newlines within path components are preserved and do not break path parsing. +// It ensures that Path treats newlines as normal characters. +#[test] +fn test_embedded_newline() { + let path = Path::new("foo\nbar"); + assert_eq!(path.file_name(), Some(OsStr::new("foo\nbar"))); + assert_eq!(path.to_str(), Some("foo\nbar")); +} + +#[test] +fn normalize_lexically() { + #[track_caller] + fn check_ok(a: &str, b: &str) { + assert_eq!(Path::new(a).normalize_lexically().unwrap(), PathBuf::from(b)); + } + + #[track_caller] + fn check_err(a: &str) { + assert!(Path::new(a).normalize_lexically().is_err()); + } + + // Relative paths + check_ok("a", "a"); + check_ok("./a", "./a"); + check_ok("a/b/c", "a/b/c"); + check_ok("a/././b/./c/.", "a/b/c"); + check_ok("a/../c", "c"); + check_ok("./a/b", "./a/b"); + check_ok("a/../b/c/..", "b"); + + check_err(".."); + check_err("../.."); + check_err("a/../.."); + check_err("a/../../b"); + check_err("a/../../b/c"); + check_err("a/../b/../.."); + + // Check we don't escape the root or prefix + #[cfg(unix)] + { + check_err("/.."); + check_err("/a/../.."); + } + #[cfg(windows)] + { + check_err(r"C:\.."); + check_err(r"C:\a\..\.."); + + check_err(r"C:.."); + check_err(r"C:a\..\.."); + + check_err(r"\\server\share\.."); + check_err(r"\\server\share\a\..\.."); + + check_err(r"\.."); + check_err(r"\a\..\.."); + + check_err(r"\\?\UNC\server\share\.."); + check_err(r"\\?\UNC\server\share\a\..\.."); + } +} diff --git a/library/stdarch b/library/stdarch index 1dfaa4db2479..b6e2249e388f 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 1dfaa4db2479753a46a3e90f2c3c89d89d0b21f1 +Subproject commit b6e2249e388f520627544812649b77b0944e1a2e diff --git a/rust-bors.toml b/rust-bors.toml index f27eb2393675..fbfaa980f056 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -1 +1,2 @@ -timeout = 14400 +# 6 hours timeout for CI builds +timeout = 21600 diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index d10d2d9bf8ce..0c8e66335609 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "shlex", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 9652d18f1a6c..b12b3dfc7b2a 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -32,7 +32,7 @@ test = false # Most of the time updating these dependencies requires modifications to the # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565); # otherwise, some targets will fail. That's why these dependencies are explicitly pinned. -cc = "=1.2.17" +cc = "=1.2.23" cmake = "=0.1.54" build_helper = { path = "../build_helper" } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 42ad14a81d02..c60c6b8db640 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1118,7 +1118,6 @@ class RustBuild(object): if "RUSTFLAGS_BOOTSTRAP" in env: env["RUSTFLAGS"] += " " + env["RUSTFLAGS_BOOTSTRAP"] - env["PATH"] = os.path.join(self.bin_root(), "bin") + os.pathsep + env["PATH"] if not os.path.isfile(self.cargo()): raise Exception("no cargo executable found at `{}`".format(self.cargo())) args = [ diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 67daf81ee543..8668550bddf3 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1409,7 +1409,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect cargo.env("LLVM_LINKER_FLAGS", llvm_linker_flags); } - // Building with a static libstdc++ is only supported on linux right now, + // Building with a static libstdc++ is only supported on Linux and windows-gnu* right now, // not for MSVC or macOS if builder.config.llvm_static_stdcpp && !target.contains("freebsd") @@ -1417,12 +1417,14 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect && !target.contains("apple") && !target.contains("solaris") { + let libstdcxx_name = + if target.contains("windows-gnullvm") { "libc++.a" } else { "libstdc++.a" }; let file = compiler_file( builder, &builder.cxx(target).unwrap(), target, CLang::Cxx, - "libstdc++.a", + libstdcxx_name, ); cargo.env("LLVM_STATIC_STDCPP", file); } @@ -1984,17 +1986,20 @@ impl Step for Assemble { trace!("installing `{tool}`"); let tool_exe = exe(tool, target_compiler.host); let src_path = llvm_bin_dir.join(&tool_exe); - // When using `download-ci-llvm`, some of the tools - // may not exist, so skip trying to copy them. - if src_path.exists() { - // There is a chance that these tools are being installed from an external LLVM. - // Use `Builder::resolve_symlink_and_copy` instead of `Builder::copy_link` to ensure - // we are copying the original file not the symlinked path, which causes issues for - // tarball distribution. - // - // See https://github.com/rust-lang/rust/issues/135554. - builder.resolve_symlink_and_copy(&src_path, &libdir_bin.join(&tool_exe)); + + // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them. + if !src_path.exists() && builder.config.llvm_from_ci { + eprintln!("{} does not exist; skipping copy", src_path.display()); + continue; } + + // There is a chance that these tools are being installed from an external LLVM. + // Use `Builder::resolve_symlink_and_copy` instead of `Builder::copy_link` to ensure + // we are copying the original file not the symlinked path, which causes issues for + // tarball distribution. + // + // See https://github.com/rust-lang/rust/issues/135554. + builder.resolve_symlink_and_copy(&src_path, &libdir_bin.join(&tool_exe)); } } } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index aa2c7509c2a4..50dc8e5ac9b5 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -776,7 +776,8 @@ impl Step for RustcDev { copy_src_dirs( builder, &builder.src, - &["compiler"], + // The compiler has a path dependency on proc_macro, so make sure to include it. + &["compiler", "library/proc_macro"], &[], &tarball.image_dir().join("lib/rustlib/rustc-src/rust"), ); @@ -2274,14 +2275,18 @@ impl Step for LlvmTools { let target = self.target; - /* run only if llvm-config isn't used */ + // Run only if a custom llvm-config is not used if let Some(config) = builder.config.target_config.get(&target) { - if let Some(ref _s) = config.llvm_config { + if !builder.config.llvm_from_ci && config.llvm_config.is_some() { builder.info(&format!("Skipping LlvmTools ({target}): external LLVM")); return None; } } + if !builder.config.dry_run() { + builder.require_submodule("src/llvm-project", None); + } + builder.ensure(crate::core::build_steps::llvm::Llvm { target }); let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple); @@ -2294,6 +2299,12 @@ impl Step for LlvmTools { let dst_bindir = format!("lib/rustlib/{}/bin", target.triple); for tool in tools_to_install(&builder.paths) { let exe = src_bindir.join(exe(tool, target)); + // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them. + if !exe.exists() && builder.config.llvm_from_ci { + eprintln!("{} does not exist; skipping copy", exe.display()); + continue; + } + tarball.add_file(&exe, &dst_bindir, FileType::Executable); } } @@ -2394,6 +2405,10 @@ impl Step for RustDev { } } + if !builder.config.dry_run() { + builder.require_submodule("src/llvm-project", None); + } + let mut tarball = Tarball::new(builder, "rust-dev", &target.triple); tarball.set_overlay(OverlayKind::Llvm); // LLVM requires a shared object symlink to exist on some platforms. diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index c31c40e846b0..5419540aa2e0 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -38,7 +38,9 @@ fn sanitize_sh(path: &Path, is_cygwin: bool) -> String { if ch.next() != Some('/') { return None; } - Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..])) + // The prefix for Windows drives in Cygwin/MSYS2 is configurable, but + // /proc/cygdrive is available regardless of configuration since 1.7.33 + Some(format!("/proc/cygdrive/{}/{}", drive, &s[drive.len_utf8() + 2..])) } } @@ -244,7 +246,7 @@ install!((self, builder, _config), ); } }; - LlvmTools, alias = "llvm-tools", Self::should_build(_config), only_hosts: true, { + LlvmTools, alias = "llvm-tools", _config.llvm_tools_enabled && _config.llvm_enabled(_config.build), only_hosts: true, { if let Some(tarball) = builder.ensure(dist::LlvmTools { target: self.target }) { install_sh(builder, "llvm-tools", self.compiler.stage, Some(self.target), &tarball); } else { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index a37881974716..979ff3992672 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -285,7 +285,8 @@ impl Step for Llvm { LlvmBuildStatus::ShouldBuild(m) => m, }; - if builder.llvm_link_shared() && target.is_windows() { + if builder.llvm_link_shared() && target.is_windows() && !target.ends_with("windows-gnullvm") + { panic!("shared linking to LLVM is not currently supported on {}", target.triple); } @@ -1430,6 +1431,7 @@ impl Step for Libunwind { cfg.flag("-funwind-tables"); cfg.flag("-fvisibility=hidden"); cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); + cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1"); cfg.include(root.join("include")); cfg.cargo_metadata(false); cfg.out_dir(&out_dir); @@ -1447,12 +1449,10 @@ impl Step for Libunwind { cfg.define("__NO_STRING_INLINES", None); cfg.define("__NO_MATH_INLINES", None); cfg.define("_LIBUNWIND_IS_BAREMETAL", None); - cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None); cfg.define("NDEBUG", None); } if self.target.is_windows() { cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1"); - cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1"); } } diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 7f4e88bd73c6..71cdb665ed47 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -1,3 +1,4 @@ +use std::env::consts::EXE_EXTENSION; use std::fmt::{Display, Formatter}; use crate::core::build_steps::compile::{Std, Sysroot}; @@ -160,7 +161,10 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); } let sysroot = builder.ensure(Sysroot::new(compiler)); - let rustc = sysroot.join("bin/rustc"); + let mut rustc = sysroot.clone(); + rustc.push("bin"); + rustc.push("rustc"); + rustc.set_extension(EXE_EXTENSION); let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); let results_dir = rustc_perf_dir.join("results"); diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 0bba441c3fa2..6ef1b13abcde 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -5,7 +5,6 @@ use std::path::PathBuf; -use crate::Mode; use crate::core::build_steps::dist::distdir; use crate::core::build_steps::test; use crate::core::build_steps::tool::{self, SourceType, Tool}; @@ -14,6 +13,7 @@ use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::core::config::flags::get_completion; use crate::utils::exec::command; +use crate::{Mode, t}; #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct BuildManifest; @@ -118,15 +118,25 @@ impl Step for Miri { fn run(self, builder: &Builder<'_>) { let host = builder.build.build; let target = self.target; - let stage = builder.top_stage; + + // `x run` uses stage 0 by default but miri does not work well with stage 0. + // Change the stage to 1 if it's not set explicitly. + let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 { + builder.top_stage + } else { + 1 + }; + if stage == 0 { eprintln!("miri cannot be run at stage 0"); std::process::exit(1); } // This compiler runs on the host, we'll just use it for the target. - let target_compiler = builder.compiler(stage, host); - let host_compiler = tool::get_tool_rustc_compiler(builder, target_compiler); + let target_compiler = builder.compiler(stage, target); + let miri_build = builder.ensure(tool::Miri { compiler: target_compiler, target }); + // Rustc tools are off by one stage, so use the build compiler to run miri. + let host_compiler = miri_build.build_compiler; // Get a target sysroot for Miri. let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target); @@ -243,6 +253,7 @@ impl Step for GenerateCopyright { cmd.env("SRC_DIR", &builder.src); cmd.env("VENDOR_DIR", &vendored_sources); cmd.env("CARGO", &builder.initial_cargo); + cmd.env("CARGO_HOME", t!(home::cargo_home())); // it is important that generate-copyright runs from the root of the // source tree, because it uses relative paths cmd.current_dir(&builder.src); diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 31ec462134d5..5e22c2d1acbd 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -585,11 +585,13 @@ Select which editor you would like to set up [default: None]: "; "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", "b5dd299b93dca3ceeb9b335f929293cb3d4bf4977866fbe7ceeac2a8a9f99088", "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9", + "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce", ], EditorKind::Helix => &[ "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233", "6736d61409fbebba0933afd2e4c44ff2f97c1cb36cf0299a7f4a7819b8775040", "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5", + "198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5", ], EditorKind::Vim | EditorKind::VsCode => &[ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", @@ -604,11 +606,13 @@ Select which editor you would like to set up [default: None]: "; "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d", "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717", "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893", + "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12", ], EditorKind::Zed => &[ "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c", "a5380cf5dd9328731aecc5dfb240d16dac46ed272126b9728006151ef42f5909", "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26", + "4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6", ], } } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 27791825aa0f..bb682751a2ec 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -739,7 +739,7 @@ impl Step for Clippy { const DEFAULT: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/clippy") + run.suite_path("src/tools/clippy/tests").path("src/tools/clippy") } fn make_run(run: RunConfig<'_>) { @@ -783,6 +783,23 @@ impl Step for Clippy { let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir()); cargo.env("HOST_LIBS", host_libs); + // Collect paths of tests to run + 'partially_test: { + let paths = &builder.config.paths[..]; + let mut test_names = Vec::new(); + for path in paths { + if let Some(path) = + helpers::is_valid_test_suite_arg(path, "src/tools/clippy/tests", builder) + { + test_names.push(path); + } else if path.ends_with("src/tools/clippy") { + // When src/tools/clippy is called directly, all tests should be run. + break 'partially_test; + } + } + cargo.env("TESTNAME", test_names.join(",")); + } + cargo.add_rustc_lib_path(builder); let cargo = prepare_cargo_test(cargo, &[], &[], host, builder); @@ -2947,7 +2964,14 @@ impl Step for Distcheck { run.builder.ensure(Distcheck); } - /// Runs "distcheck", a 'make check' from a tarball + /// Runs `distcheck`, which is a collection of smoke tests: + /// + /// - Run `make check` from an unpacked dist tarball to make sure we can at the minimum run + /// check steps from those sources. + /// - Check that selected dist components (`rust-src` only at the moment) at least have expected + /// directory shape and crate manifests that cargo can generate a lockfile from. + /// + /// FIXME(#136822): dist components are under-tested. fn run(self, builder: &Builder<'_>) { builder.info("Distcheck"); let dir = builder.tempdir().join("distcheck"); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 678aa9b01e4a..76025d4020ea 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1197,9 +1197,9 @@ fn run_tool_build_step( artifact_kind: ToolArtifactKind::Binary, }); - // FIXME: This should just be an if-let-chain, but those are unstable. - if let Some(add_bins_to_sysroot) = - add_bins_to_sysroot.filter(|bins| !bins.is_empty() && target_compiler.stage > 0) + if let Some(add_bins_to_sysroot) = add_bins_to_sysroot + && !add_bins_to_sysroot.is_empty() + && target_compiler.stage > 0 { let bindir = builder.sysroot(target_compiler).join("bin"); t!(fs::create_dir_all(&bindir)); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e41f6f16b023..51556525bc99 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1004,7 +1004,12 @@ impl Builder<'_> { // efficient initial-exec TLS model. This doesn't work with `dlopen`, // so we can't use it by default in general, but we can use it for tools // and our own internal libraries. - if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") { + // + // Cygwin only supports emutls. + if !mode.must_support_dlopen() + && !target.triple.starts_with("powerpc-") + && !target.triple.contains("cygwin") + { cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1"); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 3b8c3655b8d1..d4b5a8092150 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1832,7 +1832,9 @@ impl Config { .join(exe("rustc", config.build)) }; - config.initial_sysroot = config.initial_rustc.ancestors().nth(2).unwrap().into(); + config.initial_sysroot = t!(PathBuf::from_str( + output(Command::new(&config.initial_rustc).args(["--print", "sysroot"])).trim() + )); config.initial_cargo_clippy = cargo_clippy; diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index eb7e3799a688..af4ec679d080 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -13,7 +13,6 @@ use std::ffi::{OsStr, OsString}; use std::path::PathBuf; use std::{env, fs}; -use crate::Build; #[cfg(not(test))] use crate::builder::Builder; use crate::builder::Kind; @@ -21,6 +20,7 @@ use crate::builder::Kind; use crate::core::build_steps::tool; use crate::core::config::Target; use crate::utils::exec::command; +use crate::{Build, Subcommand}; pub struct Finder { cache: HashMap>, @@ -205,6 +205,20 @@ than building it. .map(|s| s.to_string()) .collect(); + // Compiler tools like `cc` and `ar` are not configured for cross-targets on certain subcommands + // because they are not needed. + // + // See `cc_detect::find` for more details. + let skip_tools_checks = build.config.dry_run() + || matches!( + build.config.cmd, + Subcommand::Clean { .. } + | Subcommand::Check { .. } + | Subcommand::Suggest { .. } + | Subcommand::Format { .. } + | Subcommand::Setup { .. } + ); + // We're gonna build some custom C code here and there, host triples // also build some C++ shims for LLVM so we need a C++ compiler. for target in &build.targets { @@ -278,7 +292,7 @@ than building it. } } - if !build.config.dry_run() { + if !skip_tools_checks { cmd_finder.must_have(build.cc(*target)); if let Some(ar) = build.ar(*target) { cmd_finder.must_have(ar); @@ -286,7 +300,7 @@ than building it. } } - if !build.config.dry_run() { + if !skip_tools_checks { for host in &build.hosts { cmd_finder.must_have(build.cxx(*host).unwrap()); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 9492ffaed756..7cce14841eb3 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -363,19 +363,35 @@ impl Build { let in_tree_llvm_info = config.in_tree_llvm_info.clone(); let in_tree_gcc_info = config.in_tree_gcc_info.clone(); - let initial_target_libdir_str = - config.initial_sysroot.join("lib/rustlib").join(config.build).join("lib"); + let initial_target_libdir = + output(Command::new(&config.initial_rustc).args(["--print", "target-libdir"])) + .trim() + .to_owned(); + + let initial_target_dir = Path::new(&initial_target_libdir) + .parent() + .unwrap_or_else(|| panic!("{initial_target_libdir} has no parent")); - let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap(); let initial_lld = initial_target_dir.join("bin").join("rust-lld"); - let initial_relative_libdir = initial_target_dir - .ancestors() - .nth(2) - .unwrap() - .strip_prefix(&config.initial_sysroot) - .expect("Couldn’t determine initial relative libdir.") - .to_path_buf(); + let initial_relative_libdir = if cfg!(test) { + // On tests, bootstrap uses the shim rustc, not the one from the stage0 toolchain. + PathBuf::default() + } else { + let ancestor = initial_target_dir.ancestors().nth(2).unwrap_or_else(|| { + panic!("Not enough ancestors for {}", initial_target_dir.display()) + }); + + ancestor + .strip_prefix(&config.initial_sysroot) + .unwrap_or_else(|_| { + panic!( + "Couldn’t resolve the initial relative libdir from {}", + initial_target_dir.display() + ) + }) + .to_path_buf() + }; let version = std::fs::read_to_string(src.join("src").join("version")) .expect("failed to read src/version"); diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs index 1c8cc4025df1..46eeffad88c3 100644 --- a/src/bootstrap/src/utils/cache.rs +++ b/src/bootstrap/src/utils/cache.rs @@ -20,7 +20,6 @@ use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::Deref; -use std::path::PathBuf; use std::sync::{LazyLock, Mutex}; use std::{fmt, mem}; @@ -51,26 +50,11 @@ impl PartialEq for Interned { } impl Eq for Interned {} -impl PartialEq for Interned { - fn eq(&self, other: &str) -> bool { - *self == other - } -} impl PartialEq<&str> for Interned { fn eq(&self, other: &&str) -> bool { **self == **other } } -impl PartialEq<&Interned> for Interned { - fn eq(&self, other: &&Self) -> bool { - self.0 == other.0 - } -} -impl PartialEq> for &Interned { - fn eq(&self, other: &Interned) -> bool { - self.0 == other.0 - } -} unsafe impl Send for Interned {} unsafe impl Sync for Interned {} @@ -188,8 +172,6 @@ impl TyIntern { #[derive(Default)] pub struct Interner { strs: Mutex>, - paths: Mutex>, - lists: Mutex>>, } /// Defines the behavior required for a type to be internable. @@ -210,18 +192,6 @@ impl Internable for String { } } -impl Internable for PathBuf { - fn intern_cache() -> &'static Mutex> { - &INTERNER.paths - } -} - -impl Internable for Vec { - fn intern_cache() -> &'static Mutex> { - &INTERNER.lists - } -} - impl Interner { /// Interns a string reference, ensuring it is stored uniquely. /// diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs index 28f5563a589b..8562a35b3e06 100644 --- a/src/bootstrap/src/utils/cache/tests.rs +++ b/src/bootstrap/src/utils/cache/tests.rs @@ -12,26 +12,6 @@ fn test_string_interning() { assert_ne!(s1, s3, "Different strings should have different interned values"); } -#[test] -fn test_path_interning() { - let p1 = PathBuf::from("/tmp/file").intern(); - let p2 = PathBuf::from("/tmp/file").intern(); - let p3 = PathBuf::from("/tmp/other").intern(); - - assert_eq!(p1, p2); - assert_ne!(p1, p3); -} - -#[test] -fn test_vec_interning() { - let v1 = vec!["a".to_string(), "b".to_string()].intern(); - let v2 = vec!["a".to_string(), "b".to_string()].intern(); - let v3 = vec!["c".to_string()].intern(); - - assert_eq!(v1, v2); - assert_ne!(v1, v3); -} - #[test] fn test_interned_equality() { let s1 = INTERNER.intern_str("test"); diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index ceac24d4315c..5c9e30706ee0 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -22,43 +22,13 @@ //! everything. use std::collections::HashSet; +use std::iter; use std::path::{Path, PathBuf}; -use std::{env, iter}; use crate::core::config::TargetSelection; use crate::utils::exec::{BootstrapCommand, command}; use crate::{Build, CLang, GitRepo}; -/// Finds archiver tool for the given target if possible. -/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate. -fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option { - if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) { - Some(PathBuf::from(ar)) - } else if let Some(ar) = env::var_os("AR") { - Some(PathBuf::from(ar)) - } else if target.is_msvc() { - None - } else if target.contains("musl") || target.contains("openbsd") { - Some(PathBuf::from("ar")) - } else if target.contains("vxworks") { - Some(PathBuf::from("wr-ar")) - } else if target.contains("-nto-") { - if target.starts_with("i586") { - Some(PathBuf::from("ntox86-ar")) - } else if target.starts_with("aarch64") { - Some(PathBuf::from("ntoaarch64-ar")) - } else if target.starts_with("x86_64") { - Some(PathBuf::from("ntox86_64-ar")) - } else { - panic!("Unknown architecture, cannot determine archiver for Neutrino QNX"); - } - } else if target.contains("android") || target.contains("-wasi") { - Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar"))) - } else { - Some(default_ar) - } -} - /// Creates and configures a new [`cc::Build`] instance for the given target. fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { let mut cfg = cc::Build::new(); @@ -140,7 +110,7 @@ pub fn find_target(build: &Build, target: TargetSelection) { let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { ar } else { - cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program())) + cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok() }; build.cc.borrow_mut().insert(target, compiler.clone()); diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index 43d61ce02c5a..225fb7619b55 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -5,119 +5,6 @@ use super::*; use crate::core::config::{Target, TargetSelection}; use crate::{Build, Config, Flags}; -#[test] -fn test_cc2ar_env_specific() { - let triple = "x86_64-unknown-linux-gnu"; - let key = "AR_x86_64_unknown_linux_gnu"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::set_var(key, "custom-ar") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var(key) }; - assert_eq!(result, Some(PathBuf::from("custom-ar"))); -} - -#[test] -fn test_cc2ar_musl() { - let triple = "x86_64-unknown-linux-musl"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_x86_64_unknown_linux_musl") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ar"))); -} - -#[test] -fn test_cc2ar_openbsd() { - let triple = "x86_64-unknown-openbsd"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_x86_64_unknown_openbsd") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/cc"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ar"))); -} - -#[test] -fn test_cc2ar_vxworks() { - let triple = "armv7-wrs-vxworks"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_armv7_wrs_vxworks") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("wr-ar"))); -} - -#[test] -fn test_cc2ar_nto_i586() { - let triple = "i586-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_i586_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntox86-ar"))); -} - -#[test] -fn test_cc2ar_nto_aarch64() { - let triple = "aarch64-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_aarch64_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntoaarch64-ar"))); -} - -#[test] -fn test_cc2ar_nto_x86_64() { - let triple = "x86_64-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_x86_64_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntox86_64-ar"))); -} - -#[test] -#[should_panic(expected = "Unknown architecture, cannot determine archiver for Neutrino QNX")] -fn test_cc2ar_nto_unknown() { - let triple = "powerpc-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_powerpc_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let _ = cc2ar(cc, target, default_ar); -} - #[test] fn test_ndk_compiler_c() { let ndk_path = PathBuf::from("/ndk"); diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index d07300e21d00..64e46f105638 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -332,16 +332,19 @@ impl Default for CommandOutput { /// Helper trait to format both Command and BootstrapCommand as a short execution line, /// without all the other details (e.g. environment variables). +#[cfg(feature = "tracing")] pub trait FormatShortCmd { fn format_short_cmd(&self) -> String; } +#[cfg(feature = "tracing")] impl FormatShortCmd for BootstrapCommand { fn format_short_cmd(&self) -> String { self.command.format_short_cmd() } } +#[cfg(feature = "tracing")] impl FormatShortCmd for Command { fn format_short_cmd(&self) -> String { let program = Path::new(self.get_program()); diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index 08e1c21e58e7..561af34a4478 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -46,7 +46,16 @@ pub fn dylib_path() -> Vec { /// Given an executable called `name`, return the filename for the /// executable for a particular target. pub fn exe(name: &str, target: &str) -> String { - if target.contains("windows") { + // On Cygwin, the decision to append .exe or not is not as straightforward. + // Executable files do actually have .exe extensions so on hosts other than + // Cygwin it is necessary. But on a Cygwin host there is magic happening + // that redirects requests for file X to file X.exe if it exists, and + // furthermore /proc/self/exe (and thus std::env::current_exe) always + // returns the name *without* the .exe extension. For comparisons against + // that to match, we therefore do not append .exe for Cygwin targets on + // a Cygwin host. + if target.contains("windows") || (cfg!(not(target_os = "cygwin")) && target.contains("cygwin")) + { format!("{name}.exe") } else if target.contains("uefi") { format!("{name}.efi") diff --git a/src/build_helper/src/lib.rs b/src/build_helper/src/lib.rs index dceb5fdeeea5..1f5cf7236411 100644 --- a/src/build_helper/src/lib.rs +++ b/src/build_helper/src/lib.rs @@ -10,23 +10,24 @@ pub mod util; /// The default set of crates for opt-dist to collect LLVM profiles. pub const LLVM_PGO_CRATES: &[&str] = &[ - "syn-1.0.89", - "cargo-0.60.0", - "serde-1.0.136", - "ripgrep-13.0.0", - "regex-1.5.5", - "clap-3.1.6", - "hyper-0.14.18", + "syn-2.0.101", + "cargo-0.87.1", + "serde-1.0.219", + "ripgrep-14.1.1", + "regex-automata-0.4.8", + "clap_derive-4.5.32", + "hyper-1.6.0", ]; /// The default set of crates for opt-dist to collect rustc profiles. pub const RUSTC_PGO_CRATES: &[&str] = &[ "externs", "ctfe-stress-5", - "cargo-0.60.0", + "cargo-0.87.1", "token-stream-stress", "match-stress", "tuple-stress", - "diesel-1.4.8", - "bitmaps-3.1.0", + "diesel-2.2.10", + "bitmaps-3.2.1", + "serde-1.0.219-new-solver", ]; diff --git a/src/ci/citool/Cargo.lock b/src/ci/citool/Cargo.lock index 43321d12cafc..571f18e7cf18 100644 --- a/src/ci/citool/Cargo.lock +++ b/src/ci/citool/Cargo.lock @@ -66,9 +66,9 @@ checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "askama" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" dependencies = [ "askama_derive", "itoa", @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" dependencies = [ "askama_parser", "basic-toml", @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "askama_parser" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" dependencies = [ "memchr", "serde", diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml index 0e2aba3b9e3f..f61243a4d712 100644 --- a/src/ci/citool/Cargo.toml +++ b/src/ci/citool/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] anyhow = "1" -askama = "0.13" +askama = "0.14" clap = { version = "4.5", features = ["derive"] } csv = "1" diff = "0.1" diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs index 5600d7b4db59..60cbf50c7a3a 100644 --- a/src/ci/citool/src/jobs.rs +++ b/src/ci/citool/src/jobs.rs @@ -85,14 +85,20 @@ impl JobDatabase { } pub fn load_job_db(db: &str) -> anyhow::Result { - let mut db: Value = serde_yaml::from_str(db)?; + let mut db: Value = serde_yaml::from_str(db).context("failed to parse YAML content")?; // We need to expand merge keys (<<), because serde_yaml can't deal with them // `apply_merge` only applies the merge once, so do it a few times to unwrap nested merges. - db.apply_merge()?; - db.apply_merge()?; - let db: JobDatabase = serde_yaml::from_value(db)?; + let apply_merge = |db: &mut Value| -> anyhow::Result<()> { + db.apply_merge().context("failed to apply merge keys") + }; + + // Apply merge twice to handle nested merges + apply_merge(&mut db)?; + apply_merge(&mut db)?; + + let db: JobDatabase = serde_yaml::from_value(db).context("failed to parse job database")?; Ok(db) } diff --git a/src/ci/citool/src/jobs/tests.rs b/src/ci/citool/src/jobs/tests.rs index a489656fa5dc..ed5444d4333d 100644 --- a/src/ci/citool/src/jobs/tests.rs +++ b/src/ci/citool/src/jobs/tests.rs @@ -1,4 +1,8 @@ +use std::path::Path; + +use super::Job; use crate::jobs::{JobDatabase, load_job_db}; +use crate::{DOCKER_DIRECTORY, JOBS_YML_PATH, utils}; #[test] fn lookup_job_pattern() { @@ -62,3 +66,65 @@ fn check_pattern(db: &JobDatabase, pattern: &str, expected: &[&str]) { assert_eq!(jobs, expected); } + +/// Validate that CodeBuild jobs use Docker images from ghcr.io registry. +/// This is needed because otherwise from CodeBuild we get rate limited by Docker Hub. +fn validate_codebuild_image(job: &Job) -> anyhow::Result<()> { + let is_job_on_codebuild = job.codebuild.unwrap_or(false); + if !is_job_on_codebuild { + // Jobs in GitHub Actions don't get rate limited by Docker Hub. + return Ok(()); + } + + let image_name = job.image(); + // we hardcode host-x86_64 here, because in codebuild we only run jobs for this architecture. + let dockerfile_path = + Path::new(DOCKER_DIRECTORY).join("host-x86_64").join(&image_name).join("Dockerfile"); + + if !dockerfile_path.exists() { + return Err(anyhow::anyhow!( + "Dockerfile not found for CodeBuild job '{}' at path: {}", + job.name, + dockerfile_path.display() + )); + } + + let dockerfile_content = utils::read_to_string(&dockerfile_path)?; + + // Check if all FROM statement uses ghcr.io registry + let has_ghcr_from = dockerfile_content + .lines() + .filter(|line| line.trim_start().to_lowercase().starts_with("from ")) + .all(|line| line.contains("ghcr.io")); + + if !has_ghcr_from { + return Err(anyhow::anyhow!( + "CodeBuild job '{}' must use ghcr.io registry in its Dockerfile FROM statement. \ + Dockerfile path: {dockerfile_path:?}", + job.name, + )); + } + + Ok(()) +} + +#[test] +fn validate_jobs() { + let db = { + let default_jobs_file = Path::new(JOBS_YML_PATH); + let db_str = utils::read_to_string(default_jobs_file).unwrap(); + load_job_db(&db_str).expect("Failed to load job database") + }; + + let all_jobs = + db.pr_jobs.iter().chain(db.try_jobs.iter()).chain(db.auto_jobs.iter()).collect::>(); + + let errors: Vec = + all_jobs.into_iter().filter_map(|job| validate_codebuild_image(job).err()).collect(); + + if !errors.is_empty() { + let error_messages = + errors.into_iter().map(|e| format!("- {e}")).collect::>().join("\n"); + panic!("Job validation failed:\n{error_messages}"); + } +} diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index 87ce09cfb233..bb73a5ef909f 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -27,7 +27,7 @@ use crate::test_dashboard::generate_test_dashboard; use crate::utils::{load_env_var, output_details}; const CI_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/.."); -const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker"); +pub const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker"); const JOBS_YML_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../github-actions/jobs.yml"); struct GitHubContext { diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile new file mode 100644 index 000000000000..2f9d0010573a --- /dev/null +++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile @@ -0,0 +1,58 @@ +FROM ubuntu:24.10 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + bzip2 \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-19-tools \ + llvm-19-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + mingw-w64 \ + # libgccjit dependencies + flex \ + libmpfr-dev \ + libgmp-dev \ + libmpc3 \ + libmpc-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 +ENV EXTERNAL_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=aarch64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-19 \ + --enable-llvm-link-shared \ + --set rust.randomize-layout=true \ + --set rust.thin-lto-import-instr-limit=10 + +COPY scripts/shared.sh /scripts/ + +ARG SCRIPT_ARG + +COPY scripts/stage_2_test_set1.sh /tmp/ +COPY scripts/stage_2_test_set2.sh /tmp/ + +ENV SCRIPT "/tmp/${SCRIPT_ARG}" diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile index aade95882685..bc311be05808 100644 --- a/src/ci/docker/host-x86_64/arm-android/Dockerfile +++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile @@ -28,6 +28,7 @@ RUN /scripts/android-sdk.sh ENV PATH=$PATH:/android/sdk/emulator ENV PATH=$PATH:/android/sdk/tools ENV PATH=$PATH:/android/sdk/platform-tools +ENV PATH=$PATH:/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin ENV TARGETS=arm-linux-androideabi diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile index 95fed6ee767b..7b73326e3590 100644 --- a/src/ci/docker/host-x86_64/dist-android/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile @@ -22,6 +22,8 @@ ENV RUST_CONFIGURE_ARGS \ --android-ndk=/android/ndk/ \ --disable-docs +ENV PATH=$PATH:/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin + ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile new file mode 100644 index 000000000000..996dacd71247 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile @@ -0,0 +1,35 @@ +FROM ghcr.io/rust-lang/ubuntu:22.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh + +WORKDIR /build + +COPY scripts/rustbuild-setup.sh /scripts/ +RUN sh /scripts/rustbuild-setup.sh +WORKDIR /tmp + +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PATH=$PATH:/x-tools/arm-unknown-linux-gnueabi/bin + +ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \ + AR_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-ar \ + CXX_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-g++ + +ENV HOSTS=arm-unknown-linux-gnueabi + +ENV RUST_CONFIGURE_ARGS \ + --enable-full-tools \ + --disable-docs \ + --enable-sanitizers \ + --enable-profiler +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/arm-linux-gnueabi.defconfig b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig similarity index 100% rename from src/ci/docker/host-x86_64/dist-arm-linux/arm-linux-gnueabi.defconfig rename to src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile similarity index 73% rename from src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile rename to src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile index 3795859f308e..6e055cd2bd5a 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile @@ -19,19 +19,13 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-arm-linux/arm-linux-gnueabi.defconfig /tmp/crosstool.defconfig +COPY host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -ENV PATH=$PATH:/x-tools/arm-unknown-linux-gnueabi/bin - -ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \ - AR_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-ar \ - CXX_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-g++ - -ENV HOSTS=arm-unknown-linux-gnueabi,aarch64-unknown-linux-musl +ENV HOSTS=aarch64-unknown-linux-musl ENV RUST_CONFIGURE_ARGS \ --enable-full-tools \ diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig new file mode 100644 index 000000000000..e7afdbe9d4de --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig @@ -0,0 +1,13 @@ +CT_CONFIG_VERSION="4" +CT_PREFIX_DIR="/x-tools/${CT_TARGET}" +CT_USE_MIRROR=y +CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_ARCH_ARM=y +CT_ARCH_ARCH="armv6" +CT_ARCH_FLOAT_SW=y +CT_KERNEL_LINUX=y +CT_LINUX_V_3_2=y +CT_BINUTILS_V_2_32=y +CT_GLIBC_V_2_17=y +CT_GCC_V_8=y +CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile new file mode 100644 index 000000000000..d2f1b9400ad8 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile @@ -0,0 +1,41 @@ +FROM ubuntu:22.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh + +COPY scripts/rustbuild-setup.sh /scripts/ +RUN sh /scripts/rustbuild-setup.sh + +WORKDIR /tmp + +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +WORKDIR /build + +RUN apt-get install -y --no-install-recommends rpm2cpio cpio +COPY scripts/shared.sh scripts/build-powerpc64le-toolchain.sh /build/ +RUN ./build-powerpc64le-toolchain.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV \ + AR_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-ar \ + CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc \ + CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++ + +ENV HOSTS=powerpc64le-unknown-linux-gnu + +ENV RUST_CONFIGURE_ARGS \ + --enable-extended \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --disable-docs + +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig new file mode 100644 index 000000000000..363e5850894e --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig @@ -0,0 +1,14 @@ +CT_CONFIG_VERSION="4" +CT_EXPERIMENTAL=y +CT_PREFIX_DIR="/x-tools/${CT_TARGET}" +CT_USE_MIRROR=y +CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_ARCH_POWERPC=y +CT_ARCH_LE=y +CT_ARCH_64=y +# CT_DEMULTILIB is not set +CT_ARCH_ARCH="powerpc64le" +CT_KERNEL_LINUX=y +CT_LINUX_V_4_19=y +CT_CC_LANG_CXX=y +CT_GETTEXT_NEEDED=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile similarity index 71% rename from src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile rename to src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile index cb20f43cff70..f045b2a5f655 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile @@ -12,13 +12,13 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig /tmp/crosstool.defconfig +COPY host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh WORKDIR /build RUN apt-get install -y --no-install-recommends rpm2cpio cpio -COPY scripts/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ +COPY scripts/shared.sh scripts/build-powerpc64le-toolchain.sh /build/ RUN ./build-powerpc64le-toolchain.sh COPY scripts/sccache.sh /scripts/ @@ -27,14 +27,11 @@ RUN sh /scripts/sccache.sh ENV PATH=$PATH:/x-tools/powerpc64le-unknown-linux-musl/bin ENV \ - AR_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-ar \ - CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc \ - CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++ \ AR_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-ar \ CC_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-gcc \ CXX_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-g++ -ENV HOSTS=powerpc64le-unknown-linux-gnu,powerpc64le-unknown-linux-musl +ENV HOSTS=powerpc64le-unknown-linux-musl ENV RUST_CONFIGURE_ARGS \ --enable-extended \ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig rename to src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile index 9ca8cc740a5c..d17f7ed7171f 100644 --- a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile @@ -24,6 +24,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* +COPY scripts/nodejs.sh /scripts/ +RUN sh /scripts/nodejs.sh /node +ENV PATH="/node/bin:${PATH}" + +# Install eslint +COPY host-x86_64/mingw-check-tidy/eslint.version /tmp/ + COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -36,5 +43,5 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ # NOTE: intentionally uses python2 for x.py so we can test it still works. # validate-toolstate only runs in our CI, so it's ok for it to only support python3. -ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \ - --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp +ENV SCRIPT TIDY_PRINT_DIFF=1 npm install eslint@$(head -n 1 /tmp/eslint.version) && \ + python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/eslint.version b/src/ci/docker/host-x86_64/mingw-check-tidy/eslint.version new file mode 100644 index 000000000000..1acea15afd69 --- /dev/null +++ b/src/ci/docker/host-x86_64/mingw-check-tidy/eslint.version @@ -0,0 +1 @@ +8.6.0 \ No newline at end of file diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 418408e9242a..df73c7382b51 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -65,7 +65,4 @@ ENV SCRIPT \ python3 ../x.py test collect-license-metadata && \ # Runs checks to ensure that there are no issues in our JS code. es-check es2019 ../src/librustdoc/html/static/js/*.js && \ - eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ - eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ - eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js && \ tsc --project ../src/librustdoc/html/static/js/tsconfig.json diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile index 2217e6ee7043..98fd31a22e93 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile @@ -1,3 +1,15 @@ +# Runs `distcheck`, which is a collection of smoke tests: +# +# - Run `make check` from an unpacked dist tarball to make sure we can at the +# minimum run check steps from those sources. +# - Check that selected dist components at least have expected directory shape +# and crate manifests that cargo can generate a lockfile from. +# +# Refer to `src/bootstrap/src/core/build_steps/test.rs` `Distcheck::run` for +# specifics. +# +# FIXME(#136822): dist components are generally under-tested. + FROM ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive 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 05c90af78073..e770c58bd9cf 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 @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ghcr.io/rust-lang/ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 9222710b8437..62e0451814b3 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -53,8 +53,8 @@ MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zm case $HOST_TARGET in x86_64-unknown-linux-gnu) # Only this branch runs in PR CI. - # Fully test all main OSes, including a 32bit target. - python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target x86_64-apple-darwin + # Fully test all main OSes, and all main architectures. + python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target aarch64-apple-darwin python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target i686-pc-windows-msvc # Only run "pass" tests for the remaining targets, which is quite a bit faster. python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass @@ -69,7 +69,7 @@ case $HOST_TARGET in #FIXME: Re-enable this once CI issues are fixed # See # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`. - #python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass + #python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-apple-darwin --test-args pass ;; *) echo "FATAL: unexpected host $HOST_TARGET" diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 36f7df2b0690..4e69fb2f3705 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -97,9 +97,8 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then docker --version REGISTRY=ghcr.io - # Hardcode username to reuse cache between auto and pr jobs - # FIXME: should be changed after move from rust-lang-ci - REGISTRY_USERNAME=rust-lang-ci + # Default to `rust-lang` to allow reusing the cache for local builds + REGISTRY_USERNAME=${GITHUB_REPOSITORY_OWNER:-rust-lang} # Tag used to push the final Docker image, so that it can be pulled by e.g. rustup IMAGE_TAG=${REGISTRY}/${REGISTRY_USERNAME}/rust-ci:${cksum} # Tag used to cache the Docker build diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh b/src/ci/docker/scripts/build-powerpc64le-toolchain.sh similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh rename to src/ci/docker/scripts/build-powerpc64le-toolchain.sh diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 1d280948ebe6..fa18f67583ff 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,7 +2,8 @@ set -euo pipefail -LINUX_VERSION=v6.15-rc4 +# https://github.com/Rust-for-Linux/linux/issues/1163 +LINUX_VERSION=3ca02fc80cc4fdac63aaa6796642f1e07be591d6 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm.sh b/src/ci/docker/scripts/x86_64-gnu-llvm.sh index e0435a3ff5c1..5fa17d954c3b 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm.sh @@ -2,8 +2,8 @@ set -ex -# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux. -../x.py --stage 2 test --skip src/tools/tidy +# NOTE: intentionally uses `x`, and `x.ps1` to make sure they work on Linux. +# Make sure that `x.py` is tested elsewhere. # Run the `mir-opt` tests again but this time for a 32-bit target. # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 42ad5acbdac1..9eedbd451d86 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -10,11 +10,6 @@ runners: free_disk: true <<: *base-job - # Large runner used mainly for its bigger disk capacity - - &job-linux-4c-largedisk - os: ubuntu-24.04-4core-16gb - <<: *base-job - - &job-linux-8c os: ubuntu-24.04-8core-32gb <<: *base-job @@ -77,7 +72,6 @@ envs: env-x86_64-apple-tests: &env-x86_64-apple-tests SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # Ensure that host tooling is tested on our minimum supported macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 @@ -126,9 +120,19 @@ pr: env: ENABLE_GCC_CODEGEN: "1" DOCKER_SCRIPT: x86_64-gnu-llvm.sh - <<: *job-linux-16c + <<: *job-linux-4c + - name: aarch64-gnu-llvm-19-1 + env: + IMAGE: aarch64-gnu-llvm-19 + DOCKER_SCRIPT: stage_2_test_set1.sh + <<: *job-aarch64-linux-8c + - name: aarch64-gnu-llvm-19-2 + env: + IMAGE: aarch64-gnu-llvm-19 + DOCKER_SCRIPT: stage_2_test_set2.sh + <<: *job-aarch64-linux - name: x86_64-gnu-tools - <<: *job-linux-16c + <<: *job-linux-36c-codebuild # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating @@ -137,7 +141,7 @@ try: - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-16c + <<: *job-linux-36c-codebuild # Main CI jobs that have to be green to merge a commit into master # These jobs automatically inherit envs.auto, to avoid repeating @@ -167,8 +171,11 @@ auto: - name: dist-android <<: *job-linux-4c - - name: dist-arm-linux - <<: *job-linux-8c-codebuild + - name: dist-arm-linux-gnueabi + <<: *job-linux-4c + + - name: dist-arm-linux-musl + <<: *job-linux-4c - name: dist-armhf-linux <<: *job-linux-4c @@ -203,8 +210,11 @@ auto: - name: dist-powerpc64-linux <<: *job-linux-4c - - name: dist-powerpc64le-linux - <<: *job-linux-4c-largedisk + - name: dist-powerpc64le-linux-gnu + <<: *job-linux-4c + + - name: dist-powerpc64le-linux-musl + <<: *job-linux-4c - name: dist-riscv64-linux <<: *job-linux-4c @@ -323,7 +333,7 @@ auto: <<: *job-linux-4c - name: x86_64-gnu-distcheck - <<: *job-linux-8c + <<: *job-linux-4c # The x86_64-gnu-llvm-20 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-20-1 skips tests that run in x86_64-gnu-llvm-20-{2,3}. @@ -331,7 +341,7 @@ auto: env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-20 - DOCKER_SCRIPT: stage_2_test_set1.sh + DOCKER_SCRIPT: stage_2_test_set2.sh <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-20-{1,3} @@ -356,7 +366,7 @@ auto: env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 - DOCKER_SCRIPT: stage_2_test_set1.sh + DOCKER_SCRIPT: stage_2_test_set2.sh <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,3} @@ -391,7 +401,6 @@ auto: env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # Ensure that host tooling is built to support our minimum support macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 @@ -409,7 +418,6 @@ auto: # Mac Catalyst cannot currently compile the sanitizer: # https://github.com/rust-lang/rust/issues/129069 RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # Ensure that host tooling is built to support our minimum support macOS version. # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?) MACOSX_DEPLOYMENT_TARGET: 10.12 @@ -442,7 +450,6 @@ auto: --set llvm.ninja=false --set rust.lto=thin --set rust.codegen-units=1 - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 SELECT_XCODE: /Applications/Xcode_15.4.app USE_XCODE_CLANG: 1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else @@ -458,12 +465,13 @@ auto: - name: aarch64-apple env: - SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin + SCRIPT: > + ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin && + ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin src/tools/cargo RUST_CONFIGURE_ARGS: >- --enable-sanitizers --enable-profiler --set rust.jemalloc - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 SELECT_XCODE: /Applications/Xcode_15.4.app USE_XCODE_CLANG: 1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else @@ -522,11 +530,13 @@ auto: - name: x86_64-msvc-ext2 env: SCRIPT: > - python x.py test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass && + python x.py test --stage 2 src/tools/miri --target x86_64-apple-darwin --test-args pass && python x.py test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass && python x.py miri --stage 2 library/core --test-args notest && python x.py miri --stage 2 library/alloc --test-args notest && python x.py miri --stage 2 library/std --test-args notest + # The last 3 lines smoke-test `x.py miri`. This doesn't run any actual tests (that would take + # too long), but it ensures that the crates build properly when tested with Miri. RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld <<: *job-windows diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index 5522095e3049..a9528e929159 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -10,8 +10,8 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" # Update both macOS's and Windows's tarballs when bumping the version here. -# Try to keep this in sync with src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh -LLVM_VERSION="18.1.4" +# Try to keep this in sync with src/ci/docker/scripts/build-clang.sh +LLVM_VERSION="20.1.3" if isMacOS; then # FIXME: This is the latest pre-built version of LLVM that's available for diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh index c8c501e646a9..ad852071f295 100755 --- a/src/ci/scripts/install-mingw.sh +++ b/src/ci/scripts/install-mingw.sh @@ -42,5 +42,5 @@ if isWindows && isKnownToBeMingwBuild; then curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}" 7z x -y mingw.7z > /dev/null - ciCommandAddPath "$(pwd)/${mingw_dir}/bin" + ciCommandAddPath "$(cygpath -m "$(pwd)/${mingw_dir}/bin")" fi diff --git a/src/ci/scripts/install-ninja.sh b/src/ci/scripts/install-ninja.sh index 23cbc2eb6d10..7ac19173923c 100755 --- a/src/ci/scripts/install-ninja.sh +++ b/src/ci/scripts/install-ninja.sh @@ -12,7 +12,7 @@ if isWindows; then 7z x -oninja ninja.zip rm ninja.zip ciCommandSetEnv "RUST_CONFIGURE_ARGS" "${RUST_CONFIGURE_ARGS} --enable-ninja" - ciCommandAddPath "$(pwd)/ninja" + ciCommandAddPath "$(cygpath -m "$(pwd)/ninja")" elif isMacOS; then brew install ninja fi diff --git a/src/ci/scripts/install-sccache.sh b/src/ci/scripts/install-sccache.sh index b055e76a8050..fed06063fa0b 100755 --- a/src/ci/scripts/install-sccache.sh +++ b/src/ci/scripts/install-sccache.sh @@ -15,7 +15,7 @@ elif isWindows; then mkdir -p sccache curl -fo sccache/sccache.exe \ "${MIRRORS_BASE}/2025-02-24-sccache-v0.10.0-x86_64-pc-windows-msvc.exe" - ciCommandAddPath "$(pwd)/sccache" + ciCommandAddPath "$(cygpath -m "$(pwd)/sccache")" fi # FIXME: we should probably install sccache outside the containers and then diff --git a/src/doc/book b/src/doc/book index d33916341d48..230c68bc1e08 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit d33916341d480caede1d0ae57cbeae23aab23e88 +Subproject commit 230c68bc1e08f5f3228384a28cc228c81dfbd10d diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 1b1bb49babd6..aa6ce337c0ad 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 1b1bb49babd65c732468cfa515b0c009bd1d26bc +Subproject commit aa6ce337c0adf7a63e33960d184270f2a45ab9ef diff --git a/src/doc/reference b/src/doc/reference index 387392674d74..118fd1f1f085 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 387392674d74656f7cb437c05a96f0c52ea8e601 +Subproject commit 118fd1f1f0854f50e3ae1fe4b64862aad23009ca diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 8a8918c69853..c9d151f9147c 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 8a8918c698534547fa8a1a693cb3e7277f0bfb2f +Subproject commit c9d151f9147c4808c77f0375ba3fa5d54443cb9e diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 0d889a5d5b99..b1e9eec529e6 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -e42bbfe1f7c26f8760a99c4b1f27d33aba1040bb +99e7c15e81385b38a8186b51edc4577d5d7b5bdd diff --git a/src/doc/rustc-dev-guide/src/memory.md b/src/doc/rustc-dev-guide/src/memory.md index eeb4a813980a..f766a51898e4 100644 --- a/src/doc/rustc-dev-guide/src/memory.md +++ b/src/doc/rustc-dev-guide/src/memory.md @@ -63,7 +63,7 @@ represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`). [`mk_args`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.mk_args [adtdefid]: ./ty_module/generic_arguments.md#adtdef-and-defid [`Predicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Predicate.html -[`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TraitRef.html +[`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html [`ty::TyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/type.TyKind.html [traits]: ./traits/resolution.md diff --git a/src/doc/rustc/src/check-cfg/cargo-specifics.md b/src/doc/rustc/src/check-cfg/cargo-specifics.md index 371bbd26e943..62a4dd1a3901 100644 --- a/src/doc/rustc/src/check-cfg/cargo-specifics.md +++ b/src/doc/rustc/src/check-cfg/cargo-specifics.md @@ -9,8 +9,8 @@ rustc, not Cargo. --> This document is intended to summarize the principal ways Cargo interacts with -the `unexpected_cfgs` lint and `--check-cfg` flag. It is not intended to provide -individual details, for that refer to the [`--check-cfg` documentation](../check-cfg.md) and +the `unexpected_cfgs` lint and `--check-cfg` flag. +For individual details, refer to the [`--check-cfg` documentation](../check-cfg.md) and to the [Cargo book](../../cargo/index.html). > The full list of well known cfgs (aka builtins) can be found under [Checking conditional configurations / Well known names and values](../check-cfg.md#well-known-names-and-values). diff --git a/src/doc/rustc/src/platform-support/solaris.md b/src/doc/rustc/src/platform-support/solaris.md index 0452d76f6c28..c22b5c24c125 100644 --- a/src/doc/rustc/src/platform-support/solaris.md +++ b/src/doc/rustc/src/platform-support/solaris.md @@ -8,6 +8,7 @@ Rust for Solaris operating system. ## Target maintainers [@psumbera](https://github.com/psumbera) +[@kulikjak](https://github.com/kulikjak) ## Requirements diff --git a/src/doc/rustc/theme/pagetoc.css b/src/doc/rustc/theme/pagetoc.css index 58ca1f8b26f8..fa709194f375 100644 --- a/src/doc/rustc/theme/pagetoc.css +++ b/src/doc/rustc/theme/pagetoc.css @@ -49,7 +49,7 @@ } #pagetoc a { border-left: 1px solid var(--sidebar-bg); - color: var(--sidebar-fg) !important; + color: var(--fg); display: block; padding-bottom: 5px; padding-top: 5px; diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md index 45146993371f..6ec93d1746c8 100644 --- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -88,8 +88,10 @@ on your documentation examples make requests to. ``` Now, when you press "run", the button will make a request to this domain. The request -URL will contain 2 query parameters: `code` and `edition` for the code in the documentation -and the Rust edition respectively. +URL will contain 3 query parameters: +1. `code` for the code in the documentation +2. `version` for the Rust channel, e.g. nightly, which is decided by whether `code` contain unstable features +3. `edition` for the Rust edition, e.g. 2024 If you don't use this attribute, there will be no run buttons. diff --git a/src/doc/unstable-book/README.md b/src/doc/unstable-book/README.md new file mode 100644 index 000000000000..7acdd32857fe --- /dev/null +++ b/src/doc/unstable-book/README.md @@ -0,0 +1,8 @@ +# Unstable Book + +These are the sources for . +To generate them, run `./x doc unstable-book`, which will generate HTML files in `build/host/doc/unstable-book` using `src/tools/rustbook`. +If you need to change the overall structure, modify `src/tools/unstable-book-gen/src/SUMMARY.md`. + +Note that most of this book is autogenerated by `unstable-book-gen`, with the exception of `compiler-flags` and `compiler-environment-variables`. +As a result, it does not integrate well with `mdbook`. Use `./x doc` instead. diff --git a/src/doc/unstable-book/src/compiler-environment-variables.md b/src/doc/unstable-book/src/compiler-environment-variables.md new file mode 100644 index 000000000000..db912fdf3bac --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables.md @@ -0,0 +1 @@ +# Compiler environment variables diff --git a/src/doc/unstable-book/src/compiler-flags/rustc-bootstrap.md b/src/doc/unstable-book/src/compiler-environment-variables/RUSTC_BOOTSTRAP.md similarity index 98% rename from src/doc/unstable-book/src/compiler-flags/rustc-bootstrap.md rename to src/doc/unstable-book/src/compiler-environment-variables/RUSTC_BOOTSTRAP.md index 1520b86341b2..fed28a332669 100644 --- a/src/doc/unstable-book/src/compiler-flags/rustc-bootstrap.md +++ b/src/doc/unstable-book/src/compiler-environment-variables/RUSTC_BOOTSTRAP.md @@ -14,7 +14,7 @@ Cargo disallows setting `cargo::rustc-env=RUSTC_BOOTSTRAP` in build scripts. Build systems can limit the features they enable with [`-Z allow-features=feature1,feature2`][Z-allow-features]. Crates can fully opt out of unstable features by using [`#![forbid(unstable_features)]`][unstable-features] at the crate root (or any other way of enabling lints, such as `-F unstable-features`). -[Z-allow-features]: ./allow-features.html +[Z-allow-features]: ../compiler-flags/allow-features.html [unstable-features]: ../../rustc/lints/listing/allowed-by-default.html#unstable-features ## Why does this environment variable exist? diff --git a/src/doc/unstable-book/src/compiler-flags/rustc-override-version-string.md b/src/doc/unstable-book/src/compiler-environment-variables/RUSTC_OVERRIDE_VERSION_STRING.md similarity index 100% rename from src/doc/unstable-book/src/compiler-flags/rustc-override-version-string.md rename to src/doc/unstable-book/src/compiler-environment-variables/RUSTC_OVERRIDE_VERSION_STRING.md diff --git a/src/doc/unstable-book/src/compiler-flags/allow-features.md b/src/doc/unstable-book/src/compiler-flags/allow-features.md index 84fa465c89b4..49a41a8c5a31 100644 --- a/src/doc/unstable-book/src/compiler-flags/allow-features.md +++ b/src/doc/unstable-book/src/compiler-flags/allow-features.md @@ -11,4 +11,4 @@ Features are comma-separated, for example `-Z allow-features=ffi_pure,f16`. If the flag is present, any feature listed will be allowed and any feature not listed will be disallowed. Any unrecognized feature is ignored. -[`RUSTC_BOOTSTRAP`]: ./rustc-bootstrap.html +[`RUSTC_BOOTSTRAP`]: ../compiler-environment-variables/RUSTC_BOOTSTRAP.html diff --git a/src/doc/unstable-book/src/compiler-flags/eagerly-emit-delayed-bugs.md b/src/doc/unstable-book/src/compiler-flags/eagerly-emit-delayed-bugs.md new file mode 100644 index 000000000000..39f0c04a1b5e --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/eagerly-emit-delayed-bugs.md @@ -0,0 +1,12 @@ +# `eagerly-emit-delayed-bugs` + +This feature is perma-unstable and has no tracking issue. + +------------------------ + +This flag converts all [`span_delayed_bug()`] calls to [`bug!`] calls, exiting the compiler immediately and allowing you to generate a backtrace of where the delayed bug occurred. +For full documentation, see [the rustc-dev-guide][dev-guide-delayed]. + +[`bug!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/macro.bug.html +[`span_delayed_bug()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxtHandle.html#method.span_delayed_bug +[dev-guide-delayed]: https://rustc-dev-guide.rust-lang.org/compiler-debugging.html#debugging-delayed-bugs diff --git a/src/doc/unstable-book/src/compiler-flags/track-diagnostics.md b/src/doc/unstable-book/src/compiler-flags/track-diagnostics.md new file mode 100644 index 000000000000..486202144079 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/track-diagnostics.md @@ -0,0 +1,11 @@ +# `track-diagnostics` + +This feature is perma-unstable and has no tracking issue. + +------------------------ + +This flag prints the source code span in the compiler where a diagnostic was generated, respecting [`#[track_caller]`][track_caller]. Note that this may be different from the place it was emitted. +For full documentation, see [the rustc-dev-guide][dev-guide-track-diagnostics]. + +[track_caller]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute +[dev-guide-track-diagnostics]: https://rustc-dev-guide.rust-lang.org/compiler-debugging.html#getting-the-error-creation-location diff --git a/src/doc/unstable-book/src/compiler-flags/treat-err-as-bug.md b/src/doc/unstable-book/src/compiler-flags/treat-err-as-bug.md new file mode 100644 index 000000000000..df7c380a50b7 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/treat-err-as-bug.md @@ -0,0 +1,13 @@ +# `treat-err-as-bug` + +This feature is perma-unstable and has no tracking issue. + +------------------------ + +This flag converts the selected error to a [`bug!`] call, exiting the compiler immediately and allowing you to generate a backtrace of where the error occurred. +For full documentation, see [the rustc-dev-guide][dev-guide-backtrace]. + +Note that the compiler automatically sets `RUST_BACKTRACE=1` for itself, and so you do not need to set it yourself when using this flag. + +[`bug!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/macro.bug.html +[dev-guide-backtrace]: https://rustc-dev-guide.rust-lang.org/compiler-debugging.html#getting-a-backtrace-for-errors diff --git a/src/doc/unstable-book/src/language-features/explicit-extern-abis.md b/src/doc/unstable-book/src/language-features/explicit-extern-abis.md index ba622466ba74..7728f6725b1d 100644 --- a/src/doc/unstable-book/src/language-features/explicit-extern-abis.md +++ b/src/doc/unstable-book/src/language-features/explicit-extern-abis.md @@ -1,6 +1,6 @@ # `explicit_extern_abis` -The tracking issue for this feature is: #134986 +The tracking issue for this feature is: [#134986] ------ @@ -21,3 +21,5 @@ extern "C" fn function2() {} // compiles extern "aapcs" fn function3() {} // compiles ``` + +[#134986]: https://github.com/rust-lang/rust/issues/134986 diff --git a/src/doc/unstable-book/src/language-features/repr128.md b/src/doc/unstable-book/src/language-features/repr128.md deleted file mode 100644 index 146f50ee67b5..000000000000 --- a/src/doc/unstable-book/src/language-features/repr128.md +++ /dev/null @@ -1,18 +0,0 @@ -# `repr128` - -The tracking issue for this feature is: [#56071] - -[#56071]: https://github.com/rust-lang/rust/issues/56071 - ------------------------- - -The `repr128` feature adds support for `#[repr(u128)]` on `enum`s. - -```rust -#![feature(repr128)] - -#[repr(u128)] -enum Foo { - Bar(u64), -} -``` diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el index 90bd38aa8947..3cb229cd98c1 100644 --- a/src/etc/rust_analyzer_eglot.el +++ b/src/etc/rust_analyzer_eglot.el @@ -14,7 +14,7 @@ "src/bootstrap/Cargo.toml" "src/tools/rust-analyzer/Cargo.toml"] :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt" - "--edition=2021"]) + "--edition=2024"]) :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" :enable t) :cargo ( :buildScripts ( :enable t diff --git a/src/etc/rust_analyzer_helix.toml b/src/etc/rust_analyzer_helix.toml index 05fc7716a725..1a6a14991ecf 100644 --- a/src/etc/rust_analyzer_helix.toml +++ b/src/etc/rust_analyzer_helix.toml @@ -32,7 +32,7 @@ overrideCommand = [ [language-server.rust-analyzer.config.rustfmt] overrideCommand = [ "build/rust-analyzer/host/rustfmt/bin/rustfmt", - "--edition=2021" + "--edition=2024" ] [language-server.rust-analyzer.config.procMacro] diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index 5ce886a9b659..a960cc017327 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -17,7 +17,7 @@ ], "rust-analyzer.rustfmt.overrideCommand": [ "${workspaceFolder}/build/host/rustfmt/bin/rustfmt", - "--edition=2021" + "--edition=2024" ], "rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv", "rust-analyzer.procMacro.enable": true, diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json index 3461ff887d9b..27fc524e9b52 100644 --- a/src/etc/rust_analyzer_zed.json +++ b/src/etc/rust_analyzer_zed.json @@ -29,15 +29,15 @@ ], "procMacro": { "enable": true, - "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv" + "server": "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" }, "rustc": { "source": "./Cargo.toml" }, "rustfmt": { "overrideCommand": [ - "${workspaceFolder}/build/host/rustfmt/bin/rustfmt", - "--edition=2021" + "build/host/rustfmt/bin/rustfmt", + "--edition=2024" ] }, "server": { diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index dbfdd8ebd167..bba8e630bcc2 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -9,7 +9,7 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } -askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } +askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" itertools = "0.12" indexmap = "2" diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 439777843fb0..ebc276b38fbf 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -171,10 +171,15 @@ impl Cfg { /// Renders the configuration for long display, as a long HTML description. pub(crate) fn render_long_html(&self) -> String { - let on = if self.should_use_with_in_description() { "with" } else { "on" }; + let on = if self.omit_preposition() { + "" + } else if self.should_use_with_in_description() { + "with " + } else { + "on " + }; - let mut msg = - format!("Available {on} {}", Display(self, Format::LongHtml)); + let mut msg = format!("Available {on}{}", Display(self, Format::LongHtml)); if self.should_append_only_to_description() { msg.push_str(" only"); } @@ -244,6 +249,10 @@ impl Cfg { Some(self.clone()) } } + + fn omit_preposition(&self) -> bool { + matches!(self, Cfg::True | Cfg::False) + } } impl ops::Not for Cfg { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 28dfa01534ea..7e8e087c3a20 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -449,7 +449,7 @@ fn clean_middle_term<'tcx>( term: ty::Binder<'tcx, ty::Term<'tcx>>, cx: &mut DocContext<'tcx>, ) -> Term { - match term.skip_binder().unpack() { + match term.skip_binder().kind() { ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None, None)), ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)), } @@ -1749,7 +1749,7 @@ fn maybe_expand_private_type_alias<'tcx>( } else { return None; }; - let hir::ItemKind::TyAlias(_, ty, generics) = alias else { return None }; + let hir::ItemKind::TyAlias(_, generics, ty) = alias else { return None }; let final_seg = &path.segments.last().expect("segments were empty"); let mut args = DefIdMap::default(); @@ -2803,21 +2803,21 @@ fn clean_maybe_renamed_item<'tcx>( let mut name = get_name(cx, item, renamed).unwrap(); let kind = match item.kind { - ItemKind::Static(_, ty, mutability, body_id) => StaticItem(Static { + ItemKind::Static(mutability, _, ty, body_id) => StaticItem(Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: Some(body_id), }), - ItemKind::Const(_, ty, generics, body_id) => ConstantItem(Box::new(Constant { + ItemKind::Const(_, generics, ty, body_id) => ConstantItem(Box::new(Constant { generics: clean_generics(generics, cx), type_: clean_ty(ty, cx), kind: ConstantKind::Local { body: body_id, def_id }, })), - ItemKind::TyAlias(_, hir_ty, generics) => { + ItemKind::TyAlias(_, generics, ty) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; - let rustdoc_ty = clean_ty(hir_ty, cx); + let rustdoc_ty = clean_ty(ty, cx); let type_ = - clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); + clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, ty)), cx, None, None); let generics = clean_generics(generics, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; @@ -2846,7 +2846,7 @@ fn clean_maybe_renamed_item<'tcx>( )); return ret; } - ItemKind::Enum(_, def, generics) => EnumItem(Enum { + ItemKind::Enum(_, generics, def) => EnumItem(Enum { variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), generics: clean_generics(generics, cx), }), @@ -2854,11 +2854,11 @@ fn clean_maybe_renamed_item<'tcx>( generics: clean_generics(generics, cx), bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }), - ItemKind::Union(_, variant_data, generics) => UnionItem(Union { + ItemKind::Union(_, generics, variant_data) => UnionItem(Union { generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Struct(_, variant_data, generics) => StructItem(Struct { + ItemKind::Struct(_, generics, variant_data) => StructItem(Struct { ctor_kind: variant_data.ctor_kind(), generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 75f1bc9549cb..9e46d0b47e91 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -5,7 +5,9 @@ use std::{fmt, iter}; use arrayvec::ArrayVec; use rustc_abi::{ExternAbi, VariantIdx}; -use rustc_attr_parsing::{AttributeKind, ConstStability, Deprecation, Stability, StableSince}; +use rustc_attr_data_structures::{ + AttributeKind, ConstStability, Deprecation, Stability, StableSince, +}; 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}; @@ -104,7 +106,7 @@ impl From for ItemId { } /// The crate currently being documented. -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct Crate { pub(crate) module: Item, /// Only here so that they can be filtered through the rustdoc passes. @@ -403,13 +405,13 @@ impl Item { // versions; the paths that are exposed through it are "deprecated" because they // were never supposed to work at all. let stab = self.stability(tcx)?; - if let rustc_attr_parsing::StabilityLevel::Stable { + if let rustc_attr_data_structures::StabilityLevel::Stable { allowed_through_unstable_modules: Some(note), .. } = stab.level { Some(Deprecation { - since: rustc_attr_parsing::DeprecatedSince::Unspecified, + since: rustc_attr_data_structures::DeprecatedSince::Unspecified, note: Some(note), suggestion: None, }) @@ -608,6 +610,9 @@ impl Item { UnionItem(ref union_) => Some(union_.has_stripped_entries()), EnumItem(ref enum_) => Some(enum_.has_stripped_entries()), VariantItem(ref v) => v.has_stripped_entries(), + TypeAliasItem(ref type_alias) => { + type_alias.inner_type.as_ref().and_then(|t| t.has_stripped_entries()) + } _ => None, } } @@ -759,33 +764,21 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec { + pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; - use rustc_abi::IntegerType; - - let mut attrs: Vec = self - .attrs + self.attrs .other_attrs .iter() .filter_map(|attr| { if is_json { match attr { - hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => { - // rustdoc-json stores this in `Item::deprecation`, so we - // don't want it it `Item::attrs`. - None - } - rustc_hir::Attribute::Parsed(rustc_attr_parsing::AttributeKind::Repr( - .., - )) => { - // We have separate pretty-printing logic for `#[repr(..)]` attributes. - // For example, there are circumstances where `#[repr(transparent)]` - // is applied but should not be publicly shown in rustdoc - // because it isn't public API. - None - } + // rustdoc-json stores this in `Item::deprecation`, so we + // don't want it it `Item::attrs`. + hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => None, + // We have separate pretty-printing logic for `#[repr(..)]` attributes. + hir::Attribute::Parsed(AttributeKind::Repr(..)) => None, _ => Some({ let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr); assert_eq!(s.pop(), Some('\n')); @@ -803,73 +796,28 @@ impl Item { None } }) - .collect(); + .collect() + } - // Add #[repr(...)] - if let Some(def_id) = self.def_id() - && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() - { - let adt = tcx.adt_def(def_id); - let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } - if repr.transparent() { - // Render `repr(transparent)` iff the non-1-ZST field is public or at least one - // field is public in case all fields are 1-ZST fields. - let render_transparent = cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = - field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), - ) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); + pub(crate) fn attributes_and_repr( + &self, + tcx: TyCtxt<'_>, + cache: &Cache, + is_json: bool, + ) -> Vec { + let mut attrs = self.attributes_without_repr(tcx, is_json); - if render_transparent { - out.push("transparent"); - } - } - if repr.simd() { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if !out.is_empty() { - attrs.push(format!("#[repr({})]", out.join(", "))); - } + if let Some(repr_attr) = self.repr(tcx, cache, is_json) { + attrs.push(repr_attr); } attrs } + /// Returns a stringified `#[repr(...)]` attribute. + pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option { + repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json) + } + pub fn is_doc_hidden(&self) -> bool { self.attrs.is_doc_hidden() } @@ -879,6 +827,73 @@ impl Item { } } +pub(crate) fn repr_attributes( + tcx: TyCtxt<'_>, + cache: &Cache, + def_id: DefId, + item_type: ItemType, + is_json: bool, +) -> Option { + use rustc_abi::IntegerType; + + if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) { + return None; + } + let adt = tcx.adt_def(def_id); + let repr = adt.repr(); + let mut out = Vec::new(); + if repr.c() { + out.push("C"); + } + if repr.transparent() { + // Render `repr(transparent)` iff the non-1-ZST field is public or at least one + // field is public in case all fields are 1-ZST fields. + let render_transparent = cache.document_private + || is_json + || adt + .all_fields() + .find(|field| { + let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); + tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty)) + .is_ok_and(|layout| !layout.is_1zst()) + }) + .map_or_else( + || adt.all_fields().any(|field| field.vis.is_public()), + |field| field.vis.is_public(), + ); + + if render_transparent { + out.push("transparent"); + } + } + if repr.simd() { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } +} + #[derive(Clone, Debug)] pub(crate) enum ItemKind { ExternCrateItem { @@ -1041,17 +1056,11 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator .peekable(); if doc_cfg.peek().is_some() && doc_cfg_active { let sess = tcx.sess; + doc_cfg.fold(Cfg::True, |mut cfg, item| { if let Some(cfg_mi) = item.meta_item().and_then(|item| rustc_expand::config::parse_cfg(item, sess)) { - // The result is unused here but we can gate unstable predicates - rustc_attr_parsing::cfg_matches( - cfg_mi, - tcx.sess, - rustc_ast::CRATE_NODE_ID, - Some(tcx.features()), - ); match Cfg::parse(cfg_mi) { Ok(new_cfg) => cfg &= new_cfg, Err(e) => { @@ -1607,9 +1616,7 @@ impl Type { a.def_id() == b.def_id() && a.generics() .zip(b.generics()) - .map(|(ag, bg)| { - ag.iter().zip(bg.iter()).all(|(at, bt)| at.is_doc_subtype_of(bt, cache)) - }) + .map(|(ag, bg)| ag.zip(bg).all(|(at, bt)| at.is_doc_subtype_of(bt, cache))) .unwrap_or(true) } // Other cases, such as primitives, just use recursion. @@ -1682,7 +1689,7 @@ impl Type { } } - pub(crate) fn generics(&self) -> Option> { + pub(crate) fn generics<'a>(&'a self) -> Option> { match self { Type::Path { path, .. } => path.generics(), _ => None, @@ -2113,7 +2120,7 @@ impl Enum { self.variants.iter().any(|f| f.is_stripped()) } - pub(crate) fn variants(&self) -> impl Iterator { + pub(crate) fn non_stripped_variants(&self) -> impl Iterator { self.variants.iter().filter(|v| !v.is_stripped()) } } @@ -2240,17 +2247,13 @@ impl Path { self.segments.last().map(|seg| &seg.args) } - pub(crate) fn generics(&self) -> Option> { + pub(crate) fn generics<'a>(&'a self) -> Option> { self.segments.last().and_then(|seg| { if let GenericArgs::AngleBracketed { ref args, .. } = seg.args { - Some( - args.iter() - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }) - .collect(), - ) + Some(args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + })) } else { None } @@ -2351,6 +2354,17 @@ pub(crate) enum TypeAliasInnerType { Struct { ctor_kind: Option, fields: Vec }, } +impl TypeAliasInnerType { + fn has_stripped_entries(&self) -> Option { + Some(match self { + Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()), + Self::Union { fields } | Self::Struct { fields, .. } => { + fields.iter().any(|f| f.is_stripped()) + } + }) + } +} + #[derive(Clone, Debug)] pub(crate) struct TypeAlias { pub(crate) type_: Type, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index af7986d030ee..c58b07a5b673 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,7 +1,7 @@ use std::assert_matches::debug_assert_matches; use std::fmt::{self, Display, Write as _}; -use std::mem; use std::sync::LazyLock as Lazy; +use std::{ascii, mem}; use rustc_ast::tokenstream::TokenTree; use rustc_hir::def::{DefKind, Res}; @@ -24,7 +24,7 @@ use crate::clean::{ clean_middle_ty, inline, }; use crate::core::DocContext; -use crate::display::Joined as _; +use crate::display::{Joined as _, MaybeDisplay as _}; #[cfg(test)] mod tests; @@ -124,7 +124,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( elision_has_failed_once_before = true; } - match arg.skip_binder().unpack() { + match arg.skip_binder().kind() { GenericArgKind::Lifetime(lt) => { Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) } @@ -161,7 +161,7 @@ fn can_elide_generic_arg<'tcx>( default: ty::Binder<'tcx, ty::GenericArg<'tcx>>, ) -> bool { debug_assert_matches!( - (actual.skip_binder().unpack(), default.skip_binder().unpack()), + (actual.skip_binder().kind(), default.skip_binder().kind()), (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) | (ty::GenericArgKind::Type(_), ty::GenericArgKind::Type(_)) | (ty::GenericArgKind::Const(_), ty::GenericArgKind::Const(_)) @@ -254,14 +254,7 @@ pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String { fmt::from_fn(|f| { segments .iter() - .map(|seg| { - fmt::from_fn(|f| { - if seg.ident.name != kw::PathRoot { - write!(f, "{}", seg.ident)?; - } - Ok(()) - }) - }) + .map(|seg| (seg.ident.name != kw::PathRoot).then_some(seg.ident).maybe_display()) .joined("::", f) }) .to_string() @@ -391,30 +384,12 @@ pub(crate) fn print_evaluated_const( }) } -fn format_integer_with_underscore_sep(num: &str) -> String { - let num_chars: Vec<_> = num.chars().collect(); - let mut num_start_index = if num_chars.first() == Some(&'-') { 1 } else { 0 }; - let chunk_size = match &num.as_bytes()[num_start_index..] { - [b'0', b'b' | b'x', ..] => { - num_start_index += 2; - 4 - } - [b'0', b'o', ..] => { - num_start_index += 2; - let remaining_chars = num_chars.len() - num_start_index; - if remaining_chars <= 6 { - // don't add underscores to Unix permissions like 0755 or 100755 - return num.to_string(); - } - 3 - } - _ => 3, - }; - - num_chars[..num_start_index] - .iter() - .chain(num_chars[num_start_index..].rchunks(chunk_size).rev().intersperse(&['_']).flatten()) - .collect() +fn format_integer_with_underscore_sep(num: u128, is_negative: bool) -> String { + let num = num.to_string(); + let chars = num.as_ascii().unwrap(); + let mut result = if is_negative { "-".to_string() } else { String::new() }; + result.extend(chars.rchunks(3).rev().intersperse(&[ascii::Char::LowLine]).flatten()); + result } fn print_const_with_custom_print_scalar<'tcx>( @@ -428,7 +403,10 @@ fn print_const_with_custom_print_scalar<'tcx>( match (ct, ct.ty().kind()) { (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => { let mut output = if with_underscores { - format_integer_with_underscore_sep(&int.to_string()) + format_integer_with_underscore_sep( + int.assert_scalar_int().to_bits_unchecked(), + false, + ) } else { int.to_string() }; @@ -445,7 +423,10 @@ fn print_const_with_custom_print_scalar<'tcx>( .size; let sign_extended_data = int.assert_scalar_int().to_int(size); let mut output = if with_underscores { - format_integer_with_underscore_sep(&sign_extended_data.to_string()) + format_integer_with_underscore_sep( + sign_extended_data.unsigned_abs(), + sign_extended_data.is_negative(), + ) } else { sign_extended_data.to_string() }; diff --git a/src/librustdoc/clean/utils/tests.rs b/src/librustdoc/clean/utils/tests.rs index ebf4b4954839..65c8255b2f27 100644 --- a/src/librustdoc/clean/utils/tests.rs +++ b/src/librustdoc/clean/utils/tests.rs @@ -2,40 +2,10 @@ use super::*; #[test] fn int_format_decimal() { - assert_eq!(format_integer_with_underscore_sep("12345678"), "12_345_678"); - assert_eq!(format_integer_with_underscore_sep("123"), "123"); - assert_eq!(format_integer_with_underscore_sep("123459"), "123_459"); - assert_eq!(format_integer_with_underscore_sep("-12345678"), "-12_345_678"); - assert_eq!(format_integer_with_underscore_sep("-123"), "-123"); - assert_eq!(format_integer_with_underscore_sep("-123459"), "-123_459"); -} - -#[test] -fn int_format_hex() { - assert_eq!(format_integer_with_underscore_sep("0xab3"), "0xab3"); - assert_eq!(format_integer_with_underscore_sep("0xa2345b"), "0xa2_345b"); - assert_eq!(format_integer_with_underscore_sep("0xa2e6345b"), "0xa2e6_345b"); - assert_eq!(format_integer_with_underscore_sep("-0xab3"), "-0xab3"); - assert_eq!(format_integer_with_underscore_sep("-0xa2345b"), "-0xa2_345b"); - assert_eq!(format_integer_with_underscore_sep("-0xa2e6345b"), "-0xa2e6_345b"); -} - -#[test] -fn int_format_binary() { - assert_eq!(format_integer_with_underscore_sep("0o12345671"), "0o12_345_671"); - assert_eq!(format_integer_with_underscore_sep("0o123"), "0o123"); - assert_eq!(format_integer_with_underscore_sep("0o123451"), "0o123451"); - assert_eq!(format_integer_with_underscore_sep("-0o12345671"), "-0o12_345_671"); - assert_eq!(format_integer_with_underscore_sep("-0o123"), "-0o123"); - assert_eq!(format_integer_with_underscore_sep("-0o123451"), "-0o123451"); -} - -#[test] -fn int_format_octal() { - assert_eq!(format_integer_with_underscore_sep("0b101"), "0b101"); - assert_eq!(format_integer_with_underscore_sep("0b101101011"), "0b1_0110_1011"); - assert_eq!(format_integer_with_underscore_sep("0b01101011"), "0b0110_1011"); - assert_eq!(format_integer_with_underscore_sep("-0b101"), "-0b101"); - assert_eq!(format_integer_with_underscore_sep("-0b101101011"), "-0b1_0110_1011"); - assert_eq!(format_integer_with_underscore_sep("-0b01101011"), "-0b0110_1011"); + assert_eq!(format_integer_with_underscore_sep(12345678, false), "12_345_678"); + assert_eq!(format_integer_with_underscore_sep(123, false), "123"); + assert_eq!(format_integer_with_underscore_sep(123459, false), "123_459"); + assert_eq!(format_integer_with_underscore_sep(12345678, true), "-12_345_678"); + assert_eq!(format_integer_with_underscore_sep(123, true), "-123"); + assert_eq!(format_integer_with_underscore_sep(123459, true), "-123_459"); } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index ef70b8621857..b2fe24db0a2e 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::{panic, str}; -pub(crate) use make::DocTestBuilder; +pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder}; pub(crate) use markdown::test as test_markdown; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_errors::emitter::HumanReadableErrorType; @@ -23,9 +23,9 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_interface::interface; use rustc_session::config::{self, CrateType, ErrorOutputType, Input}; use rustc_session::lint; -use rustc_span::FileName; use rustc_span::edition::Edition; use rustc_span::symbol::sym; +use rustc_span::{FileName, Span}; use rustc_target::spec::{Target, TargetTuple}; use tempfile::{Builder as TempFileBuilder, TempDir}; use tracing::debug; @@ -197,7 +197,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions } } else { let mut collector = CreateRunnableDocTests::new(options, opts); - tests.into_iter().for_each(|t| collector.add_test(t)); + tests.into_iter().for_each(|t| collector.add_test(t, Some(compiler.sess.dcx()))); Ok(Some(collector)) } @@ -847,6 +847,7 @@ pub(crate) struct ScrapedDocTest { langstr: LangString, text: String, name: String, + span: Span, } impl ScrapedDocTest { @@ -856,6 +857,7 @@ impl ScrapedDocTest { logical_path: Vec, langstr: LangString, text: String, + span: Span, ) -> Self { let mut item_path = logical_path.join("::"); item_path.retain(|c| c != ' '); @@ -865,7 +867,7 @@ impl ScrapedDocTest { let name = format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()); - Self { filename, line, langstr, text, name } + Self { filename, line, langstr, text, name, span } } fn edition(&self, opts: &RustdocOptions) -> Edition { self.langstr.edition.unwrap_or(opts.edition) @@ -921,7 +923,7 @@ impl CreateRunnableDocTests { } } - fn add_test(&mut self, scraped_test: ScrapedDocTest) { + fn add_test(&mut self, scraped_test: ScrapedDocTest, dcx: Option>) { // For example `module/file.rs` would become `module_file_rs` let file = scraped_test .filename @@ -945,14 +947,14 @@ impl CreateRunnableDocTests { ); let edition = scraped_test.edition(&self.rustdoc_options); - let doctest = DocTestBuilder::new( - &scraped_test.text, - Some(&self.opts.crate_name), - edition, - self.can_merge_doctests, - Some(test_id), - Some(&scraped_test.langstr), - ); + let doctest = BuildDocTestBuilder::new(&scraped_test.text) + .crate_name(&self.opts.crate_name) + .edition(edition) + .can_merge_doctests(self.can_merge_doctests) + .test_id(test_id) + .lang_str(&scraped_test.langstr) + .span(scraped_test.span) + .build(dcx); let is_standalone = !doctest.can_be_merged || scraped_test.langstr.compile_fail || scraped_test.langstr.test_harness diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs index ce362eabfc4c..3b17ccc78c71 100644 --- a/src/librustdoc/doctest/extracted.rs +++ b/src/librustdoc/doctest/extracted.rs @@ -5,7 +5,7 @@ use serde::Serialize; -use super::{DocTestBuilder, ScrapedDocTest}; +use super::{BuildDocTestBuilder, ScrapedDocTest}; use crate::config::Options as RustdocOptions; use crate::html::markdown; @@ -35,16 +35,13 @@ impl ExtractedDocTests { ) { let edition = scraped_test.edition(options); - let ScrapedDocTest { filename, line, langstr, text, name } = scraped_test; + let ScrapedDocTest { filename, line, langstr, text, name, .. } = scraped_test; - let doctest = DocTestBuilder::new( - &text, - Some(&opts.crate_name), - edition, - false, - None, - Some(&langstr), - ); + let doctest = BuildDocTestBuilder::new(&text) + .crate_name(&opts.crate_name) + .edition(edition) + .lang_str(&langstr) + .build(None); let (full_test_code, size) = doctest.generate_unique_doctest( &text, langstr.test_harness, diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index d4fbfb12582e..66647b880186 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -8,14 +8,14 @@ use std::sync::Arc; use rustc_ast::token::{Delimiter, TokenKind}; use rustc_ast::tokenstream::TokenTree; use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind}; -use rustc_errors::ColorConfig; use rustc_errors::emitter::stderr_destination; +use rustc_errors::{ColorConfig, DiagCtxtHandle}; use rustc_parse::new_parser_from_source_str; use rustc_session::parse::ParseSess; -use rustc_span::edition::Edition; +use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::{FileName, kw}; +use rustc_span::{DUMMY_SP, FileName, Span, kw}; use tracing::debug; use super::GlobalTestOptions; @@ -35,6 +35,138 @@ struct ParseSourceInfo { maybe_crate_attrs: String, } +/// Builder type for `DocTestBuilder`. +pub(crate) struct BuildDocTestBuilder<'a> { + source: &'a str, + crate_name: Option<&'a str>, + edition: Edition, + can_merge_doctests: bool, + // If `test_id` is `None`, it means we're generating code for a code example "run" link. + test_id: Option, + lang_str: Option<&'a LangString>, + span: Span, +} + +impl<'a> BuildDocTestBuilder<'a> { + pub(crate) fn new(source: &'a str) -> Self { + Self { + source, + crate_name: None, + edition: DEFAULT_EDITION, + can_merge_doctests: false, + test_id: None, + lang_str: None, + span: DUMMY_SP, + } + } + + #[inline] + pub(crate) fn crate_name(mut self, crate_name: &'a str) -> Self { + self.crate_name = Some(crate_name); + self + } + + #[inline] + pub(crate) fn can_merge_doctests(mut self, can_merge_doctests: bool) -> Self { + self.can_merge_doctests = can_merge_doctests; + self + } + + #[inline] + pub(crate) fn test_id(mut self, test_id: String) -> Self { + self.test_id = Some(test_id); + self + } + + #[inline] + pub(crate) fn lang_str(mut self, lang_str: &'a LangString) -> Self { + self.lang_str = Some(lang_str); + self + } + + #[inline] + pub(crate) fn span(mut self, span: Span) -> Self { + self.span = span; + self + } + + #[inline] + pub(crate) fn edition(mut self, edition: Edition) -> Self { + self.edition = edition; + self + } + + pub(crate) fn build(self, dcx: Option>) -> DocTestBuilder { + let BuildDocTestBuilder { + source, + crate_name, + edition, + can_merge_doctests, + // If `test_id` is `None`, it means we're generating code for a code example "run" link. + test_id, + lang_str, + span, + } = self; + let can_merge_doctests = can_merge_doctests + && lang_str.is_some_and(|lang_str| { + !lang_str.compile_fail && !lang_str.test_harness && !lang_str.standalone_crate + }); + + let result = rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + parse_source(source, &crate_name, dcx, span) + }) + }); + + let Ok(Ok(ParseSourceInfo { + has_main_fn, + already_has_extern_crate, + supports_color, + has_global_allocator, + has_macro_def, + everything_else, + crates, + crate_attrs, + maybe_crate_attrs, + })) = result + else { + // If the AST returned an error, we don't want this doctest to be merged with the + // others. + return DocTestBuilder::invalid( + String::new(), + String::new(), + String::new(), + source.to_string(), + test_id, + ); + }; + + debug!("crate_attrs:\n{crate_attrs}{maybe_crate_attrs}"); + debug!("crates:\n{crates}"); + debug!("after:\n{everything_else}"); + + // If it contains `#[feature]` or `#[no_std]`, we don't want it to be merged either. + let can_be_merged = can_merge_doctests + && !has_global_allocator + && crate_attrs.is_empty() + // If this is a merged doctest and a defined macro uses `$crate`, then the path will + // not work, so better not put it into merged doctests. + && !(has_macro_def && everything_else.contains("$crate")); + DocTestBuilder { + supports_color, + has_main_fn, + crate_attrs, + maybe_crate_attrs, + crates, + everything_else, + already_has_extern_crate, + test_id, + invalid_ast: false, + can_be_merged, + } + } +} + /// This struct contains information about the doctest itself which is then used to generate /// doctest source code appropriately. pub(crate) struct DocTestBuilder { @@ -53,74 +185,6 @@ pub(crate) struct DocTestBuilder { } impl DocTestBuilder { - pub(crate) fn new( - source: &str, - crate_name: Option<&str>, - edition: Edition, - can_merge_doctests: bool, - // If `test_id` is `None`, it means we're generating code for a code example "run" link. - test_id: Option, - lang_str: Option<&LangString>, - ) -> Self { - let can_merge_doctests = can_merge_doctests - && lang_str.is_some_and(|lang_str| { - !lang_str.compile_fail && !lang_str.test_harness && !lang_str.standalone_crate - }); - - let result = rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - parse_source(source, &crate_name) - }) - }); - - let Ok(Ok(ParseSourceInfo { - has_main_fn, - already_has_extern_crate, - supports_color, - has_global_allocator, - has_macro_def, - everything_else, - crates, - crate_attrs, - maybe_crate_attrs, - })) = result - else { - // If the AST returned an error, we don't want this doctest to be merged with the - // others. - return Self::invalid( - String::new(), - String::new(), - String::new(), - source.to_string(), - test_id, - ); - }; - - debug!("crate_attrs:\n{crate_attrs}{maybe_crate_attrs}"); - debug!("crates:\n{crates}"); - debug!("after:\n{everything_else}"); - - // If it contains `#[feature]` or `#[no_std]`, we don't want it to be merged either. - let can_be_merged = can_merge_doctests - && !has_global_allocator - && crate_attrs.is_empty() - // If this is a merged doctest and a defined macro uses `$crate`, then the path will - // not work, so better not put it into merged doctests. - && !(has_macro_def && everything_else.contains("$crate")); - Self { - supports_color, - has_main_fn, - crate_attrs, - maybe_crate_attrs, - crates, - everything_else, - already_has_extern_crate, - test_id, - invalid_ast: false, - can_be_merged, - } - } - fn invalid( crate_attrs: String, maybe_crate_attrs: String, @@ -289,7 +353,12 @@ fn reset_error_count(psess: &ParseSess) { const DOCTEST_CODE_WRAPPER: &str = "fn f(){"; -fn parse_source(source: &str, crate_name: &Option<&str>) -> Result { +fn parse_source( + source: &str, + crate_name: &Option<&str>, + parent_dcx: Option>, + span: Span, +) -> Result { use rustc_errors::DiagCtxt; use rustc_errors::emitter::{Emitter, HumanEmitter}; use rustc_span::source_map::FilePathMapping; @@ -466,8 +535,17 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result Result<(), String> { find_testable_code(&input_str, &mut md_collector, codes, None); let mut collector = CreateRunnableDocTests::new(options.clone(), opts); - md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); + md_collector.tests.into_iter().for_each(|t| collector.add_test(t, None)); let CreateRunnableDocTests { opts, rustdoc_options, standalone_tests, mergeable_tests, .. } = collector; crate::doctest::run_tests( diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 43dcfab880b5..f9d2aa3d3b4b 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -1,5 +1,6 @@ //! Doctest functionality used only for doctests in `.rs` source files. +use std::cell::Cell; use std::env; use std::sync::Arc; @@ -47,13 +48,33 @@ impl RustCollector { impl DocTestVisitor for RustCollector { fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { - let line = self.get_base_line() + rel_line.offset(); + let base_line = self.get_base_line(); + let line = base_line + rel_line.offset(); + let count = Cell::new(base_line); + let span = if line > base_line { + match self.source_map.span_extend_while(self.position, |c| { + if c == '\n' { + let count_v = count.get(); + count.set(count_v + 1); + if count_v >= line { + return false; + } + } + true + }) { + Ok(sp) => self.source_map.span_extend_to_line(sp.shrink_to_hi()), + _ => self.position, + } + } else { + self.position + }; self.tests.push(ScrapedDocTest::new( self.get_filename(), line, self.cur_path.clone(), config, test, + span, )); } diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 618c2041b43c..08248fdf39bb 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,8 +1,6 @@ use std::path::PathBuf; -use rustc_span::edition::DEFAULT_EDITION; - -use super::{DocTestBuilder, GlobalTestOptions}; +use super::{BuildDocTestBuilder, GlobalTestOptions}; fn make_test( test_code: &str, @@ -11,14 +9,14 @@ fn make_test( opts: &GlobalTestOptions, test_id: Option<&str>, ) -> (String, usize) { - let doctest = DocTestBuilder::new( - test_code, - crate_name, - DEFAULT_EDITION, - false, - test_id.map(|s| s.to_string()), - None, - ); + let mut builder = BuildDocTestBuilder::new(test_code); + if let Some(crate_name) = crate_name { + builder = builder.crate_name(crate_name); + } + if let Some(test_id) = test_id { + builder = builder.test_id(test_id.to_string()); + } + let doctest = builder.build(None); let (code, line_offset) = doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name); (code, line_offset) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 19402004ed59..4989bd718c9f 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,6 +1,6 @@ use std::mem; -use rustc_attr_parsing::StabilityLevel; +use rustc_attr_data_structures::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 8c7ab640bed3..e9a7f4367a38 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -7,15 +7,14 @@ //! some of them support an alternate format that emits text, but that should //! not be used external to this module. -use std::borrow::Cow; use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::{self, once}; use std::slice; -use itertools::Either; +use itertools::{Either, Itertools}; use rustc_abi::ExternAbi; -use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince}; +use rustc_attr_data_structures::{ConstStability, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -483,12 +482,12 @@ fn generate_item_def_id_path( let mut is_remote = false; let url_parts = url_parts(cx.cache(), def_id, module_fqp, &cx.current, &mut is_remote)?; - let (url_parts, shortty, fqp) = make_href(root_path, shortty, url_parts, &fqp, is_remote)?; - if def_id == original_def_id { - return Ok((url_parts, shortty, fqp)); - } - let kind = ItemType::from_def_kind(original_def_kind, Some(def_kind)); - Ok((format!("{url_parts}#{kind}.{}", tcx.item_name(original_def_id)), shortty, fqp)) + let mut url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote); + if def_id != original_def_id { + let kind = ItemType::from_def_kind(original_def_kind, Some(def_kind)); + url_parts = format!("{url_parts}#{kind}.{}", tcx.item_name(original_def_id)) + }; + Ok((url_parts, shortty, fqp)) } fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { @@ -510,7 +509,7 @@ fn url_parts( builder.extend(module_fqp.iter().copied()); Ok(builder) } - ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to).collect()), + ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to)), ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt), } } @@ -521,7 +520,7 @@ fn make_href( mut url_parts: UrlPartsBuilder, fqp: &[Symbol], is_remote: bool, -) -> Result<(String, ItemType, Vec), HrefError> { +) -> String { if !is_remote && let Some(root_path) = root_path { let root = root_path.trim_end_matches('/'); url_parts.push_front(root); @@ -536,7 +535,7 @@ fn make_href( url_parts.push_fmt(format_args!("{shortty}.{last}.html")); } } - Ok((url_parts.finish(), shortty, fqp.to_vec())) + url_parts.finish() } pub(crate) fn href_with_root_path( @@ -587,7 +586,7 @@ pub(crate) fn href_with_root_path( Some(&(ref fqp, shortty)) => (fqp, shortty, { let module_fqp = to_module_fqp(shortty, fqp.as_slice()); debug!(?fqp, ?shortty, ?module_fqp); - href_relative_parts(module_fqp, relative_to).collect() + href_relative_parts(module_fqp, relative_to) }), None => { // Associated items are handled differently with "jump to def". The anchor is generated @@ -606,7 +605,8 @@ pub(crate) fn href_with_root_path( } } }; - make_href(root_path, shortty, url_parts, fqp, is_remote) + let url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote); + Ok((url_parts, shortty, fqp.clone())) } pub(crate) fn href( @@ -619,34 +619,30 @@ pub(crate) fn href( /// Both paths should only be modules. /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will /// both need `../iter/trait.Iterator.html` to get at the iterator trait. -pub(crate) fn href_relative_parts<'fqp>( - fqp: &'fqp [Symbol], - relative_to_fqp: &[Symbol], -) -> Box + 'fqp> { +pub(crate) fn href_relative_parts(fqp: &[Symbol], relative_to_fqp: &[Symbol]) -> UrlPartsBuilder { for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() { // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1) if f != r { let dissimilar_part_count = relative_to_fqp.len() - i; let fqp_module = &fqp[i..]; - return Box::new( - iter::repeat_n(sym::dotdot, dissimilar_part_count) - .chain(fqp_module.iter().copied()), - ); + return iter::repeat_n(sym::dotdot, dissimilar_part_count) + .chain(fqp_module.iter().copied()) + .collect(); } } match relative_to_fqp.len().cmp(&fqp.len()) { Ordering::Less => { // e.g. linking to std::sync::atomic from std::sync - Box::new(fqp[relative_to_fqp.len()..fqp.len()].iter().copied()) + fqp[relative_to_fqp.len()..fqp.len()].iter().copied().collect() } Ordering::Greater => { // e.g. linking to std::sync from std::sync::atomic let dissimilar_part_count = relative_to_fqp.len() - fqp.len(); - Box::new(iter::repeat_n(sym::dotdot, dissimilar_part_count)) + iter::repeat_n(sym::dotdot, dissimilar_part_count).collect() } Ordering::Equal => { // linking to the same module - Box::new(iter::empty()) + UrlPartsBuilder::new() } } } @@ -708,13 +704,13 @@ fn resolved_path( f, "{path}::{anchor}", path = join_with_double_colon(&fqp[..fqp.len() - 1]), - anchor = anchor(did, *fqp.last().unwrap(), cx) + anchor = print_anchor(did, *fqp.last().unwrap(), cx) ) } else { write!(f, "{}", last.name) } } else { - write!(f, "{}", anchor(did, last.name, cx)) + write!(f, "{}", print_anchor(did, last.name, cx)) } }); write!(w, "{path}{args}", args = last.args.print(cx))?; @@ -800,7 +796,7 @@ fn primitive_link_fragment( Ok(()) } -fn tybounds( +fn print_tybounds( bounds: &[clean::PolyTrait], lt: &Option, cx: &Context<'_>, @@ -832,7 +828,7 @@ fn print_higher_ranked_params_with_space( }) } -pub(crate) fn anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display { +pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let parts = href(did, cx); if let Ok((url, short_ty, fqp)) = parts { @@ -866,7 +862,7 @@ fn fmt_type( } clean::DynTrait(bounds, lt) => { f.write_str("dyn ")?; - tybounds(bounds, lt, cx).fmt(f) + print_tybounds(bounds, lt, cx).fmt(f) } clean::Infer => write!(f, "_"), clean::Primitive(clean::PrimitiveType::Never) => { @@ -1122,8 +1118,8 @@ impl clean::Impl { write!(f, "!")?; } if self.kind.is_fake_variadic() - && let generics = ty.generics() - && let &[inner_type] = generics.as_ref().map_or(&[][..], |v| &v[..]) + && let Some(generics) = ty.generics() + && let Ok(inner_type) = generics.exactly_one() { let last = ty.last(); if f.alternate() { @@ -1131,7 +1127,7 @@ impl clean::Impl { self.print_type(inner_type, f, use_absolute, cx)?; write!(f, ">")?; } else { - write!(f, "{}<", anchor(ty.def_id(), last, cx))?; + write!(f, "{}<", print_anchor(ty.def_id(), last, cx))?; self.print_type(inner_type, f, use_absolute, cx)?; write!(f, ">")?; } @@ -1202,11 +1198,10 @@ impl clean::Impl { } } else if let clean::Type::Path { path } = type_ && let Some(generics) = path.generics() - && generics.len() == 1 + && let Ok(ty) = generics.exactly_one() && self.kind.is_fake_variadic() { - let ty = generics[0]; - let wrapper = anchor(path.def_id(), path.last(), cx); + let wrapper = print_anchor(path.def_id(), path.last(), cx); if f.alternate() { write!(f, "{wrapper:#}<")?; } else { @@ -1394,50 +1389,47 @@ impl clean::FnDecl { } pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display { - use std::fmt::Write as _; - let vis: Cow<'static, str> = match item.visibility(cx.tcx()) { - None => "".into(), - Some(ty::Visibility::Public) => "pub ".into(), - Some(ty::Visibility::Restricted(vis_did)) => { - // FIXME(camelid): This may not work correctly if `item_did` is a module. - // However, rustdoc currently never displays a module's - // visibility, so it shouldn't matter. - let parent_module = find_nearest_parent_module(cx.tcx(), item.item_id.expect_def_id()); - - if vis_did.is_crate_root() { - "pub(crate) ".into() - } else if parent_module == Some(vis_did) { - // `pub(in foo)` where `foo` is the parent module - // is the same as no visibility modifier - "".into() - } else if parent_module.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent)) - == Some(vis_did) - { - "pub(super) ".into() - } else { - let path = cx.tcx().def_path(vis_did); - debug!("path={path:?}"); - // modified from `resolved_path()` to work with `DefPathData` - let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); - let anchor = anchor(vis_did, last_name, cx); - - let mut s = "pub(in ".to_owned(); - for seg in &path.data[..path.data.len() - 1] { - let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap()); - } - let _ = write!(s, "{anchor}) "); - s.into() - } - } - }; - - let is_doc_hidden = item.is_doc_hidden(); fmt::from_fn(move |f| { - if is_doc_hidden { + if item.is_doc_hidden() { f.write_str("#[doc(hidden)] ")?; } - f.write_str(&vis) + match item.visibility(cx.tcx()) { + None => {} + Some(ty::Visibility::Public) => f.write_str("pub ")?, + Some(ty::Visibility::Restricted(vis_did)) => { + // FIXME(camelid): This may not work correctly if `item_did` is a module. + // However, rustdoc currently never displays a module's + // visibility, so it shouldn't matter. + let parent_module = + find_nearest_parent_module(cx.tcx(), item.item_id.expect_def_id()); + + if vis_did.is_crate_root() { + f.write_str("pub(crate) ")?; + } else if parent_module == Some(vis_did) { + // `pub(in foo)` where `foo` is the parent module + // is the same as no visibility modifier; do nothing + } else if parent_module + .and_then(|parent| find_nearest_parent_module(cx.tcx(), parent)) + == Some(vis_did) + { + f.write_str("pub(super) ")?; + } else { + let path = cx.tcx().def_path(vis_did); + debug!("path={path:?}"); + // modified from `resolved_path()` to work with `DefPathData` + let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); + let anchor = print_anchor(vis_did, last_name, cx); + + f.write_str("pub(in ")?; + for seg in &path.data[..path.data.len() - 1] { + write!(f, "{}::", seg.data.get_opt_name().unwrap())?; + } + write!(f, "{anchor}) ")?; + } + } + } + Ok(()) }) } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 2db1ea8450ce..b2feee36c939 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -6,7 +6,7 @@ //! Use the `render_with_highlighting` to highlight some rust code. use std::collections::VecDeque; -use std::fmt::{Display, Write}; +use std::fmt::{self, Display, Write}; use rustc_data_structures::fx::FxIndexMap; use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind}; @@ -36,9 +36,10 @@ pub(crate) struct HrefContext<'a, 'tcx> { #[derive(Default)] pub(crate) struct DecorationInfo(pub(crate) FxIndexMap<&'static str, Vec<(u32, u32)>>); -#[derive(Eq, PartialEq, Clone, Copy)] +#[derive(Eq, PartialEq, Clone)] pub(crate) enum Tooltip { - Ignore, + IgnoreAll, + IgnoreSome(Vec), CompileFail, ShouldPanic, Edition(Edition), @@ -70,7 +71,7 @@ fn write_header( format_args!( "
", match tooltip { - Tooltip::Ignore => " ignore", + Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore", Tooltip::CompileFail => " compile_fail", Tooltip::ShouldPanic => " should_panic", Tooltip::Edition(_) => " edition", @@ -80,23 +81,29 @@ fn write_header( ); if tooltip != Tooltip::None { - let edition_code; - write_str( - out, - format_args!( - "", - match tooltip { - Tooltip::Ignore => "This example is not tested", - Tooltip::CompileFail => "This example deliberately fails to compile", - Tooltip::ShouldPanic => "This example panics", - Tooltip::Edition(edition) => { - edition_code = format!("This example runs with edition {edition}"); - &edition_code + let tooltip = fmt::from_fn(|f| match &tooltip { + Tooltip::IgnoreAll => f.write_str("This example is not tested"), + Tooltip::IgnoreSome(platforms) => { + f.write_str("This example is not tested on ")?; + match &platforms[..] { + [] => unreachable!(), + [platform] => f.write_str(platform)?, + [first, second] => write!(f, "{first} or {second}")?, + [platforms @ .., last] => { + for platform in platforms { + write!(f, "{platform}, ")?; + } + write!(f, "or {last}")?; } - Tooltip::None => unreachable!(), } - ), - ); + Ok(()) + } + Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"), + Tooltip::ShouldPanic => f.write_str("This example panics"), + Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"), + Tooltip::None => unreachable!(), + }); + write_str(out, format_args!("")); } if let Some(extra) = extra_content { diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 44b3be23914c..50320cb231d2 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -1,4 +1,4 @@ -use std::fmt::{self, Display}; +use std::fmt::Display; use std::path::PathBuf; use askama::Template; @@ -8,7 +8,6 @@ use super::static_files::{STATIC_FILES, StaticFiles}; use crate::externalfiles::ExternalHtml; use crate::html::render::{StylePath, ensure_trailing_slash}; -#[derive(Clone)] pub(crate) struct Layout { pub(crate) logo: String, pub(crate) favicon: String, @@ -71,23 +70,6 @@ struct PageLayout<'a> { pub(crate) use crate::html::render::sidebar::filters; -/// Implements [`Display`] for a function that accepts a mutable reference to a [`String`], and (optionally) writes to it. -/// -/// The wrapped function will receive an empty string, and can modify it, -/// and the `Display` implementation will write the contents of the string after the function has finished. -pub(crate) struct BufDisplay(pub F); - -impl Display for BufDisplay -where - F: Fn(&mut String), -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buf = String::new(); - self.0(&mut buf); - f.write_str(&buf) - } -} - pub(crate) fn render( layout: &Layout, page: &Page<'_>, diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index fc46293e7eaa..68ba1245520d 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -195,7 +195,7 @@ fn slugify(c: char) -> Option { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Playground { pub crate_name: Option, pub url: String, @@ -303,7 +303,11 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { attrs: vec![], args_file: PathBuf::new(), }; - let doctest = doctest::DocTestBuilder::new(&test, krate, edition, false, None, None); + let mut builder = doctest::BuildDocTestBuilder::new(&test).edition(edition); + if let Some(krate) = krate { + builder = builder.crate_name(krate); + } + let doctest = builder.build(None); let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; @@ -316,8 +320,10 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { )) }); - let tooltip = if ignore != Ignore::None { - highlight::Tooltip::Ignore + let tooltip = if ignore == Ignore::All { + highlight::Tooltip::IgnoreAll + } else if let Ignore::Some(platforms) = ignore { + highlight::Tooltip::IgnoreSome(platforms) } else if compile_fail { highlight::Tooltip::CompileFail } else if should_panic { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index f22935df96c8..5984dcd74caf 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -14,7 +14,7 @@ use rustc_span::edition::Edition; use rustc_span::{FileName, Symbol, sym}; use tracing::info; -use super::print_item::{full_path, item_path, print_item}; +use super::print_item::{full_path, print_item, print_item_path}; use super::sidebar::{ModuleLike, Sidebar, print_sidebar, sidebar_module_like}; use super::{AllTypes, LinkFromSrc, StylePath, collect_spans_and_sources, scrape_examples_help}; use crate::clean::types::ExternalLocation; @@ -28,11 +28,10 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::format::join_with_double_colon; -use crate::html::layout::{self, BufDisplay}; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; use crate::html::render::write_shared::write_shared; use crate::html::url_parts_builder::UrlPartsBuilder; -use crate::html::{sources, static_files}; +use crate::html::{layout, sources, static_files}; use crate::scrape_examples::AllCallLocations; use crate::{DOC_RUST_LANG_ORG_VERSION, try_err}; @@ -250,9 +249,7 @@ impl<'tcx> Context<'tcx> { layout::render( &self.shared.layout, &page, - BufDisplay(|buf: &mut String| { - print_sidebar(self, it, buf); - }), + fmt::from_fn(|f| print_sidebar(self, it, f)), content, &self.shared.style_files, ) @@ -269,7 +266,7 @@ impl<'tcx> Context<'tcx> { for name in &names[..names.len() - 1] { write!(f, "{name}/")?; } - write!(f, "{}", item_path(ty, names.last().unwrap().as_str())) + write!(f, "{}", print_item_path(ty, names.last().unwrap().as_str())) }); match self.shared.redirections { Some(ref redirections) => { @@ -281,7 +278,7 @@ impl<'tcx> Context<'tcx> { let _ = write!( current_path, "{}", - item_path(ty, names.last().unwrap().as_str()) + print_item_path(ty, names.last().unwrap().as_str()) ); redirections.borrow_mut().insert(current_path, path.to_string()); } @@ -850,7 +847,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { if !buf.is_empty() { let name = item.name.as_ref().unwrap(); let item_type = item.type_(); - let file_name = item_path(item_type, name.as_str()).to_string(); + let file_name = print_item_path(item_type, name.as_str()).to_string(); self.shared.ensure_dir(&self.dst)?; let joint_dst = self.dst.join(&file_name); self.shared.fs.write(joint_dst, buf)?; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index beaa6497b8ca..66d5aafa3c1e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -49,7 +49,7 @@ use std::{fs, str}; use askama::Template; use itertools::Either; -use rustc_attr_parsing::{ +use rustc_attr_data_structures::{ ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -538,7 +538,7 @@ fn document( } fmt::from_fn(move |f| { - document_item_info(cx, item, parent).render_into(f).unwrap(); + document_item_info(cx, item, parent).render_into(f)?; if parent.is_none() { write!(f, "{}", document_full_collapsible(item, cx, heading_offset)) } else { @@ -582,7 +582,7 @@ fn document_short( show_def_docs: bool, ) -> impl fmt::Display { fmt::from_fn(move |f| { - document_item_info(cx, item, Some(parent)).render_into(f).unwrap(); + document_item_info(cx, item, Some(parent)).render_into(f)?; if !show_def_docs { return Ok(()); } @@ -661,7 +661,7 @@ fn document_full_inner( }; if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind { - render_call_locations(f, cx, item); + render_call_locations(f, cx, item)?; } Ok(()) }) @@ -1194,18 +1194,36 @@ fn render_assoc_item( // a whitespace prefix and newline. fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display { fmt::from_fn(move |f| { - for a in it.attributes(cx.tcx(), cx.cache(), false) { + for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { writeln!(f, "{prefix}{a}")?; } Ok(()) }) } +struct CodeAttribute(String); + +fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) { + write!(w, "
{}
", code_attr.0).unwrap(); +} + // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { - for attr in it.attributes(cx.tcx(), cx.cache(), false) { - write!(w, "
{attr}
").unwrap(); + for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { + render_code_attribute(CodeAttribute(attr), w); + } +} + +/// used for type aliases to only render their `repr` attribute. +fn render_repr_attributes_in_code( + w: &mut impl fmt::Write, + cx: &Context<'_>, + def_id: DefId, + item_type: ItemType, +) { + if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type, false) { + render_code_attribute(CodeAttribute(repr), w); } } @@ -2530,7 +2548,7 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection { /// types are re-exported, we don't use the corresponding /// entry from the js file, as inlining will have already /// picked up the impl -fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec { +fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec { let mut out = Vec::new(); let mut visited = FxHashSet::default(); let mut work = VecDeque::new(); @@ -2547,7 +2565,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec { work.push_back(first_ty); while let Some(ty) = work.pop_front() { - if !visited.insert(ty.clone()) { + if !visited.insert(ty) { continue; } @@ -2557,16 +2575,16 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec { work.extend(tys.into_iter()); } clean::Type::Slice(ty) => { - work.push_back(*ty); + work.push_back(ty); } clean::Type::Array(ty, _) => { - work.push_back(*ty); + work.push_back(ty); } clean::Type::RawPointer(_, ty) => { - work.push_back(*ty); + work.push_back(ty); } clean::Type::BorrowedRef { type_, .. } => { - work.push_back(*type_); + work.push_back(type_); } clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => { work.push_back(self_type); @@ -2584,11 +2602,15 @@ const MAX_FULL_EXAMPLES: usize = 5; const NUM_VISIBLE_LINES: usize = 10; /// Generates the HTML for example call locations generated via the --scrape-examples flag. -fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean::Item) { +fn render_call_locations( + mut w: W, + cx: &Context<'_>, + item: &clean::Item, +) -> fmt::Result { let tcx = cx.tcx(); let def_id = item.item_id.expect_def_id(); let key = tcx.def_path_hash(def_id); - let Some(call_locations) = cx.shared.call_locations.get(&key) else { return }; + let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) }; // Generate a unique ID so users can link to this section for a given method let id = cx.derive_id("scraped-examples"); @@ -2602,8 +2624,7 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean ", root_path = cx.root_path(), id = id - ) - .unwrap(); + )?; // Create a URL to a particular location in a reverse-dependency's source file let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) { @@ -2705,7 +2726,8 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean title: init_title, locations: locations_encoded, }), - ); + ) + .unwrap(); true }; @@ -2761,8 +2783,7 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean
Hide additional examples
\
\
" - ) - .unwrap(); + )?; // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could // make the page arbitrarily huge! @@ -2774,9 +2795,8 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean if it.peek().is_some() { w.write_str( r#"").unwrap(); + })?; + w.write_str("
")?; } - w.write_str("
").unwrap(); + w.write_str("

::Pointee: ptr::Pointee; /// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`. /// @@ -3595,306 +3616,38 @@ impl AggregateRawPtr<*mut T> for *mut P { #[rustc_intrinsic] pub const fn ptr_metadata + ?Sized, M>(ptr: *const P) -> M; -// Some functions are defined here because they accidentally got made -// available in this module on stable. See . -// (`transmute` also falls into this category, but it cannot be wrapped due to the -// check that `T` and `U` have the same size.) - -/// Copies `count * size_of::()` bytes from `src` to `dst`. The source -/// and destination must *not* overlap. -/// -/// For regions of memory which might overlap, use [`copy`] instead. -/// -/// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but -/// with the source and destination arguments swapped, -/// and `count` counting the number of `T`s instead of bytes. -/// -/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the -/// requirements of `T`. The initialization state is preserved exactly. -/// -/// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. -/// -/// * Both `src` and `dst` must be properly aligned. -/// -/// * The region of memory beginning at `src` with a size of `count * -/// size_of::()` bytes must *not* overlap with the region of memory -/// beginning at `dst` with the same size. -/// -/// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values -/// in the region beginning at `*src` and the region beginning at `*dst` can -/// [violate memory safety][read-ownership]. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be properly aligned. -/// -/// [`read`]: crate::ptr::read -/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value -/// [valid]: crate::ptr#safety -/// -/// # Examples -/// -/// Manually implement [`Vec::append`]: -/// -/// ``` -/// use std::ptr; -/// -/// /// Moves all the elements of `src` into `dst`, leaving `src` empty. -/// fn append(dst: &mut Vec, src: &mut Vec) { -/// let src_len = src.len(); -/// let dst_len = dst.len(); -/// -/// // Ensure that `dst` has enough capacity to hold all of `src`. -/// dst.reserve(src_len); -/// -/// unsafe { -/// // The call to add is always safe because `Vec` will never -/// // allocate more than `isize::MAX` bytes. -/// let dst_ptr = dst.as_mut_ptr().add(dst_len); -/// let src_ptr = src.as_ptr(); -/// -/// // Truncate `src` without dropping its contents. We do this first, -/// // to avoid problems in case something further down panics. -/// src.set_len(0); -/// -/// // The two regions cannot overlap because mutable references do -/// // not alias, and two different vectors cannot own the same -/// // memory. -/// ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); -/// -/// // Notify `dst` that it now holds the contents of `src`. -/// dst.set_len(dst_len + src_len); -/// } -/// } -/// -/// let mut a = vec!['r']; -/// let mut b = vec!['u', 's', 't']; -/// -/// append(&mut a, &mut b); -/// -/// assert_eq!(a, &['r', 'u', 's', 't']); -/// assert!(b.is_empty()); -/// ``` -/// -/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append -#[doc(alias = "memcpy")] +/// This is an accidentally-stable alias to [`ptr::copy_nonoverlapping`]; use that instead. +// Note (intentionally not in the doc comment): `ptr::copy_nonoverlapping` adds some extra +// debug assertions; if you are writing compiler tests or code inside the standard library +// that wants to avoid those debug assertions, directly call this intrinsic instead. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] -#[inline(always)] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"] -pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - #[rustc_intrinsic_const_stable_indirect] - #[rustc_nounwind] - #[rustc_intrinsic] - const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); - ub_checks::assert_unsafe_precondition!( - check_language_ub, - "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", - ( - src: *const () = src as *const (), - dst: *mut () = dst as *mut (), - size: usize = size_of::(), - align: usize = align_of::(), - count: usize = count, - ) => { - let zero_size = count == 0 || size == 0; - ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) - && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) - && ub_checks::maybe_is_nonoverlapping(src, dst, size, count) - } - ); - - // SAFETY: the safety contract for `copy_nonoverlapping` must be - // upheld by the caller. - unsafe { copy_nonoverlapping(src, dst, count) } -} - -/// Copies `count * size_of::()` bytes from `src` to `dst`. The source -/// and destination may overlap. -/// -/// If the source and destination will *never* overlap, -/// [`copy_nonoverlapping`] can be used instead. -/// -/// `copy` is semantically equivalent to C's [`memmove`], but -/// with the source and destination arguments swapped, -/// and `count` counting the number of `T`s instead of bytes. -/// Copying takes place as if the bytes were copied from `src` -/// to a temporary array and then copied from the array to `dst`. -/// -/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the -/// requirements of `T`. The initialization state is preserved exactly. -/// -/// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes, and must remain valid even -/// when `src` is read for `count * size_of::()` bytes. (This means if the memory ranges -/// overlap, the `dst` pointer must not be invalidated by `src` reads.) -/// -/// * Both `src` and `dst` must be properly aligned. -/// -/// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values -/// in the region beginning at `*src` and the region beginning at `*dst` can -/// [violate memory safety][read-ownership]. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be properly aligned. -/// -/// [`read`]: crate::ptr::read -/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value -/// [valid]: crate::ptr#safety -/// -/// # Examples -/// -/// Efficiently create a Rust vector from an unsafe buffer: -/// -/// ``` -/// use std::ptr; -/// -/// /// # Safety -/// /// -/// /// * `ptr` must be correctly aligned for its type and non-zero. -/// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`. -/// /// * Those elements must not be used after calling this function unless `T: Copy`. -/// # #[allow(dead_code)] -/// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { -/// let mut dst = Vec::with_capacity(elts); -/// -/// // SAFETY: Our precondition ensures the source is aligned and valid, -/// // and `Vec::with_capacity` ensures that we have usable space to write them. -/// unsafe { ptr::copy(ptr, dst.as_mut_ptr(), elts); } -/// -/// // SAFETY: We created it with this much capacity earlier, -/// // and the previous `copy` has initialized these elements. -/// unsafe { dst.set_len(elts); } -/// dst -/// } -/// ``` -#[doc(alias = "memmove")] +/// This is an accidentally-stable alias to [`ptr::copy`]; use that instead. +// Note (intentionally not in the doc comment): `ptr::copy` adds some extra +// debug assertions; if you are writing compiler tests or code inside the standard library +// that wants to avoid those debug assertions, directly call this intrinsic instead. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] -#[inline(always)] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[rustc_diagnostic_item = "ptr_copy"] -pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - #[rustc_intrinsic_const_stable_indirect] - #[rustc_nounwind] - #[rustc_intrinsic] - const unsafe fn copy(src: *const T, dst: *mut T, count: usize); +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize); - // SAFETY: the safety contract for `copy` must be upheld by the caller. - unsafe { - ub_checks::assert_unsafe_precondition!( - check_language_ub, - "ptr::copy requires that both pointer arguments are aligned and non-null", - ( - src: *const () = src as *const (), - dst: *mut () = dst as *mut (), - align: usize = align_of::(), - zero_size: bool = T::IS_ZST || count == 0, - ) => - ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) - && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) - ); - copy(src, dst, count) - } -} - -/// Sets `count * size_of::()` bytes of memory starting at `dst` to -/// `val`. -/// -/// `write_bytes` is similar to C's [`memset`], but sets `count * -/// size_of::()` bytes to `val`. -/// -/// [`memset`]: https://en.cppreference.com/w/c/string/byte/memset -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. -/// -/// * `dst` must be properly aligned. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointer must be properly aligned. -/// -/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB) -/// later if the written bytes are not a valid representation of some `T`. For instance, the -/// following is an **incorrect** use of this function: -/// -/// ```rust,no_run -/// unsafe { -/// let mut value: u8 = 0; -/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool; -/// let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`. -/// ptr.write_bytes(42u8, 1); // This function itself does not cause UB... -/// let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️ -/// } -/// ``` -/// -/// [valid]: crate::ptr#safety -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::ptr; -/// -/// let mut vec = vec![0u32; 4]; -/// unsafe { -/// let vec_ptr = vec.as_mut_ptr(); -/// ptr::write_bytes(vec_ptr, 0xfe, 2); -/// } -/// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]); -/// ``` -#[doc(alias = "memset")] +/// This is an accidentally-stable alias to [`ptr::write_bytes`]; use that instead. +// Note (intentionally not in the doc comment): `ptr::write_bytes` adds some extra +// debug assertions; if you are writing compiler tests or code inside the standard library +// that wants to avoid those debug assertions, directly call this intrinsic instead. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"] -#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] -#[inline(always)] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[rustc_diagnostic_item = "ptr_write_bytes"] -pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { - #[rustc_intrinsic_const_stable_indirect] - #[rustc_nounwind] - #[rustc_intrinsic] - const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); - - // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. - unsafe { - ub_checks::assert_unsafe_precondition!( - check_language_ub, - "ptr::write_bytes requires that the destination pointer is aligned and non-null", - ( - addr: *const () = dst as *const (), - align: usize = align_of::(), - zero_size: bool = T::IS_ZST || count == 0, - ) => ub_checks::maybe_is_aligned_and_not_null(addr, align, zero_size) - ); - write_bytes(dst, val, count) - } -} +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); /// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values. /// diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 97bb21c8a36e..3bc9cff8072b 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -97,32 +97,32 @@ use super::TrustedLen; #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( on( - _Self = "&[{A}]", + Self = "&[{A}]", message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere", label = "try explicitly collecting into a `Vec<{A}>`", ), on( - all(A = "{integer}", any(_Self = "&[{integral}]",)), + all(A = "{integer}", any(Self = "&[{integral}]",)), message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere", label = "try explicitly collecting into a `Vec<{A}>`", ), on( - _Self = "[{A}]", + Self = "[{A}]", message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size", label = "try explicitly collecting into a `Vec<{A}>`", ), on( - all(A = "{integer}", any(_Self = "[{integral}]",)), + all(A = "{integer}", any(Self = "[{integral}]",)), message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size", label = "try explicitly collecting into a `Vec<{A}>`", ), on( - _Self = "[{A}; _]", + Self = "[{A}; _]", message = "an array of type `{Self}` cannot be built directly from an iterator", label = "try collecting into a `Vec<{A}>`, then using `.try_into()`", ), on( - all(A = "{integer}", any(_Self = "[{integral}; _]",)), + all(A = "{integer}", any(Self = "[{integral}; _]",)), message = "an array of type `{Self}` cannot be built directly from an iterator", label = "try collecting into a `Vec<{A}>`, then using `.try_into()`", ), @@ -239,41 +239,38 @@ pub trait FromIterator: Sized { #[rustc_diagnostic_item = "IntoIterator"] #[rustc_on_unimplemented( on( - _Self = "core::ops::range::RangeTo", + Self = "core::ops::range::RangeTo", label = "if you meant to iterate until a value, add a starting value", note = "`..end` is a `RangeTo`, which cannot be iterated on; you might have meant to have a \ bounded `Range`: `0..end`" ), on( - _Self = "core::ops::range::RangeToInclusive", + Self = "core::ops::range::RangeToInclusive", label = "if you meant to iterate until a value (including it), add a starting value", note = "`..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant \ to have a bounded `RangeInclusive`: `0..=end`" ), on( - _Self = "[]", + Self = "[]", label = "`{Self}` is not an iterator; try calling `.into_iter()` or `.iter()`" ), - on(_Self = "&[]", label = "`{Self}` is not an iterator; try calling `.iter()`"), + on(Self = "&[]", label = "`{Self}` is not an iterator; try calling `.iter()`"), on( - _Self = "alloc::vec::Vec", + Self = "alloc::vec::Vec", label = "`{Self}` is not an iterator; try calling `.into_iter()` or `.iter()`" ), + on(Self = "&str", label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`"), on( - _Self = "&str", + Self = "alloc::string::String", label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`" ), on( - _Self = "alloc::string::String", - label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`" - ), - on( - _Self = "{integral}", + Self = "{integral}", note = "if you want to iterate between `start` until a value `end`, use the exclusive range \ syntax `start..end` or the inclusive range syntax `start..=end`" ), on( - _Self = "{float}", + Self = "{float}", note = "if you want to iterate between `start` until a value `end`, use the exclusive range \ syntax `start..end` or the inclusive range syntax `start..=end`" ), diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index d1e71f0e60f2..f296792b1dcb 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -22,11 +22,11 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator) {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( on( - _Self = "core::ops::range::RangeTo", + Self = "core::ops::range::RangeTo", note = "you might have meant to use a bounded `Range`" ), on( - _Self = "core::ops::range::RangeToInclusive", + Self = "core::ops::range::RangeToInclusive", note = "you might have meant to use a bounded `RangeInclusive`" ), label = "`{Self}` is not an iterator", @@ -294,13 +294,39 @@ pub trait Iterator { #[inline] #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { - for i in 0..n { - if self.next().is_none() { - // SAFETY: `i` is always less than `n`. - return Err(unsafe { NonZero::new_unchecked(n - i) }); + /// Helper trait to specialize `advance_by` via `try_fold` for `Sized` iterators. + trait SpecAdvanceBy { + fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero>; + } + + impl SpecAdvanceBy for I { + default fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero> { + for i in 0..n { + if self.next().is_none() { + // SAFETY: `i` is always less than `n`. + return Err(unsafe { NonZero::new_unchecked(n - i) }); + } + } + Ok(()) } } - Ok(()) + + impl SpecAdvanceBy for I { + fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero> { + let Some(n) = NonZero::new(n) else { + return Ok(()); + }; + + let res = self.try_fold(n, |n, _| NonZero::new(n.get() - 1)); + + match res { + None => Ok(()), + Some(n) => Err(n), + } + } + } + + self.spec_advance_by(n) } /// Returns the `n`th element of the iterator. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e605d7e0d784..989ab80b77d4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -100,7 +100,7 @@ #![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] -#![feature(cfg_match)] +#![feature(cfg_select)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(const_carrying_mul_add)] #![feature(const_eval_select)] @@ -158,7 +158,6 @@ #![feature(intra_doc_pointers)] #![feature(intrinsics)] #![feature(lang_items)] -#![feature(let_chains)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(marker_trait_attr)] @@ -226,17 +225,18 @@ pub mod assert_matches { // We don't export this through #[macro_export] for now, to avoid breakage. #[unstable(feature = "autodiff", issue = "124509")] +#[cfg(not(bootstrap))] /// Unstable module containing the unstable `autodiff` macro. pub mod autodiff { #[unstable(feature = "autodiff", issue = "124509")] - pub use crate::macros::builtin::autodiff; + pub use crate::macros::builtin::{autodiff_forward, autodiff_reverse}; } #[unstable(feature = "contracts", issue = "128044")] pub mod contracts; -#[unstable(feature = "cfg_match", issue = "115585")] -pub use crate::macros::cfg_match; +#[unstable(feature = "cfg_select", issue = "115585")] +pub use crate::macros::cfg_select; #[macro_use] mod internal_macros; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 7dc8c060cd5b..e70a1dab6e9a 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -210,9 +210,9 @@ pub macro assert_matches { /// # Example /// /// ``` -/// #![feature(cfg_match)] +/// #![feature(cfg_select)] /// -/// cfg_match! { +/// cfg_select! { /// unix => { /// fn foo() { /* unix specific functionality */ } /// } @@ -228,19 +228,19 @@ pub macro assert_matches { /// If desired, it is possible to return expressions through the use of surrounding braces: /// /// ``` -/// #![feature(cfg_match)] +/// #![feature(cfg_select)] /// -/// let _some_string = cfg_match! {{ +/// let _some_string = cfg_select! {{ /// unix => { "With great power comes great electricity bills" } /// _ => { "Behind every successful diet is an unwatched pizza" } /// }}; /// ``` -#[unstable(feature = "cfg_match", issue = "115585")] -#[rustc_diagnostic_item = "cfg_match"] +#[unstable(feature = "cfg_select", issue = "115585")] +#[rustc_diagnostic_item = "cfg_select"] #[rustc_macro_transparency = "semitransparent"] -pub macro cfg_match { +pub macro cfg_select { ({ $($tt:tt)* }) => {{ - $crate::cfg_match! { $($tt)* } + $crate::cfg_select! { $($tt)* } }}, (_ => { $($output:tt)* }) => { $($output)* @@ -250,10 +250,10 @@ pub macro cfg_match { $($( $rest:tt )+)? ) => { #[cfg($cfg)] - $crate::cfg_match! { _ => $output } + $crate::cfg_select! { _ => $output } $( #[cfg(not($cfg))] - $crate::cfg_match! { $($rest)+ } + $crate::cfg_select! { $($rest)+ } )? }, } @@ -1519,20 +1519,41 @@ pub(crate) mod builtin { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } - /// Automatic Differentiation macro which allows generating a new function to compute - /// the derivative of a given function. It may only be applied to a function. - /// The expected usage syntax is - /// `#[autodiff(NAME, MODE, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` - /// where: - /// NAME is a string that represents a valid function name. - /// MODE is any of Forward, Reverse, ForwardFirst, ReverseFirst. - /// INPUT_ACTIVITIES consists of one valid activity for each input parameter. - /// OUTPUT_ACTIVITY must not be set if we implicitly return nothing (or explicitly return - /// `-> ()`). Otherwise it must be set to one of the allowed activities. + /// This macro uses forward-mode automatic differentiation to generate a new function. + /// It may only be applied to a function. The new function will compute the derivative + /// of the function to which the macro was applied. + /// + /// The expected usage syntax is: + /// `#[autodiff_forward(NAME, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` + /// + /// - `NAME`: A string that represents a valid function name. + /// - `INPUT_ACTIVITIES`: Specifies one valid activity for each input parameter. + /// - `OUTPUT_ACTIVITY`: Must not be set if the function implicitly returns nothing + /// (or explicitly returns `-> ()`). Otherwise, it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] - pub macro autodiff($item:item) { + #[cfg(not(bootstrap))] + pub macro autodiff_forward($item:item) { + /* compiler built-in */ + } + + /// This macro uses reverse-mode automatic differentiation to generate a new function. + /// It may only be applied to a function. The new function will compute the derivative + /// of the function to which the macro was applied. + /// + /// The expected usage syntax is: + /// `#[autodiff_reverse(NAME, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` + /// + /// - `NAME`: A string that represents a valid function name. + /// - `INPUT_ACTIVITIES`: Specifies one valid activity for each input parameter. + /// - `OUTPUT_ACTIVITY`: Must not be set if the function implicitly returns nothing + /// (or explicitly returns `-> ()`). Otherwise, it must be set to one of the allowed activities. + #[unstable(feature = "autodiff", issue = "124509")] + #[allow_internal_unstable(rustc_attrs)] + #[rustc_builtin_macro] + #[cfg(not(bootstrap))] + pub macro autodiff_reverse($item:item) { /* compiler built-in */ } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index f33b8d188d86..700fb0f386ee 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -550,72 +550,72 @@ pub trait BikeshedGuaranteedNoDrop {} #[lang = "sync"] #[rustc_on_unimplemented( on( - _Self = "core::cell::once::OnceCell", + Self = "core::cell::once::OnceCell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead" ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU16` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU32` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicUsize` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI8` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI16` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI64` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicIsize` instead", ), on( - _Self = "core::cell::Cell", + Self = "core::cell::Cell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead", ), on( all( - _Self = "core::cell::Cell", - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell"), - not(_Self = "core::cell::Cell") + Self = "core::cell::Cell", + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell"), + not(Self = "core::cell::Cell") ), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`", ), on( - _Self = "core::cell::RefCell", + Self = "core::cell::RefCell", note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead", ), message = "`{Self}` cannot be shared between threads safely", diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs index 235f8a3bb79f..f9638fea225b 100644 --- a/library/core/src/marker/variance.rs +++ b/library/core/src/marker/variance.rs @@ -131,6 +131,8 @@ phantom_lifetime! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `'a` is otherwise contravariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `'a`, the following are guaranteed: @@ -146,6 +148,8 @@ phantom_lifetime! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `'a` is otherwise covariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `'a`, the following are guaranteed: @@ -180,6 +184,8 @@ phantom_type! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `T` is otherwise contravariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `T`, the following are guaranteed: @@ -196,6 +202,8 @@ phantom_type! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `T` is otherwise covariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `T`, the following are guaranteed: diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 27387754633d..0a5f3ee35b10 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -21,6 +21,8 @@ mod transmutability; #[unstable(feature = "transmutability", issue = "99571")] pub use transmutability::{Assume, TransmuteFrom}; +// This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do +// the special magic "types have equal size" check at the call site. #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] pub use crate::intrinsics::transmute; diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 0c2c4155d66c..6b9b2ba86891 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1447,8 +1447,10 @@ impl f128 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f128 { + pub const fn floor(self) -> f128 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::floorf128(self) } } @@ -1477,8 +1479,10 @@ impl f128 { #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f128 { + pub const fn ceil(self) -> f128 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::ceilf128(self) } } @@ -1513,8 +1517,10 @@ impl f128 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f128 { + pub const fn round(self) -> f128 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::roundf128(self) } } @@ -1547,8 +1553,10 @@ impl f128 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f128 { + pub const fn round_ties_even(self) -> f128 { intrinsics::round_ties_even_f128(self) } @@ -1579,8 +1587,10 @@ impl f128 { #[doc(alias = "truncate")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f128 { + pub const fn trunc(self) -> f128 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::truncf128(self) } } @@ -1610,8 +1620,10 @@ impl f128 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "f128", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f128 { + pub const fn fract(self) -> f128 { self - self.trunc() } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 1a859f2277ff..eb7af993c60a 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1423,8 +1423,10 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f16 { + pub const fn floor(self) -> f16 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::floorf16(self) } } @@ -1453,8 +1455,10 @@ impl f16 { #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f16 { + pub const fn ceil(self) -> f16 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::ceilf16(self) } } @@ -1489,8 +1493,10 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f16 { + pub const fn round(self) -> f16 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::roundf16(self) } } @@ -1523,8 +1529,10 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f16 { + pub const fn round_ties_even(self) -> f16 { intrinsics::round_ties_even_f16(self) } @@ -1555,8 +1563,10 @@ impl f16 { #[doc(alias = "truncate")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f16 { + pub const fn trunc(self) -> f16 { // SAFETY: intrinsic with no preconditions unsafe { intrinsics::truncf16(self) } } @@ -1586,8 +1596,10 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "f16", issue = "116909")] + // #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f16 { + pub const fn fract(self) -> f16 { self - self.trunc() } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 9525bdb6762a..bf923d4070ad 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -12,9 +12,9 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -use crate::num::{FpCategory, libm}; +use crate::num::FpCategory; use crate::panic::const_assert; -use crate::{cfg_match, intrinsics, mem}; +use crate::{cfg_select, intrinsics, mem}; /// The radix or base of the internal representation of `f32`. /// Use [`f32::RADIX`] instead. @@ -990,7 +990,7 @@ impl f32 { #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f32) -> f32 { - cfg_match! { + cfg_select! { // Allow faster implementation that have known good 64-bit float // implementations. Falling back to the branchy code on targets that don't // have 64-bit hardware floats or buggy implementations. @@ -999,6 +999,7 @@ impl f32 { target_arch = "x86_64", target_arch = "aarch64", all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "d"), + all(target_arch = "loongarch64", target_feature = "d"), all(target_arch = "arm", target_feature = "vfp2"), target_arch = "wasm32", target_arch = "wasm64", @@ -1557,413 +1558,447 @@ impl f32 { } } -/// Experimental version of `floor` in `core`. See [`f32::floor`] for details. +/// Experimental implementations of floating point functions in `core`. /// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let f = 3.7_f32; -/// let g = 3.0_f32; -/// let h = -3.7_f32; -/// -/// assert_eq!(f32::floor(f), 3.0); -/// assert_eq!(f32::floor(g), 3.0); -/// assert_eq!(f32::floor(h), -4.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::floor`]: ../../std/primitive.f32.html#method.floor -#[inline] +/// _The standalone functions in this module are for testing only. +/// They will be stabilized as inherent methods._ #[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn floor(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::floorf32(x) } -} +pub mod math { + use crate::intrinsics; + use crate::num::libm; -/// Experimental version of `ceil` in `core`. See [`f32::ceil`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let f = 3.01_f32; -/// let g = 4.0_f32; -/// -/// assert_eq!(f32::ceil(f), 4.0); -/// assert_eq!(f32::ceil(g), 4.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::ceil`]: ../../std/primitive.f32.html#method.ceil -#[inline] -#[doc(alias = "ceiling")] -#[must_use = "method returns a new number and does not mutate the original value"] -#[unstable(feature = "core_float_math", issue = "137578")] -pub fn ceil(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::ceilf32(x) } -} - -/// Experimental version of `round` in `core`. See [`f32::round`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let f = 3.3_f32; -/// let g = -3.3_f32; -/// let h = -3.7_f32; -/// let i = 3.5_f32; -/// let j = 4.5_f32; -/// -/// assert_eq!(f32::round(f), 3.0); -/// assert_eq!(f32::round(g), -3.0); -/// assert_eq!(f32::round(h), -4.0); -/// assert_eq!(f32::round(i), 4.0); -/// assert_eq!(f32::round(j), 5.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::round`]: ../../std/primitive.f32.html#method.round -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn round(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::roundf32(x) } -} - -/// Experimental version of `round_ties_even` in `core`. See [`f32::round_ties_even`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let f = 3.3_f32; -/// let g = -3.3_f32; -/// let h = 3.5_f32; -/// let i = 4.5_f32; -/// -/// assert_eq!(f32::round_ties_even(f), 3.0); -/// assert_eq!(f32::round_ties_even(g), -3.0); -/// assert_eq!(f32::round_ties_even(h), 4.0); -/// assert_eq!(f32::round_ties_even(i), 4.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::round_ties_even`]: ../../std/primitive.f32.html#method.round_ties_even -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn round_ties_even(x: f32) -> f32 { - intrinsics::round_ties_even_f32(x) -} - -/// Experimental version of `trunc` in `core`. See [`f32::trunc`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let f = 3.7_f32; -/// let g = 3.0_f32; -/// let h = -3.7_f32; -/// -/// assert_eq!(f32::trunc(f), 3.0); -/// assert_eq!(f32::trunc(g), 3.0); -/// assert_eq!(f32::trunc(h), -3.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::trunc`]: ../../std/primitive.f32.html#method.trunc -#[inline] -#[doc(alias = "truncate")] -#[must_use = "method returns a new number and does not mutate the original value"] -#[unstable(feature = "core_float_math", issue = "137578")] -pub fn trunc(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::truncf32(x) } -} - -/// Experimental version of `fract` in `core`. See [`f32::fract`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let x = 3.6_f32; -/// let y = -3.6_f32; -/// let abs_difference_x = (f32::fract(x) - 0.6).abs(); -/// let abs_difference_y = (f32::fract(y) - (-0.6)).abs(); -/// -/// assert!(abs_difference_x <= f32::EPSILON); -/// assert!(abs_difference_y <= f32::EPSILON); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::fract`]: ../../std/primitive.f32.html#method.fract -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn fract(x: f32) -> f32 { - x - trunc(x) -} - -/// Experimental version of `mul_add` in `core`. See [`f32::mul_add`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// # // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ -/// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { -/// use core::f32; -/// -/// let m = 10.0_f32; -/// let x = 4.0_f32; -/// let b = 60.0_f32; -/// -/// assert_eq!(f32::mul_add(m, x, b), 100.0); -/// assert_eq!(m * x + b, 100.0); -/// -/// let one_plus_eps = 1.0_f32 + f32::EPSILON; -/// let one_minus_eps = 1.0_f32 - f32::EPSILON; -/// let minus_one = -1.0_f32; -/// -/// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. -/// assert_eq!(f32::mul_add(one_plus_eps, one_minus_eps, minus_one), -f32::EPSILON * f32::EPSILON); -/// // Different rounding with the non-fused multiply and add. -/// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); -/// # } -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::mul_add`]: ../../std/primitive.f32.html#method.mul_add -#[inline] -#[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] -#[must_use = "method returns a new number and does not mutate the original value"] -#[unstable(feature = "core_float_math", issue = "137578")] -pub fn mul_add(x: f32, y: f32, z: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::fmaf32(x, y, z) } -} - -/// Experimental version of `div_euclid` in `core`. See [`f32::div_euclid`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let a: f32 = 7.0; -/// let b = 4.0; -/// assert_eq!(f32::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 -/// assert_eq!(f32::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 -/// assert_eq!(f32::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 -/// assert_eq!(f32::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::div_euclid`]: ../../std/primitive.f32.html#method.div_euclid -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn div_euclid(x: f32, rhs: f32) -> f32 { - let q = trunc(x / rhs); - if x % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + /// Experimental version of `floor` in `core`. See [`f32::floor`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.7_f32; + /// let g = 3.0_f32; + /// let h = -3.7_f32; + /// + /// assert_eq!(f32::math::floor(f), 3.0); + /// assert_eq!(f32::math::floor(g), 3.0); + /// assert_eq!(f32::math::floor(h), -4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::floor`]: ../../../std/primitive.f32.html#method.floor + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn floor(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf32(x) } } - q -} -/// Experimental version of `rem_euclid` in `core`. See [`f32::rem_euclid`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let a: f32 = 7.0; -/// let b = 4.0; -/// assert_eq!(f32::rem_euclid(a, b), 3.0); -/// assert_eq!(f32::rem_euclid(-a, b), 1.0); -/// assert_eq!(f32::rem_euclid(a, -b), 3.0); -/// assert_eq!(f32::rem_euclid(-a, -b), 1.0); -/// // limitation due to round-off error -/// assert!(f32::rem_euclid(-f32::EPSILON, 3.0) != 0.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::rem_euclid`]: ../../std/primitive.f32.html#method.rem_euclid -#[inline] -#[doc(alias = "modulo", alias = "mod")] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn rem_euclid(x: f32, rhs: f32) -> f32 { - let r = x % rhs; - if r < 0.0 { r + rhs.abs() } else { r } -} + /// Experimental version of `ceil` in `core`. See [`f32::ceil`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.01_f32; + /// let g = 4.0_f32; + /// + /// assert_eq!(f32::math::ceil(f), 4.0); + /// assert_eq!(f32::math::ceil(g), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::ceil`]: ../../../std/primitive.f32.html#method.ceil + #[inline] + #[doc(alias = "ceiling")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + pub const fn ceil(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf32(x) } + } -/// Experimental version of `powi` in `core`. See [`f32::powi`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let x = 2.0_f32; -/// let abs_difference = (f32::powi(x, 2) - (x * x)).abs(); -/// assert!(abs_difference <= f32::EPSILON); -/// -/// assert_eq!(f32::powi(f32::NAN, 0), 1.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::powi`]: ../../std/primitive.f32.html#method.powi -#[inline] -#[must_use = "method returns a new number and does not mutate the original value"] -#[unstable(feature = "core_float_math", issue = "137578")] -pub fn powi(x: f32, n: i32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::powif32(x, n) } -} + /// Experimental version of `round` in `core`. See [`f32::round`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.3_f32; + /// let g = -3.3_f32; + /// let h = -3.7_f32; + /// let i = 3.5_f32; + /// let j = 4.5_f32; + /// + /// assert_eq!(f32::math::round(f), 3.0); + /// assert_eq!(f32::math::round(g), -3.0); + /// assert_eq!(f32::math::round(h), -4.0); + /// assert_eq!(f32::math::round(i), 4.0); + /// assert_eq!(f32::math::round(j), 5.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::round`]: ../../../std/primitive.f32.html#method.round + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + pub const fn round(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf32(x) } + } -/// Experimental version of `sqrt` in `core`. See [`f32::sqrt`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let positive = 4.0_f32; -/// let negative = -4.0_f32; -/// let negative_zero = -0.0_f32; -/// -/// assert_eq!(f32::sqrt(positive), 2.0); -/// assert!(f32::sqrt(negative).is_nan()); -/// assert_eq!(f32::sqrt(negative_zero), negative_zero); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::sqrt`]: ../../std/primitive.f32.html#method.sqrt -#[inline] -#[doc(alias = "squareRoot")] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn sqrt(x: f32) -> f32 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::sqrtf32(x) } -} + /// Experimental version of `round_ties_even` in `core`. See [`f32::round_ties_even`] for + /// details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.3_f32; + /// let g = -3.3_f32; + /// let h = 3.5_f32; + /// let i = 4.5_f32; + /// + /// assert_eq!(f32::math::round_ties_even(f), 3.0); + /// assert_eq!(f32::math::round_ties_even(g), -3.0); + /// assert_eq!(f32::math::round_ties_even(h), 4.0); + /// assert_eq!(f32::math::round_ties_even(i), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::round_ties_even`]: ../../../std/primitive.f32.html#method.round_ties_even + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + pub const fn round_ties_even(x: f32) -> f32 { + intrinsics::round_ties_even_f32(x) + } -/// Experimental version of `abs_sub` in `core`. See [`f32::abs_sub`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let x = 3.0f32; -/// let y = -3.0f32; -/// -/// let abs_difference_x = (f32::abs_sub(x, 1.0) - 2.0).abs(); -/// let abs_difference_y = (f32::abs_sub(y, 1.0) - 0.0).abs(); -/// -/// assert!(abs_difference_x <= f32::EPSILON); -/// assert!(abs_difference_y <= f32::EPSILON); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::abs_sub`]: ../../std/primitive.f32.html#method.abs_sub -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated( - since = "1.10.0", - note = "you probably meant `(self - other).abs()`: \ + /// Experimental version of `trunc` in `core`. See [`f32::trunc`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.7_f32; + /// let g = 3.0_f32; + /// let h = -3.7_f32; + /// + /// assert_eq!(f32::math::trunc(f), 3.0); + /// assert_eq!(f32::math::trunc(g), 3.0); + /// assert_eq!(f32::math::trunc(h), -3.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::trunc`]: ../../../std/primitive.f32.html#method.trunc + #[inline] + #[doc(alias = "truncate")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + pub const fn trunc(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf32(x) } + } + + /// Experimental version of `fract` in `core`. See [`f32::fract`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 3.6_f32; + /// let y = -3.6_f32; + /// let abs_difference_x = (f32::math::fract(x) - 0.6).abs(); + /// let abs_difference_y = (f32::math::fract(y) - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f32::EPSILON); + /// assert!(abs_difference_y <= f32::EPSILON); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::fract`]: ../../../std/primitive.f32.html#method.fract + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn fract(x: f32) -> f32 { + x - trunc(x) + } + + /// Experimental version of `mul_add` in `core`. See [`f32::mul_add`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// # // FIXME(#140515): mingw has an incorrect fma + /// # // https://sourceforge.net/p/mingw-w64/bugs/848/ + /// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { + /// use core::f32; + /// + /// let m = 10.0_f32; + /// let x = 4.0_f32; + /// let b = 60.0_f32; + /// + /// assert_eq!(f32::math::mul_add(m, x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f32 + f32::EPSILON; + /// let one_minus_eps = 1.0_f32 - f32::EPSILON; + /// let minus_one = -1.0_f32; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!( + /// f32::math::mul_add(one_plus_eps, one_minus_eps, minus_one), + /// -f32::EPSILON * f32::EPSILON + /// ); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::mul_add`]: ../../../std/primitive.f32.html#method.mul_add + #[inline] + #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn mul_add(x: f32, y: f32, z: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf32(x, y, z) } + } + + /// Experimental version of `div_euclid` in `core`. See [`f32::div_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let a: f32 = 7.0; + /// let b = 4.0; + /// assert_eq!(f32::math::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!(f32::math::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(f32::math::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!(f32::math::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::div_euclid`]: ../../../std/primitive.f32.html#method.div_euclid + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(x: f32, rhs: f32) -> f32 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Experimental version of `rem_euclid` in `core`. See [`f32::rem_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let a: f32 = 7.0; + /// let b = 4.0; + /// assert_eq!(f32::math::rem_euclid(a, b), 3.0); + /// assert_eq!(f32::math::rem_euclid(-a, b), 1.0); + /// assert_eq!(f32::math::rem_euclid(a, -b), 3.0); + /// assert_eq!(f32::math::rem_euclid(-a, -b), 1.0); + /// // limitation due to round-off error + /// assert!(f32::math::rem_euclid(-f32::EPSILON, 3.0) != 0.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::rem_euclid`]: ../../../std/primitive.f32.html#method.rem_euclid + #[inline] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(x: f32, rhs: f32) -> f32 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Experimental version of `powi` in `core`. See [`f32::powi`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 2.0_f32; + /// let abs_difference = (f32::math::powi(x, 2) - (x * x)).abs(); + /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::math::powi(f32::NAN, 0), 1.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::powi`]: ../../../std/primitive.f32.html#method.powi + #[inline] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn powi(x: f32, n: i32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif32(x, n) } + } + + /// Experimental version of `sqrt` in `core`. See [`f32::sqrt`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let positive = 4.0_f32; + /// let negative = -4.0_f32; + /// let negative_zero = -0.0_f32; + /// + /// assert_eq!(f32::math::sqrt(positive), 2.0); + /// assert!(f32::math::sqrt(negative).is_nan()); + /// assert_eq!(f32::math::sqrt(negative_zero), negative_zero); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::sqrt`]: ../../../std/primitive.f32.html#method.sqrt + #[inline] + #[doc(alias = "squareRoot")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf32(x) } + } + + /// Experimental version of `abs_sub` in `core`. See [`f32::abs_sub`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 3.0f32; + /// let y = -3.0f32; + /// + /// let abs_difference_x = (f32::math::abs_sub(x, 1.0) - 2.0).abs(); + /// let abs_difference_y = (f32::math::abs_sub(y, 1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x <= f32::EPSILON); + /// assert!(abs_difference_y <= f32::EPSILON); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::abs_sub`]: ../../../std/primitive.f32.html#method.abs_sub + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated( + since = "1.10.0", + note = "you probably meant `(self - other).abs()`: \ this operation is `(self - other).max(0.0)` \ except that `abs_sub` also propagates NaNs (also \ known as `fdimf` in C). If you truly need the positive \ difference, consider using that expression or the C function \ `fdimf`, depending on how you wish to handle NaN (please consider \ filing an issue describing your use-case too)." -)] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn abs_sub(x: f32, other: f32) -> f32 { - libm::fdimf(x, other) -} + )] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs_sub(x: f32, other: f32) -> f32 { + libm::fdimf(x, other) + } -/// Experimental version of `cbrt` in `core`. See [`f32::cbrt`] for details. -/// -/// # Unspecified precision -/// -/// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and -/// can even differ within the same execution from one invocation to the next. -/// This function currently corresponds to the `cbrtf` from libc on Unix -/// and Windows. Note that this might change in the future. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f32; -/// -/// let x = 8.0f32; -/// -/// // x^(1/3) - 2 == 0 -/// let abs_difference = (f32::cbrt(x) - 2.0).abs(); -/// -/// assert!(abs_difference <= f32::EPSILON); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f32::cbrt`]: ../../std/primitive.f32.html#method.cbrt -#[inline] -#[must_use = "method returns a new number and does not mutate the original value"] -#[unstable(feature = "core_float_math", issue = "137578")] -pub fn cbrt(x: f32) -> f32 { - libm::cbrtf(x) + /// Experimental version of `cbrt` in `core`. See [`f32::cbrt`] for details. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and + /// can even differ within the same execution from one invocation to the next. + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 8.0f32; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (f32::math::cbrt(x) - 2.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::cbrt`]: ../../../std/primitive.f32.html#method.cbrt + #[inline] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn cbrt(x: f32) -> f32 { + libm::cbrtf(x) + } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 76c4e5d1a6f7..0a63ed828aed 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -12,7 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -use crate::num::{FpCategory, libm}; +use crate::num::FpCategory; use crate::panic::const_assert; use crate::{intrinsics, mem}; @@ -1556,406 +1556,440 @@ impl f64 { } } -/// Experimental version of `floor` in `core`. See [`f64::floor`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let f = 3.7_f64; -/// let g = 3.0_f64; -/// let h = -3.7_f64; -/// -/// assert_eq!(f64::floor(f), 3.0); -/// assert_eq!(f64::floor(g), 3.0); -/// assert_eq!(f64::floor(h), -4.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::floor`]: ../../std/primitive.f64.html#method.floor -#[inline] #[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn floor(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::floorf64(x) } -} +/// Experimental implementations of floating point functions in `core`. +/// +/// _The standalone functions in this module are for testing only. +/// They will be stabilized as inherent methods._ +pub mod math { + use crate::intrinsics; + use crate::num::libm; -/// Experimental version of `ceil` in `core`. See [`f64::ceil`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let f = 3.01_f64; -/// let g = 4.0_f64; -/// -/// assert_eq!(f64::ceil(f), 4.0); -/// assert_eq!(f64::ceil(g), 4.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::ceil`]: ../../std/primitive.f64.html#method.ceil -#[inline] -#[doc(alias = "ceiling")] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn ceil(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::ceilf64(x) } -} - -/// Experimental version of `round` in `core`. See [`f64::round`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let f = 3.3_f64; -/// let g = -3.3_f64; -/// let h = -3.7_f64; -/// let i = 3.5_f64; -/// let j = 4.5_f64; -/// -/// assert_eq!(f64::round(f), 3.0); -/// assert_eq!(f64::round(g), -3.0); -/// assert_eq!(f64::round(h), -4.0); -/// assert_eq!(f64::round(i), 4.0); -/// assert_eq!(f64::round(j), 5.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::round`]: ../../std/primitive.f64.html#method.round -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn round(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::roundf64(x) } -} - -/// Experimental version of `round_ties_even` in `core`. See [`f64::round_ties_even`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let f = 3.3_f64; -/// let g = -3.3_f64; -/// let h = 3.5_f64; -/// let i = 4.5_f64; -/// -/// assert_eq!(f64::round_ties_even(f), 3.0); -/// assert_eq!(f64::round_ties_even(g), -3.0); -/// assert_eq!(f64::round_ties_even(h), 4.0); -/// assert_eq!(f64::round_ties_even(i), 4.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::round_ties_even`]: ../../std/primitive.f64.html#method.round_ties_even -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn round_ties_even(x: f64) -> f64 { - intrinsics::round_ties_even_f64(x) -} - -/// Experimental version of `trunc` in `core`. See [`f64::trunc`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let f = 3.7_f64; -/// let g = 3.0_f64; -/// let h = -3.7_f64; -/// -/// assert_eq!(f64::trunc(f), 3.0); -/// assert_eq!(f64::trunc(g), 3.0); -/// assert_eq!(f64::trunc(h), -3.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::trunc`]: ../../std/primitive.f64.html#method.trunc -#[inline] -#[doc(alias = "truncate")] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn trunc(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::truncf64(x) } -} - -/// Experimental version of `fract` in `core`. See [`f64::fract`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let x = 3.6_f64; -/// let y = -3.6_f64; -/// let abs_difference_x = (f64::fract(x) - 0.6).abs(); -/// let abs_difference_y = (f64::fract(y) - (-0.6)).abs(); -/// -/// assert!(abs_difference_x < 1e-10); -/// assert!(abs_difference_y < 1e-10); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::fract`]: ../../std/primitive.f64.html#method.fract -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn fract(x: f64) -> f64 { - x - trunc(x) -} - -/// Experimental version of `mul_add` in `core`. See [`f64::mul_add`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// # // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ -/// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { -/// use core::f64; -/// -/// let m = 10.0_f64; -/// let x = 4.0_f64; -/// let b = 60.0_f64; -/// -/// assert_eq!(f64::mul_add(m, x, b), 100.0); -/// assert_eq!(m * x + b, 100.0); -/// -/// let one_plus_eps = 1.0_f64 + f64::EPSILON; -/// let one_minus_eps = 1.0_f64 - f64::EPSILON; -/// let minus_one = -1.0_f64; -/// -/// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. -/// assert_eq!(f64::mul_add(one_plus_eps, one_minus_eps, minus_one), -f64::EPSILON * f64::EPSILON); -/// // Different rounding with the non-fused multiply and add. -/// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); -/// # } -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::mul_add`]: ../../std/primitive.f64.html#method.mul_add -#[inline] -#[doc(alias = "fma", alias = "fusedMultiplyAdd")] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn mul_add(x: f64, a: f64, b: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::fmaf64(x, a, b) } -} - -/// Experimental version of `div_euclid` in `core`. See [`f64::div_euclid`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let a: f64 = 7.0; -/// let b = 4.0; -/// assert_eq!(f64::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 -/// assert_eq!(f64::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 -/// assert_eq!(f64::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 -/// assert_eq!(f64::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::div_euclid`]: ../../std/primitive.f64.html#method.div_euclid -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn div_euclid(x: f64, rhs: f64) -> f64 { - let q = trunc(x / rhs); - if x % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + /// Experimental version of `floor` in `core`. See [`f64::floor`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.7_f64; + /// let g = 3.0_f64; + /// let h = -3.7_f64; + /// + /// assert_eq!(f64::math::floor(f), 3.0); + /// assert_eq!(f64::math::floor(g), 3.0); + /// assert_eq!(f64::math::floor(h), -4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::floor`]: ../../../std/primitive.f64.html#method.floor + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn floor(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf64(x) } } - q -} -/// Experimental version of `rem_euclid` in `core`. See [`f64::rem_euclid`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let a: f64 = 7.0; -/// let b = 4.0; -/// assert_eq!(f64::rem_euclid(a, b), 3.0); -/// assert_eq!(f64::rem_euclid(-a, b), 1.0); -/// assert_eq!(f64::rem_euclid(a, -b), 3.0); -/// assert_eq!(f64::rem_euclid(-a, -b), 1.0); -/// // limitation due to round-off error -/// assert!(f64::rem_euclid(-f64::EPSILON, 3.0) != 0.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::rem_euclid`]: ../../std/primitive.f64.html#method.rem_euclid -#[inline] -#[doc(alias = "modulo", alias = "mod")] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn rem_euclid(x: f64, rhs: f64) -> f64 { - let r = x % rhs; - if r < 0.0 { r + rhs.abs() } else { r } -} + /// Experimental version of `ceil` in `core`. See [`f64::ceil`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.01_f64; + /// let g = 4.0_f64; + /// + /// assert_eq!(f64::math::ceil(f), 4.0); + /// assert_eq!(f64::math::ceil(g), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::ceil`]: ../../../std/primitive.f64.html#method.ceil + #[inline] + #[doc(alias = "ceiling")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn ceil(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf64(x) } + } -/// Experimental version of `powi` in `core`. See [`f64::powi`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let x = 2.0_f64; -/// let abs_difference = (f64::powi(x, 2) - (x * x)).abs(); -/// assert!(abs_difference <= f64::EPSILON); -/// -/// assert_eq!(f64::powi(f64::NAN, 0), 1.0); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::powi`]: ../../std/primitive.f64.html#method.powi -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn powi(x: f64, n: i32) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::powif64(x, n) } -} + /// Experimental version of `round` in `core`. See [`f64::round`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.3_f64; + /// let g = -3.3_f64; + /// let h = -3.7_f64; + /// let i = 3.5_f64; + /// let j = 4.5_f64; + /// + /// assert_eq!(f64::math::round(f), 3.0); + /// assert_eq!(f64::math::round(g), -3.0); + /// assert_eq!(f64::math::round(h), -4.0); + /// assert_eq!(f64::math::round(i), 4.0); + /// assert_eq!(f64::math::round(j), 5.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::round`]: ../../../std/primitive.f64.html#method.round + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn round(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf64(x) } + } -/// Experimental version of `sqrt` in `core`. See [`f64::sqrt`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let positive = 4.0_f64; -/// let negative = -4.0_f64; -/// let negative_zero = -0.0_f64; -/// -/// assert_eq!(f64::sqrt(positive), 2.0); -/// assert!(f64::sqrt(negative).is_nan()); -/// assert_eq!(f64::sqrt(negative_zero), negative_zero); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::sqrt`]: ../../std/primitive.f64.html#method.sqrt -#[inline] -#[doc(alias = "squareRoot")] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn sqrt(x: f64) -> f64 { - // SAFETY: intrinsic with no preconditions - unsafe { intrinsics::sqrtf64(x) } -} + /// Experimental version of `round_ties_even` in `core`. See [`f64::round_ties_even`] for + /// details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.3_f64; + /// let g = -3.3_f64; + /// let h = 3.5_f64; + /// let i = 4.5_f64; + /// + /// assert_eq!(f64::math::round_ties_even(f), 3.0); + /// assert_eq!(f64::math::round_ties_even(g), -3.0); + /// assert_eq!(f64::math::round_ties_even(h), 4.0); + /// assert_eq!(f64::math::round_ties_even(i), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::round_ties_even`]: ../../../std/primitive.f64.html#method.round_ties_even + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn round_ties_even(x: f64) -> f64 { + intrinsics::round_ties_even_f64(x) + } -/// Experimental version of `abs_sub` in `core`. See [`f64::abs_sub`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let x = 3.0_f64; -/// let y = -3.0_f64; -/// -/// let abs_difference_x = (f64::abs_sub(x, 1.0) - 2.0).abs(); -/// let abs_difference_y = (f64::abs_sub(y, 1.0) - 0.0).abs(); -/// -/// assert!(abs_difference_x < 1e-10); -/// assert!(abs_difference_y < 1e-10); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::abs_sub`]: ../../std/primitive.f64.html#method.abs_sub -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[deprecated( - since = "1.10.0", - note = "you probably meant `(self - other).abs()`: \ + /// Experimental version of `trunc` in `core`. See [`f64::trunc`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.7_f64; + /// let g = 3.0_f64; + /// let h = -3.7_f64; + /// + /// assert_eq!(f64::math::trunc(f), 3.0); + /// assert_eq!(f64::math::trunc(g), 3.0); + /// assert_eq!(f64::math::trunc(h), -3.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::trunc`]: ../../../std/primitive.f64.html#method.trunc + #[inline] + #[doc(alias = "truncate")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn trunc(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf64(x) } + } + + /// Experimental version of `fract` in `core`. See [`f64::fract`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 3.6_f64; + /// let y = -3.6_f64; + /// let abs_difference_x = (f64::math::fract(x) - 0.6).abs(); + /// let abs_difference_y = (f64::math::fract(y) - (-0.6)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::fract`]: ../../../std/primitive.f64.html#method.fract + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn fract(x: f64) -> f64 { + x - trunc(x) + } + + /// Experimental version of `mul_add` in `core`. See [`f64::mul_add`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// # // FIXME(#140515): mingw has an incorrect fma + /// # // https://sourceforge.net/p/mingw-w64/bugs/848/ + /// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { + /// use core::f64; + /// + /// let m = 10.0_f64; + /// let x = 4.0_f64; + /// let b = 60.0_f64; + /// + /// assert_eq!(f64::math::mul_add(m, x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f64 + f64::EPSILON; + /// let one_minus_eps = 1.0_f64 - f64::EPSILON; + /// let minus_one = -1.0_f64; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!( + /// f64::math::mul_add(one_plus_eps, one_minus_eps, minus_one), + /// -f64::EPSILON * f64::EPSILON + /// ); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::mul_add`]: ../../../std/primitive.f64.html#method.mul_add + #[inline] + #[doc(alias = "fma", alias = "fusedMultiplyAdd")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(x: f64, a: f64, b: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf64(x, a, b) } + } + + /// Experimental version of `div_euclid` in `core`. See [`f64::div_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let a: f64 = 7.0; + /// let b = 4.0; + /// assert_eq!(f64::math::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!(f64::math::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(f64::math::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!(f64::math::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::div_euclid`]: ../../../std/primitive.f64.html#method.div_euclid + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(x: f64, rhs: f64) -> f64 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Experimental version of `rem_euclid` in `core`. See [`f64::rem_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let a: f64 = 7.0; + /// let b = 4.0; + /// assert_eq!(f64::math::rem_euclid(a, b), 3.0); + /// assert_eq!(f64::math::rem_euclid(-a, b), 1.0); + /// assert_eq!(f64::math::rem_euclid(a, -b), 3.0); + /// assert_eq!(f64::math::rem_euclid(-a, -b), 1.0); + /// // limitation due to round-off error + /// assert!(f64::math::rem_euclid(-f64::EPSILON, 3.0) != 0.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::rem_euclid`]: ../../../std/primitive.f64.html#method.rem_euclid + #[inline] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(x: f64, rhs: f64) -> f64 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Experimental version of `powi` in `core`. See [`f64::powi`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 2.0_f64; + /// let abs_difference = (f64::math::powi(x, 2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); + /// + /// assert_eq!(f64::math::powi(f64::NAN, 0), 1.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::powi`]: ../../../std/primitive.f64.html#method.powi + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(x: f64, n: i32) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif64(x, n) } + } + + /// Experimental version of `sqrt` in `core`. See [`f64::sqrt`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let positive = 4.0_f64; + /// let negative = -4.0_f64; + /// let negative_zero = -0.0_f64; + /// + /// assert_eq!(f64::math::sqrt(positive), 2.0); + /// assert!(f64::math::sqrt(negative).is_nan()); + /// assert_eq!(f64::math::sqrt(negative_zero), negative_zero); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::sqrt`]: ../../../std/primitive.f64.html#method.sqrt + #[inline] + #[doc(alias = "squareRoot")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf64(x) } + } + + /// Experimental version of `abs_sub` in `core`. See [`f64::abs_sub`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 3.0_f64; + /// let y = -3.0_f64; + /// + /// let abs_difference_x = (f64::math::abs_sub(x, 1.0) - 2.0).abs(); + /// let abs_difference_y = (f64::math::abs_sub(y, 1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::abs_sub`]: ../../../std/primitive.f64.html#method.abs_sub + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[deprecated( + since = "1.10.0", + note = "you probably meant `(self - other).abs()`: \ this operation is `(self - other).max(0.0)` \ except that `abs_sub` also propagates NaNs (also \ known as `fdim` in C). If you truly need the positive \ difference, consider using that expression or the C function \ `fdim`, depending on how you wish to handle NaN (please consider \ filing an issue describing your use-case too)." -)] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn abs_sub(x: f64, other: f64) -> f64 { - libm::fdim(x, other) -} + )] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs_sub(x: f64, other: f64) -> f64 { + libm::fdim(x, other) + } -/// Experimental version of `cbrt` in `core`. See [`f64::cbrt`] for details. -/// -/// # Examples -/// -/// ``` -/// #![feature(core_float_math)] -/// -/// use core::f64; -/// -/// let x = 8.0_f64; -/// -/// // x^(1/3) - 2 == 0 -/// let abs_difference = (f64::cbrt(x) - 2.0).abs(); -/// -/// assert!(abs_difference < 1e-10); -/// ``` -/// -/// _This standalone function is for testing only. It will be stabilized as an inherent method._ -/// -/// [`f64::cbrt`]: ../../std/primitive.f64.html#method.cbrt -#[inline] -#[unstable(feature = "core_float_math", issue = "137578")] -#[must_use = "method returns a new number and does not mutate the original value"] -pub fn cbrt(x: f64) -> f64 { - libm::cbrt(x) + /// Experimental version of `cbrt` in `core`. See [`f64::cbrt`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 8.0_f64; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (f64::math::cbrt(x) - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::cbrt`]: ../../../std/primitive.f64.html#method.cbrt + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(x: f64) -> f64 { + libm::cbrt(x) + } } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 84e1482ed31d..65560f63c185 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -4,7 +4,7 @@ macro_rules! int_impl { ActualT = $ActualT:ident, UnsignedT = $UnsignedT:ty, - // There are all for use *only* in doc comments. + // These are all for use *only* in doc comments. // As such, they're all passed as literals -- passing them as a string // literal is fine if they need to be multiple code tokens. // In non-comments, use the associated constants rather than these. @@ -555,7 +555,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -705,7 +705,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -855,7 +855,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1018,6 +1018,110 @@ macro_rules! int_impl { if b { overflow_panic::div() } else { a } } + /// Checked integer division without remainder. Computes `self / rhs`, + /// returning `None` if `rhs == 0`, the division results in overflow, + /// or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_exact_div(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_exact_div(2), None);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_exact_div(-1), None);")] + #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_exact_div(0), None);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_exact_div(self, rhs: Self) -> Option { + if intrinsics::unlikely(rhs == 0 || ((self == Self::MIN) && (rhs == -1))) { + None + } else { + // SAFETY: division by zero and overflow are checked above + unsafe { + if intrinsics::unlikely(intrinsics::unchecked_rem(self, rhs) != 0) { + None + } else { + Some(intrinsics::exact_div(self, rhs)) + } + } + } + } + + /// Checked integer division without remainder. Computes `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs == 0`, the division results in overflow, + /// or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).exact_div(-1), ", stringify!($Max), ");")] + /// ``` + /// + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + /// ``` + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.exact_div(-1);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_div(self, rhs: Self) -> Self { + match self.checked_exact_div(rhs) { + Some(x) => x, + None => panic!("Failed to divide without remainder"), + } + } + + /// Unchecked integer division without remainder. Computes `self / rhs`. + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs == 0`, `self % rhs != 0`, or + #[doc = concat!("`self == ", stringify!($SelfT), "::MIN && rhs == -1`,")] + /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`. + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_exact_div cannot overflow, divide by zero, or leave a remainder"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => rhs > 0 && lhs % rhs == 0 && (lhs != <$SelfT>::MIN || rhs != -1), + ); + // SAFETY: Same precondition + unsafe { intrinsics::exact_div(self, rhs) } + } + /// Checked integer remainder. Computes `self % rhs`, returning `None` if /// `rhs == 0` or the division results in overflow. /// @@ -1199,7 +1303,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_neg(self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1327,7 +1431,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1448,7 +1552,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 8a8b2733d5e8..a279f002772e 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -388,6 +388,7 @@ where #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] #[must_use] #[inline] + #[track_caller] pub const unsafe fn new_unchecked(n: T) -> Self { match Self::new(n) { Some(n) => n, @@ -428,6 +429,7 @@ where #[unstable(feature = "nonzero_from_mut", issue = "106290")] #[must_use] #[inline] + #[track_caller] pub unsafe fn from_mut_unchecked(n: &mut T) -> &mut Self { match Self::from_mut(n) { Some(n) => n, diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index f38d809c1544..5f82e6af86b0 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -601,7 +601,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -791,7 +791,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -974,7 +974,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1110,6 +1110,108 @@ macro_rules! uint_impl { self / rhs } + /// Checked integer division without remainder. Computes `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs == 0` or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] + /// ``` + /// + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_exact_div(self, rhs: Self) -> Option { + if intrinsics::unlikely(rhs == 0) { + None + } else { + // SAFETY: division by zero is checked above + unsafe { + if intrinsics::unlikely(intrinsics::unchecked_rem(self, rhs) != 0) { + None + } else { + Some(intrinsics::exact_div(self, rhs)) + } + } + } + } + + /// Checked integer division without remainder. Computes `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs == 0` or `self % rhs != 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(exact_div)] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] + /// ``` + /// + /// ```should_panic + /// #![feature(exact_div)] + #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + /// ``` + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn exact_div(self, rhs: Self) -> Self { + match self.checked_exact_div(rhs) { + Some(x) => x, + None => panic!("Failed to divide without remainder"), + } + } + + /// Unchecked integer division without remainder. Computes `self / rhs`. + /// + /// # Safety + /// + /// This results in undefined behavior when `rhs == 0` or `self % rhs != 0`, + /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`. + #[unstable( + feature = "exact_div", + issue = "139911", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_exact_div divide by zero or leave a remainder"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => rhs > 0 && lhs % rhs == 0, + ); + // SAFETY: Same precondition + unsafe { intrinsics::exact_div(self, rhs) } + } + /// Checked integer remainder. Computes `self % rhs`, returning `None` /// if `rhs == 0`. /// @@ -1588,7 +1690,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1709,7 +1811,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 54d79beca95a..098ce4531f0c 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -67,8 +67,8 @@ #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] #[rustc_on_unimplemented( - on(all(_Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), - on(all(_Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), + on(all(Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), + on(all(Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), message = "cannot add `{Rhs}` to `{Self}`", label = "no implementation for `{Self} + {Rhs}`", append_const_msg diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index e9014458b48e..df48c104410c 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -62,7 +62,7 @@ use crate::marker::Tuple; note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" ), on( - _Self = "unsafe fn", + Self = "unsafe fn", note = "unsafe function cannot be called generically without an unsafe block", // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" @@ -149,7 +149,7 @@ pub trait Fn: FnMut { note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" ), on( - _Self = "unsafe fn", + Self = "unsafe fn", note = "unsafe function cannot be called generically without an unsafe block", // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" @@ -228,7 +228,7 @@ pub trait FnMut: FnOnce { note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" ), on( - _Self = "unsafe fn", + Self = "unsafe fn", note = "unsafe function cannot be called generically without an unsafe block", // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index 46e19bed43ab..8092fa9eb2fc 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -144,17 +144,17 @@ pub trait Index { #[lang = "index_mut"] #[rustc_on_unimplemented( on( - _Self = "&str", + Self = "&str", note = "you can use `.chars().nth()` or `.bytes().nth()` see chapter in The Book " ), on( - _Self = "str", + Self = "str", note = "you can use `.chars().nth()` or `.bytes().nth()` see chapter in The Book " ), on( - _Self = "alloc::string::String", + Self = "alloc::string::String", note = "you can use `.chars().nth()` or `.bytes().nth()` see chapter in The Book " ), diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index c645c996eb70..507fa9460bea 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -19,6 +19,7 @@ impl IndexRange { /// # Safety /// - `start <= end` #[inline] + #[track_caller] pub(crate) const unsafe fn new_unchecked(start: usize, end: usize) -> Self { ub_checks::assert_unsafe_precondition!( check_library_ub, diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 3ba2957526f9..bac8ffb074ba 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -117,12 +117,12 @@ use crate::ops::ControlFlow; on( all(from_desugaring = "TryBlock"), message = "a `try` block must return `Result` or `Option` \ - (or another type that implements `{Try}`)", + (or another type that implements `{This}`)", label = "could not wrap the final value of the block as `{Self}` doesn't implement `Try`", ), on( all(from_desugaring = "QuestionMark"), - message = "the `?` operator can only be applied to values that implement `{Try}`", + message = "the `?` operator can only be applied to values that implement `{This}`", label = "the `?` operator cannot be applied to type `{Self}`" ) )] @@ -226,7 +226,7 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "core::result::Result", + Self = "core::result::Result", R = "core::option::Option", ), message = "the `?` operator can only be used on `Result`s, not `Option`s, \ @@ -237,7 +237,7 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "core::result::Result", + Self = "core::result::Result", ), // There's a special error message in the trait selection code for // `From` in `?`, so this is not shown for result-in-result errors, @@ -250,7 +250,7 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "core::option::Option", + Self = "core::option::Option", R = "core::result::Result", ), message = "the `?` operator can only be used on `Option`s, not `Result`s, \ @@ -261,7 +261,7 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "core::option::Option", + Self = "core::option::Option", ), // `Option`-in-`Option` always works, as there's only one possible // residual, so this can also be phrased strongly. @@ -273,7 +273,7 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "core::ops::control_flow::ControlFlow", + Self = "core::ops::control_flow::ControlFlow", R = "core::ops::control_flow::ControlFlow", ), message = "the `?` operator in {ItemContext} that returns `ControlFlow` \ @@ -285,7 +285,7 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "core::ops::control_flow::ControlFlow", + Self = "core::ops::control_flow::ControlFlow", // `R` is not a `ControlFlow`, as that case was matched previously ), message = "the `?` operator can only be used on `ControlFlow`s \ @@ -297,7 +297,7 @@ pub trait Try: FromResidual { all(from_desugaring = "QuestionMark"), message = "the `?` operator can only be used in {ItemContext} \ that returns `Result` or `Option` \ - (or another type that implements `{FromResidual}`)", + (or another type that implements `{This}`)", label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", parent_label = "this function should return `Result` or `Option` to accept `?`" ), diff --git a/library/core/src/option.rs b/library/core/src/option.rs index aed5a043c11a..675556b07a83 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -120,20 +120,22 @@ //! //! Rust guarantees to optimize the following types `T` such that //! [`Option`] has the same size, alignment, and [function call ABI] as `T`. In some -//! of these cases, Rust further guarantees that -//! `transmute::<_, Option>([0u8; size_of::()])` is sound and -//! produces `Option::::None`. These cases are identified by the -//! second column: +//! of these cases, Rust further guarantees the following: +//! - `transmute::<_, Option>([0u8; size_of::()])` is sound and produces +//! `Option::::None` +//! - `transmute::<_, [u8; size_of::()]>(Option::::None)` is sound and produces +//! `[0u8; size_of::()]` +//! These cases are identified by the second column: //! -//! | `T` | `transmute::<_, Option>([0u8; size_of::()])` sound? | -//! |---------------------------------------------------------------------|----------------------------------------------------------------------| -//! | [`Box`] (specifically, only `Box`) | when `U: Sized` | -//! | `&U` | when `U: Sized` | -//! | `&mut U` | when `U: Sized` | -//! | `fn`, `extern "C" fn`[^extern_fn] | always | -//! | [`num::NonZero*`] | always | -//! | [`ptr::NonNull`] | when `U: Sized` | -//! | `#[repr(transparent)]` struct around one of the types in this list. | when it holds for the inner type | +//! | `T` | Transmuting between `[0u8; size_of::()]` and `Option::::None` sound? | +//! |---------------------------------------------------------------------|----------------------------------------------------------------------------| +//! | [`Box`] (specifically, only `Box`) | when `U: Sized` | +//! | `&U` | when `U: Sized` | +//! | `&mut U` | when `U: Sized` | +//! | `fn`, `extern "C" fn`[^extern_fn] | always | +//! | [`num::NonZero*`] | always | +//! | [`ptr::NonNull`] | when `U: Sized` | +//! | `#[repr(transparent)]` struct around one of the types in this list. | when it holds for the inner type | //! //! [^extern_fn]: this remains true for any argument/return types and any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`) //! @@ -1251,6 +1253,36 @@ impl Option { } } + /// Maps an `Option` to a `U` by applying function `f` to the contained + /// value if the option is [`Some`], otherwise if [`None`], returns the + /// [default value] for the type `U`. + /// + /// # Examples + /// + /// ``` + /// #![feature(result_option_map_or_default)] + /// + /// let x: Option<&str> = Some("hi"); + /// let y: Option<&str> = None; + /// + /// assert_eq!(x.map_or_default(|x| x.len()), 2); + /// assert_eq!(y.map_or_default(|y| y.len()), 0); + /// ``` + /// + /// [default value]: Default::default + #[inline] + #[unstable(feature = "result_option_map_or_default", issue = "138099")] + pub fn map_or_default(self, f: F) -> U + where + U: Default, + F: FnOnce(T) -> U, + { + match self { + Some(t) => f(t), + None => U::default(), + } + } + /// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to /// [`Ok(v)`] and [`None`] to [`Err(err)`]. /// diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 257424b355fb..aad073cc8cdd 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1419,7 +1419,7 @@ impl Pin { #[stable(feature = "pin_deref_mut", since = "1.84.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline(always)] - pub fn as_deref_mut(self: Pin<&mut Pin>) -> Pin<&mut Ptr::Target> { + pub fn as_deref_mut(self: Pin<&mut Self>) -> Pin<&mut Ptr::Target> { // SAFETY: What we're asserting here is that going from // // Pin<&mut Pin> diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 17c4b4883613..10b11613f90f 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1623,7 +1623,7 @@ mod prim_usize {} /// * if `size_of_val(t) > 0`, then `t` is dereferenceable for `size_of_val(t)` many bytes /// /// If `t` points at address `a`, being "dereferenceable" for N bytes means that the memory range -/// `[a, a + N)` is all contained within a single [allocated object]. +/// `[a, a + N)` is all contained within a single [allocation]. /// /// For instance, this means that unsafe code in a safe function may assume these invariants are /// ensured of arguments passed by the caller, and it may assume that these invariants are ensured @@ -1639,7 +1639,7 @@ mod prim_usize {} /// may be unsound or become unsound in future versions of Rust depending on how this question is /// decided. /// -/// [allocated object]: ptr#allocated-object +/// [allocation]: ptr#allocation #[stable(feature = "rust1", since = "1.0.0")] mod prim_ref {} diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 19311e39b454..3e66e271f03b 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -73,6 +73,7 @@ impl Alignment { /// It must *not* be zero. #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[track_caller] pub const unsafe fn new_unchecked(align: usize) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 35089b4853d7..a1dab23ea7b4 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -66,6 +66,32 @@ impl *const T { self as _ } + /// Try to cast to a pointer of another type by checking aligment. + /// + /// If the pointer is properly aligned to the target type, it will be + /// cast to the target type. Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(pointer_try_cast_aligned)] + /// + /// let x = 0u64; + /// + /// let aligned: *const u64 = &x; + /// let unaligned = unsafe { aligned.byte_add(1) }; + /// + /// assert!(aligned.try_cast_aligned::().is_some()); + /// assert!(unaligned.try_cast_aligned::().is_none()); + /// ``` + #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn try_cast_aligned(self) -> Option<*const U> { + if self.is_aligned_to(align_of::()) { Some(self.cast()) } else { None } + } + /// Uses the address value in a new pointer of another type. /// /// This operation will ignore the address part of its `meta` operand and discard existing @@ -371,35 +397,7 @@ impl *const T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Adds a signed offset to a pointer. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. Note that "range" here refers to a half-open range as usual in Rust, - /// i.e., `self..result` for non-negative offsets and `result..self` for negative offsets. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_offset`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_offset`]: #method.wrapping_offset - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/offset.md")] /// /// # Examples /// @@ -416,7 +414,7 @@ impl *const T { #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn offset(self, count: isize) -> *const T where T: Sized, @@ -469,7 +467,7 @@ impl *const T { #[inline(always)] #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset`. unsafe { self.cast::().offset(count).with_metadata_of(self) } @@ -484,17 +482,17 @@ impl *const T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to + /// The resulting pointer "remembers" the [allocation] that `self` points to /// (this is called "[Provenance](ptr/index.html#provenance)"). - /// The pointer must not be used to read or write other allocated objects. + /// The pointer must not be used to read or write other allocations. /// /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless - /// `x` and `y` point into the same allocated object. + /// `x` and `y` point into the same allocation. /// /// Compared to [`offset`], this method basically delays the requirement of staying within the - /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object + /// same allocation: [`offset`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_offset` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`offset`] /// can be optimized better and is thus preferable in performance-sensitive code. @@ -502,10 +500,10 @@ impl *const T { /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other - /// words, leaving the allocated object and then re-entering it later is permitted. + /// words, leaving the allocation and then re-entering it later is permitted. /// /// [`offset`]: #method.offset - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -618,7 +616,7 @@ impl *const T { /// * `self` and `origin` must either /// /// * point to the same address, or - /// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between + /// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocation], and the memory range between /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple @@ -626,10 +624,10 @@ impl *const T { /// /// As a consequence, the absolute distance between the pointers, in bytes, computed on /// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is - /// implied by the in-bounds requirement, and the fact that no allocated object can be larger + /// implied by the in-bounds requirement, and the fact that no allocation can be larger /// than `isize::MAX` bytes. /// - /// The requirement for pointers to be derived from the same allocated object is primarily + /// The requirement for pointers to be derived from the same allocation is primarily /// needed for `const`-compatibility: the distance between pointers into *different* allocated /// objects is not known at compile-time. However, the requirement also exists at /// runtime and may be exploited by optimizations. If you wish to compute the difference between @@ -638,7 +636,7 @@ impl *const T { // FIXME: recommend `addr()` instead of `as usize` once that is stable. /// /// [`add`]: #method.add - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Panics /// @@ -768,7 +766,7 @@ impl *const T { #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn offset_from_unsigned(self, origin: *const T) -> usize where T: Sized, @@ -813,7 +811,7 @@ impl *const T { #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_offset_from_unsigned(self, origin: *const U) -> usize { // SAFETY: the caller must uphold the safety contract for `offset_from_unsigned`. unsafe { self.cast::().offset_from_unsigned(origin.cast::()) } @@ -879,38 +877,7 @@ impl *const T { } } - /// Adds an unsigned offset to a pointer. - /// - /// This can only move the pointer forward (or not move it). If you need to move forward or - /// backward depending on the value, then you might want [`offset`](#method.offset) instead - /// which takes a signed offset. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_add`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_add`]: #method.wrapping_add - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/add.md")] /// /// # Examples /// @@ -927,7 +894,7 @@ impl *const T { #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -979,7 +946,7 @@ impl *const T { #[inline(always)] #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add`. unsafe { self.cast::().add(count).with_metadata_of(self) } @@ -1002,12 +969,12 @@ impl *const T { /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// [allocation], and the entire memory range between `self` and the result must be in + /// bounds of that allocation. In particular, this range must not "wrap around" the edge /// of the address space. /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement. /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always /// safe. /// @@ -1016,7 +983,7 @@ impl *const T { /// enables more aggressive compiler optimizations. /// /// [`wrapping_sub`]: #method.wrapping_sub - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -1033,7 +1000,7 @@ impl *const T { #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, @@ -1091,7 +1058,7 @@ impl *const T { #[inline(always)] #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub`. unsafe { self.cast::().sub(count).with_metadata_of(self) } @@ -1106,16 +1073,16 @@ impl *const T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not - /// be used to read or write other allocated objects. + /// The resulting pointer "remembers" the [allocation] that `self` points to; it must not + /// be used to read or write other allocations. /// /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless - /// `x` and `y` point into the same allocated object. + /// `x` and `y` point into the same allocation. /// /// Compared to [`add`], this method basically delays the requirement of staying within the - /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object + /// same allocation: [`add`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_add` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`add`] /// can be optimized better and is thus preferable in performance-sensitive code. @@ -1123,10 +1090,10 @@ impl *const T { /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the - /// allocated object and then re-entering it later is permitted. + /// allocation and then re-entering it later is permitted. /// /// [`add`]: #method.add - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -1185,16 +1152,16 @@ impl *const T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not - /// be used to read or write other allocated objects. + /// The resulting pointer "remembers" the [allocation] that `self` points to; it must not + /// be used to read or write other allocations. /// /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless - /// `x` and `y` point into the same allocated object. + /// `x` and `y` point into the same allocation. /// /// Compared to [`sub`], this method basically delays the requirement of staying within the - /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object + /// same allocation: [`sub`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_sub` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`sub`] /// can be optimized better and is thus preferable in performance-sensitive code. @@ -1202,10 +1169,10 @@ impl *const T { /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the - /// allocated object and then re-entering it later is permitted. + /// allocation and then re-entering it later is permitted. /// /// [`sub`]: #method.sub - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -1264,7 +1231,7 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn read(self) -> T where T: Sized, @@ -1285,7 +1252,7 @@ impl *const T { /// [`ptr::read_volatile`]: crate::ptr::read_volatile() #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub unsafe fn read_volatile(self) -> T where T: Sized, @@ -1305,7 +1272,7 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn read_unaligned(self) -> T where T: Sized, @@ -1325,7 +1292,7 @@ impl *const T { #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn copy_to(self, dest: *mut T, count: usize) where T: Sized, @@ -1345,7 +1312,7 @@ impl *const T { #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) where T: Sized, @@ -1597,8 +1564,8 @@ impl *const [T] { /// * The pointer must be [valid] for reads for `ptr.len() * size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single [allocated object]! - /// Slices can never span across multiple allocated objects. + /// * The entire memory range of this slice must be contained within a single [allocation]! + /// Slices can never span across multiple allocations. /// /// * The pointer must be aligned even for zero-length slices. One /// reason for this is that enum layout optimizations may rely on references @@ -1619,7 +1586,7 @@ impl *const [T] { /// See also [`slice::from_raw_parts`][]. /// /// [valid]: crate::ptr#safety - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Panics during const evaluation /// diff --git a/library/core/src/ptr/docs/add.md b/library/core/src/ptr/docs/add.md new file mode 100644 index 000000000000..ae7c7785684c --- /dev/null +++ b/library/core/src/ptr/docs/add.md @@ -0,0 +1,32 @@ +Adds an unsigned offset to a pointer. + +This can only move the pointer forward (or not move it). If you need to move forward or +backward depending on the value, then you might want [`offset`](#method.offset) instead +which takes a signed offset. + +`count` is in units of T; e.g., a `count` of 3 represents a pointer +offset of `3 * size_of::()` bytes. + +# Safety + +If any of the following conditions are violated, the result is Undefined Behavior: + +* The offset in bytes, `count * size_of::()`, computed on mathematical integers (without +"wrapping around"), must fit in an `isize`. + +* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some +[allocation], and the entire memory range between `self` and the result must be in +bounds of that allocation. In particular, this range must not "wrap around" the edge +of the address space. + +Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset +stays in bounds of the allocation, it is guaranteed to satisfy the first requirement. +This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always +safe. + +Consider using [`wrapping_add`] instead if these constraints are +difficult to satisfy. The only advantage of this method is that it +enables more aggressive compiler optimizations. + +[`wrapping_add`]: #method.wrapping_add +[allocation]: crate::ptr#allocation diff --git a/library/core/src/ptr/docs/offset.md b/library/core/src/ptr/docs/offset.md new file mode 100644 index 000000000000..f2e335a79a5c --- /dev/null +++ b/library/core/src/ptr/docs/offset.md @@ -0,0 +1,29 @@ +Adds a signed offset to a pointer. + +`count` is in units of T; e.g., a `count` of 3 represents a pointer +offset of `3 * size_of::()` bytes. + +# Safety + +If any of the following conditions are violated, the result is Undefined Behavior: + +* The offset in bytes, `count * size_of::()`, computed on mathematical integers (without +"wrapping around"), must fit in an `isize`. + +* If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some +[allocation], and the entire memory range between `self` and the result must be in +bounds of that allocation. In particular, this range must not "wrap around" the edge +of the address space. Note that "range" here refers to a half-open range as usual in Rust, +i.e., `self..result` for non-negative offsets and `result..self` for negative offsets. + +Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset +stays in bounds of the allocation, it is guaranteed to satisfy the first requirement. +This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always +safe. + +Consider using [`wrapping_offset`] instead if these constraints are +difficult to satisfy. The only advantage of this method is that it +enables more aggressive compiler optimizations. + +[`wrapping_offset`]: #method.wrapping_offset +[allocation]: crate::ptr#allocation diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index bd6c4daa509d..81bf6778b05d 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -19,10 +19,10 @@ //! pointer. The following points are only concerned with non-zero-sized accesses. //! * A [null] pointer is *never* valid. //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be -//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated -//! object] it is derived from; a pointer is dereferenceable if the memory range of the given size -//! starting at the pointer is entirely contained within the bounds of that allocated object. Note -//! that in Rust, every (stack-allocated) variable is considered a separate allocated object. +//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocation] +//! it is derived from; a pointer is dereferenceable if the memory range of the given size +//! starting at the pointer is entirely contained within the bounds of that allocation. Note +//! that in Rust, every (stack-allocated) variable is considered a separate allocation. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different @@ -30,7 +30,7 @@ //! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot //! be used for inter-thread synchronization. //! * The result of casting a reference to a pointer is valid for as long as the -//! underlying object is live and no reference (just raw pointers) is used to +//! underlying allocation is live and no reference (just raw pointers) is used to //! access the same memory. That is, reference and pointer accesses cannot be //! interleaved. //! @@ -95,24 +95,26 @@ //! //! [valid value]: ../../reference/behavior-considered-undefined.html#invalid-values //! -//! ## Allocated object +//! ## Allocation //! -//! An *allocated object* is a subset of program memory which is addressable +//! +//! +//! An *allocation* is a subset of program memory which is addressable //! from Rust, and within which pointer arithmetic is possible. Examples of -//! allocated objects include heap allocations, stack-allocated variables, +//! allocations include heap allocations, stack-allocated variables, //! statics, and consts. The safety preconditions of some Rust operations - //! such as `offset` and field projections (`expr.field`) - are defined in -//! terms of the allocated objects on which they operate. +//! terms of the allocations on which they operate. //! -//! An allocated object has a base address, a size, and a set of memory -//! addresses. It is possible for an allocated object to have zero size, but -//! such an allocated object will still have a base address. The base address -//! of an allocated object is not necessarily unique. While it is currently the -//! case that an allocated object always has a set of memory addresses which is +//! An allocation has a base address, a size, and a set of memory +//! addresses. It is possible for an allocation to have zero size, but +//! such an allocation will still have a base address. The base address +//! of an allocation is not necessarily unique. While it is currently the +//! case that an allocation always has a set of memory addresses which is //! fully contiguous (i.e., has no "holes"), there is no guarantee that this //! will not change in the future. //! -//! For any allocated object with `base` address, `size`, and a set of +//! For any allocation with `base` address, `size`, and a set of //! `addresses`, the following are guaranteed: //! - For all addresses `a` in `addresses`, `a` is in the range `base .. (base + //! size)` (note that this requires `a < base + size`, not `a <= base + size`) @@ -122,11 +124,11 @@ //! - `size <= isize::MAX` //! //! As a consequence of these guarantees, given any address `a` within the set -//! of addresses of an allocated object: +//! of addresses of an allocation: //! - It is guaranteed that `a - base` does not overflow `isize` //! - It is guaranteed that `a - base` is non-negative //! - It is guaranteed that, given `o = a - base` (i.e., the offset of `a` within -//! the allocated object), `base + o` will not wrap around the address space (in +//! the allocation), `base + o` will not wrap around the address space (in //! other words, will not overflow `usize`) //! //! [`null()`]: null @@ -138,8 +140,8 @@ //! and the freed memory gets reallocated before your read/write (in fact this is the //! worst-case scenario, UAFs would be much less concerning if this didn't happen!). //! As another example, consider that [`wrapping_offset`] is documented to "remember" -//! the allocated object that the original pointer points to, even if it is offset far -//! outside the memory range occupied by that allocated object. +//! the allocation that the original pointer points to, even if it is offset far +//! outside the memory range occupied by that allocation. //! To rationalize claims like this, pointers need to somehow be *more* than just their addresses: //! they must have **provenance**. //! @@ -159,12 +161,12 @@ //! writes. Note that this can interact with the other components, e.g. a pointer might permit //! mutation only for a subset of addresses, or only for a subset of its maximal timespan. //! -//! When an [allocated object] is created, it has a unique Original Pointer. For alloc +//! When an [allocation] is created, it has a unique Original Pointer. For alloc //! APIs this is literally the pointer the call returns, and for local variables and statics, //! this is the name of the variable/static. (This is mildly overloading the term "pointer" //! for the sake of brevity/exposition.) //! -//! The Original Pointer for an allocated object has provenance that constrains the *spatial* +//! The Original Pointer for an allocation has provenance that constrains the *spatial* //! permissions of this pointer to the memory range of the allocation, and the *temporal* //! permissions to the lifetime of the allocation. Provenance is implicitly inherited by all //! pointers transitively derived from the Original Pointer through operations like [`offset`], @@ -192,10 +194,10 @@ //! provenance since they access an empty range of memory. //! //! * It is undefined behavior to [`offset`] a pointer across a memory range that is not contained -//! in the allocated object it is derived from, or to [`offset_from`] two pointers not derived -//! from the same allocated object. Provenance is used to say what exactly "derived from" even +//! in the allocation it is derived from, or to [`offset_from`] two pointers not derived +//! from the same allocation. Provenance is used to say what exactly "derived from" even //! means: the lineage of a pointer is traced back to the Original Pointer it descends from, and -//! that identifies the relevant allocated object. In particular, it's always UB to offset a +//! that identifies the relevant allocation. In particular, it's always UB to offset a //! pointer derived from something that is now deallocated, except if the offset is 0. //! //! But it *is* still sound to: @@ -216,7 +218,7 @@ //! * Compare arbitrary pointers by address. Pointer comparison ignores provenance and addresses //! *are* just integers, so there is always a coherent answer, even if the pointers are dangling //! or from different provenances. Note that if you get "lucky" and notice that a pointer at the -//! end of one allocated object is the "same" address as the start of another allocated object, +//! end of one allocation is the "same" address as the start of another allocation, //! anything you do with that fact is *probably* going to be gibberish. The scope of that //! gibberish is kept under control by the fact that the two pointers *still* aren't allowed to //! access the other's allocation (bytes), because they still have different provenance. @@ -369,7 +371,7 @@ //! integer-to-pointer casts. //! //! [aliasing]: ../../nomicon/aliasing.html -//! [allocated object]: #allocated-object +//! [allocation]: #allocation //! [provenance]: #provenance //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer //! [ub]: ../../reference/behavior-considered-undefined.html @@ -405,16 +407,6 @@ mod alignment; #[unstable(feature = "ptr_alignment_type", issue = "102070")] pub use alignment::Alignment; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::copy; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::copy_nonoverlapping; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::write_bytes; - mod metadata; #[unstable(feature = "ptr_metadata", issue = "81513")] pub use metadata::{DynMetadata, Pointee, Thin, from_raw_parts, from_raw_parts_mut, metadata}; @@ -430,6 +422,289 @@ pub use unique::Unique; mod const_ptr; mod mut_ptr; +// Some functions are defined here because they accidentally got made +// available in this module on stable. See . +// (`transmute` also falls into this category, but it cannot be wrapped due to the +// check that `T` and `U` have the same size.) + +/// Copies `count * size_of::()` bytes from `src` to `dst`. The source +/// and destination must *not* overlap. +/// +/// For regions of memory which might overlap, use [`copy`] instead. +/// +/// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but +/// with the source and destination arguments swapped, +/// and `count` counting the number of `T`s instead of bytes. +/// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// +/// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `src` must be [valid] for reads of `count * size_of::()` bytes. +/// +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. +/// +/// * Both `src` and `dst` must be properly aligned. +/// +/// * The region of memory beginning at `src` with a size of `count * +/// size_of::()` bytes must *not* overlap with the region of memory +/// beginning at `dst` with the same size. +/// +/// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of +/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values +/// in the region beginning at `*src` and the region beginning at `*dst` can +/// [violate memory safety][read-ownership]. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is +/// `0`, the pointers must be properly aligned. +/// +/// [`read`]: crate::ptr::read +/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value +/// [valid]: crate::ptr#safety +/// +/// # Examples +/// +/// Manually implement [`Vec::append`]: +/// +/// ``` +/// use std::ptr; +/// +/// /// Moves all the elements of `src` into `dst`, leaving `src` empty. +/// fn append(dst: &mut Vec, src: &mut Vec) { +/// let src_len = src.len(); +/// let dst_len = dst.len(); +/// +/// // Ensure that `dst` has enough capacity to hold all of `src`. +/// dst.reserve(src_len); +/// +/// unsafe { +/// // The call to add is always safe because `Vec` will never +/// // allocate more than `isize::MAX` bytes. +/// let dst_ptr = dst.as_mut_ptr().add(dst_len); +/// let src_ptr = src.as_ptr(); +/// +/// // Truncate `src` without dropping its contents. We do this first, +/// // to avoid problems in case something further down panics. +/// src.set_len(0); +/// +/// // The two regions cannot overlap because mutable references do +/// // not alias, and two different vectors cannot own the same +/// // memory. +/// ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); +/// +/// // Notify `dst` that it now holds the contents of `src`. +/// dst.set_len(dst_len + src_len); +/// } +/// } +/// +/// let mut a = vec!['r']; +/// let mut b = vec!['u', 's', 't']; +/// +/// append(&mut a, &mut b); +/// +/// assert_eq!(a, &['r', 'u', 's', 't']); +/// assert!(b.is_empty()); +/// ``` +/// +/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append +#[doc(alias = "memcpy")] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] +#[inline(always)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"] +pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { + ub_checks::assert_unsafe_precondition!( + check_language_ub, + "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ + and the specified memory ranges do not overlap", + ( + src: *const () = src as *const (), + dst: *mut () = dst as *mut (), + size: usize = size_of::(), + align: usize = align_of::(), + count: usize = count, + ) => { + let zero_size = count == 0 || size == 0; + ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) + && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) + && ub_checks::maybe_is_nonoverlapping(src, dst, size, count) + } + ); + + // SAFETY: the safety contract for `copy_nonoverlapping` must be + // upheld by the caller. + unsafe { crate::intrinsics::copy_nonoverlapping(src, dst, count) } +} + +/// Copies `count * size_of::()` bytes from `src` to `dst`. The source +/// and destination may overlap. +/// +/// If the source and destination will *never* overlap, +/// [`copy_nonoverlapping`] can be used instead. +/// +/// `copy` is semantically equivalent to C's [`memmove`], but +/// with the source and destination arguments swapped, +/// and `count` counting the number of `T`s instead of bytes. +/// Copying takes place as if the bytes were copied from `src` +/// to a temporary array and then copied from the array to `dst`. +/// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// +/// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `src` must be [valid] for reads of `count * size_of::()` bytes. +/// +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes, and must remain valid even +/// when `src` is read for `count * size_of::()` bytes. (This means if the memory ranges +/// overlap, the `dst` pointer must not be invalidated by `src` reads.) +/// +/// * Both `src` and `dst` must be properly aligned. +/// +/// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of +/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values +/// in the region beginning at `*src` and the region beginning at `*dst` can +/// [violate memory safety][read-ownership]. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is +/// `0`, the pointers must be properly aligned. +/// +/// [`read`]: crate::ptr::read +/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value +/// [valid]: crate::ptr#safety +/// +/// # Examples +/// +/// Efficiently create a Rust vector from an unsafe buffer: +/// +/// ``` +/// use std::ptr; +/// +/// /// # Safety +/// /// +/// /// * `ptr` must be correctly aligned for its type and non-zero. +/// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`. +/// /// * Those elements must not be used after calling this function unless `T: Copy`. +/// # #[allow(dead_code)] +/// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { +/// let mut dst = Vec::with_capacity(elts); +/// +/// // SAFETY: Our precondition ensures the source is aligned and valid, +/// // and `Vec::with_capacity` ensures that we have usable space to write them. +/// unsafe { ptr::copy(ptr, dst.as_mut_ptr(), elts); } +/// +/// // SAFETY: We created it with this much capacity earlier, +/// // and the previous `copy` has initialized these elements. +/// unsafe { dst.set_len(elts); } +/// dst +/// } +/// ``` +#[doc(alias = "memmove")] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] +#[inline(always)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[rustc_diagnostic_item = "ptr_copy"] +pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { + // SAFETY: the safety contract for `copy` must be upheld by the caller. + unsafe { + ub_checks::assert_unsafe_precondition!( + check_language_ub, + "ptr::copy requires that both pointer arguments are aligned and non-null", + ( + src: *const () = src as *const (), + dst: *mut () = dst as *mut (), + align: usize = align_of::(), + zero_size: bool = T::IS_ZST || count == 0, + ) => + ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) + && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) + ); + crate::intrinsics::copy(src, dst, count) + } +} + +/// Sets `count * size_of::()` bytes of memory starting at `dst` to +/// `val`. +/// +/// `write_bytes` is similar to C's [`memset`], but sets `count * +/// size_of::()` bytes to `val`. +/// +/// [`memset`]: https://en.cppreference.com/w/c/string/byte/memset +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. +/// +/// * `dst` must be properly aligned. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is +/// `0`, the pointer must be properly aligned. +/// +/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB) +/// later if the written bytes are not a valid representation of some `T`. For instance, the +/// following is an **incorrect** use of this function: +/// +/// ```rust,no_run +/// unsafe { +/// let mut value: u8 = 0; +/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool; +/// let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`. +/// ptr.write_bytes(42u8, 1); // This function itself does not cause UB... +/// let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️ +/// } +/// ``` +/// +/// [valid]: crate::ptr#safety +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::ptr; +/// +/// let mut vec = vec![0u32; 4]; +/// unsafe { +/// let vec_ptr = vec.as_mut_ptr(); +/// ptr::write_bytes(vec_ptr, 0xfe, 2); +/// } +/// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]); +/// ``` +#[doc(alias = "memset")] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] +#[inline(always)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[rustc_diagnostic_item = "ptr_write_bytes"] +pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { + // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. + unsafe { + ub_checks::assert_unsafe_precondition!( + check_language_ub, + "ptr::write_bytes requires that the destination pointer is aligned and non-null", + ( + addr: *const () = dst as *const (), + align: usize = align_of::(), + zero_size: bool = T::IS_ZST || count == 0, + ) => ub_checks::maybe_is_aligned_and_not_null(addr, align, zero_size) + ); + crate::intrinsics::write_bytes(dst, val, count) + } +} + /// Executes the destructor (if any) of the pointed-to value. /// /// This is almost the same as calling [`ptr::read`] and discarding @@ -1016,7 +1291,7 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { // SAFETY: the caller must guarantee that `x` and `y` are // valid for writes and properly aligned. `tmp` cannot be // overlapping either `x` or `y` because `tmp` was just allocated - // on the stack as a separate allocated object. + // on the stack as a separate allocation. unsafe { copy_nonoverlapping(x, tmp.as_mut_ptr(), 1); copy(y, x, 1); // `x` and `y` may overlap @@ -1104,6 +1379,7 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { #[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] #[rustc_diagnostic_item = "ptr_swap_nonoverlapping"] #[rustc_allow_const_fn_unstable(const_eval_select)] // both implementations behave the same +#[track_caller] pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { ub_checks::assert_unsafe_precondition!( check_library_ub, @@ -1135,7 +1411,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { // Going though a slice here helps codegen know the size fits in `isize` let slice = slice_from_raw_parts_mut(x, count); // SAFETY: This is all readable from the pointer, meaning it's one - // allocated object, and thus cannot be more than isize::MAX bytes. + // allocation, and thus cannot be more than isize::MAX bytes. let bytes = unsafe { mem::size_of_val_raw::<[T]>(slice) }; if let Some(bytes) = NonZero::new(bytes) { // SAFETY: These are the same ranges, just expressed in a different @@ -1284,11 +1560,12 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, bytes: NonZero(dst: *mut T, src: T) -> T { // SAFETY: the caller must guarantee that `dst` is valid to be // cast to a mutable reference (valid for writes, aligned, initialized), // and cannot overlap `src` since `dst` must point to a distinct - // allocated object. + // allocation. unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, @@ -1411,7 +1688,7 @@ pub const unsafe fn replace(dst: *mut T, src: T) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] #[rustc_diagnostic_item = "ptr_read"] pub const unsafe fn read(src: *const T) -> T { // It would be semantically correct to implement this via `copy_nonoverlapping` @@ -1529,13 +1806,13 @@ pub const unsafe fn read(src: *const T) -> T { #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] #[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] #[rustc_diagnostic_item = "ptr_read_unaligned"] pub const unsafe fn read_unaligned(src: *const T) -> T { let mut tmp = MaybeUninit::::uninit(); // SAFETY: the caller must guarantee that `src` is valid for reads. // `src` cannot overlap `tmp` because `tmp` was just allocated on - // the stack as a separate allocated object. + // the stack as a separate allocation. // // Also, since we just wrote a valid value into `tmp`, it is guaranteed // to be properly initialized. @@ -1628,7 +1905,7 @@ pub const unsafe fn read_unaligned(src: *const T) -> T { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[rustc_diagnostic_item = "ptr_write"] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] pub const unsafe fn write(dst: *mut T, src: T) { // Semantically, it would be fine for this to be implemented as a // `copy_nonoverlapping` and appropriate drop suppression of `src`. @@ -1732,7 +2009,7 @@ pub const unsafe fn write(dst: *mut T, src: T) { #[stable(feature = "ptr_unaligned", since = "1.17.0")] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[rustc_diagnostic_item = "ptr_write_unaligned"] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] pub const unsafe fn write_unaligned(dst: *mut T, src: T) { // SAFETY: the caller must guarantee that `dst` is valid for writes. // `dst` cannot overlap `src` because the caller has mutable access @@ -1806,7 +2083,7 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { /// ``` #[inline] #[stable(feature = "volatile", since = "1.9.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] #[rustc_diagnostic_item = "ptr_read_volatile"] pub unsafe fn read_volatile(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. @@ -1887,7 +2164,7 @@ pub unsafe fn read_volatile(src: *const T) -> T { #[inline] #[stable(feature = "volatile", since = "1.9.0")] #[rustc_diagnostic_item = "ptr_write_volatile"] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[track_caller] pub unsafe fn write_volatile(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 9cf251742d42..968f033bf598 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -48,6 +48,32 @@ impl *mut T { self as _ } + /// Try to cast to a pointer of another type by checking aligment. + /// + /// If the pointer is properly aligned to the target type, it will be + /// cast to the target type. Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(pointer_try_cast_aligned)] + /// + /// let mut x = 0u64; + /// + /// let aligned: *mut u64 = &mut x; + /// let unaligned = unsafe { aligned.byte_add(1) }; + /// + /// assert!(aligned.try_cast_aligned::().is_some()); + /// assert!(unaligned.try_cast_aligned::().is_none()); + /// ``` + #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn try_cast_aligned(self) -> Option<*mut U> { + if self.is_aligned_to(align_of::()) { Some(self.cast()) } else { None } + } + /// Uses the address value in a new pointer of another type. /// /// This operation will ignore the address part of its `meta` operand and discard existing @@ -368,34 +394,7 @@ impl *mut T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Adds a signed offset to a pointer. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_offset`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_offset`]: #method.wrapping_offset - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/offset.md")] /// /// # Examples /// @@ -412,7 +411,7 @@ impl *mut T { #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn offset(self, count: isize) -> *mut T where T: Sized, @@ -449,7 +448,7 @@ impl *mut T { // SAFETY: the caller must uphold the safety contract for `offset`. // The obtained pointer is valid for writes since the caller must - // guarantee that it points to the same allocated object as `self`. + // guarantee that it points to the same allocation as `self`. unsafe { intrinsics::offset(self, count) } } @@ -467,7 +466,7 @@ impl *mut T { #[inline(always)] #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset`. unsafe { self.cast::().offset(count).with_metadata_of(self) } @@ -482,17 +481,17 @@ impl *mut T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to + /// The resulting pointer "remembers" the [allocation] that `self` points to /// (this is called "[Provenance](ptr/index.html#provenance)"). - /// The pointer must not be used to read or write other allocated objects. + /// The pointer must not be used to read or write other allocations. /// /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless - /// `x` and `y` point into the same allocated object. + /// `x` and `y` point into the same allocation. /// /// Compared to [`offset`], this method basically delays the requirement of staying within the - /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object + /// same allocation: [`offset`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_offset` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`offset`] /// can be optimized better and is thus preferable in performance-sensitive code. @@ -500,10 +499,10 @@ impl *mut T { /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other - /// words, leaving the allocated object and then re-entering it later is permitted. + /// words, leaving the allocation and then re-entering it later is permitted. /// /// [`offset`]: #method.offset - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -792,7 +791,7 @@ impl *mut T { /// * `self` and `origin` must either /// /// * point to the same address, or - /// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between + /// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocation], and the memory range between /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple @@ -800,10 +799,10 @@ impl *mut T { /// /// As a consequence, the absolute distance between the pointers, in bytes, computed on /// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is - /// implied by the in-bounds requirement, and the fact that no allocated object can be larger + /// implied by the in-bounds requirement, and the fact that no allocation can be larger /// than `isize::MAX` bytes. /// - /// The requirement for pointers to be derived from the same allocated object is primarily + /// The requirement for pointers to be derived from the same allocation is primarily /// needed for `const`-compatibility: the distance between pointers into *different* allocated /// objects is not known at compile-time. However, the requirement also exists at /// runtime and may be exploited by optimizations. If you wish to compute the difference between @@ -812,7 +811,7 @@ impl *mut T { // FIXME: recommend `addr()` instead of `as usize` once that is stable. /// /// [`add`]: #method.add - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Panics /// @@ -942,7 +941,7 @@ impl *mut T { #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn offset_from_unsigned(self, origin: *const T) -> usize where T: Sized, @@ -964,50 +963,19 @@ impl *mut T { #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_offset_from_unsigned(self, origin: *mut U) -> usize { // SAFETY: the caller must uphold the safety contract for `byte_offset_from_unsigned`. unsafe { (self as *const T).byte_offset_from_unsigned(origin) } } - /// Adds an unsigned offset to a pointer. - /// - /// This can only move the pointer forward (or not move it). If you need to move forward or - /// backward depending on the value, then you might want [`offset`](#method.offset) instead - /// which takes a signed offset. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined Behavior: - /// - /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without - /// "wrapping around"), must fit in an `isize`. - /// - /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. - /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. - /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always - /// safe. - /// - /// Consider using [`wrapping_add`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_add`]: #method.wrapping_add - /// [allocated object]: crate::ptr#allocated-object + #[doc = include_str!("./docs/add.md")] /// /// # Examples /// /// ``` - /// let s: &str = "123"; - /// let ptr: *const u8 = s.as_ptr(); + /// let mut s: String = "123".to_string(); + /// let ptr: *mut u8 = s.as_mut_ptr(); /// /// unsafe { /// assert_eq!('2', *ptr.add(1) as char); @@ -1018,7 +986,7 @@ impl *mut T { #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -1070,7 +1038,7 @@ impl *mut T { #[inline(always)] #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add`. unsafe { self.cast::().add(count).with_metadata_of(self) } @@ -1093,12 +1061,12 @@ impl *mut T { /// "wrapping around"), must fit in an `isize`. /// /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// [allocation], and the entire memory range between `self` and the result must be in + /// bounds of that allocation. In particular, this range must not "wrap around" the edge /// of the address space. /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement. /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always /// safe. /// @@ -1107,7 +1075,7 @@ impl *mut T { /// enables more aggressive compiler optimizations. /// /// [`wrapping_sub`]: #method.wrapping_sub - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -1124,7 +1092,7 @@ impl *mut T { #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, @@ -1182,7 +1150,7 @@ impl *mut T { #[inline(always)] #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub`. unsafe { self.cast::().sub(count).with_metadata_of(self) } @@ -1197,16 +1165,16 @@ impl *mut T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not - /// be used to read or write other allocated objects. + /// The resulting pointer "remembers" the [allocation] that `self` points to; it must not + /// be used to read or write other allocations. /// /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless - /// `x` and `y` point into the same allocated object. + /// `x` and `y` point into the same allocation. /// /// Compared to [`add`], this method basically delays the requirement of staying within the - /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object + /// same allocation: [`add`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_add` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`add`] /// can be optimized better and is thus preferable in performance-sensitive code. @@ -1214,10 +1182,10 @@ impl *mut T { /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the - /// allocated object and then re-entering it later is permitted. + /// allocation and then re-entering it later is permitted. /// /// [`add`]: #method.add - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -1273,16 +1241,16 @@ impl *mut T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not - /// be used to read or write other allocated objects. + /// The resulting pointer "remembers" the [allocation] that `self` points to; it must not + /// be used to read or write other allocations. /// /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless - /// `x` and `y` point into the same allocated object. + /// `x` and `y` point into the same allocation. /// /// Compared to [`sub`], this method basically delays the requirement of staying within the - /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object + /// same allocation: [`sub`] is immediate Undefined Behavior when crossing object /// boundaries; `wrapping_sub` produces a pointer but still leads to Undefined Behavior if a /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`sub`] /// can be optimized better and is thus preferable in performance-sensitive code. @@ -1290,10 +1258,10 @@ impl *mut T { /// The delayed check only considers the value of the pointer that was dereferenced, not the /// intermediate values used during the computation of the final result. For example, /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the - /// allocated object and then re-entering it later is permitted. + /// allocation and then re-entering it later is permitted. /// /// [`sub`]: #method.sub - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -1349,7 +1317,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn read(self) -> T where T: Sized, @@ -1370,7 +1338,7 @@ impl *mut T { /// [`ptr::read_volatile`]: crate::ptr::read_volatile() #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub unsafe fn read_volatile(self) -> T where T: Sized, @@ -1390,7 +1358,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn read_unaligned(self) -> T where T: Sized, @@ -1410,7 +1378,7 @@ impl *mut T { #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn copy_to(self, dest: *mut T, count: usize) where T: Sized, @@ -1430,7 +1398,7 @@ impl *mut T { #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) where T: Sized, @@ -1450,7 +1418,7 @@ impl *mut T { #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn copy_from(self, src: *const T, count: usize) where T: Sized, @@ -1470,7 +1438,7 @@ impl *mut T { #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn copy_from_nonoverlapping(self, src: *const T, count: usize) where T: Sized, @@ -1500,7 +1468,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn write(self, val: T) where T: Sized, @@ -1519,7 +1487,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn write_bytes(self, val: u8, count: usize) where T: Sized, @@ -1540,7 +1508,7 @@ impl *mut T { /// [`ptr::write_volatile`]: crate::ptr::write_volatile() #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub unsafe fn write_volatile(self, val: T) where T: Sized, @@ -1560,7 +1528,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[inline(always)] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[track_caller] pub const unsafe fn write_unaligned(self, val: T) where T: Sized, @@ -1802,7 +1770,7 @@ impl *mut [T] { /// /// # Safety /// - /// `mid` must be [in-bounds] of the underlying [allocated object]. + /// `mid` must be [in-bounds] of the underlying [allocation]. /// Which means `self` must be dereferenceable and span a single allocation /// that is at least `mid * size_of::()` bytes long. Not upholding these /// requirements is *[undefined behavior]* even if the resulting pointers are not used. @@ -1813,7 +1781,7 @@ impl *mut [T] { /// /// [`split_at_mut_unchecked`]: #method.split_at_mut_unchecked /// [in-bounds]: #method.add - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples @@ -1848,13 +1816,14 @@ impl *mut [T] { /// /// # Safety /// - /// `mid` must be [in-bounds] of the underlying [allocated object]. + /// `mid` must be [in-bounds] of the underlying [allocation]. /// Which means `self` must be dereferenceable and span a single allocation /// that is at least `mid * size_of::()` bytes long. Not upholding these /// requirements is *[undefined behavior]* even if the resulting pointers are not used. /// /// [in-bounds]: #method.add /// [out-of-bounds index]: #method.add + /// [allocation]: crate::ptr#allocation /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples @@ -1954,8 +1923,8 @@ impl *mut [T] { /// * The pointer must be [valid] for reads for `ptr.len() * size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single [allocated object]! - /// Slices can never span across multiple allocated objects. + /// * The entire memory range of this slice must be contained within a single [allocation]! + /// Slices can never span across multiple allocations. /// /// * The pointer must be aligned even for zero-length slices. One /// reason for this is that enum layout optimizations may rely on references @@ -1976,7 +1945,7 @@ impl *mut [T] { /// See also [`slice::from_raw_parts`][]. /// /// [valid]: crate::ptr#safety - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Panics during const evaluation /// @@ -2012,8 +1981,8 @@ impl *mut [T] { /// * The pointer must be [valid] for reads and writes for `ptr.len() * size_of::()` /// many bytes, and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single [allocated object]! - /// Slices can never span across multiple allocated objects. + /// * The entire memory range of this slice must be contained within a single [allocation]! + /// Slices can never span across multiple allocations. /// /// * The pointer must be aligned even for zero-length slices. One /// reason for this is that enum layout optimizations may rely on references @@ -2034,7 +2003,7 @@ impl *mut [T] { /// See also [`slice::from_raw_parts_mut`][]. /// /// [valid]: crate::ptr#safety - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Panics during const evaluation /// diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 8b31328de047..91b8d1bf9a70 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -216,6 +216,7 @@ impl NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[rustc_const_stable(feature = "const_nonnull_new_unchecked", since = "1.25.0")] #[inline] + #[track_caller] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { // SAFETY: the caller must guarantee that `ptr` is non-null. unsafe { @@ -490,6 +491,33 @@ impl NonNull { unsafe { NonNull { pointer: self.as_ptr() as *mut U } } } + /// Try to cast to a pointer of another type by checking aligment. + /// + /// If the pointer is properly aligned to the target type, it will be + /// cast to the target type. Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(pointer_try_cast_aligned)] + /// use std::ptr::NonNull; + /// + /// let mut x = 0u64; + /// + /// let aligned = NonNull::from_mut(&mut x); + /// let unaligned = unsafe { aligned.byte_add(1) }; + /// + /// assert!(aligned.try_cast_aligned::().is_some()); + /// assert!(unaligned.try_cast_aligned::().is_none()); + /// ``` + #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn try_cast_aligned(self) -> Option> { + if self.is_aligned_to(align_of::()) { Some(self.cast()) } else { None } + } + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -502,16 +530,16 @@ impl NonNull { /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// [allocation], and the entire memory range between `self` and the result must be in + /// bounds of that allocation. In particular, this range must not "wrap around" the edge /// of the address space. /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement. /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always /// safe. /// - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -578,16 +606,16 @@ impl NonNull { /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// [allocation], and the entire memory range between `self` and the result must be in + /// bounds of that allocation. In particular, this range must not "wrap around" the edge /// of the address space. /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement. /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always /// safe. /// - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -655,16 +683,16 @@ impl NonNull { /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some - /// [allocated object], and the entire memory range between `self` and the result must be in - /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// [allocation], and the entire memory range between `self` and the result must be in + /// bounds of that allocation. In particular, this range must not "wrap around" the edge /// of the address space. /// - /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset - /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// Allocations can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocation, it is guaranteed to satisfy the first requirement. /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always /// safe. /// - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Examples /// @@ -747,7 +775,7 @@ impl NonNull { /// * `self` and `origin` must either /// /// * point to the same address, or - /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// * both be *derived from* a pointer to the same [allocation], and the memory range between /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple @@ -755,10 +783,10 @@ impl NonNull { /// /// As a consequence, the absolute distance between the pointers, in bytes, computed on /// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is - /// implied by the in-bounds requirement, and the fact that no allocated object can be larger + /// implied by the in-bounds requirement, and the fact that no allocation can be larger /// than `isize::MAX` bytes. /// - /// The requirement for pointers to be derived from the same allocated object is primarily + /// The requirement for pointers to be derived from the same allocation is primarily /// needed for `const`-compatibility: the distance between pointers into *different* allocated /// objects is not known at compile-time. However, the requirement also exists at /// runtime and may be exploited by optimizations. If you wish to compute the difference between @@ -767,7 +795,7 @@ impl NonNull { // FIXME: recommend `addr()` instead of `as usize` once that is stable. /// /// [`add`]: #method.add - /// [allocated object]: crate::ptr#allocated-object + /// [allocation]: crate::ptr#allocation /// /// # Panics /// @@ -1447,8 +1475,8 @@ impl NonNull<[T]> { /// * The pointer must be [valid] for reads for `ptr.len() * size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single allocated object! - /// Slices can never span across multiple allocated objects. + /// * The entire memory range of this slice must be contained within a single allocation! + /// Slices can never span across multiple allocations. /// /// * The pointer must be aligned even for zero-length slices. One /// reason for this is that enum layout optimizations may rely on references @@ -1492,8 +1520,8 @@ impl NonNull<[T]> { /// * The pointer must be [valid] for reads and writes for `ptr.len() * size_of::()` /// many bytes, and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single allocated object! - /// Slices can never span across multiple allocated objects. + /// * The entire memory range of this slice must be contained within a single allocation! + /// Slices can never span across multiple allocations. /// /// * The pointer must be aligned even for zero-length slices. One /// reason for this is that enum layout optimizations may rely on references diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 736ffb7d0caf..23e32c2e0f01 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -858,6 +858,36 @@ impl Result { } } + /// Maps a `Result` to a `U` by applying function `f` to the contained + /// value if the result is [`Ok`], otherwise if [`Err`], returns the + /// [default value] for the type `U`. + /// + /// # Examples + /// + /// ``` + /// #![feature(result_option_map_or_default)] + /// + /// let x: Result<_, &str> = Ok("foo"); + /// let y: Result<&str, _> = Err("bar"); + /// + /// assert_eq!(x.map_or_default(|x| x.len()), 3); + /// assert_eq!(y.map_or_default(|y| y.len()), 0); + /// ``` + /// + /// [default value]: Default::default + #[inline] + #[unstable(feature = "result_option_map_or_default", issue = "138099")] + pub fn map_or_default(self, f: F) -> U + where + U: Default, + F: FnOnce(T) -> U, + { + match self { + Ok(t) => f(t), + Err(_) => U::default(), + } + } + /// Maps a `Result` to `Result` by applying a function to a /// contained [`Err`] value, leaving an [`Ok`] value untouched. /// @@ -1692,7 +1722,6 @@ impl Result, E> { /// # Examples /// /// ``` - /// #![feature(result_flattening)] /// let x: Result, u32> = Ok(Ok("hello")); /// assert_eq!(Ok("hello"), x.flatten()); /// @@ -1706,14 +1735,14 @@ impl Result, E> { /// Flattening only removes one level of nesting at a time: /// /// ``` - /// #![feature(result_flattening)] /// let x: Result, u32>, u32> = Ok(Ok(Ok("hello"))); /// assert_eq!(Ok(Ok("hello")), x.flatten()); /// assert_eq!(Ok("hello"), x.flatten().flatten()); /// ``` #[inline] - #[unstable(feature = "result_flattening", issue = "70142")] - #[rustc_const_unstable(feature = "result_flattening", issue = "70142")] + #[stable(feature = "result_flattening", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_stable(feature = "result_flattening", since = "CURRENT_RUSTC_VERSION")] pub const fn flatten(self) -> Result { // FIXME(const-hack): could be written with `and_then` match self { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index aafa19c0dd3d..69160a911b21 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,5 +1,7 @@ //! Indexing implementations for `[T]`. +#[cfg(not(bootstrap))] +use crate::intrinsics::slice_get_unchecked; use crate::panic::const_panic; use crate::ub_checks::assert_unsafe_precondition; use crate::{ops, range}; @@ -83,6 +85,7 @@ const fn slice_end_index_overflow_fail() -> ! { // Both the safe and unsafe public methods share these helpers, // which use intrinsics directly to get *no* extra checks. +#[cfg(bootstrap)] #[inline(always)] const unsafe fn get_noubcheck(ptr: *const [T], index: usize) -> *const T { let ptr = ptr as *const T; @@ -90,6 +93,7 @@ const unsafe fn get_noubcheck(ptr: *const [T], index: usize) -> *const T { unsafe { crate::intrinsics::offset(ptr, index) } } +#[cfg(bootstrap)] #[inline(always)] const unsafe fn get_mut_noubcheck(ptr: *mut [T], index: usize) -> *mut T { let ptr = ptr as *mut T; @@ -103,8 +107,9 @@ const unsafe fn get_offset_len_noubcheck( offset: usize, len: usize, ) -> *const [T] { + let ptr = ptr as *const T; // SAFETY: The caller already checked these preconditions - let ptr = unsafe { get_noubcheck(ptr, offset) }; + let ptr = unsafe { crate::intrinsics::offset(ptr, offset) }; crate::intrinsics::aggregate_raw_ptr(ptr, len) } @@ -114,8 +119,9 @@ const unsafe fn get_offset_len_mut_noubcheck( offset: usize, len: usize, ) -> *mut [T] { + let ptr = ptr as *mut T; // SAFETY: The caller already checked these preconditions - let ptr = unsafe { get_mut_noubcheck(ptr, offset) }; + let ptr = unsafe { crate::intrinsics::offset(ptr, offset) }; crate::intrinsics::aggregate_raw_ptr(ptr, len) } @@ -161,7 +167,7 @@ mod private_slice_index { #[rustc_on_unimplemented( on(T = "str", label = "string indices are ranges of `usize`",), on( - all(any(T = "str", T = "&str", T = "alloc::string::String"), _Self = "{integer}"), + all(any(T = "str", T = "&str", T = "alloc::string::String"), Self = "{integer}"), note = "you can use `.chars().nth()` or `.bytes().nth()`\n\ for more information, see chapter 8 in The Book: \ " @@ -224,21 +230,42 @@ unsafe impl SliceIndex<[T]> for usize { #[inline] fn get(self, slice: &[T]) -> Option<&T> { - // SAFETY: `self` is checked to be in bounds. - if self < slice.len() { unsafe { Some(&*get_noubcheck(slice, self)) } } else { None } - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut T> { if self < slice.len() { + #[cfg(bootstrap)] // SAFETY: `self` is checked to be in bounds. - unsafe { Some(&mut *get_mut_noubcheck(slice, self)) } + unsafe { + Some(&*get_noubcheck(slice, self)) + } + #[cfg(not(bootstrap))] + // SAFETY: `self` is checked to be in bounds. + unsafe { + Some(slice_get_unchecked(slice, self)) + } } else { None } } #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut T> { + if self < slice.len() { + #[cfg(bootstrap)] + // SAFETY: `self` is checked to be in bounds. + unsafe { + Some(&mut *get_mut_noubcheck(slice, self)) + } + #[cfg(not(bootstrap))] + // SAFETY: `self` is checked to be in bounds. + unsafe { + Some(slice_get_unchecked(slice, self)) + } + } else { + None + } + } + + #[inline] + #[track_caller] unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { assert_unsafe_precondition!( check_language_ub, @@ -253,11 +280,19 @@ unsafe impl SliceIndex<[T]> for usize { // Use intrinsics::assume instead of hint::assert_unchecked so that we don't check the // precondition of this function twice. crate::intrinsics::assume(self < slice.len()); - get_noubcheck(slice, self) + #[cfg(bootstrap)] + { + get_noubcheck(slice, self) + } + #[cfg(not(bootstrap))] + { + slice_get_unchecked(slice, self) + } } } #[inline] + #[track_caller] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { assert_unsafe_precondition!( check_library_ub, @@ -265,7 +300,16 @@ unsafe impl SliceIndex<[T]> for usize { (this: usize = self, len: usize = slice.len()) => this < len ); // SAFETY: see comments for `get_unchecked` above. - unsafe { get_mut_noubcheck(slice, self) } + unsafe { + #[cfg(bootstrap)] + { + get_mut_noubcheck(slice, self) + } + #[cfg(not(bootstrap))] + { + slice_get_unchecked(slice, self) + } + } } #[inline] @@ -307,6 +351,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { } #[inline] + #[track_caller] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { assert_unsafe_precondition!( check_library_ub, @@ -321,6 +366,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { } #[inline] + #[track_caller] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { assert_unsafe_precondition!( check_library_ub, @@ -386,6 +432,7 @@ unsafe impl SliceIndex<[T]> for ops::Range { } #[inline] + #[track_caller] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { assert_unsafe_precondition!( check_library_ub, @@ -410,6 +457,7 @@ unsafe impl SliceIndex<[T]> for ops::Range { } #[inline] + #[track_caller] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { assert_unsafe_precondition!( check_library_ub, diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 058491b53a16..4f7e14408804 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -631,6 +631,7 @@ impl [T] { #[rustc_no_implicit_autorefs] #[inline] #[must_use] + #[track_caller] pub unsafe fn get_unchecked(&self, index: I) -> &I::Output where I: SliceIndex, @@ -674,6 +675,7 @@ impl [T] { #[rustc_no_implicit_autorefs] #[inline] #[must_use] + #[track_caller] pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output where I: SliceIndex, @@ -935,6 +937,7 @@ impl [T] { /// [`swap`]: slice::swap /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[unstable(feature = "slice_swap_unchecked", issue = "88539")] + #[track_caller] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { assert_unsafe_precondition!( check_library_ub, @@ -1307,6 +1310,7 @@ impl [T] { #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[must_use] + #[track_caller] pub const unsafe fn as_chunks_unchecked(&self) -> &[[T; N]] { assert_unsafe_precondition!( check_language_ub, @@ -1502,6 +1506,7 @@ impl [T] { #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[must_use] + #[track_caller] pub const unsafe fn as_chunks_unchecked_mut(&mut self) -> &mut [[T; N]] { assert_unsafe_precondition!( check_language_ub, @@ -2061,6 +2066,7 @@ impl [T] { #[rustc_const_stable(feature = "const_slice_split_at_unchecked", since = "1.77.0")] #[inline] #[must_use] + #[track_caller] pub const unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) { // FIXME(const-hack): the const function `from_raw_parts` is used to make this // function const; previously the implementation used @@ -2114,6 +2120,7 @@ impl [T] { #[rustc_const_stable(feature = "const_slice_split_at_mut", since = "1.83.0")] #[inline] #[must_use] + #[track_caller] pub const unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut [T], &mut [T]) { let len = self.len(); let ptr = self.as_mut_ptr(); @@ -2986,7 +2993,7 @@ impl [T] { self.binary_search_by(|k| f(k).cmp(b)) } - /// Sorts the slice **without** preserving the initial order of equal elements. + /// Sorts the slice in ascending order **without** preserving the initial order of equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. @@ -3047,8 +3054,8 @@ impl [T] { sort::unstable::sort(self, &mut T::lt); } - /// Sorts the slice with a comparison function, **without** preserving the initial order of - /// equal elements. + /// Sorts the slice in ascending order with a comparison function, **without** preserving the + /// initial order of equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. @@ -3102,8 +3109,8 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| compare(a, b) == Ordering::Less); } - /// Sorts the slice with a key extraction function, **without** preserving the initial order of - /// equal elements. + /// Sorts the slice in ascending order with a key extraction function, **without** preserving + /// the initial order of equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. @@ -4383,7 +4390,7 @@ impl [T] { /// assert_eq!(first_three, &['a', 'b', 'c']); /// ``` /// - /// Splitting off the last two elements of a slice: + /// Splitting off a slice starting with the third element: /// /// ``` /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; @@ -4449,7 +4456,7 @@ impl [T] { /// assert_eq!(first_three, &mut ['a', 'b', 'c']); /// ``` /// - /// Taking the last two elements of a slice: + /// Splitting off a slice starting with the third element: /// /// ``` /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; @@ -4642,6 +4649,7 @@ impl [T] { /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[stable(feature = "get_many_mut", since = "1.86.0")] #[inline] + #[track_caller] pub unsafe fn get_disjoint_unchecked_mut( &mut self, indices: [I; N], diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 3582c7e8b3f3..eba2f89a169a 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -14,8 +14,8 @@ use crate::{array, ptr, ub_checks}; /// * `data` must be non-null, [valid] for reads for `len * size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// -/// * The entire memory range of this slice must be contained within a single allocated object! -/// Slices can never span across multiple allocated objects. See [below](#incorrect-usage) +/// * The entire memory range of this slice must be contained within a single allocation! +/// Slices can never span across multiple allocations. See [below](#incorrect-usage) /// for an example incorrectly not taking this into account. /// * `data` must be non-null and aligned even for zero-length slices or slices of ZSTs. One /// reason for this is that enum layout optimizations may rely on references @@ -65,14 +65,14 @@ use crate::{array, ptr, ub_checks}; /// assert_eq!(fst_end, snd_start, "Slices must be contiguous!"); /// unsafe { /// // The assertion above ensures `fst` and `snd` are contiguous, but they might -/// // still be contained within _different allocated objects_, in which case +/// // still be contained within _different allocations_, in which case /// // creating this slice is undefined behavior. /// slice::from_raw_parts(fst.as_ptr(), fst.len() + snd.len()) /// } /// } /// /// fn main() { -/// // `a` and `b` are different allocated objects... +/// // `a` and `b` are different allocations... /// let a = 42; /// let b = 27; /// // ... which may nevertheless be laid out contiguously in memory: | a | b | @@ -120,6 +120,7 @@ use crate::{array, ptr, ub_checks}; #[rustc_const_stable(feature = "const_slice_from_raw_parts", since = "1.64.0")] #[must_use] #[rustc_diagnostic_item = "slice_from_raw_parts"] +#[track_caller] pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { @@ -149,8 +150,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] /// * `data` must be non-null, [valid] for both reads and writes for `len * size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// -/// * The entire memory range of this slice must be contained within a single allocated object! -/// Slices can never span across multiple allocated objects. +/// * The entire memory range of this slice must be contained within a single allocation! +/// Slices can never span across multiple allocations. /// * `data` must be non-null and aligned even for zero-length slices or slices of ZSTs. One /// reason for this is that enum layout optimizations may rely on references /// (including slices of any length) being aligned and non-null to distinguish @@ -174,6 +175,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] #[rustc_const_stable(feature = "const_slice_from_raw_parts_mut", since = "1.83.0")] #[must_use] #[rustc_diagnostic_item = "slice_from_raw_parts_mut"] +#[track_caller] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { @@ -226,8 +228,8 @@ pub const fn from_mut(s: &mut T) -> &mut [T] { /// the last element, such that the offset from the end to the start pointer is /// the length of the slice. /// -/// * The entire memory range of this slice must be contained within a single allocated object! -/// Slices can never span across multiple allocated objects. +/// * The entire memory range of this slice must be contained within a single allocation! +/// Slices can never span across multiple allocations. /// /// * The range must contain `N` consecutive properly initialized values of type `T`. /// @@ -270,6 +272,7 @@ pub const fn from_mut(s: &mut T) -> &mut [T] { /// [valid]: ptr#safety #[unstable(feature = "slice_from_ptr_range", issue = "89792")] #[rustc_const_unstable(feature = "const_slice_from_ptr_range", issue = "89792")] +#[track_caller] pub const unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_ptr_range`. unsafe { from_raw_parts(range.start, range.end.offset_from_unsigned(range.start)) } @@ -295,8 +298,8 @@ pub const unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] { /// the last element, such that the offset from the end to the start pointer is /// the length of the slice. /// -/// * The entire memory range of this slice must be contained within a single allocated object! -/// Slices can never span across multiple allocated objects. +/// * The entire memory range of this slice must be contained within a single allocation! +/// Slices can never span across multiple allocations. /// /// * The range must contain `N` consecutive properly initialized values of type `T`. /// diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index c4808b1065d0..82194db7fd86 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -6,7 +6,7 @@ //! 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::cfg_match; +use crate::cfg_select; use crate::mem::{self, SizedTypeProperties}; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; @@ -42,7 +42,7 @@ where let min_idx = min_index(v, &mut is_less).unwrap(); v.swap(min_idx, index); } else { - cfg_match! { + cfg_select! { feature = "optimize_for_size" => { median_of_medians(v, &mut is_less, index); } diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index a36e5f7801d4..8b4e5c0c8c3a 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -7,7 +7,7 @@ use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::slice::sort::shared::smallsort::{ SMALL_SORT_GENERAL_SCRATCH_LEN, StableSmallSortTypeImpl, insertion_sort_shift_left, }; -use crate::{cfg_match, intrinsics}; +use crate::{cfg_select, intrinsics}; pub(crate) mod merge; @@ -39,13 +39,13 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less return; } - cfg_match! { + cfg_select! { any(feature = "optimize_for_size", target_pointer_width = "16") => { // Unlike driftsort, mergesort only requires len / 2, // not len - len / 2. let alloc_len = len / 2; - cfg_match! { + cfg_select! { target_pointer_width = "16" => { let mut heap_buf = BufT::with_capacity(alloc_len); let scratch = heap_buf.as_uninit_slice_mut(); diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index b6c2e05a06a0..d4df8d3a264d 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -5,7 +5,7 @@ use crate::mem::SizedTypeProperties; use crate::slice::sort::shared::find_existing_run; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; -use crate::{cfg_match, intrinsics}; +use crate::{cfg_select, intrinsics}; pub(crate) mod heapsort; pub(crate) mod quicksort; @@ -30,7 +30,7 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { return; } - cfg_match! { + cfg_select! { any(feature = "optimize_for_size", target_pointer_width = "16") => { heapsort::heapsort(v, is_less); } diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index 7e6cfb559905..98efee242eba 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -9,7 +9,7 @@ use crate::slice::sort::shared::pivot::choose_pivot; use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::unstable::heapsort; -use crate::{cfg_match, intrinsics, ptr}; +use crate::{cfg_select, intrinsics, ptr}; /// Sorts `v` recursively. /// @@ -142,7 +142,7 @@ const fn inst_partition bool>() -> fn(&mut [T], &T, &mut if 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_match! { + cfg_select! { feature = "optimize_for_size" => { partition_lomuto_branchless_simple:: } diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index 058628797ea8..6da9dce2d870 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -6,6 +6,8 @@ use crate::{mem, ptr}; /// Converts a slice of bytes to a string slice. /// +/// This is an alias to [`str::from_utf8`]. +/// /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice /// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between /// the two. Not all byte slices are valid string slices, however: [`&str`] requires @@ -97,6 +99,8 @@ pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { /// Converts a mutable slice of bytes to a mutable string slice. /// +/// This is an alias to [`str::from_utf8_mut`]. +/// /// # Examples /// /// Basic usage: @@ -142,6 +146,8 @@ pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { /// Converts a slice of bytes to a string slice without checking /// that the string contains valid UTF-8. /// +/// This is an alias to [`str::from_utf8_unchecked`]. +/// /// See the safe version, [`from_utf8`], for more information. /// /// # Safety @@ -178,6 +184,8 @@ pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { /// Converts a slice of bytes to a string slice without checking /// that the string contains valid UTF-8; mutable version. /// +/// This is an alias to [`str::from_utf8_unchecked_mut`]. +/// /// See the immutable version, [`from_utf8_unchecked()`] for documentation and safety requirements. /// /// # Examples diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index e505e2280953..06161cb6c7ce 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -1173,6 +1173,7 @@ impl str { /// The iterator returned will return string slices that are sub-slices of /// the original string slice, separated by any amount of ASCII whitespace. /// + /// This uses the same definition as [`char::is_ascii_whitespace`]. /// To split by Unicode `Whitespace` instead, use [`split_whitespace`]. /// /// [`split_whitespace`]: str::split_whitespace @@ -1191,7 +1192,8 @@ impl str { /// assert_eq!(None, iter.next()); /// ``` /// - /// All kinds of ASCII whitespace are considered: + /// Various kinds of ASCII whitespace are considered + /// (see [`char::is_ascii_whitespace`]): /// /// ``` /// let mut iter = " Mary had\ta little \n\t lamb".split_ascii_whitespace(); diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 77c70b978fd1..4baf9aacad7b 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -186,6 +186,7 @@ unsafe impl SliceIndex for ops::Range { } } #[inline] + #[track_caller] unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { let slice = slice as *const [u8]; @@ -213,6 +214,7 @@ unsafe impl SliceIndex for ops::Range { } } #[inline] + #[track_caller] unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { let slice = slice as *mut [u8]; @@ -288,6 +290,7 @@ unsafe impl SliceIndex for range::Range { } } #[inline] + #[track_caller] unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { let slice = slice as *const [u8]; @@ -315,6 +318,7 @@ unsafe impl SliceIndex for range::Range { } } #[inline] + #[track_caller] unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { let slice = slice as *mut [u8]; diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 84c7f1aafe1b..b43f3bad6e2c 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -193,7 +193,7 @@ //! //! A simple spinlock: //! -//! ``` +//! ```ignore-wasm //! use std::sync::Arc; //! use std::sync::atomic::{AtomicUsize, Ordering}; //! use std::{hint, thread}; @@ -622,7 +622,7 @@ impl AtomicBool { /// /// # Examples /// - /// ``` + /// ```ignore-wasm /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// @@ -653,7 +653,7 @@ impl AtomicBool { /// /// # Examples /// - /// ``` + /// ```rust,ignore-wasm /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// @@ -1548,7 +1548,7 @@ impl AtomicPtr { /// /// # Examples /// - /// ``` + /// ```ignore-wasm /// #![feature(atomic_from_mut)] /// use std::ptr::null_mut; /// use std::sync::atomic::{AtomicPtr, Ordering}; @@ -1585,7 +1585,7 @@ impl AtomicPtr { /// /// # Examples /// - /// ``` + /// ```ignore-wasm /// #![feature(atomic_from_mut)] /// use std::ptr::null_mut; /// use std::sync::atomic::{AtomicPtr, Ordering}; @@ -2692,7 +2692,7 @@ macro_rules! atomic_int { /// /// # Examples /// - /// ``` + /// ```ignore-wasm /// #![feature(atomic_from_mut)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// @@ -2725,7 +2725,7 @@ macro_rules! atomic_int { /// /// # Examples /// - /// ``` + /// ```ignore-wasm /// #![feature(atomic_from_mut)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// @@ -3822,6 +3822,7 @@ unsafe fn atomic_store(dst: *mut T, val: T, order: Ordering) { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[cfg(bootstrap)] unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { // SAFETY: the caller must uphold the safety contract for `atomic_load`. unsafe { @@ -3835,6 +3836,23 @@ unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { } } +#[inline] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[cfg(not(bootstrap))] +unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { + use intrinsics::AtomicOrdering; + // SAFETY: the caller must uphold the safety contract for `atomic_load`. + unsafe { + match order { + Relaxed => intrinsics::atomic_load::(dst), + Acquire => intrinsics::atomic_load::(dst), + SeqCst => intrinsics::atomic_load::(dst), + Release => panic!("there is no such thing as a release load"), + AcqRel => panic!("there is no such thing as an acquire-release load"), + } + } +} + #[inline] #[cfg(target_has_atomic)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -3885,10 +3903,13 @@ unsafe fn atomic_sub(dst: *mut T, val: T, order: Ordering) -> T { } } +/// Publicly exposed for stdarch; nobody else should use this. #[inline] #[cfg(target_has_atomic)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -unsafe fn atomic_compare_exchange( +#[unstable(feature = "core_intrinsics", issue = "none")] +#[doc(hidden)] +pub unsafe fn atomic_compare_exchange( dst: *mut T, old: T, new: T, diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index 9eb71922218f..a7caaeb95cdb 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -63,11 +63,13 @@ macro_rules! assert_unsafe_precondition { #[rustc_no_mir_inline] #[inline] #[rustc_nounwind] + #[track_caller] const fn precondition_check($($name:$ty),*) { if !$e { - ::core::panicking::panic_nounwind(concat!("unsafe precondition(s) violated: ", $message, + let msg = concat!("unsafe precondition(s) violated: ", $message, "\n\nThis indicates a bug in the program. \ - This Undefined Behavior check is optional, and cannot be relied on for safety.")); + This Undefined Behavior check is optional, and cannot be relied on for safety."); + ::core::panicking::panic_nounwind_fmt(::core::fmt::Arguments::new_const(&[msg]), false); } } diff --git a/library/coretests/tests/ffi/cstr.rs b/library/coretests/tests/ffi/cstr.rs index 0d85b22c585a..dc34240cd99d 100644 --- a/library/coretests/tests/ffi/cstr.rs +++ b/library/coretests/tests/ffi/cstr.rs @@ -19,3 +19,9 @@ fn debug() { let s = c"abc\x01\x02\n\xE2\x80\xA6\xFF"; assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#); } + +#[test] +fn display() { + let s = c"\xf0\x28\x8c\xbc"; + assert_eq!(format!("{}", s.display()), "�(��"); +} diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 12cf651f03f4..01770f119df1 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -1,12 +1,9 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(target_has_reliable_f128)] +use core::ops::{Add, Div, Mul, Sub}; use std::f128::consts; use std::num::FpCategory as Fp; -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -use std::ops::Rem; -use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. @@ -39,68 +36,46 @@ const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; /// Second pattern over the mantissa const NAN_MASK2: u128 = 0x00005555555555555555555555555555; -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f128_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f128, &f128) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); - }; -} - #[test] fn test_num_f128() { // FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` // function is available on all platforms. let ten = 10f128; let two = 2f128; - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); + assert_biteq!(ten.add(two), ten + two); + assert_biteq!(ten.sub(two), ten - two); + assert_biteq!(ten.mul(two), ten * two); + assert_biteq!(ten.div(two), ten / two); + #[cfg(any(miri, target_has_reliable_f128_math))] + assert_biteq!(core::ops::Rem::rem(ten, two), ten % two); } // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_num_f128_rem() { - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.rem(two), ten % two); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_min_nan() { - assert_eq!(f128::NAN.min(2.0), 2.0); - assert_eq!(2.0f128.min(f128::NAN), 2.0); + assert_biteq!(f128::NAN.min(2.0), 2.0); + assert_biteq!(2.0f128.min(f128::NAN), 2.0); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_max_nan() { - assert_eq!(f128::NAN.max(2.0), 2.0); - assert_eq!(2.0f128.max(f128::NAN), 2.0); + assert_biteq!(f128::NAN.max(2.0), 2.0); + assert_biteq!(2.0f128.max(f128::NAN), 2.0); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_minimum() { assert!(f128::NAN.minimum(2.0).is_nan()); assert!(2.0f128.minimum(f128::NAN).is_nan()); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_maximum() { assert!(f128::NAN.maximum(2.0).is_nan()); assert!(2.0f128.maximum(f128::NAN).is_nan()); @@ -147,7 +122,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f128 = 0.0f128; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -161,6 +136,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f128 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -173,7 +149,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f128 = 1.0f128; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -257,114 +233,107 @@ fn test_classify() { } #[test] -#[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] fn test_floor() { - assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); + assert_biteq!(1.0f128.floor(), 1.0f128); + assert_biteq!(1.3f128.floor(), 1.0f128); + assert_biteq!(1.5f128.floor(), 1.0f128); + assert_biteq!(1.7f128.floor(), 1.0f128); + assert_biteq!(0.0f128.floor(), 0.0f128); + assert_biteq!((-0.0f128).floor(), -0.0f128); + assert_biteq!((-1.0f128).floor(), -1.0f128); + assert_biteq!((-1.3f128).floor(), -2.0f128); + assert_biteq!((-1.5f128).floor(), -2.0f128); + assert_biteq!((-1.7f128).floor(), -2.0f128); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_ceil() { - assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); + assert_biteq!(1.0f128.ceil(), 1.0f128); + assert_biteq!(1.3f128.ceil(), 2.0f128); + assert_biteq!(1.5f128.ceil(), 2.0f128); + assert_biteq!(1.7f128.ceil(), 2.0f128); + assert_biteq!(0.0f128.ceil(), 0.0f128); + assert_biteq!((-0.0f128).ceil(), -0.0f128); + assert_biteq!((-1.0f128).ceil(), -1.0f128); + assert_biteq!((-1.3f128).ceil(), -1.0f128); + assert_biteq!((-1.5f128).ceil(), -1.0f128); + assert_biteq!((-1.7f128).ceil(), -1.0f128); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_round() { - assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); + assert_biteq!(2.5f128.round(), 3.0f128); + assert_biteq!(1.0f128.round(), 1.0f128); + assert_biteq!(1.3f128.round(), 1.0f128); + assert_biteq!(1.5f128.round(), 2.0f128); + assert_biteq!(1.7f128.round(), 2.0f128); + assert_biteq!(0.0f128.round(), 0.0f128); + assert_biteq!((-0.0f128).round(), -0.0f128); + assert_biteq!((-1.0f128).round(), -1.0f128); + assert_biteq!((-1.3f128).round(), -1.0f128); + assert_biteq!((-1.5f128).round(), -2.0f128); + assert_biteq!((-1.7f128).round(), -2.0f128); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_round_ties_even() { - assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_biteq!(2.5f128.round_ties_even(), 2.0f128); + assert_biteq!(1.0f128.round_ties_even(), 1.0f128); + assert_biteq!(1.3f128.round_ties_even(), 1.0f128); + assert_biteq!(1.5f128.round_ties_even(), 2.0f128); + assert_biteq!(1.7f128.round_ties_even(), 2.0f128); + assert_biteq!(0.0f128.round_ties_even(), 0.0f128); + assert_biteq!((-0.0f128).round_ties_even(), -0.0f128); + assert_biteq!((-1.0f128).round_ties_even(), -1.0f128); + assert_biteq!((-1.3f128).round_ties_even(), -1.0f128); + assert_biteq!((-1.5f128).round_ties_even(), -2.0f128); + assert_biteq!((-1.7f128).round_ties_even(), -2.0f128); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_trunc() { - assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); + assert_biteq!(1.0f128.trunc(), 1.0f128); + assert_biteq!(1.3f128.trunc(), 1.0f128); + assert_biteq!(1.5f128.trunc(), 1.0f128); + assert_biteq!(1.7f128.trunc(), 1.0f128); + assert_biteq!(0.0f128.trunc(), 0.0f128); + assert_biteq!((-0.0f128).trunc(), -0.0f128); + assert_biteq!((-1.0f128).trunc(), -1.0f128); + assert_biteq!((-1.3f128).trunc(), -1.0f128); + assert_biteq!((-1.5f128).trunc(), -1.0f128); + assert_biteq!((-1.7f128).trunc(), -1.0f128); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_fract() { - assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); + assert_biteq!(1.0f128.fract(), 0.0f128); + assert_biteq!(1.3f128.fract(), 0.300000000000000000000000000000000039f128); + assert_biteq!(1.5f128.fract(), 0.5f128); + assert_biteq!(1.7f128.fract(), 0.7f128); + assert_biteq!(0.0f128.fract(), 0.0f128); + assert_biteq!((-0.0f128).fract(), 0.0f128); + assert_biteq!((-1.0f128).fract(), 0.0f128); + assert_biteq!((-1.3f128).fract(), -0.300000000000000000000000000000000039f128); + assert_biteq!((-1.5f128).fract(), -0.5f128); + assert_biteq!((-1.7f128).fract(), -0.699999999999999999999999999999999961f128); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_abs() { - assert_eq!(f128::INFINITY.abs(), f128::INFINITY); - assert_eq!(1f128.abs(), 1f128); - assert_eq!(0f128.abs(), 0f128); - assert_eq!((-0f128).abs(), 0f128); - assert_eq!((-1f128).abs(), 1f128); - assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); - assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); + assert_biteq!(f128::INFINITY.abs(), f128::INFINITY); + assert_biteq!(1f128.abs(), 1f128); + assert_biteq!(0f128.abs(), 0f128); + assert_biteq!((-0f128).abs(), 0f128); + assert_biteq!((-1f128).abs(), 1f128); + assert_biteq!(f128::NEG_INFINITY.abs(), f128::INFINITY); + assert_biteq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); assert!(f128::NAN.abs().is_nan()); } @@ -401,27 +370,27 @@ fn test_next_up() { let max_down = f128::from_bits(MAX_DOWN_BITS); let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); - assert_f128_biteq!(f128::MIN.next_up(), -max_down); - assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); - assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f128_biteq!((-tiny_up).next_up(), -tiny); - assert_f128_biteq!((-tiny).next_up(), -0.0f128); - assert_f128_biteq!((-0.0f128).next_up(), tiny); - assert_f128_biteq!(0.0f128.next_up(), tiny); - assert_f128_biteq!(tiny.next_up(), tiny_up); - assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); - assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); - assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); + assert_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); + assert_biteq!(f128::MIN.next_up(), -max_down); + assert_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0f128); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f128); + assert_biteq!((-0.0f128).next_up(), tiny); + assert_biteq!(0.0f128.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + assert_biteq!(f128::MAX.next_up(), f128::INFINITY); + assert_biteq!(f128::INFINITY.next_up(), f128::INFINITY); // Check that NaNs roundtrip. let nan0 = f128::NAN; let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_up(), nan0); - assert_f128_biteq!(nan1.next_up(), nan1); - assert_f128_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -431,28 +400,28 @@ fn test_next_down() { let max_down = f128::from_bits(MAX_DOWN_BITS); let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!((-max_down).next_down(), f128::MIN); - assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); - assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f128_biteq!((-tiny).next_down(), -tiny_up); - assert_f128_biteq!((-0.0f128).next_down(), -tiny); - assert_f128_biteq!((0.0f128).next_down(), -tiny); - assert_f128_biteq!(tiny.next_down(), 0.0f128); - assert_f128_biteq!(tiny_up.next_down(), tiny); - assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); - assert_f128_biteq!(f128::MAX.next_down(), max_down); - assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); + assert_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); + assert_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f128::MIN); + assert_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f128).next_down(), -tiny); + assert_biteq!((0.0f128).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f128); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); + assert_biteq!(f128::MAX.next_down(), max_down); + assert_biteq!(f128::INFINITY.next_down(), f128::MAX); // Check that NaNs roundtrip. let nan0 = f128::NAN; let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_down(), nan0); - assert_f128_biteq!(nan1.next_down(), nan1); - assert_f128_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } #[test] @@ -462,36 +431,35 @@ fn test_mul_add() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); - assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); - assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); - assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert_biteq!(12.3f128.mul_add(4.5, 6.7), 62.0500000000000000000000000000000037); + assert_biteq!((-12.3f128).mul_add(-4.5, -6.7), 48.6500000000000000000000000000000049); + assert_biteq!(0.0f128.mul_add(8.9, 1.2), 1.2); + assert_biteq!(3.4f128.mul_add(-0.0, 5.6), 5.6); assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f128.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); + assert_biteq!(inf.mul_add(7.8, 9.0), inf); + assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_biteq!(8.9f128.mul_add(inf, 3.2), inf); + assert_biteq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.recip(), 1.0); - assert_eq!(2.0f128.recip(), 0.5); - assert_eq!((-0.4f128).recip(), -2.5); - assert_eq!(0.0f128.recip(), inf); + assert_biteq!(1.0f128.recip(), 1.0); + assert_biteq!(2.0f128.recip(), 0.5); + assert_biteq!((-0.4f128).recip(), -2.5); + assert_biteq!(0.0f128.recip(), inf); assert_approx_eq!( f128::MAX.recip(), 8.40525785778023376565669454330438228902076605e-4933, 1e-4900 ); assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -501,13 +469,13 @@ fn test_powi() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powi(1), 1.0); + assert_biteq!(1.0f128.powi(1), 1.0); assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); - assert_eq!(8.3f128.powi(0), 1.0); + assert_biteq!(8.3f128.powi(0), 1.0); assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -517,10 +485,10 @@ fn test_sqrt_domain() { assert!(f128::NAN.sqrt().is_nan()); assert!(f128::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f128).sqrt().is_nan()); - assert_eq!((-0.0f128).sqrt(), -0.0); - assert_eq!(0.0f128.sqrt(), 0.0); - assert_eq!(1.0f128.sqrt(), 1.0); - assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); + assert_biteq!((-0.0f128).sqrt(), -0.0); + assert_biteq!(0.0f128.sqrt(), 0.0); + assert_biteq!(1.0f128.sqrt(), 1.0); + assert_biteq!(f128::INFINITY.sqrt(), f128::INFINITY); } #[test] @@ -529,13 +497,13 @@ fn test_to_degrees() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_degrees(), 0.0); + assert_biteq!(0.0f128.to_degrees(), 0.0); assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); assert_approx_eq!(pi.to_degrees(), 180.0, TOL); assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); } #[test] @@ -544,15 +512,15 @@ fn test_to_radians() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_radians(), 0.0); + assert_biteq!(0.0f128.to_radians(), 0.0); assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); // check approx rather than exact because round trip for pi doesn't fall on an exactly // representable value (unlike `f32` and `f64`). assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -561,10 +529,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); + assert_biteq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); + assert_biteq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); + assert_biteq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); + assert_biteq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits @@ -760,26 +728,26 @@ fn test_algebraic() { #[test] fn test_from() { - assert_eq!(f128::from(false), 0.0); - assert_eq!(f128::from(true), 1.0); - assert_eq!(f128::from(u8::MIN), 0.0); - assert_eq!(f128::from(42_u8), 42.0); - assert_eq!(f128::from(u8::MAX), 255.0); - assert_eq!(f128::from(i8::MIN), -128.0); - assert_eq!(f128::from(42_i8), 42.0); - assert_eq!(f128::from(i8::MAX), 127.0); - assert_eq!(f128::from(u16::MIN), 0.0); - assert_eq!(f128::from(42_u16), 42.0); - assert_eq!(f128::from(u16::MAX), 65535.0); - assert_eq!(f128::from(i16::MIN), -32768.0); - assert_eq!(f128::from(42_i16), 42.0); - assert_eq!(f128::from(i16::MAX), 32767.0); - assert_eq!(f128::from(u32::MIN), 0.0); - assert_eq!(f128::from(42_u32), 42.0); - assert_eq!(f128::from(u32::MAX), 4294967295.0); - assert_eq!(f128::from(i32::MIN), -2147483648.0); - assert_eq!(f128::from(42_i32), 42.0); - assert_eq!(f128::from(i32::MAX), 2147483647.0); + assert_biteq!(f128::from(false), 0.0); + assert_biteq!(f128::from(true), 1.0); + assert_biteq!(f128::from(u8::MIN), 0.0); + assert_biteq!(f128::from(42_u8), 42.0); + assert_biteq!(f128::from(u8::MAX), 255.0); + assert_biteq!(f128::from(i8::MIN), -128.0); + assert_biteq!(f128::from(42_i8), 42.0); + assert_biteq!(f128::from(i8::MAX), 127.0); + assert_biteq!(f128::from(u16::MIN), 0.0); + assert_biteq!(f128::from(42_u16), 42.0); + assert_biteq!(f128::from(u16::MAX), 65535.0); + assert_biteq!(f128::from(i16::MIN), -32768.0); + assert_biteq!(f128::from(42_i16), 42.0); + assert_biteq!(f128::from(i16::MAX), 32767.0); + assert_biteq!(f128::from(u32::MIN), 0.0); + assert_biteq!(f128::from(42_u32), 42.0); + assert_biteq!(f128::from(u32::MAX), 4294967295.0); + assert_biteq!(f128::from(i32::MIN), -2147483648.0); + assert_biteq!(f128::from(42_i32), 42.0); + assert_biteq!(f128::from(i32::MAX), 2147483647.0); // FIXME(f16_f128): Uncomment these tests once the From<{u64,i64}> impls are added. // assert_eq!(f128::from(u64::MIN), 0.0); // assert_eq!(f128::from(42_u64), 42.0); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index db98181226c8..4797573f7d0d 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -41,17 +41,6 @@ const NAN_MASK1: u16 = 0x02aa; /// Second pattern over the mantissa const NAN_MASK2: u16 = 0x0155; -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f16_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f16, &f16) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); - }; -} - #[test] fn test_num_f16() { super::test_num(10f16, 2f16); @@ -61,32 +50,28 @@ fn test_num_f16() { // the intrinsics. #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_min_nan() { - assert_eq!(f16::NAN.min(2.0), 2.0); - assert_eq!(2.0f16.min(f16::NAN), 2.0); + assert_biteq!(f16::NAN.min(2.0), 2.0); + assert_biteq!(2.0f16.min(f16::NAN), 2.0); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_max_nan() { - assert_eq!(f16::NAN.max(2.0), 2.0); - assert_eq!(2.0f16.max(f16::NAN), 2.0); + assert_biteq!(f16::NAN.max(2.0), 2.0); + assert_biteq!(2.0f16.max(f16::NAN), 2.0); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_minimum() { assert!(f16::NAN.minimum(2.0).is_nan()); assert!(2.0f16.minimum(f16::NAN).is_nan()); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_maximum() { assert!(f16::NAN.maximum(2.0).is_nan()); assert!(2.0f16.maximum(f16::NAN).is_nan()); @@ -133,7 +118,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f16 = 0.0f16; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -147,6 +132,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f16 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -159,7 +145,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f16 = 1.0f16; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -243,114 +229,107 @@ fn test_classify() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_floor() { - assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); + assert_biteq!(1.0f16.floor(), 1.0f16); + assert_biteq!(1.3f16.floor(), 1.0f16); + assert_biteq!(1.5f16.floor(), 1.0f16); + assert_biteq!(1.7f16.floor(), 1.0f16); + assert_biteq!(0.0f16.floor(), 0.0f16); + assert_biteq!((-0.0f16).floor(), -0.0f16); + assert_biteq!((-1.0f16).floor(), -1.0f16); + assert_biteq!((-1.3f16).floor(), -2.0f16); + assert_biteq!((-1.5f16).floor(), -2.0f16); + assert_biteq!((-1.7f16).floor(), -2.0f16); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_ceil() { - assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); + assert_biteq!(1.0f16.ceil(), 1.0f16); + assert_biteq!(1.3f16.ceil(), 2.0f16); + assert_biteq!(1.5f16.ceil(), 2.0f16); + assert_biteq!(1.7f16.ceil(), 2.0f16); + assert_biteq!(0.0f16.ceil(), 0.0f16); + assert_biteq!((-0.0f16).ceil(), -0.0f16); + assert_biteq!((-1.0f16).ceil(), -1.0f16); + assert_biteq!((-1.3f16).ceil(), -1.0f16); + assert_biteq!((-1.5f16).ceil(), -1.0f16); + assert_biteq!((-1.7f16).ceil(), -1.0f16); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_round() { - assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); - assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); + assert_biteq!(2.5f16.round(), 3.0f16); + assert_biteq!(1.0f16.round(), 1.0f16); + assert_biteq!(1.3f16.round(), 1.0f16); + assert_biteq!(1.5f16.round(), 2.0f16); + assert_biteq!(1.7f16.round(), 2.0f16); + assert_biteq!(0.0f16.round(), 0.0f16); + assert_biteq!((-0.0f16).round(), -0.0f16); + assert_biteq!((-1.0f16).round(), -1.0f16); + assert_biteq!((-1.3f16).round(), -1.0f16); + assert_biteq!((-1.5f16).round(), -2.0f16); + assert_biteq!((-1.7f16).round(), -2.0f16); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_round_ties_even() { - assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); + assert_biteq!(2.5f16.round_ties_even(), 2.0f16); + assert_biteq!(1.0f16.round_ties_even(), 1.0f16); + assert_biteq!(1.3f16.round_ties_even(), 1.0f16); + assert_biteq!(1.5f16.round_ties_even(), 2.0f16); + assert_biteq!(1.7f16.round_ties_even(), 2.0f16); + assert_biteq!(0.0f16.round_ties_even(), 0.0f16); + assert_biteq!((-0.0f16).round_ties_even(), -0.0f16); + assert_biteq!((-1.0f16).round_ties_even(), -1.0f16); + assert_biteq!((-1.3f16).round_ties_even(), -1.0f16); + assert_biteq!((-1.5f16).round_ties_even(), -2.0f16); + assert_biteq!((-1.7f16).round_ties_even(), -2.0f16); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_trunc() { - assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); + assert_biteq!(1.0f16.trunc(), 1.0f16); + assert_biteq!(1.3f16.trunc(), 1.0f16); + assert_biteq!(1.5f16.trunc(), 1.0f16); + assert_biteq!(1.7f16.trunc(), 1.0f16); + assert_biteq!(0.0f16.trunc(), 0.0f16); + assert_biteq!((-0.0f16).trunc(), -0.0f16); + assert_biteq!((-1.0f16).trunc(), -1.0f16); + assert_biteq!((-1.3f16).trunc(), -1.0f16); + assert_biteq!((-1.5f16).trunc(), -1.0f16); + assert_biteq!((-1.7f16).trunc(), -1.0f16); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_fract() { - assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); - assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); - assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); - assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); - assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); - assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); + assert_biteq!(1.0f16.fract(), 0.0f16); + assert_biteq!(1.3f16.fract(), 0.2998f16); + assert_biteq!(1.5f16.fract(), 0.5f16); + assert_biteq!(1.7f16.fract(), 0.7f16); + assert_biteq!(0.0f16.fract(), 0.0f16); + assert_biteq!((-0.0f16).fract(), 0.0f16); + assert_biteq!((-1.0f16).fract(), 0.0f16); + assert_biteq!((-1.3f16).fract(), -0.2998f16); + assert_biteq!((-1.5f16).fract(), -0.5f16); + assert_biteq!((-1.7f16).fract(), -0.7f16); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_abs() { - assert_eq!(f16::INFINITY.abs(), f16::INFINITY); - assert_eq!(1f16.abs(), 1f16); - assert_eq!(0f16.abs(), 0f16); - assert_eq!((-0f16).abs(), 0f16); - assert_eq!((-1f16).abs(), 1f16); - assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); - assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); + assert_biteq!(f16::INFINITY.abs(), f16::INFINITY); + assert_biteq!(1f16.abs(), 1f16); + assert_biteq!(0f16.abs(), 0f16); + assert_biteq!((-0f16).abs(), 0f16); + assert_biteq!((-1f16).abs(), 1f16); + assert_biteq!(f16::NEG_INFINITY.abs(), f16::INFINITY); + assert_biteq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); assert!(f16::NAN.abs().is_nan()); } @@ -387,27 +366,27 @@ fn test_next_up() { let max_down = f16::from_bits(MAX_DOWN_BITS); let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); - assert_f16_biteq!(f16::MIN.next_up(), -max_down); - assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); - assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f16_biteq!((-tiny_up).next_up(), -tiny); - assert_f16_biteq!((-tiny).next_up(), -0.0f16); - assert_f16_biteq!((-0.0f16).next_up(), tiny); - assert_f16_biteq!(0.0f16.next_up(), tiny); - assert_f16_biteq!(tiny.next_up(), tiny_up); - assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); - assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); - assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); + assert_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); + assert_biteq!(f16::MIN.next_up(), -max_down); + assert_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0f16); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f16); + assert_biteq!((-0.0f16).next_up(), tiny); + assert_biteq!(0.0f16.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + assert_biteq!(f16::MAX.next_up(), f16::INFINITY); + assert_biteq!(f16::INFINITY.next_up(), f16::INFINITY); // Check that NaNs roundtrip. let nan0 = f16::NAN; let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_up(), nan0); - assert_f16_biteq!(nan1.next_up(), nan1); - assert_f16_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -417,28 +396,28 @@ fn test_next_down() { let max_down = f16::from_bits(MAX_DOWN_BITS); let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!((-max_down).next_down(), f16::MIN); - assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); - assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f16_biteq!((-tiny).next_down(), -tiny_up); - assert_f16_biteq!((-0.0f16).next_down(), -tiny); - assert_f16_biteq!((0.0f16).next_down(), -tiny); - assert_f16_biteq!(tiny.next_down(), 0.0f16); - assert_f16_biteq!(tiny_up.next_down(), tiny); - assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); - assert_f16_biteq!(f16::MAX.next_down(), max_down); - assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); + assert_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); + assert_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f16::MIN); + assert_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f16).next_down(), -tiny); + assert_biteq!((0.0f16).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f16); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); + assert_biteq!(f16::MAX.next_down(), max_down); + assert_biteq!(f16::INFINITY.next_down(), f16::MAX); // Check that NaNs roundtrip. let nan0 = f16::NAN; let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_down(), nan0); - assert_f16_biteq!(nan1.next_down(), nan1); - assert_f16_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } #[test] @@ -448,32 +427,31 @@ fn test_mul_add() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); - assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); - assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); - assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert_biteq!(12.3f16.mul_add(4.5, 6.7), 62.031); + assert_biteq!((-12.3f16).mul_add(-4.5, -6.7), 48.625); + assert_biteq!(0.0f16.mul_add(8.9, 1.2), 1.2); + assert_biteq!(3.4f16.mul_add(-0.0, 5.6), 5.6); assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f16.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); + assert_biteq!(inf.mul_add(7.8, 9.0), inf); + assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_biteq!(8.9f16.mul_add(inf, 3.2), inf); + assert_biteq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.recip(), 1.0); - assert_eq!(2.0f16.recip(), 0.5); - assert_eq!((-0.4f16).recip(), -2.5); - assert_eq!(0.0f16.recip(), inf); + assert_biteq!(1.0f16.recip(), 1.0); + assert_biteq!(2.0f16.recip(), 0.5); + assert_biteq!((-0.4f16).recip(), -2.5); + assert_biteq!(0.0f16.recip(), inf); assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -483,13 +461,13 @@ fn test_powi() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.powi(1), 1.0); + assert_biteq!(1.0f16.powi(1), 1.0); assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); - assert_eq!(8.3f16.powi(0), 1.0); + assert_biteq!(8.3f16.powi(0), 1.0); assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -499,10 +477,10 @@ fn test_sqrt_domain() { assert!(f16::NAN.sqrt().is_nan()); assert!(f16::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f16).sqrt().is_nan()); - assert_eq!((-0.0f16).sqrt(), -0.0); - assert_eq!(0.0f16.sqrt(), 0.0); - assert_eq!(1.0f16.sqrt(), 1.0); - assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); + assert_biteq!((-0.0f16).sqrt(), -0.0); + assert_biteq!(0.0f16.sqrt(), 0.0); + assert_biteq!(1.0f16.sqrt(), 1.0); + assert_biteq!(f16::INFINITY.sqrt(), f16::INFINITY); } #[test] @@ -511,13 +489,13 @@ fn test_to_degrees() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_degrees(), 0.0); + assert_biteq!(0.0f16.to_degrees(), 0.0); assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); } #[test] @@ -526,13 +504,13 @@ fn test_to_radians() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_radians(), 0.0); + assert_biteq!(0.0f16.to_radians(), 0.0); assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -541,10 +519,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f16).to_bits(), 0x4a40); assert_eq!((1337f16).to_bits(), 0x6539); assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); + assert_biteq!(f16::from_bits(0x3c00), 1.0); + assert_biteq!(f16::from_bits(0x4a40), 12.5); + assert_biteq!(f16::from_bits(0x6539), 1337.0); + assert_biteq!(f16::from_bits(0xcb20), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; @@ -596,12 +574,15 @@ fn test_total_cmp() { f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) } - fn s_nan() -> f16 { - f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) - } + // FIXME(f16_f128): Tests involving sNaN are disabled because without optimizations, + // `total_cmp` is getting incorrectly lowered to code that includes a `extend`/`trunc` round + // trip, which quiets sNaNs. See: https://github.com/llvm/llvm-project/issues/104915 + // fn s_nan() -> f16 { + // f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) + // } assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + // assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); @@ -622,11 +603,11 @@ fn test_total_cmp() { assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + // assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); @@ -646,11 +627,11 @@ fn test_total_cmp() { assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + // assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); + // assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); + // assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + // assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); @@ -670,10 +651,10 @@ fn test_total_cmp() { assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + // assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); + // assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); @@ -694,29 +675,29 @@ fn test_total_cmp() { assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); } #[test] @@ -742,12 +723,12 @@ fn test_algebraic() { #[test] fn test_from() { - assert_eq!(f16::from(false), 0.0); - assert_eq!(f16::from(true), 1.0); - assert_eq!(f16::from(u8::MIN), 0.0); - assert_eq!(f16::from(42_u8), 42.0); - assert_eq!(f16::from(u8::MAX), 255.0); - assert_eq!(f16::from(i8::MIN), -128.0); - assert_eq!(f16::from(42_i8), 42.0); - assert_eq!(f16::from(i8::MAX), 127.0); + assert_biteq!(f16::from(false), 0.0); + assert_biteq!(f16::from(true), 1.0); + assert_biteq!(f16::from(u8::MIN), 0.0); + assert_biteq!(f16::from(42_u8), 42.0); + assert_biteq!(f16::from(u8::MAX), 255.0); + assert_biteq!(f16::from(i8::MIN), -128.0); + assert_biteq!(f16::from(42_i8), 42.0); + assert_biteq!(f16::from(i8::MAX), 127.0); } diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 9b551643bae2..4e6509ead2ba 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -23,17 +23,6 @@ const NAN_MASK1: u32 = 0x002a_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u32 = 0x0055_5555; -#[allow(unused_macros)] -macro_rules! assert_f32_biteq { - ($left : expr, $right : expr) => { - let l: &f32 = &$left; - let r: &f32 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); - }; -} - #[test] fn test_num_f32() { super::test_num(10f32, 2f32); @@ -41,14 +30,14 @@ fn test_num_f32() { #[test] fn test_min_nan() { - assert_eq!(f32::NAN.min(2.0), 2.0); - assert_eq!(2.0f32.min(f32::NAN), 2.0); + assert_biteq!(f32::NAN.min(2.0), 2.0); + assert_biteq!(2.0f32.min(f32::NAN), 2.0); } #[test] fn test_max_nan() { - assert_eq!(f32::NAN.max(2.0), 2.0); - assert_eq!(2.0f32.max(f32::NAN), 2.0); + assert_biteq!(f32::NAN.max(2.0), 2.0); + assert_biteq!(2.0f32.max(f32::NAN), 2.0); } #[test] @@ -104,7 +93,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f32 = 0.0f32; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -118,6 +107,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f32 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -130,7 +120,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f32 = 1.0f32; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -215,111 +205,111 @@ fn test_classify() { #[test] fn test_floor() { - assert_approx_eq!(f32::floor(1.0f32), 1.0f32); - assert_approx_eq!(f32::floor(1.3f32), 1.0f32); - assert_approx_eq!(f32::floor(1.5f32), 1.0f32); - assert_approx_eq!(f32::floor(1.7f32), 1.0f32); - assert_approx_eq!(f32::floor(0.0f32), 0.0f32); - assert_approx_eq!(f32::floor(-0.0f32), -0.0f32); - assert_approx_eq!(f32::floor(-1.0f32), -1.0f32); - assert_approx_eq!(f32::floor(-1.3f32), -2.0f32); - assert_approx_eq!(f32::floor(-1.5f32), -2.0f32); - assert_approx_eq!(f32::floor(-1.7f32), -2.0f32); + assert_biteq!(f32::math::floor(1.0f32), 1.0f32); + assert_biteq!(f32::math::floor(1.3f32), 1.0f32); + assert_biteq!(f32::math::floor(1.5f32), 1.0f32); + assert_biteq!(f32::math::floor(1.7f32), 1.0f32); + assert_biteq!(f32::math::floor(0.0f32), 0.0f32); + assert_biteq!(f32::math::floor(-0.0f32), -0.0f32); + assert_biteq!(f32::math::floor(-1.0f32), -1.0f32); + assert_biteq!(f32::math::floor(-1.3f32), -2.0f32); + assert_biteq!(f32::math::floor(-1.5f32), -2.0f32); + assert_biteq!(f32::math::floor(-1.7f32), -2.0f32); } #[test] fn test_ceil() { - assert_approx_eq!(f32::ceil(1.0f32), 1.0f32); - assert_approx_eq!(f32::ceil(1.3f32), 2.0f32); - assert_approx_eq!(f32::ceil(1.5f32), 2.0f32); - assert_approx_eq!(f32::ceil(1.7f32), 2.0f32); - assert_approx_eq!(f32::ceil(0.0f32), 0.0f32); - assert_approx_eq!(f32::ceil(-0.0f32), -0.0f32); - assert_approx_eq!(f32::ceil(-1.0f32), -1.0f32); - assert_approx_eq!(f32::ceil(-1.3f32), -1.0f32); - assert_approx_eq!(f32::ceil(-1.5f32), -1.0f32); - assert_approx_eq!(f32::ceil(-1.7f32), -1.0f32); + assert_biteq!(f32::math::ceil(1.0f32), 1.0f32); + assert_biteq!(f32::math::ceil(1.3f32), 2.0f32); + assert_biteq!(f32::math::ceil(1.5f32), 2.0f32); + assert_biteq!(f32::math::ceil(1.7f32), 2.0f32); + assert_biteq!(f32::math::ceil(0.0f32), 0.0f32); + assert_biteq!(f32::math::ceil(-0.0f32), -0.0f32); + assert_biteq!(f32::math::ceil(-1.0f32), -1.0f32); + assert_biteq!(f32::math::ceil(-1.3f32), -1.0f32); + assert_biteq!(f32::math::ceil(-1.5f32), -1.0f32); + assert_biteq!(f32::math::ceil(-1.7f32), -1.0f32); } #[test] fn test_round() { - assert_approx_eq!(f32::round(2.5f32), 3.0f32); - assert_approx_eq!(f32::round(1.0f32), 1.0f32); - assert_approx_eq!(f32::round(1.3f32), 1.0f32); - assert_approx_eq!(f32::round(1.5f32), 2.0f32); - assert_approx_eq!(f32::round(1.7f32), 2.0f32); - assert_approx_eq!(f32::round(0.0f32), 0.0f32); - assert_approx_eq!(f32::round(-0.0f32), -0.0f32); - assert_approx_eq!(f32::round(-1.0f32), -1.0f32); - assert_approx_eq!(f32::round(-1.3f32), -1.0f32); - assert_approx_eq!(f32::round(-1.5f32), -2.0f32); - assert_approx_eq!(f32::round(-1.7f32), -2.0f32); + assert_biteq!(f32::math::round(2.5f32), 3.0f32); + assert_biteq!(f32::math::round(1.0f32), 1.0f32); + assert_biteq!(f32::math::round(1.3f32), 1.0f32); + assert_biteq!(f32::math::round(1.5f32), 2.0f32); + assert_biteq!(f32::math::round(1.7f32), 2.0f32); + assert_biteq!(f32::math::round(0.0f32), 0.0f32); + assert_biteq!(f32::math::round(-0.0f32), -0.0f32); + assert_biteq!(f32::math::round(-1.0f32), -1.0f32); + assert_biteq!(f32::math::round(-1.3f32), -1.0f32); + assert_biteq!(f32::math::round(-1.5f32), -2.0f32); + assert_biteq!(f32::math::round(-1.7f32), -2.0f32); } #[test] fn test_round_ties_even() { - assert_approx_eq!(f32::round_ties_even(2.5f32), 2.0f32); - assert_approx_eq!(f32::round_ties_even(1.0f32), 1.0f32); - assert_approx_eq!(f32::round_ties_even(1.3f32), 1.0f32); - assert_approx_eq!(f32::round_ties_even(1.5f32), 2.0f32); - assert_approx_eq!(f32::round_ties_even(1.7f32), 2.0f32); - assert_approx_eq!(f32::round_ties_even(0.0f32), 0.0f32); - assert_approx_eq!(f32::round_ties_even(-0.0f32), -0.0f32); - assert_approx_eq!(f32::round_ties_even(-1.0f32), -1.0f32); - assert_approx_eq!(f32::round_ties_even(-1.3f32), -1.0f32); - assert_approx_eq!(f32::round_ties_even(-1.5f32), -2.0f32); - assert_approx_eq!(f32::round_ties_even(-1.7f32), -2.0f32); + assert_biteq!(f32::math::round_ties_even(2.5f32), 2.0f32); + assert_biteq!(f32::math::round_ties_even(1.0f32), 1.0f32); + assert_biteq!(f32::math::round_ties_even(1.3f32), 1.0f32); + assert_biteq!(f32::math::round_ties_even(1.5f32), 2.0f32); + assert_biteq!(f32::math::round_ties_even(1.7f32), 2.0f32); + assert_biteq!(f32::math::round_ties_even(0.0f32), 0.0f32); + assert_biteq!(f32::math::round_ties_even(-0.0f32), -0.0f32); + assert_biteq!(f32::math::round_ties_even(-1.0f32), -1.0f32); + assert_biteq!(f32::math::round_ties_even(-1.3f32), -1.0f32); + assert_biteq!(f32::math::round_ties_even(-1.5f32), -2.0f32); + assert_biteq!(f32::math::round_ties_even(-1.7f32), -2.0f32); } #[test] fn test_trunc() { - assert_approx_eq!(f32::trunc(1.0f32), 1.0f32); - assert_approx_eq!(f32::trunc(1.3f32), 1.0f32); - assert_approx_eq!(f32::trunc(1.5f32), 1.0f32); - assert_approx_eq!(f32::trunc(1.7f32), 1.0f32); - assert_approx_eq!(f32::trunc(0.0f32), 0.0f32); - assert_approx_eq!(f32::trunc(-0.0f32), -0.0f32); - assert_approx_eq!(f32::trunc(-1.0f32), -1.0f32); - assert_approx_eq!(f32::trunc(-1.3f32), -1.0f32); - assert_approx_eq!(f32::trunc(-1.5f32), -1.0f32); - assert_approx_eq!(f32::trunc(-1.7f32), -1.0f32); + assert_biteq!(f32::math::trunc(1.0f32), 1.0f32); + assert_biteq!(f32::math::trunc(1.3f32), 1.0f32); + assert_biteq!(f32::math::trunc(1.5f32), 1.0f32); + assert_biteq!(f32::math::trunc(1.7f32), 1.0f32); + assert_biteq!(f32::math::trunc(0.0f32), 0.0f32); + assert_biteq!(f32::math::trunc(-0.0f32), -0.0f32); + assert_biteq!(f32::math::trunc(-1.0f32), -1.0f32); + assert_biteq!(f32::math::trunc(-1.3f32), -1.0f32); + assert_biteq!(f32::math::trunc(-1.5f32), -1.0f32); + assert_biteq!(f32::math::trunc(-1.7f32), -1.0f32); } #[test] fn test_fract() { - assert_approx_eq!(f32::fract(1.0f32), 0.0f32); - assert_approx_eq!(f32::fract(1.3f32), 0.3f32); - assert_approx_eq!(f32::fract(1.5f32), 0.5f32); - assert_approx_eq!(f32::fract(1.7f32), 0.7f32); - assert_approx_eq!(f32::fract(0.0f32), 0.0f32); - assert_approx_eq!(f32::fract(-0.0f32), -0.0f32); - assert_approx_eq!(f32::fract(-1.0f32), -0.0f32); - assert_approx_eq!(f32::fract(-1.3f32), -0.3f32); - assert_approx_eq!(f32::fract(-1.5f32), -0.5f32); - assert_approx_eq!(f32::fract(-1.7f32), -0.7f32); + assert_biteq!(f32::math::fract(1.0f32), 0.0f32); + assert_biteq!(f32::math::fract(1.3f32), 0.29999995f32); + assert_biteq!(f32::math::fract(1.5f32), 0.5f32); + assert_biteq!(f32::math::fract(1.7f32), 0.70000005f32); + assert_biteq!(f32::math::fract(0.0f32), 0.0f32); + assert_biteq!(f32::math::fract(-0.0f32), 0.0f32); + assert_biteq!(f32::math::fract(-1.0f32), 0.0f32); + assert_biteq!(f32::math::fract(-1.3f32), -0.29999995f32); + assert_biteq!(f32::math::fract(-1.5f32), -0.5f32); + assert_biteq!(f32::math::fract(-1.7f32), -0.70000005f32); } #[test] fn test_abs() { - assert_eq!(f32::INFINITY.abs(), f32::INFINITY); - assert_eq!(1f32.abs(), 1f32); - assert_eq!(0f32.abs(), 0f32); - assert_eq!((-0f32).abs(), 0f32); - assert_eq!((-1f32).abs(), 1f32); - assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); - assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); + assert_biteq!(f32::INFINITY.abs(), f32::INFINITY); + assert_biteq!(1f32.abs(), 1f32); + assert_biteq!(0f32.abs(), 0f32); + assert_biteq!((-0f32).abs(), 0f32); + assert_biteq!((-1f32).abs(), 1f32); + assert_biteq!(f32::NEG_INFINITY.abs(), f32::INFINITY); + assert_biteq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); assert!(f32::NAN.abs().is_nan()); } #[test] fn test_signum() { - assert_eq!(f32::INFINITY.signum(), 1f32); - assert_eq!(1f32.signum(), 1f32); - assert_eq!(0f32.signum(), 1f32); - assert_eq!((-0f32).signum(), -1f32); - assert_eq!((-1f32).signum(), -1f32); - assert_eq!(f32::NEG_INFINITY.signum(), -1f32); - assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); + assert_biteq!(f32::INFINITY.signum(), 1f32); + assert_biteq!(1f32.signum(), 1f32); + assert_biteq!(0f32.signum(), 1f32); + assert_biteq!((-0f32).signum(), -1f32); + assert_biteq!((-1f32).signum(), -1f32); + assert_biteq!(f32::NEG_INFINITY.signum(), -1f32); + assert_biteq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); assert!(f32::NAN.signum().is_nan()); } @@ -356,27 +346,27 @@ fn test_next_up() { let max_down = f32::from_bits(MAX_DOWN_BITS); let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); - assert_f32_biteq!(f32::MIN.next_up(), -max_down); - assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); - assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f32_biteq!((-tiny_up).next_up(), -tiny); - assert_f32_biteq!((-tiny).next_up(), -0.0f32); - assert_f32_biteq!((-0.0f32).next_up(), tiny); - assert_f32_biteq!(0.0f32.next_up(), tiny); - assert_f32_biteq!(tiny.next_up(), tiny_up); - assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); - assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); - assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); + assert_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); + assert_biteq!(f32::MIN.next_up(), -max_down); + assert_biteq!((-1.0f32 - f32::EPSILON).next_up(), -1.0f32); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f32); + assert_biteq!((-0.0f32).next_up(), tiny); + assert_biteq!(0.0f32.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + assert_biteq!(f32::MAX.next_up(), f32::INFINITY); + assert_biteq!(f32::INFINITY.next_up(), f32::INFINITY); // Check that NaNs roundtrip. let nan0 = f32::NAN; let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_up(), nan0); - assert_f32_biteq!(nan1.next_up(), nan1); - assert_f32_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -386,28 +376,28 @@ fn test_next_down() { let max_down = f32::from_bits(MAX_DOWN_BITS); let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!((-max_down).next_down(), f32::MIN); - assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); - assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f32_biteq!((-tiny).next_down(), -tiny_up); - assert_f32_biteq!((-0.0f32).next_down(), -tiny); - assert_f32_biteq!((0.0f32).next_down(), -tiny); - assert_f32_biteq!(tiny.next_down(), 0.0f32); - assert_f32_biteq!(tiny_up.next_down(), tiny); - assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); - assert_f32_biteq!(f32::MAX.next_down(), max_down); - assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); + assert_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); + assert_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f32::MIN); + assert_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f32).next_down(), -tiny); + assert_biteq!((0.0f32).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f32); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); + assert_biteq!(f32::MAX.next_down(), max_down); + assert_biteq!(f32::INFINITY.next_down(), f32::MAX); // Check that NaNs roundtrip. let nan0 = f32::NAN; let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_down(), nan0); - assert_f32_biteq!(nan1.next_down(), nan1); - assert_f32_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ @@ -417,15 +407,15 @@ fn test_mul_add() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(f32::mul_add(12.3f32, 4.5, 6.7), 62.05); - assert_approx_eq!(f32::mul_add(-12.3f32, -4.5, -6.7), 48.65); - assert_approx_eq!(f32::mul_add(0.0f32, 8.9, 1.2), 1.2); - assert_approx_eq!(f32::mul_add(3.4f32, -0.0, 5.6), 5.6); - assert!(f32::mul_add(nan, 7.8, 9.0).is_nan()); - assert_eq!(f32::mul_add(inf, 7.8, 9.0), inf); - assert_eq!(f32::mul_add(neg_inf, 7.8, 9.0), neg_inf); - assert_eq!(f32::mul_add(8.9f32, inf, 3.2), inf); - assert_eq!(f32::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); + assert_biteq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05); + assert_biteq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65); + assert_biteq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2); + assert_biteq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6); + assert!(f32::math::mul_add(nan, 7.8, 9.0).is_nan()); + assert_biteq!(f32::math::mul_add(inf, 7.8, 9.0), inf); + assert_biteq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf); + assert_biteq!(f32::math::mul_add(8.9f32, inf, 3.2), inf); + assert_biteq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); } #[test] @@ -433,13 +423,13 @@ fn test_recip() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.recip(), 1.0); - assert_eq!(2.0f32.recip(), 0.5); - assert_eq!((-0.4f32).recip(), -2.5); - assert_eq!(0.0f32.recip(), inf); + assert_biteq!(1.0f32.recip(), 1.0); + assert_biteq!(2.0f32.recip(), 0.5); + assert_biteq!((-0.4f32).recip(), -2.5); + assert_biteq!(0.0f32.recip(), inf); assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -447,13 +437,13 @@ fn test_powi() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powi(1), 1.0); + assert_biteq!(1.0f32.powi(1), 1.0); assert_approx_eq!((-3.1f32).powi(2), 9.61); assert_approx_eq!(5.9f32.powi(-2), 0.028727); - assert_eq!(8.3f32.powi(0), 1.0); + assert_biteq!(8.3f32.powi(0), 1.0); assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -461,10 +451,10 @@ fn test_sqrt_domain() { assert!(f32::NAN.sqrt().is_nan()); assert!(f32::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f32).sqrt().is_nan()); - assert_eq!((-0.0f32).sqrt(), -0.0); - assert_eq!(0.0f32.sqrt(), 0.0); - assert_eq!(1.0f32.sqrt(), 1.0); - assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); + assert_biteq!((-0.0f32).sqrt(), -0.0); + assert_biteq!(0.0f32.sqrt(), 0.0); + assert_biteq!(1.0f32.sqrt(), 1.0); + assert_biteq!(f32::INFINITY.sqrt(), f32::INFINITY); } #[test] @@ -473,13 +463,13 @@ fn test_to_degrees() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_degrees(), 0.0); + assert_biteq!(0.0f32.to_degrees(), 0.0); assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_biteq!(pi.to_degrees(), 180.0); assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); } #[test] @@ -488,13 +478,13 @@ fn test_to_radians() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_radians(), 0.0); + assert_biteq!(0.0f32.to_radians(), 0.0); assert_approx_eq!(154.6f32.to_radians(), 2.698279); assert_approx_eq!((-332.31f32).to_radians(), -5.799903); - assert_eq!(180.0f32.to_radians(), pi); + assert_biteq!(180.0f32.to_radians(), pi); assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -503,10 +493,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f32).to_bits(), 0x41480000); assert_eq!((1337f32).to_bits(), 0x44a72000); assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); - assert_approx_eq!(f32::from_bits(0x41480000), 12.5); - assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); - assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); + assert_biteq!(f32::from_bits(0x3f800000), 1.0); + assert_biteq!(f32::from_bits(0x41480000), 12.5); + assert_biteq!(f32::from_bits(0x44a72000), 1337.0); + assert_biteq!(f32::from_bits(0xc1640000), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 988108371d73..74202a374091 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -1,5 +1,6 @@ -use std::f64::consts; -use std::num::FpCategory as Fp; +use core::f64; +use core::f64::consts; +use core::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u64 = 0x1; @@ -22,17 +23,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[allow(unused_macros)] -macro_rules! assert_f64_biteq { - ($left : expr, $right : expr) => { - let l: &f64 = &$left; - let r: &f64 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); - }; -} - #[test] fn test_num_f64() { super::test_num(10f64, 2f64); @@ -40,14 +30,14 @@ fn test_num_f64() { #[test] fn test_min_nan() { - assert_eq!(f64::NAN.min(2.0), 2.0); - assert_eq!(2.0f64.min(f64::NAN), 2.0); + assert_biteq!(f64::NAN.min(2.0), 2.0); + assert_biteq!(2.0f64.min(f64::NAN), 2.0); } #[test] fn test_max_nan() { - assert_eq!(f64::NAN.max(2.0), 2.0); - assert_eq!(2.0f64.max(f64::NAN), 2.0); + assert_biteq!(f64::NAN.max(2.0), 2.0); + assert_biteq!(2.0f64.max(f64::NAN), 2.0); } #[test] @@ -91,7 +81,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f64 = 0.0f64; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -105,6 +95,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f64 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -117,7 +108,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f64 = 1.0f64; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -201,111 +192,111 @@ fn test_classify() { #[test] fn test_floor() { - assert_approx_eq!(f64::floor(1.0f64), 1.0f64); - assert_approx_eq!(f64::floor(1.3f64), 1.0f64); - assert_approx_eq!(f64::floor(1.5f64), 1.0f64); - assert_approx_eq!(f64::floor(1.7f64), 1.0f64); - assert_approx_eq!(f64::floor(0.0f64), 0.0f64); - assert_approx_eq!(f64::floor(-0.0f64), -0.0f64); - assert_approx_eq!(f64::floor(-1.0f64), -1.0f64); - assert_approx_eq!(f64::floor(-1.3f64), -2.0f64); - assert_approx_eq!(f64::floor(-1.5f64), -2.0f64); - assert_approx_eq!(f64::floor(-1.7f64), -2.0f64); + assert_biteq!(f64::math::floor(1.0f64), 1.0f64); + assert_biteq!(f64::math::floor(1.3f64), 1.0f64); + assert_biteq!(f64::math::floor(1.5f64), 1.0f64); + assert_biteq!(f64::math::floor(1.7f64), 1.0f64); + assert_biteq!(f64::math::floor(0.0f64), 0.0f64); + assert_biteq!(f64::math::floor(-0.0f64), -0.0f64); + assert_biteq!(f64::math::floor(-1.0f64), -1.0f64); + assert_biteq!(f64::math::floor(-1.3f64), -2.0f64); + assert_biteq!(f64::math::floor(-1.5f64), -2.0f64); + assert_biteq!(f64::math::floor(-1.7f64), -2.0f64); } #[test] fn test_ceil() { - assert_approx_eq!(f64::ceil(1.0f64), 1.0f64); - assert_approx_eq!(f64::ceil(1.3f64), 2.0f64); - assert_approx_eq!(f64::ceil(1.5f64), 2.0f64); - assert_approx_eq!(f64::ceil(1.7f64), 2.0f64); - assert_approx_eq!(f64::ceil(0.0f64), 0.0f64); - assert_approx_eq!(f64::ceil(-0.0f64), -0.0f64); - assert_approx_eq!(f64::ceil(-1.0f64), -1.0f64); - assert_approx_eq!(f64::ceil(-1.3f64), -1.0f64); - assert_approx_eq!(f64::ceil(-1.5f64), -1.0f64); - assert_approx_eq!(f64::ceil(-1.7f64), -1.0f64); + assert_biteq!(f64::math::ceil(1.0f64), 1.0f64); + assert_biteq!(f64::math::ceil(1.3f64), 2.0f64); + assert_biteq!(f64::math::ceil(1.5f64), 2.0f64); + assert_biteq!(f64::math::ceil(1.7f64), 2.0f64); + assert_biteq!(f64::math::ceil(0.0f64), 0.0f64); + assert_biteq!(f64::math::ceil(-0.0f64), -0.0f64); + assert_biteq!(f64::math::ceil(-1.0f64), -1.0f64); + assert_biteq!(f64::math::ceil(-1.3f64), -1.0f64); + assert_biteq!(f64::math::ceil(-1.5f64), -1.0f64); + assert_biteq!(f64::math::ceil(-1.7f64), -1.0f64); } #[test] fn test_round() { - assert_approx_eq!(f64::round(2.5f64), 3.0f64); - assert_approx_eq!(f64::round(1.0f64), 1.0f64); - assert_approx_eq!(f64::round(1.3f64), 1.0f64); - assert_approx_eq!(f64::round(1.5f64), 2.0f64); - assert_approx_eq!(f64::round(1.7f64), 2.0f64); - assert_approx_eq!(f64::round(0.0f64), 0.0f64); - assert_approx_eq!(f64::round(-0.0f64), -0.0f64); - assert_approx_eq!(f64::round(-1.0f64), -1.0f64); - assert_approx_eq!(f64::round(-1.3f64), -1.0f64); - assert_approx_eq!(f64::round(-1.5f64), -2.0f64); - assert_approx_eq!(f64::round(-1.7f64), -2.0f64); + assert_biteq!(f64::math::round(2.5f64), 3.0f64); + assert_biteq!(f64::math::round(1.0f64), 1.0f64); + assert_biteq!(f64::math::round(1.3f64), 1.0f64); + assert_biteq!(f64::math::round(1.5f64), 2.0f64); + assert_biteq!(f64::math::round(1.7f64), 2.0f64); + assert_biteq!(f64::math::round(0.0f64), 0.0f64); + assert_biteq!(f64::math::round(-0.0f64), -0.0f64); + assert_biteq!(f64::math::round(-1.0f64), -1.0f64); + assert_biteq!(f64::math::round(-1.3f64), -1.0f64); + assert_biteq!(f64::math::round(-1.5f64), -2.0f64); + assert_biteq!(f64::math::round(-1.7f64), -2.0f64); } #[test] fn test_round_ties_even() { - assert_approx_eq!(f64::round_ties_even(2.5f64), 2.0f64); - assert_approx_eq!(f64::round_ties_even(1.0f64), 1.0f64); - assert_approx_eq!(f64::round_ties_even(1.3f64), 1.0f64); - assert_approx_eq!(f64::round_ties_even(1.5f64), 2.0f64); - assert_approx_eq!(f64::round_ties_even(1.7f64), 2.0f64); - assert_approx_eq!(f64::round_ties_even(0.0f64), 0.0f64); - assert_approx_eq!(f64::round_ties_even(-0.0f64), -0.0f64); - assert_approx_eq!(f64::round_ties_even(-1.0f64), -1.0f64); - assert_approx_eq!(f64::round_ties_even(-1.3f64), -1.0f64); - assert_approx_eq!(f64::round_ties_even(-1.5f64), -2.0f64); - assert_approx_eq!(f64::round_ties_even(-1.7f64), -2.0f64); + assert_biteq!(f64::math::round_ties_even(2.5f64), 2.0f64); + assert_biteq!(f64::math::round_ties_even(1.0f64), 1.0f64); + assert_biteq!(f64::math::round_ties_even(1.3f64), 1.0f64); + assert_biteq!(f64::math::round_ties_even(1.5f64), 2.0f64); + assert_biteq!(f64::math::round_ties_even(1.7f64), 2.0f64); + assert_biteq!(f64::math::round_ties_even(0.0f64), 0.0f64); + assert_biteq!(f64::math::round_ties_even(-0.0f64), -0.0f64); + assert_biteq!(f64::math::round_ties_even(-1.0f64), -1.0f64); + assert_biteq!(f64::math::round_ties_even(-1.3f64), -1.0f64); + assert_biteq!(f64::math::round_ties_even(-1.5f64), -2.0f64); + assert_biteq!(f64::math::round_ties_even(-1.7f64), -2.0f64); } #[test] fn test_trunc() { - assert_approx_eq!(f64::trunc(1.0f64), 1.0f64); - assert_approx_eq!(f64::trunc(1.3f64), 1.0f64); - assert_approx_eq!(f64::trunc(1.5f64), 1.0f64); - assert_approx_eq!(f64::trunc(1.7f64), 1.0f64); - assert_approx_eq!(f64::trunc(0.0f64), 0.0f64); - assert_approx_eq!(f64::trunc(-0.0f64), -0.0f64); - assert_approx_eq!(f64::trunc(-1.0f64), -1.0f64); - assert_approx_eq!(f64::trunc(-1.3f64), -1.0f64); - assert_approx_eq!(f64::trunc(-1.5f64), -1.0f64); - assert_approx_eq!(f64::trunc(-1.7f64), -1.0f64); + assert_biteq!(f64::math::trunc(1.0f64), 1.0f64); + assert_biteq!(f64::math::trunc(1.3f64), 1.0f64); + assert_biteq!(f64::math::trunc(1.5f64), 1.0f64); + assert_biteq!(f64::math::trunc(1.7f64), 1.0f64); + assert_biteq!(f64::math::trunc(0.0f64), 0.0f64); + assert_biteq!(f64::math::trunc(-0.0f64), -0.0f64); + assert_biteq!(f64::math::trunc(-1.0f64), -1.0f64); + assert_biteq!(f64::math::trunc(-1.3f64), -1.0f64); + assert_biteq!(f64::math::trunc(-1.5f64), -1.0f64); + assert_biteq!(f64::math::trunc(-1.7f64), -1.0f64); } #[test] fn test_fract() { - assert_approx_eq!(f64::fract(1.0f64), 0.0f64); - assert_approx_eq!(f64::fract(1.3f64), 0.3f64); - assert_approx_eq!(f64::fract(1.5f64), 0.5f64); - assert_approx_eq!(f64::fract(1.7f64), 0.7f64); - assert_approx_eq!(f64::fract(0.0f64), 0.0f64); - assert_approx_eq!(f64::fract(-0.0f64), -0.0f64); - assert_approx_eq!(f64::fract(-1.0f64), -0.0f64); - assert_approx_eq!(f64::fract(-1.3f64), -0.3f64); - assert_approx_eq!(f64::fract(-1.5f64), -0.5f64); - assert_approx_eq!(f64::fract(-1.7f64), -0.7f64); + assert_biteq!(f64::math::fract(1.0f64), 0.0f64); + assert_biteq!(f64::math::fract(1.3f64), 0.30000000000000004f64); + assert_biteq!(f64::math::fract(1.5f64), 0.5f64); + assert_biteq!(f64::math::fract(1.7f64), 0.7f64); + assert_biteq!(f64::math::fract(0.0f64), 0.0f64); + assert_biteq!(f64::math::fract(-0.0f64), 0.0f64); + assert_biteq!(f64::math::fract(-1.0f64), 0.0f64); + assert_biteq!(f64::math::fract(-1.3f64), -0.30000000000000004f64); + assert_biteq!(f64::math::fract(-1.5f64), -0.5f64); + assert_biteq!(f64::math::fract(-1.7f64), -0.69999999999999996f64); } #[test] fn test_abs() { - assert_eq!(f64::INFINITY.abs(), f64::INFINITY); - assert_eq!(1f64.abs(), 1f64); - assert_eq!(0f64.abs(), 0f64); - assert_eq!((-0f64).abs(), 0f64); - assert_eq!((-1f64).abs(), 1f64); - assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); - assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); + assert_biteq!(f64::INFINITY.abs(), f64::INFINITY); + assert_biteq!(1f64.abs(), 1f64); + assert_biteq!(0f64.abs(), 0f64); + assert_biteq!((-0f64).abs(), 0f64); + assert_biteq!((-1f64).abs(), 1f64); + assert_biteq!(f64::NEG_INFINITY.abs(), f64::INFINITY); + assert_biteq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); assert!(f64::NAN.abs().is_nan()); } #[test] fn test_signum() { - assert_eq!(f64::INFINITY.signum(), 1f64); - assert_eq!(1f64.signum(), 1f64); - assert_eq!(0f64.signum(), 1f64); - assert_eq!((-0f64).signum(), -1f64); - assert_eq!((-1f64).signum(), -1f64); - assert_eq!(f64::NEG_INFINITY.signum(), -1f64); - assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); + assert_biteq!(f64::INFINITY.signum(), 1f64); + assert_biteq!(1f64.signum(), 1f64); + assert_biteq!(0f64.signum(), 1f64); + assert_biteq!((-0f64).signum(), -1f64); + assert_biteq!((-1f64).signum(), -1f64); + assert_biteq!(f64::NEG_INFINITY.signum(), -1f64); + assert_biteq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); assert!(f64::NAN.signum().is_nan()); } @@ -342,26 +333,26 @@ fn test_next_up() { let max_down = f64::from_bits(MAX_DOWN_BITS); let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); - assert_f64_biteq!(f64::MIN.next_up(), -max_down); - assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); - assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f64_biteq!((-tiny_up).next_up(), -tiny); - assert_f64_biteq!((-tiny).next_up(), -0.0f64); - assert_f64_biteq!((-0.0f64).next_up(), tiny); - assert_f64_biteq!(0.0f64.next_up(), tiny); - assert_f64_biteq!(tiny.next_up(), tiny_up); - assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); - assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); - assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); + assert_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); + assert_biteq!(f64::MIN.next_up(), -max_down); + assert_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0f64); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f64); + assert_biteq!((-0.0f64).next_up(), tiny); + assert_biteq!(0.0f64.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + assert_biteq!(f64::MAX.next_up(), f64::INFINITY); + assert_biteq!(f64::INFINITY.next_up(), f64::INFINITY); let nan0 = f64::NAN; let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_up(), nan0); - assert_f64_biteq!(nan1.next_up(), nan1); - assert_f64_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -371,27 +362,27 @@ fn test_next_down() { let max_down = f64::from_bits(MAX_DOWN_BITS); let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!((-max_down).next_down(), f64::MIN); - assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); - assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f64_biteq!((-tiny).next_down(), -tiny_up); - assert_f64_biteq!((-0.0f64).next_down(), -tiny); - assert_f64_biteq!((0.0f64).next_down(), -tiny); - assert_f64_biteq!(tiny.next_down(), 0.0f64); - assert_f64_biteq!(tiny_up.next_down(), tiny); - assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); - assert_f64_biteq!(f64::MAX.next_down(), max_down); - assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); + assert_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); + assert_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f64::MIN); + assert_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f64).next_down(), -tiny); + assert_biteq!((0.0f64).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f64); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); + assert_biteq!(f64::MAX.next_down(), max_down); + assert_biteq!(f64::INFINITY.next_down(), f64::MAX); let nan0 = f64::NAN; let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_down(), nan0); - assert_f64_biteq!(nan1.next_down(), nan1); - assert_f64_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ @@ -401,15 +392,15 @@ fn test_mul_add() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); + assert_biteq!(12.3f64.mul_add(4.5, 6.7), 62.050000000000004); + assert_biteq!((-12.3f64).mul_add(-4.5, -6.7), 48.650000000000006); + assert_biteq!(0.0f64.mul_add(8.9, 1.2), 1.2); + assert_biteq!(3.4f64.mul_add(-0.0, 5.6), 5.6); assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f64.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); + assert_biteq!(inf.mul_add(7.8, 9.0), inf); + assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_biteq!(8.9f64.mul_add(inf, 3.2), inf); + assert_biteq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); } #[test] @@ -417,13 +408,13 @@ fn test_recip() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.recip(), 1.0); - assert_eq!(2.0f64.recip(), 0.5); - assert_eq!((-0.4f64).recip(), -2.5); - assert_eq!(0.0f64.recip(), inf); + assert_biteq!(1.0f64.recip(), 1.0); + assert_biteq!(2.0f64.recip(), 0.5); + assert_biteq!((-0.4f64).recip(), -2.5); + assert_biteq!(0.0f64.recip(), inf); assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -431,13 +422,13 @@ fn test_powi() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powi(1), 1.0); + assert_biteq!(1.0f64.powi(1), 1.0); assert_approx_eq!((-3.1f64).powi(2), 9.61); assert_approx_eq!(5.9f64.powi(-2), 0.028727); - assert_eq!(8.3f64.powi(0), 1.0); + assert_biteq!(8.3f64.powi(0), 1.0); assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -445,10 +436,10 @@ fn test_sqrt_domain() { assert!(f64::NAN.sqrt().is_nan()); assert!(f64::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f64).sqrt().is_nan()); - assert_eq!((-0.0f64).sqrt(), -0.0); - assert_eq!(0.0f64.sqrt(), 0.0); - assert_eq!(1.0f64.sqrt(), 1.0); - assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); + assert_biteq!((-0.0f64).sqrt(), -0.0); + assert_biteq!(0.0f64.sqrt(), 0.0); + assert_biteq!(1.0f64.sqrt(), 1.0); + assert_biteq!(f64::INFINITY.sqrt(), f64::INFINITY); } #[test] @@ -457,12 +448,12 @@ fn test_to_degrees() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_degrees(), 0.0); + assert_biteq!(0.0f64.to_degrees(), 0.0); assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_biteq!(pi.to_degrees(), 180.0); assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); } #[test] @@ -471,13 +462,13 @@ fn test_to_radians() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_radians(), 0.0); + assert_biteq!(0.0f64.to_radians(), 0.0); assert_approx_eq!(154.6f64.to_radians(), 2.698279); assert_approx_eq!((-332.31f64).to_radians(), -5.799903); - assert_eq!(180.0f64.to_radians(), pi); + assert_biteq!(180.0f64.to_radians(), pi); assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -486,10 +477,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f64).to_bits(), 0x4029000000000000); assert_eq!((1337f64).to_bits(), 0x4094e40000000000); assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); - assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); - assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); - assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); - assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); + assert_biteq!(f64::from_bits(0x3ff0000000000000), 1.0); + assert_biteq!(f64::from_bits(0x4029000000000000), 12.5); + assert_biteq!(f64::from_bits(0x4094e40000000000), 1337.0); + assert_biteq!(f64::from_bits(0xc02c800000000000), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 7de34271ad05..7e27028a2a2d 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -15,6 +15,43 @@ macro_rules! assert_approx_eq { }}; } +/// Verify that floats have the same bitwise representation. Used to avoid the default `0.0 == -0.0` +/// behavior, as well as to ensure exact NaN bitpatterns. +macro_rules! assert_biteq { + (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{ + let l = $left; + let r = $right; + + // Hack to coerce left and right to the same type + let mut _eq_ty = l; + _eq_ty = r; + + // Hack to get the width from a value + let bits = (l.to_bits() - l.to_bits()).leading_zeros(); + assert!( + l.to_bits() == r.to_bits(), + "{msg}{nl}l: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", + msg = format_args!($($tt)*), + nl = $msg_sep, + lb = l.to_bits(), + rb = r.to_bits(), + width = ((bits / 4) + 2) as usize, + ); + + if !l.is_nan() && !r.is_nan() { + // Also check that standard equality holds, since most tests use `assert_biteq` rather + // than `assert_eq`. + assert_eq!(l, r) + } + }}; + ($left:expr, $right:expr , $($tt:tt)*) => { + assert_biteq!(@inner $left, $right, "\n", $($tt)*) + }; + ($left:expr, $right:expr $(,)?) => { + assert_biteq!(@inner $left, $right, "", "") + }; +} + /// Helper function for testing numeric operations pub fn test_num(ten: T, two: T) where diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b98e52718f60..dd7cdd79c0d6 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,6 +1,6 @@ // tidy-alphabetical-start #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] -#![cfg_attr(test, feature(cfg_match))] +#![cfg_attr(test, feature(cfg_select))] #![feature(alloc_layout_extra)] #![feature(array_chunks)] #![feature(array_ptr_get)] @@ -16,6 +16,7 @@ #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(const_eval_select)] +#![feature(const_float_round_methods)] #![feature(const_trait_impl)] #![feature(core_float_math)] #![feature(core_intrinsics)] @@ -23,6 +24,7 @@ #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] +#![feature(cstr_display)] #![feature(dec2flt)] #![feature(duration_constants)] #![feature(duration_constructors)] diff --git a/library/coretests/tests/macros.rs b/library/coretests/tests/macros.rs index b30a40b7df28..d220e628d733 100644 --- a/library/coretests/tests/macros.rs +++ b/library/coretests/tests/macros.rs @@ -9,7 +9,7 @@ trait Trait { struct Struct; impl Trait for Struct { - cfg_match! { + cfg_select! { feature = "blah" => { fn blah(&self) { unimplemented!(); @@ -45,22 +45,22 @@ fn matches_leading_pipe() { } #[test] -fn cfg_match_basic() { - cfg_match! { +fn cfg_select_basic() { + cfg_select! { target_pointer_width = "64" => { fn f0_() -> bool { true }} } - cfg_match! { + cfg_select! { unix => { fn f1_() -> bool { true } } any(target_os = "macos", target_os = "linux") => { fn f1_() -> bool { false }} } - cfg_match! { + cfg_select! { target_pointer_width = "32" => { fn f2_() -> bool { false } } target_pointer_width = "64" => { fn f2_() -> bool { true } } } - cfg_match! { + cfg_select! { target_pointer_width = "16" => { fn f3_() -> i32 { 1 } } _ => { fn f3_() -> i32 { 2 }} } @@ -81,8 +81,8 @@ fn cfg_match_basic() { } #[test] -fn cfg_match_debug_assertions() { - cfg_match! { +fn cfg_select_debug_assertions() { + cfg_select! { debug_assertions => { assert!(cfg!(debug_assertions)); assert_eq!(4, 2+2); @@ -96,8 +96,8 @@ fn cfg_match_debug_assertions() { #[cfg(target_pointer_width = "64")] #[test] -fn cfg_match_no_duplication_on_64() { - cfg_match! { +fn cfg_select_no_duplication_on_64() { + cfg_select! { windows => { fn foo() {} } @@ -112,8 +112,8 @@ fn cfg_match_no_duplication_on_64() { } #[test] -fn cfg_match_options() { - cfg_match! { +fn cfg_select_options() { + cfg_select! { test => { use core::option::Option as Option2; fn works1() -> Option2 { Some(1) } @@ -121,25 +121,25 @@ fn cfg_match_options() { _ => { fn works1() -> Option { None } } } - cfg_match! { + cfg_select! { feature = "foo" => { fn works2() -> bool { false } } test => { fn works2() -> bool { true } } _ => { fn works2() -> bool { false } } } - cfg_match! { + cfg_select! { feature = "foo" => { fn works3() -> bool { false } } _ => { fn works3() -> bool { true } } } - cfg_match! { + cfg_select! { test => { use core::option::Option as Option3; fn works4() -> Option3 { Some(1) } } } - cfg_match! { + cfg_select! { feature = "foo" => { fn works5() -> bool { false } } test => { fn works5() -> bool { true } } } @@ -152,8 +152,8 @@ fn cfg_match_options() { } #[test] -fn cfg_match_two_functions() { - cfg_match! { +fn cfg_select_two_functions() { + cfg_select! { target_pointer_width = "64" => { fn foo1() {} fn bar1() {} @@ -177,7 +177,7 @@ fn cfg_match_two_functions() { } fn _accepts_expressions() -> i32 { - cfg_match! { + cfg_select! { unix => { 1 } _ => { 2 } } @@ -188,14 +188,14 @@ fn _accepts_expressions() -> i32 { fn _allows_stmt_expr_attributes() { let one = 1; let two = 2; - cfg_match! { + cfg_select! { unix => { one * two; } _ => { one + two; } } } fn _expression() { - let _ = cfg_match!({ + let _ = cfg_select!({ windows => { " XP" } diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index a6b75f702660..fa05bbdd9b77 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -731,8 +731,11 @@ assume_usize_width! { } } +// FIXME(141726): there is a lot of duplication between the following tests and +// the tests in `coretests/tests/floats/f*.rs` +// See issue https://github.com/rust-lang/rust/issues/141726 for more details. macro_rules! test_float { - ($modname: ident, $fassert: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { + ($modname: ident, $fassert: ident, $fty: ty) => { mod $modname { #[test] fn min() { @@ -747,19 +750,19 @@ macro_rules! test_float { $fassert!((-0.0 as $fty).min(9.0), -0.0); $fassert!((-0.0 as $fty).min(9.0).is_sign_negative()); $fassert!((-0.0 as $fty).min(-9.0), -9.0); - $fassert!(($inf as $fty).min(9.0), 9.0); - $fassert!((9.0 as $fty).min($inf), 9.0); - $fassert!(($inf as $fty).min(-9.0), -9.0); - $fassert!((-9.0 as $fty).min($inf), -9.0); - $fassert!(($neginf as $fty).min(9.0), $neginf); - $fassert!((9.0 as $fty).min($neginf), $neginf); - $fassert!(($neginf as $fty).min(-9.0), $neginf); - $fassert!((-9.0 as $fty).min($neginf), $neginf); - $fassert!(($nan as $fty).min(9.0), 9.0); - $fassert!(($nan as $fty).min(-9.0), -9.0); - $fassert!((9.0 as $fty).min($nan), 9.0); - $fassert!((-9.0 as $fty).min($nan), -9.0); - $fassert!(($nan as $fty).min($nan).is_nan()); + $fassert!(<$fty>::INFINITY.min(9.0), 9.0); + $fassert!((9.0 as $fty).min(<$fty>::INFINITY), 9.0); + $fassert!(<$fty>::INFINITY.min(-9.0), -9.0); + $fassert!((-9.0 as $fty).min(<$fty>::INFINITY), -9.0); + $fassert!(<$fty>::NEG_INFINITY.min(9.0), <$fty>::NEG_INFINITY); + $fassert!((9.0 as $fty).min(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); + $fassert!(<$fty>::NEG_INFINITY.min(-9.0), <$fty>::NEG_INFINITY); + $fassert!((-9.0 as $fty).min(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); + $fassert!(<$fty>::NAN.min(9.0), 9.0); + $fassert!(<$fty>::NAN.min(-9.0), -9.0); + $fassert!((9.0 as $fty).min(<$fty>::NAN), 9.0); + $fassert!((-9.0 as $fty).min(<$fty>::NAN), -9.0); + $fassert!(<$fty>::NAN.min(<$fty>::NAN).is_nan()); } #[test] fn max() { @@ -777,19 +780,19 @@ macro_rules! test_float { $fassert!((0.0 as $fty).max(-9.0).is_sign_positive()); $fassert!((-0.0 as $fty).max(-9.0), -0.0); $fassert!((-0.0 as $fty).max(-9.0).is_sign_negative()); - $fassert!(($inf as $fty).max(9.0), $inf); - $fassert!((9.0 as $fty).max($inf), $inf); - $fassert!(($inf as $fty).max(-9.0), $inf); - $fassert!((-9.0 as $fty).max($inf), $inf); - $fassert!(($neginf as $fty).max(9.0), 9.0); - $fassert!((9.0 as $fty).max($neginf), 9.0); - $fassert!(($neginf as $fty).max(-9.0), -9.0); - $fassert!((-9.0 as $fty).max($neginf), -9.0); - $fassert!(($nan as $fty).max(9.0), 9.0); - $fassert!(($nan as $fty).max(-9.0), -9.0); - $fassert!((9.0 as $fty).max($nan), 9.0); - $fassert!((-9.0 as $fty).max($nan), -9.0); - $fassert!(($nan as $fty).max($nan).is_nan()); + $fassert!(<$fty>::INFINITY.max(9.0), <$fty>::INFINITY); + $fassert!((9.0 as $fty).max(<$fty>::INFINITY), <$fty>::INFINITY); + $fassert!(<$fty>::INFINITY.max(-9.0), <$fty>::INFINITY); + $fassert!((-9.0 as $fty).max(<$fty>::INFINITY), <$fty>::INFINITY); + $fassert!(<$fty>::NEG_INFINITY.max(9.0), 9.0); + $fassert!((9.0 as $fty).max(<$fty>::NEG_INFINITY), 9.0); + $fassert!(<$fty>::NEG_INFINITY.max(-9.0), -9.0); + $fassert!((-9.0 as $fty).max(<$fty>::NEG_INFINITY), -9.0); + $fassert!(<$fty>::NAN.max(9.0), 9.0); + $fassert!(<$fty>::NAN.max(-9.0), -9.0); + $fassert!((9.0 as $fty).max(<$fty>::NAN), 9.0); + $fassert!((-9.0 as $fty).max(<$fty>::NAN), -9.0); + $fassert!(<$fty>::NAN.max(<$fty>::NAN).is_nan()); } #[test] fn minimum() { @@ -806,19 +809,19 @@ macro_rules! test_float { $fassert!((-0.0 as $fty).minimum(9.0), -0.0); $fassert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); $fassert!((-0.0 as $fty).minimum(-9.0), -9.0); - $fassert!(($inf as $fty).minimum(9.0), 9.0); - $fassert!((9.0 as $fty).minimum($inf), 9.0); - $fassert!(($inf as $fty).minimum(-9.0), -9.0); - $fassert!((-9.0 as $fty).minimum($inf), -9.0); - $fassert!(($neginf as $fty).minimum(9.0), $neginf); - $fassert!((9.0 as $fty).minimum($neginf), $neginf); - $fassert!(($neginf as $fty).minimum(-9.0), $neginf); - $fassert!((-9.0 as $fty).minimum($neginf), $neginf); - $fassert!(($nan as $fty).minimum(9.0).is_nan()); - $fassert!(($nan as $fty).minimum(-9.0).is_nan()); - $fassert!((9.0 as $fty).minimum($nan).is_nan()); - $fassert!((-9.0 as $fty).minimum($nan).is_nan()); - $fassert!(($nan as $fty).minimum($nan).is_nan()); + $fassert!(<$fty>::INFINITY.minimum(9.0), 9.0); + $fassert!((9.0 as $fty).minimum(<$fty>::INFINITY), 9.0); + $fassert!(<$fty>::INFINITY.minimum(-9.0), -9.0); + $fassert!((-9.0 as $fty).minimum(<$fty>::INFINITY), -9.0); + $fassert!(<$fty>::NEG_INFINITY.minimum(9.0), <$fty>::NEG_INFINITY); + $fassert!((9.0 as $fty).minimum(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); + $fassert!(<$fty>::NEG_INFINITY.minimum(-9.0), <$fty>::NEG_INFINITY); + $fassert!((-9.0 as $fty).minimum(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); + $fassert!(<$fty>::NAN.minimum(9.0).is_nan()); + $fassert!(<$fty>::NAN.minimum(-9.0).is_nan()); + $fassert!((9.0 as $fty).minimum(<$fty>::NAN).is_nan()); + $fassert!((-9.0 as $fty).minimum(<$fty>::NAN).is_nan()); + $fassert!(<$fty>::NAN.minimum(<$fty>::NAN).is_nan()); } #[test] fn maximum() { @@ -838,19 +841,19 @@ macro_rules! test_float { $fassert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); $fassert!((-0.0 as $fty).maximum(-9.0), -0.0); $fassert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); - $fassert!(($inf as $fty).maximum(9.0), $inf); - $fassert!((9.0 as $fty).maximum($inf), $inf); - $fassert!(($inf as $fty).maximum(-9.0), $inf); - $fassert!((-9.0 as $fty).maximum($inf), $inf); - $fassert!(($neginf as $fty).maximum(9.0), 9.0); - $fassert!((9.0 as $fty).maximum($neginf), 9.0); - $fassert!(($neginf as $fty).maximum(-9.0), -9.0); - $fassert!((-9.0 as $fty).maximum($neginf), -9.0); - $fassert!(($nan as $fty).maximum(9.0).is_nan()); - $fassert!(($nan as $fty).maximum(-9.0).is_nan()); - $fassert!((9.0 as $fty).maximum($nan).is_nan()); - $fassert!((-9.0 as $fty).maximum($nan).is_nan()); - $fassert!(($nan as $fty).maximum($nan).is_nan()); + $fassert!(<$fty>::INFINITY.maximum(9.0), <$fty>::INFINITY); + $fassert!((9.0 as $fty).maximum(<$fty>::INFINITY), <$fty>::INFINITY); + $fassert!(<$fty>::INFINITY.maximum(-9.0), <$fty>::INFINITY); + $fassert!((-9.0 as $fty).maximum(<$fty>::INFINITY), <$fty>::INFINITY); + $fassert!(<$fty>::NEG_INFINITY.maximum(9.0), 9.0); + $fassert!((9.0 as $fty).maximum(<$fty>::NEG_INFINITY), 9.0); + $fassert!(<$fty>::NEG_INFINITY.maximum(-9.0), -9.0); + $fassert!((-9.0 as $fty).maximum(<$fty>::NEG_INFINITY), -9.0); + $fassert!(<$fty>::NAN.maximum(9.0).is_nan()); + $fassert!(<$fty>::NAN.maximum(-9.0).is_nan()); + $fassert!((9.0 as $fty).maximum(<$fty>::NAN).is_nan()); + $fassert!((-9.0 as $fty).maximum(<$fty>::NAN).is_nan()); + $fassert!(<$fty>::NAN.maximum(<$fty>::NAN).is_nan()); } #[test] fn midpoint() { @@ -863,38 +866,47 @@ macro_rules! test_float { $fassert!((0.0 as $fty).midpoint(0.0), 0.0); $fassert!((-0.0 as $fty).midpoint(-0.0), -0.0); $fassert!((-5.0 as $fty).midpoint(5.0), 0.0); - $fassert!(($max as $fty).midpoint($min), 0.0); - $fassert!(($min as $fty).midpoint($max), -0.0); - $fassert!(($max as $fty).midpoint($min_pos), $max / 2.); - $fassert!((-$max as $fty).midpoint($min_pos), -$max / 2.); - $fassert!(($max as $fty).midpoint(-$min_pos), $max / 2.); - $fassert!((-$max as $fty).midpoint(-$min_pos), -$max / 2.); - $fassert!(($min_pos as $fty).midpoint($max), $max / 2.); - $fassert!(($min_pos as $fty).midpoint(-$max), -$max / 2.); - $fassert!((-$min_pos as $fty).midpoint($max), $max / 2.); - $fassert!((-$min_pos as $fty).midpoint(-$max), -$max / 2.); - $fassert!(($max as $fty).midpoint($max), $max); - $fassert!(($min_pos as $fty).midpoint($min_pos), $min_pos); - $fassert!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos); - $fassert!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5); - $fassert!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5); - $fassert!(($inf as $fty).midpoint($inf), $inf); - $fassert!(($neginf as $fty).midpoint($neginf), $neginf); - $fassert!(($nan as $fty).midpoint(1.0).is_nan()); - $fassert!((1.0 as $fty).midpoint($nan).is_nan()); - $fassert!(($nan as $fty).midpoint($nan).is_nan()); + $fassert!(<$fty>::MAX.midpoint(<$fty>::MIN), 0.0); + $fassert!(<$fty>::MIN.midpoint(<$fty>::MAX), -0.0); + $fassert!(<$fty>::MAX.midpoint(<$fty>::MIN_POSITIVE), <$fty>::MAX / 2.); + $fassert!((-<$fty>::MAX).midpoint(<$fty>::MIN_POSITIVE), -<$fty>::MAX / 2.); + $fassert!(<$fty>::MAX.midpoint(-<$fty>::MIN_POSITIVE), <$fty>::MAX / 2.); + $fassert!((-<$fty>::MAX).midpoint(-<$fty>::MIN_POSITIVE), -<$fty>::MAX / 2.); + $fassert!((<$fty>::MIN_POSITIVE).midpoint(<$fty>::MAX), <$fty>::MAX / 2.); + $fassert!((<$fty>::MIN_POSITIVE).midpoint(-<$fty>::MAX), -<$fty>::MAX / 2.); + $fassert!((-<$fty>::MIN_POSITIVE).midpoint(<$fty>::MAX), <$fty>::MAX / 2.); + $fassert!((-<$fty>::MIN_POSITIVE).midpoint(-<$fty>::MAX), -<$fty>::MAX / 2.); + $fassert!(<$fty>::MAX.midpoint(<$fty>::MAX), <$fty>::MAX); + $fassert!( + (<$fty>::MIN_POSITIVE).midpoint(<$fty>::MIN_POSITIVE), + <$fty>::MIN_POSITIVE + ); + $fassert!( + (-<$fty>::MIN_POSITIVE).midpoint(-<$fty>::MIN_POSITIVE), + -<$fty>::MIN_POSITIVE + ); + $fassert!(<$fty>::MAX.midpoint(5.0), <$fty>::MAX / 2.0 + 2.5); + $fassert!(<$fty>::MAX.midpoint(-5.0), <$fty>::MAX / 2.0 - 2.5); + $fassert!(<$fty>::INFINITY.midpoint(<$fty>::INFINITY), <$fty>::INFINITY); + $fassert!( + <$fty>::NEG_INFINITY.midpoint(<$fty>::NEG_INFINITY), + <$fty>::NEG_INFINITY + ); + $fassert!(<$fty>::NAN.midpoint(1.0).is_nan()); + $fassert!((1.0 as $fty).midpoint(<$fty>::NAN).is_nan()); + $fassert!(<$fty>::NAN.midpoint(<$fty>::NAN).is_nan()); // test if large differences in magnitude are still correctly computed. // NOTE: that because of how small x and y are, x + y can never overflow // so (x + y) / 2.0 is always correct // in particular, `2.pow(i)` will never be at the max exponent, so it could // be safely doubled, while j is significantly smaller. - for i in $max_exp.saturating_sub(64)..$max_exp { + for i in <$fty>::MAX_EXP.saturating_sub(64)..<$fty>::MAX_EXP { for j in 0..64u8 { - let large = <$fty>::from(2.0f32).powi(i); + let large = (2.0 as $fty).powi(i); // a much smaller number, such that there is no chance of overflow to test // potential double rounding in midpoint's implementation. - let small = <$fty>::from(2.0f32).powi($max_exp - 1) + let small = (2.0 as $fty).powi(<$fty>::MAX_EXP - 1) * <$fty>::EPSILON * <$fty>::from(j); @@ -906,23 +918,148 @@ macro_rules! test_float { } } #[test] + fn abs() { + $fassert!((-1.0 as $fty).abs(), 1.0); + $fassert!((1.0 as $fty).abs(), 1.0); + $fassert!(<$fty>::NEG_INFINITY.abs(), <$fty>::INFINITY); + $fassert!(<$fty>::INFINITY.abs(), <$fty>::INFINITY); + } + #[test] + fn copysign() { + $fassert!((1.0 as $fty).copysign(-2.0), -1.0); + $fassert!((-1.0 as $fty).copysign(2.0), 1.0); + $fassert!(<$fty>::INFINITY.copysign(-0.0), <$fty>::NEG_INFINITY); + $fassert!(<$fty>::NEG_INFINITY.copysign(0.0), <$fty>::INFINITY); + } + #[test] fn rem_euclid() { // FIXME: Use $fassert when rem_euclid becomes const - assert!($inf.rem_euclid((42.0 as $fty)).is_nan()); - assert_eq!((42.0 as $fty).rem_euclid($inf), (42.0 as $fty)); - assert!((42.0 as $fty).rem_euclid($nan).is_nan()); - assert!($inf.rem_euclid($inf).is_nan()); - assert!($inf.rem_euclid($nan).is_nan()); - assert!($nan.rem_euclid($inf).is_nan()); + assert!(<$fty>::INFINITY.rem_euclid((42.0 as $fty)).is_nan()); + assert_eq!((42.0 as $fty).rem_euclid(<$fty>::INFINITY), (42.0 as $fty)); + assert!((42.0 as $fty).rem_euclid(<$fty>::NAN).is_nan()); + assert!(<$fty>::INFINITY.rem_euclid(<$fty>::INFINITY).is_nan()); + assert!(<$fty>::INFINITY.rem_euclid(<$fty>::NAN).is_nan()); + assert!(<$fty>::NAN.rem_euclid(<$fty>::INFINITY).is_nan()); } #[test] fn div_euclid() { // FIXME: Use $fassert when div_euclid becomes const - assert_eq!((42.0 as $fty).div_euclid($inf), 0.0); - assert!((42.0 as $fty).div_euclid($nan).is_nan()); - assert!($inf.div_euclid($inf).is_nan()); - assert!($inf.div_euclid($nan).is_nan()); - assert!($nan.div_euclid($inf).is_nan()); + assert_eq!((42.0 as $fty).div_euclid(<$fty>::INFINITY), 0.0); + assert!((42.0 as $fty).div_euclid(<$fty>::NAN).is_nan()); + assert!(<$fty>::INFINITY.div_euclid(<$fty>::INFINITY).is_nan()); + assert!(<$fty>::INFINITY.div_euclid(<$fty>::NAN).is_nan()); + assert!(<$fty>::NAN.div_euclid(<$fty>::INFINITY).is_nan()); + } + #[test] + #[cfg(not(bootstrap))] + fn floor() { + $fassert!((0.0 as $fty).floor(), 0.0); + $fassert!((0.0 as $fty).floor().is_sign_positive()); + $fassert!((-0.0 as $fty).floor(), -0.0); + $fassert!((-0.0 as $fty).floor().is_sign_negative()); + $fassert!((0.5 as $fty).floor(), 0.0); + $fassert!((-0.5 as $fty).floor(), -1.0); + $fassert!((1.5 as $fty).floor(), 1.0); + $fassert!(<$fty>::MAX.floor(), <$fty>::MAX); + $fassert!(<$fty>::MIN.floor(), <$fty>::MIN); + $fassert!(<$fty>::MIN_POSITIVE.floor(), 0.0); + $fassert!((-<$fty>::MIN_POSITIVE).floor(), -1.0); + $fassert!(<$fty>::NAN.floor().is_nan()); + $fassert!(<$fty>::INFINITY.floor(), <$fty>::INFINITY); + $fassert!(<$fty>::NEG_INFINITY.floor(), <$fty>::NEG_INFINITY); + } + #[test] + #[cfg(not(bootstrap))] + fn ceil() { + $fassert!((0.0 as $fty).ceil(), 0.0); + $fassert!((0.0 as $fty).ceil().is_sign_positive()); + $fassert!((-0.0 as $fty).ceil(), 0.0); + $fassert!((-0.0 as $fty).ceil().is_sign_negative()); + $fassert!((0.5 as $fty).ceil(), 1.0); + $fassert!((-0.5 as $fty).ceil(), 0.0); + $fassert!(<$fty>::MAX.ceil(), <$fty>::MAX); + $fassert!(<$fty>::MIN.ceil(), <$fty>::MIN); + $fassert!(<$fty>::MIN_POSITIVE.ceil(), 1.0); + $fassert!((-<$fty>::MIN_POSITIVE).ceil(), 0.0); + $fassert!(<$fty>::NAN.ceil().is_nan()); + $fassert!(<$fty>::INFINITY.ceil(), <$fty>::INFINITY); + $fassert!(<$fty>::NEG_INFINITY.ceil(), <$fty>::NEG_INFINITY); + } + #[test] + #[cfg(not(bootstrap))] + fn round() { + $fassert!((0.0 as $fty).round(), 0.0); + $fassert!((0.0 as $fty).round().is_sign_positive()); + $fassert!((-0.0 as $fty).round(), -0.0); + $fassert!((-0.0 as $fty).round().is_sign_negative()); + $fassert!((0.5 as $fty).round(), 1.0); + $fassert!((-0.5 as $fty).round(), -1.0); + $fassert!(<$fty>::MAX.round(), <$fty>::MAX); + $fassert!(<$fty>::MIN.round(), <$fty>::MIN); + $fassert!(<$fty>::MIN_POSITIVE.round(), 0.0); + $fassert!((-<$fty>::MIN_POSITIVE).round(), 0.0); + $fassert!(<$fty>::NAN.round().is_nan()); + $fassert!(<$fty>::INFINITY.round(), <$fty>::INFINITY); + $fassert!(<$fty>::NEG_INFINITY.round(), <$fty>::NEG_INFINITY); + } + #[test] + #[cfg(not(bootstrap))] + fn round_ties_even() { + $fassert!((0.0 as $fty).round_ties_even(), 0.0); + $fassert!((0.0 as $fty).round_ties_even().is_sign_positive()); + $fassert!((-0.0 as $fty).round_ties_even(), -0.0); + $fassert!((-0.0 as $fty).round_ties_even().is_sign_negative()); + $fassert!((0.5 as $fty).round_ties_even(), 0.0); + $fassert!((0.5 as $fty).round_ties_even().is_sign_positive()); + $fassert!((-0.5 as $fty).round_ties_even(), -0.0); + $fassert!((-0.5 as $fty).round_ties_even().is_sign_negative()); + $fassert!(<$fty>::MAX.round_ties_even(), <$fty>::MAX); + $fassert!(<$fty>::MIN.round_ties_even(), <$fty>::MIN); + $fassert!(<$fty>::MIN_POSITIVE.round_ties_even(), 0.0); + $fassert!((-<$fty>::MIN_POSITIVE).round_ties_even(), 0.0); + $fassert!(<$fty>::NAN.round_ties_even().is_nan()); + $fassert!(<$fty>::INFINITY.round_ties_even(), <$fty>::INFINITY); + $fassert!(<$fty>::NEG_INFINITY.round_ties_even(), <$fty>::NEG_INFINITY); + } + #[test] + #[cfg(not(bootstrap))] + fn trunc() { + $fassert!((0.0 as $fty).trunc(), 0.0); + $fassert!((0.0 as $fty).trunc().is_sign_positive()); + $fassert!((-0.0 as $fty).trunc(), -0.0); + $fassert!((-0.0 as $fty).trunc().is_sign_negative()); + $fassert!((0.5 as $fty).trunc(), 0.0); + $fassert!((0.5 as $fty).trunc().is_sign_positive()); + $fassert!((-0.5 as $fty).trunc(), -0.0); + $fassert!((-0.5 as $fty).trunc().is_sign_negative()); + $fassert!(<$fty>::MAX.trunc(), <$fty>::MAX); + $fassert!(<$fty>::MIN.trunc(), <$fty>::MIN); + $fassert!(<$fty>::MIN_POSITIVE.trunc(), 0.0); + $fassert!((-<$fty>::MIN_POSITIVE).trunc(), 0.0); + $fassert!(<$fty>::NAN.trunc().is_nan()); + $fassert!(<$fty>::INFINITY.trunc(), <$fty>::INFINITY); + $fassert!(<$fty>::NEG_INFINITY.trunc(), <$fty>::NEG_INFINITY); + } + #[test] + #[cfg(not(bootstrap))] + fn fract() { + $fassert!((0.0 as $fty).fract(), 0.0); + $fassert!((0.0 as $fty).fract().is_sign_positive()); + $fassert!((-0.0 as $fty).fract(), 0.0); + $fassert!((-0.0 as $fty).fract().is_sign_positive()); + $fassert!((0.5 as $fty).fract(), 0.5); + $fassert!((0.5 as $fty).fract().is_sign_positive()); + $fassert!((-0.5 as $fty).fract(), -0.5); + $fassert!((-0.5 as $fty).fract().is_sign_negative()); + $fassert!(<$fty>::MAX.fract(), 0.0); + $fassert!(<$fty>::MIN.fract(), 0.0); + $fassert!(<$fty>::MIN_POSITIVE.fract(), <$fty>::MIN_POSITIVE); + $fassert!(<$fty>::MIN_POSITIVE.fract().is_sign_positive()); + $fassert!((-<$fty>::MIN_POSITIVE).fract(), -<$fty>::MIN_POSITIVE); + $fassert!((-<$fty>::MIN_POSITIVE).fract().is_sign_negative()); + $fassert!(<$fty>::NAN.fract().is_nan()); + $fassert!(<$fty>::INFINITY.fract().is_nan()); + $fassert!(<$fty>::NEG_INFINITY.fract().is_nan()); } } }; @@ -948,51 +1085,7 @@ macro_rules! float_const_assert { }; } -test_float!( - f32, - float_assert, - f32, - f32::INFINITY, - f32::NEG_INFINITY, - f32::NAN, - f32::MIN, - f32::MAX, - f32::MIN_POSITIVE, - f32::MAX_EXP -); -test_float!( - f32_const, - float_const_assert, - f32, - f32::INFINITY, - f32::NEG_INFINITY, - f32::NAN, - f32::MIN, - f32::MAX, - f32::MIN_POSITIVE, - f32::MAX_EXP -); -test_float!( - f64, - float_assert, - f64, - f64::INFINITY, - f64::NEG_INFINITY, - f64::NAN, - f64::MIN, - f64::MAX, - f64::MIN_POSITIVE, - f64::MAX_EXP -); -test_float!( - f64_const, - float_const_assert, - f64, - f64::INFINITY, - f64::NEG_INFINITY, - f64::NAN, - f64::MIN, - f64::MAX, - f64::MIN_POSITIVE, - f64::MAX_EXP -); +test_float!(f32, float_assert, f32); +test_float!(f32_const, float_const_assert, f32); +test_float!(f64, float_assert, f64); +test_float!(f64_const, float_const_assert, f64); diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index b8bc2a3af4cd..1d79246356a3 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -10,3 +10,7 @@ std = { path = "../std" } # loaded from sysroot causing duplicate lang items and other similar errors. core = { path = "../core" } rustc-literal-escaper = { version = "0.0.2", features = ["rustc-dep-of-std"] } + +[features] +default = ["rustc-dep-of-std"] +rustc-dep-of-std = [] diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index b4fd20c0c176..32c306be94ec 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -32,6 +32,7 @@ #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] +#![allow(rustc::internal)] // Can't use FxHashMap when compiled as part of the standard library #![warn(rustdoc::unescaped_backticks)] #![warn(unreachable_pub)] #![deny(unsafe_op_in_unsafe_fn)] @@ -95,7 +96,7 @@ pub fn is_available() -> bool { /// /// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` /// and `#[proc_macro_derive]` definitions. -#[rustc_diagnostic_item = "TokenStream"] +#[cfg_attr(feature = "rustc-dep-of-std", rustc_diagnostic_item = "TokenStream")] #[stable(feature = "proc_macro_lib", since = "1.15.0")] #[derive(Clone)] pub struct TokenStream(Option); diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 4ff4895ecde7..31371f06b386 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -18,7 +18,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.159" } +compiler_builtins = { version = "=0.1.160" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 3530f890f529..edbdd0411457 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -648,14 +648,14 @@ impl HashMap { Drain { base: self.base.drain() } } - /// Creates an iterator which uses a closure to determine if an element should be removed. + /// Creates an iterator which uses a closure to determine if an element (key-value pair) should be removed. /// - /// If the closure returns true, the element is removed from the map and yielded. - /// If the closure returns false, or panics, the element remains in the map and will not be - /// yielded. + /// If the closure returns `true`, the element is removed from the map and + /// yielded. If the closure returns `false`, or panics, the element remains + /// in the map and will not be yielded. /// - /// Note that `extract_if` lets you mutate every value in the filter closure, regardless of - /// whether you choose to keep or remove it. + /// The iterator also lets you mutate the value of each element in the + /// closure, regardless of whether you choose to keep or remove it. /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 8514dfd9a984..482d57b47f67 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -276,11 +276,11 @@ impl HashSet { Drain { base: self.base.drain() } } - /// Creates an iterator which uses a closure to determine if a value should be removed. + /// Creates an iterator which uses a closure to determine if an element should be removed. /// - /// If the closure returns true, then the value is removed and yielded. - /// If the closure returns false, the value will remain in the list and will not be yielded - /// by the iterator. + /// If the closure returns `true`, the element is removed from the set and + /// yielded. If the closure returns `false`, or panics, the element remains + /// in the set and will not be yielded. /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 567916099101..024cb71b915d 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -172,7 +172,7 @@ pub use core::ffi::c_void; all supported platforms", issue = "44930" )] -pub use core::ffi::{VaList, VaListImpl}; +pub use core::ffi::{VaArgSafe, VaList, VaListImpl}; #[stable(feature = "core_ffi_c", since = "1.64.0")] pub use core::ffi::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 72bdf03ee61a..b0580b467be6 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -594,9 +594,9 @@ impl OsString { /// The slice must be valid for the platform encoding (as described in /// [`OsStr::from_encoded_bytes_unchecked`]). /// - /// This bypasses the encoding-dependent surrogate joining, so `self` must - /// not end with a leading surrogate half and `other` must not start with - /// with a trailing surrogate half. + /// This bypasses the encoding-dependent surrogate joining, so either + /// `self` must not end with a leading surrogate half, or `other` must not + /// start with a trailing surrogate half. #[inline] pub(crate) unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { // SAFETY: Guaranteed by caller. @@ -1040,7 +1040,7 @@ impl OsStr { /// Converts a [Box]<[OsStr]> into an [`OsString`] without copying or allocating. #[stable(feature = "into_boxed_os_str", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] - pub fn into_os_string(self: Box) -> OsString { + pub fn into_os_string(self: Box) -> OsString { let boxed = unsafe { Box::from_raw(Box::into_raw(self) as *mut Slice) }; OsString { inner: Buf::from_box(boxed) } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 11f439b9996d..711efc7d011c 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -285,8 +285,7 @@ pub fn read>(path: P) -> io::Result> { fn inner(path: &Path) -> io::Result> { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len() as usize).ok(); - let mut bytes = Vec::new(); - bytes.try_reserve_exact(size.unwrap_or(0))?; + let mut bytes = Vec::try_with_capacity(size.unwrap_or(0))?; io::default_read_to_end(&mut file, &mut bytes, size)?; Ok(bytes) } @@ -392,6 +391,16 @@ impl fmt::Display for TryLockError { } } +#[unstable(feature = "file_lock", issue = "130994")] +impl From for io::Error { + fn from(err: TryLockError) -> io::Error { + match err { + TryLockError::Error(err) => err, + TryLockError::WouldBlock => io::ErrorKind::WouldBlock.into(), + } + } +} + impl File { /// Attempts to open a file in read-only mode. /// @@ -821,11 +830,14 @@ impl File { /// /// fn main() -> std::io::Result<()> { /// let f = File::create("foo.txt")?; + /// // Explicit handling of the WouldBlock error /// match f.try_lock() { /// Ok(_) => (), /// Err(TryLockError::WouldBlock) => (), // Lock not acquired /// Err(TryLockError::Error(err)) => return Err(err), /// } + /// // Alternately, propagate the error as an io::Error + /// f.try_lock()?; /// Ok(()) /// } /// ``` @@ -882,11 +894,14 @@ impl File { /// /// fn main() -> std::io::Result<()> { /// let f = File::open("foo.txt")?; + /// // Explicit handling of the WouldBlock error /// match f.try_lock_shared() { /// Ok(_) => (), /// Err(TryLockError::WouldBlock) => (), // Lock not acquired /// Err(TryLockError::Error(err)) => return Err(err), /// } + /// // Alternately, propagate the error as an io::Error + /// f.try_lock_shared()?; /// /// Ok(()) /// } @@ -2804,8 +2819,8 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// Recursively create a directory and all of its parent components if they /// are missing. /// -/// If this function returns an error, some of the parent components might have -/// been created already. +/// This function is not atomic. If it returns an error, any parent components it was able to create +/// will remain. /// /// If the empty path is passed to this function, it always succeeds without /// creating any directories. @@ -2900,17 +2915,28 @@ 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 REDOX) and the `CreateFileW`, `GetFileInformationByHandleEx`, -/// `SetFileInformationByHandle`, and `NtCreateFile` functions on Windows. Note that, this -/// [may change in the future][changes]. +/// These implementation details [may change in the future][changes]. +/// +/// - "Unix-like": By default, this function currently corresponds to +/// `openat`, `fdopendir`, `unlinkat` and `lstat` +/// on Unix-family platforms, except where noted otherwise. +/// - "Windows": This function currently corresponds to `CreateFileW`, +/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile`. +/// +/// ## Time-of-check to time-of-use (TOCTOU) race conditions +/// On a few platforms there is no way to remove a directory's contents without following symlinks +/// unless you perform a check and then operate on paths based on that directory. +/// This allows concurrently-running code to replace the directory with a symlink after the check, +/// causing a removal to instead operate on a path based on the symlink. This is a TOCTOU race. +/// By default, `fs::remove_dir_all` protects against a symlink TOCTOU race on all platforms +/// except the following. It should not be used in security-sensitive contexts on these platforms: +/// - Miri: Even when emulating targets where the underlying implementation will protect against +/// TOCTOU races, Miri will not do so. +/// - Redox OS: This function does not protect against TOCTOU races, as Redox does not implement +/// the required platform support to do so. /// /// [changes]: io#platform-specific-behavior /// -/// 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 /// /// See [`fs::remove_file`] and [`fs::remove_dir`]. diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index c81a5ff4d96e..c81e3af2f0d4 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -366,6 +366,28 @@ fn file_lock_blocking_async() { t.join().unwrap(); } +#[test] +#[cfg(windows)] +fn file_try_lock_async() { + const FILE_FLAG_OVERLAPPED: u32 = 0x40000000; + + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_try_lock_async.txt"); + let f1 = check!(File::create(filename)); + let f2 = + check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); + + // Check that shared locks block exclusive locks + check!(f1.lock_shared()); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); + check!(f1.unlock()); + + // Check that exclusive locks block all locks + check!(f1.lock()); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); + assert_matches!(f2.try_lock_shared(), Err(TryLockError::WouldBlock)); +} + #[test] fn file_test_io_seek_shakedown() { // 01234567890123 @@ -1760,8 +1782,30 @@ fn test_eq_windows_file_type() { // Change the readonly attribute of one file. let mut perms = file1.metadata().unwrap().permissions(); perms.set_readonly(true); - file1.set_permissions(perms).unwrap(); + file1.set_permissions(perms.clone()).unwrap(); + #[cfg(target_vendor = "win7")] + let _g = ReadonlyGuard { file: &file1, perms }; assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type()); + + // Reset the attribute before the `TmpDir`'s drop that removes the + // associated directory, which fails with a `PermissionDenied` error when + // running under Windows 7. + #[cfg(target_vendor = "win7")] + struct ReadonlyGuard<'f> { + file: &'f File, + perms: fs::Permissions, + } + #[cfg(target_vendor = "win7")] + impl<'f> Drop for ReadonlyGuard<'f> { + fn drop(&mut self) { + self.perms.set_readonly(false); + let res = self.file.set_permissions(self.perms.clone()); + + if !thread::panicking() { + res.unwrap(); + } + } + } } /// Regression test for https://github.com/rust-lang/rust/issues/50619. diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 96fac4f6bde6..03f5f838311a 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1214,7 +1214,7 @@ pub trait Read { where Self: Sized, { - Take { inner: self, limit } + Take { inner: self, len: limit, limit } } } @@ -2830,6 +2830,7 @@ impl SizeHint for Chain { #[derive(Debug)] pub struct Take { inner: T, + len: u64, limit: u64, } @@ -2864,6 +2865,12 @@ impl Take { self.limit } + /// Returns the number of bytes read so far. + #[unstable(feature = "seek_io_take_position", issue = "97227")] + pub fn position(&self) -> u64 { + self.len - self.limit + } + /// Sets the number of bytes that can be read before this instance will /// return EOF. This is the same as constructing a new `Take` instance, so /// the amount of bytes read and the previous limit value don't matter when @@ -2889,6 +2896,7 @@ impl Take { /// ``` #[stable(feature = "take_set_limit", since = "1.27.0")] pub fn set_limit(&mut self, limit: u64) { + self.len = limit; self.limit = limit; } @@ -3076,6 +3084,49 @@ impl SizeHint for Take { } } +#[stable(feature = "seek_io_take", since = "CURRENT_RUSTC_VERSION")] +impl Seek for Take { + fn seek(&mut self, pos: SeekFrom) -> Result { + let new_position = match pos { + SeekFrom::Start(v) => Some(v), + SeekFrom::Current(v) => self.position().checked_add_signed(v), + SeekFrom::End(v) => self.len.checked_add_signed(v), + }; + let new_position = match new_position { + Some(v) if v <= self.len => v, + _ => return Err(ErrorKind::InvalidInput.into()), + }; + while new_position != self.position() { + if let Some(offset) = new_position.checked_signed_diff(self.position()) { + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + break; + } + let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + } + Ok(new_position) + } + + fn stream_len(&mut self) -> Result { + Ok(self.len) + } + + fn stream_position(&mut self) -> Result { + Ok(self.position()) + } + + fn seek_relative(&mut self, offset: i64) -> Result<()> { + if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { + return Err(ErrorKind::InvalidInput.into()); + } + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + Ok(()) + } +} + /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs index 47243806cd2d..16727d445416 100644 --- a/library/std/src/io/pipe.rs +++ b/library/std/src/io/pipe.rs @@ -38,30 +38,44 @@ use crate::sys_common::{FromInner, IntoInner}; /// > not rely on a particular capacity: an application should be designed so that a reading process /// > consumes data as soon as it is available, so that a writing process does not remain blocked. /// -/// # Examples +/// # Example /// /// ```no_run /// # #[cfg(miri)] fn main() {} /// # #[cfg(not(miri))] /// # fn main() -> std::io::Result<()> { +/// use std::io::{Read, Write, pipe}; /// use std::process::Command; -/// use std::io::{pipe, Read, Write}; -/// let (ping_rx, mut ping_tx) = pipe()?; -/// let (mut pong_rx, pong_tx) = pipe()?; +/// let (ping_reader, mut ping_writer) = pipe()?; +/// let (mut pong_reader, pong_writer) = pipe()?; /// -/// // Spawn a process that echoes its input. -/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; +/// // Spawn a child process that echoes its input. +/// let mut echo_command = Command::new("cat"); +/// echo_command.stdin(ping_reader); +/// echo_command.stdout(pong_writer); +/// let mut echo_child = echo_command.spawn()?; /// -/// ping_tx.write_all(b"hello")?; -/// // Close to unblock echo_server's reader. -/// drop(ping_tx); +/// // Send input to the child process. Note that because we're writing all the input before we +/// // read any output, this could deadlock if the child's input and output pipe buffers both +/// // filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer +/// // inputs we'd need to read and write at the same time, e.g. using threads. +/// ping_writer.write_all(b"hello")?; +/// +/// // `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer +/// // remains open. We need to drop our ping writer, or read_to_string will deadlock below. +/// drop(ping_writer); +/// +/// // The pong reader can't report EOF while any pong writer remains open. Our Command object is +/// // holding a pong writer, and again read_to_string will deadlock if we don't drop it. +/// drop(echo_command); /// /// let mut buf = String::new(); -/// // Block until echo_server's writer is closed. -/// pong_rx.read_to_string(&mut buf)?; +/// // Block until `cat` closes its stdout (a pong writer). +/// pong_reader.read_to_string(&mut buf)?; /// assert_eq!(&buf, "hello"); /// -/// echo_server.wait()?; +/// // At this point we know `cat` has exited, but we still need to wait to clean up the "zombie". +/// echo_child.wait()?; /// # Ok(()) /// # } /// ``` diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index fd962b0415c7..b22988d4a8a9 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -416,6 +416,126 @@ fn seek_position() -> io::Result<()> { Ok(()) } +#[test] +fn take_seek() -> io::Result<()> { + let mut buf = Cursor::new(b"0123456789"); + buf.set_position(2); + let mut take = buf.by_ref().take(4); + let mut buf1 = [0u8; 1]; + let mut buf2 = [0u8; 2]; + assert_eq!(take.position(), 0); + + assert_eq!(take.seek(SeekFrom::Start(0))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + assert_eq!(take.seek(SeekFrom::Start(1))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + assert_eq!(take.seek(SeekFrom::Start(2))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + assert_eq!(take.seek(SeekFrom::Start(3))?, 3); + take.read_exact(&mut buf1)?; + assert_eq!(buf1, [b'5']); + assert_eq!(take.seek(SeekFrom::Start(4))?, 4); + assert_eq!(take.read(&mut buf1)?, 0); + + assert_eq!(take.seek(SeekFrom::End(0))?, 4); + assert_eq!(take.seek(SeekFrom::End(-1))?, 3); + take.read_exact(&mut buf1)?; + assert_eq!(buf1, [b'5']); + assert_eq!(take.seek(SeekFrom::End(-2))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + assert_eq!(take.seek(SeekFrom::End(-3))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + assert_eq!(take.seek(SeekFrom::End(-4))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + + assert_eq!(take.seek(SeekFrom::Current(0))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + + assert_eq!(take.seek(SeekFrom::Current(-3))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + + assert_eq!(take.seek(SeekFrom::Current(-1))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + + assert_eq!(take.seek(SeekFrom::Current(-4))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + + assert_eq!(take.seek(SeekFrom::Current(2))?, 4); + assert_eq!(take.read(&mut buf1)?, 0); + + Ok(()) +} + +#[test] +fn take_seek_error() { + let buf = Cursor::new(b"0123456789"); + let mut take = buf.take(2); + assert!(take.seek(SeekFrom::Start(3)).is_err()); + assert!(take.seek(SeekFrom::End(1)).is_err()); + assert!(take.seek(SeekFrom::End(-3)).is_err()); + assert!(take.seek(SeekFrom::Current(-1)).is_err()); + assert!(take.seek(SeekFrom::Current(3)).is_err()); +} + +struct ExampleHugeRangeOfZeroes { + position: u64, +} + +impl Read for ExampleHugeRangeOfZeroes { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let max = buf.len().min(usize::MAX); + for i in 0..max { + if self.position == u64::MAX { + return Ok(i); + } + self.position += 1; + buf[i] = 0; + } + Ok(max) + } +} + +impl Seek for ExampleHugeRangeOfZeroes { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + match pos { + io::SeekFrom::Start(i) => self.position = i, + io::SeekFrom::End(i) if i >= 0 => self.position = u64::MAX, + io::SeekFrom::End(i) => self.position = self.position - i.unsigned_abs(), + io::SeekFrom::Current(i) => { + self.position = if i >= 0 { + self.position.saturating_add(i.unsigned_abs()) + } else { + self.position.saturating_sub(i.unsigned_abs()) + }; + } + } + Ok(self.position) + } +} + +#[test] +fn take_seek_big_offsets() -> io::Result<()> { + let inner = ExampleHugeRangeOfZeroes { position: 1 }; + let mut take = inner.take(u64::MAX - 2); + assert_eq!(take.seek(io::SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2); + assert_eq!(take.inner.position, u64::MAX - 1); + assert_eq!(take.seek(io::SeekFrom::Start(0))?, 0); + assert_eq!(take.inner.position, 1); + assert_eq!(take.seek(io::SeekFrom::End(-1))?, u64::MAX - 3); + assert_eq!(take.inner.position, u64::MAX - 2); + Ok(()) +} + // A simple example reader which uses the default implementation of // read_to_end. struct ExampleSliceReader<'a> { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ca04a381271b..74a343398602 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -276,12 +276,12 @@ // tidy-alphabetical-start // stabilization was reverted after it hit beta +#![cfg_attr(not(bootstrap), feature(autodiff))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_experimental_arch)] -#![feature(autodiff)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] @@ -325,6 +325,7 @@ #![feature(try_blocks)] #![feature(try_trait_v2)] #![feature(type_alias_impl_trait)] +#![feature(unsigned_signed_diff)] // tidy-alphabetical-end // // Library features (core): @@ -334,6 +335,7 @@ #![feature(bstr_internals)] #![feature(char_internals)] #![feature(clone_to_uninit)] +#![feature(const_float_round_methods)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(duration_constants)] @@ -584,11 +586,13 @@ pub use alloc_crate::string; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::vec; -#[unstable(feature = "f128", issue = "116909")] +#[path = "num/f128.rs"] pub mod f128; -#[unstable(feature = "f16", issue = "116909")] +#[path = "num/f16.rs"] pub mod f16; +#[path = "num/f32.rs"] pub mod f32; +#[path = "num/f64.rs"] pub mod f64; #[macro_use] @@ -635,12 +639,15 @@ pub mod simd { #[doc(inline)] pub use crate::std_float::StdFloat; } + #[unstable(feature = "autodiff", issue = "124509")] +#[cfg(not(bootstrap))] /// This module provides support for automatic differentiation. pub mod autodiff { /// This macro handles automatic differentiation. - pub use core::autodiff::autodiff; + pub use core::autodiff::{autodiff_forward, autodiff_reverse}; } + #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. @@ -699,8 +706,8 @@ mod panicking; #[allow(dead_code, unused_attributes, fuzzy_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; -#[unstable(feature = "cfg_match", issue = "115585")] -pub use core::cfg_match; +#[unstable(feature = "cfg_select", issue = "115585")] +pub use core::cfg_select; #[unstable( feature = "concat_bytes", issue = "87555", diff --git a/library/std/src/f128.rs b/library/std/src/num/f128.rs similarity index 99% rename from library/std/src/f128.rs rename to library/std/src/num/f128.rs index bb4acde48224..c0190de089f4 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/num/f128.rs @@ -4,6 +4,8 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. +#![unstable(feature = "f128", issue = "116909")] + #[unstable(feature = "f128", issue = "116909")] pub use core::f128::consts; diff --git a/library/std/src/f16.rs b/library/std/src/num/f16.rs similarity index 99% rename from library/std/src/f16.rs rename to library/std/src/num/f16.rs index 4792eac1f9e2..4a4a8fd839a9 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/num/f16.rs @@ -4,6 +4,8 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. +#![unstable(feature = "f16", issue = "116909")] + #[unstable(feature = "f16", issue = "116909")] pub use core::f16::consts; diff --git a/library/std/src/f32.rs b/library/std/src/num/f32.rs similarity index 97% rename from library/std/src/f32.rs rename to library/std/src/num/f32.rs index 94140d01d8b7..b7f6529ac402 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/num/f32.rs @@ -44,9 +44,10 @@ impl f32 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn floor(self) -> f32 { - core::f32::floor(self) + pub const fn floor(self) -> f32 { + core::f32::math::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -66,9 +67,10 @@ impl f32 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn ceil(self) -> f32 { - core::f32::ceil(self) + pub const fn ceil(self) -> f32 { + core::f32::math::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -94,9 +96,10 @@ impl f32 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn round(self) -> f32 { - core::f32::round(self) + pub const fn round(self) -> f32 { + core::f32::math::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -120,9 +123,10 @@ impl f32 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "round_ties_even", since = "1.77.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn round_ties_even(self) -> f32 { - core::f32::round_ties_even(self) + pub const fn round_ties_even(self) -> f32 { + core::f32::math::round_ties_even(self) } /// Returns the integer part of `self`. @@ -145,9 +149,10 @@ impl f32 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn trunc(self) -> f32 { - core::f32::trunc(self) + pub const fn trunc(self) -> f32 { + core::f32::math::trunc(self) } /// Returns the fractional part of `self`. @@ -168,9 +173,10 @@ impl f32 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn fract(self) -> f32 { - core::f32::fract(self) + pub const fn fract(self) -> f32 { + core::f32::math::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +218,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f32, b: f32) -> f32 { - core::f32::mul_add(self, a, b) + core::f32::math::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,7 +248,7 @@ impl f32 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f32) -> f32 { - core::f32::div_euclid(self, rhs) + core::f32::math::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -279,7 +285,7 @@ impl f32 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f32) -> f32 { - core::f32::rem_euclid(self, rhs) + core::f32::math::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -307,7 +313,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f32 { - core::f32::powi(self, n) + core::f32::math::powi(self, n) } /// Raises a number to a floating point power. @@ -362,7 +368,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f32 { - core::f32::sqrt(self) + core::f32::math::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -595,7 +601,7 @@ impl f32 { )] pub fn abs_sub(self, other: f32) -> f32 { #[allow(deprecated)] - core::f32::abs_sub(self, other) + core::f32::math::abs_sub(self, other) } /// Returns the cube root of a number. @@ -622,7 +628,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f32 { - core::f32::cbrt(self) + core::f32::math::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the diff --git a/library/std/src/f64.rs b/library/std/src/num/f64.rs similarity index 97% rename from library/std/src/f64.rs rename to library/std/src/num/f64.rs index 051061ae6055..75e35a8db335 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/num/f64.rs @@ -44,9 +44,10 @@ impl f64 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn floor(self) -> f64 { - core::f64::floor(self) + pub const fn floor(self) -> f64 { + core::f64::math::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -66,9 +67,10 @@ impl f64 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn ceil(self) -> f64 { - core::f64::ceil(self) + pub const fn ceil(self) -> f64 { + core::f64::math::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -94,9 +96,10 @@ impl f64 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn round(self) -> f64 { - core::f64::round(self) + pub const fn round(self) -> f64 { + core::f64::math::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -120,9 +123,10 @@ impl f64 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "round_ties_even", since = "1.77.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn round_ties_even(self) -> f64 { - core::f64::round_ties_even(self) + pub const fn round_ties_even(self) -> f64 { + core::f64::math::round_ties_even(self) } /// Returns the integer part of `self`. @@ -145,9 +149,10 @@ impl f64 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn trunc(self) -> f64 { - core::f64::trunc(self) + pub const fn trunc(self) -> f64 { + core::f64::math::trunc(self) } /// Returns the fractional part of `self`. @@ -168,9 +173,10 @@ impl f64 { #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_round_methods", issue = "141555")] #[inline] - pub fn fract(self) -> f64 { - core::f64::fract(self) + pub const fn fract(self) -> f64 { + core::f64::math::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +218,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f64, b: f64) -> f64 { - core::f64::mul_add(self, a, b) + core::f64::math::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,7 +248,7 @@ impl f64 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f64) -> f64 { - core::f64::div_euclid(self, rhs) + core::f64::math::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -279,7 +285,7 @@ impl f64 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f64) -> f64 { - core::f64::rem_euclid(self, rhs) + core::f64::math::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -307,7 +313,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f64 { - core::f64::powi(self, n) + core::f64::math::powi(self, n) } /// Raises a number to a floating point power. @@ -362,7 +368,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f64 { - core::f64::sqrt(self) + core::f64::math::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -595,7 +601,7 @@ impl f64 { )] pub fn abs_sub(self, other: f64) -> f64 { #[allow(deprecated)] - core::f64::abs_sub(self, other) + core::f64::math::abs_sub(self, other) } /// Returns the cube root of a number. @@ -622,7 +628,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f64 { - core::f64::cbrt(self) + core::f64::math::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the diff --git a/library/std/src/num.rs b/library/std/src/num/mod.rs similarity index 100% rename from library/std/src/num.rs rename to library/std/src/num/mod.rs diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs index aed772056e1b..41009c0e2845 100644 --- a/library/std/src/os/net/linux_ext/addr.rs +++ b/library/std/src/os/net/linux_ext/addr.rs @@ -23,7 +23,10 @@ pub trait SocketAddrExt: Sealed { /// /// ```no_run /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// #[cfg(target_os = "linux")] /// use std::os::linux::net::SocketAddrExt; + /// #[cfg(target_os = "android")] + /// use std::os::android::net::SocketAddrExt; /// /// fn main() -> std::io::Result<()> { /// let addr = SocketAddr::from_abstract_name(b"hidden")?; @@ -48,7 +51,10 @@ pub trait SocketAddrExt: Sealed { /// /// ```no_run /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// #[cfg(target_os = "linux")] /// use std::os::linux::net::SocketAddrExt; + /// #[cfg(target_os = "android")] + /// use std::os::android::net::SocketAddrExt; /// /// fn main() -> std::io::Result<()> { /// let name = b"hidden"; diff --git a/library/std/src/os/net/linux_ext/socket.rs b/library/std/src/os/net/linux_ext/socket.rs index 4e4168f693c3..a15feb6bd9f2 100644 --- a/library/std/src/os/net/linux_ext/socket.rs +++ b/library/std/src/os/net/linux_ext/socket.rs @@ -27,7 +27,10 @@ pub trait UnixSocketExt: Sealed { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] + /// #[cfg(target_os = "linux")] /// use std::os::linux::net::UnixSocketExt; + /// #[cfg(target_os = "android")] + /// use std::os::android::net::UnixSocketExt; /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index c8d012962d45..95dffb3bc434 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -25,7 +25,10 @@ pub trait TcpStreamExt: Sealed { /// ```no_run /// #![feature(tcp_quickack)] /// use std::net::TcpStream; + /// #[cfg(target_os = "linux")] /// use std::os::linux::net::TcpStreamExt; + /// #[cfg(target_os = "android")] + /// use std::os::android::net::TcpStreamExt; /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); @@ -43,7 +46,10 @@ pub trait TcpStreamExt: Sealed { /// ```no_run /// #![feature(tcp_quickack)] /// use std::net::TcpStream; + /// #[cfg(target_os = "linux")] /// use std::os::linux::net::TcpStreamExt; + /// #[cfg(target_os = "android")] + /// use std::os::android::net::TcpStreamExt; /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 7c3fa7d6507e..57ce3c5a4bf4 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -8,6 +8,7 @@ use cfg_if::cfg_if; use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::path::Path; use crate::sealed::Sealed; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; @@ -197,6 +198,18 @@ pub trait CommandExt: Sealed { /// ``` #[stable(feature = "process_set_process_group", since = "1.64.0")] fn process_group(&mut self, pgroup: i32) -> &mut process::Command; + + /// Set the root of the child process. This calls `chroot` in the child process before executing + /// the command. + /// + /// This happens before changing to the directory specified with + /// [`process::Command::current_dir`], and that directory will be relative to the new root. + /// + /// If no directory has been specified with [`process::Command::current_dir`], this will set the + /// directory to `/`, to avoid leaving the current directory outside the chroot. (This is an + /// intentional difference from the underlying `chroot` system call.) + #[unstable(feature = "process_chroot", issue = "141298")] + fn chroot>(&mut self, dir: P) -> &mut process::Command; } #[stable(feature = "rust1", since = "1.0.0")] @@ -242,6 +255,11 @@ impl CommandExt for process::Command { self.as_inner_mut().pgroup(pgroup); self } + + fn chroot>(&mut self, dir: P) -> &mut process::Command { + self.as_inner_mut().chroot(dir.as_ref()); + self + } } /// Unix-specific extensions to [`process::ExitStatus`] and diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index f3b26ac64dfa..234fb284a590 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -356,7 +356,7 @@ pub use core::panic::abort_unwind; /// ``` #[stable(feature = "catch_unwind", since = "1.9.0")] pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { - unsafe { panicking::r#try(f) } + unsafe { panicking::catch_unwind(f) } } /// Triggers a panic without invoking the panic hook. diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 4bfedf78366e..7873049d20bf 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -499,13 +499,13 @@ pub use realstd::rt::panic_count; /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. #[cfg(feature = "panic_immediate_abort")] -pub unsafe fn r#try R>(f: F) -> Result> { +pub unsafe fn catch_unwind R>(f: F) -> Result> { Ok(f()) } /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. #[cfg(not(feature = "panic_immediate_abort"))] -pub unsafe fn r#try R>(f: F) -> Result> { +pub unsafe fn catch_unwind R>(f: F) -> Result> { union Data { f: ManuallyDrop, r: ManuallyDrop, @@ -541,7 +541,7 @@ pub unsafe fn r#try R>(f: F) -> Result> let data_ptr = (&raw mut data) as *mut u8; // SAFETY: // - // Access to the union's fields: this is `std` and we know that the `r#try` + // Access to the union's fields: this is `std` and we know that the `catch_unwind` // intrinsic fills in the `r` or `p` union field based on its return value. // // The call to `intrinsics::catch_unwind` is made safe by: @@ -602,7 +602,7 @@ pub unsafe fn r#try R>(f: F) -> Result> // This function cannot be marked as `unsafe` because `intrinsics::catch_unwind` // expects normal function pointers. #[inline] - #[rustc_nounwind] // `intrinsic::r#try` requires catch fn to be nounwind + #[rustc_nounwind] // `intrinsic::catch_unwind` requires catch fn to be nounwind fn do_catch R, R>(data: *mut u8, payload: *mut u8) { // SAFETY: this is the responsibility of the caller, see above. // diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 1a4a7aa7448c..050c617f5649 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2154,6 +2154,13 @@ pub struct Path { #[stable(since = "1.7.0", feature = "strip_prefix")] pub struct StripPrefixError(()); +/// An error returned from [`Path::normalize_lexically`] if a `..` parent reference +/// would escape the path. +#[unstable(feature = "normalize_lexically", issue = "134694")] +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct NormalizeError; + impl Path { // The following (private!) function allows construction of a path from a u8 // slice, which is only safe when it is known to follow the OsStr encoding. @@ -2739,15 +2746,30 @@ impl Path { /// # Examples /// /// ``` - /// use std::path::{Path, PathBuf}; + /// use std::path::Path; /// /// let path = Path::new("foo.rs"); - /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); + /// assert_eq!(path.with_extension("txt"), Path::new("foo.txt")); + /// assert_eq!(path.with_extension(""), Path::new("foo")); + /// ``` + /// + /// Handling multiple extensions: + /// + /// ``` + /// use std::path::Path; /// /// let path = Path::new("foo.tar.gz"); - /// assert_eq!(path.with_extension(""), PathBuf::from("foo.tar")); - /// assert_eq!(path.with_extension("xz"), PathBuf::from("foo.tar.xz")); - /// assert_eq!(path.with_extension("").with_extension("txt"), PathBuf::from("foo.txt")); + /// assert_eq!(path.with_extension("xz"), Path::new("foo.tar.xz")); + /// assert_eq!(path.with_extension("").with_extension("txt"), Path::new("foo.txt")); + /// ``` + /// + /// Adding an extension where one did not exist: + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo"); + /// assert_eq!(path.with_extension("rs"), Path::new("foo.rs")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with_extension>(&self, extension: S) -> PathBuf { @@ -2961,6 +2983,67 @@ impl Path { fs::canonicalize(self) } + /// Normalize a path, including `..` without traversing the filesystem. + /// + /// Returns an error if normalization would leave leading `..` components. + /// + ///

")?; } - w.write_str("").unwrap(); + w.write_str("") } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 39a631b637bd..a75088d27ccd 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -20,7 +20,7 @@ use super::{ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, - render_impl, render_rightside, render_stability_since_raw, + render_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -413,7 +413,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i match myitem.kind { clean::ExternCrateItem { ref src } => { - use crate::html::format::anchor; + use crate::html::format::print_anchor; match *src { Some(src) => { @@ -421,7 +421,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i w, "
{}extern crate {} as {};", visibility_print_with_space(myitem, cx), - anchor(myitem.item_id.expect_def_id(), src, cx), + print_anchor(myitem.item_id.expect_def_id(), src, cx), EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()) )?; } @@ -430,7 +430,11 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i w, "
{}extern crate {};", visibility_print_with_space(myitem, cx), - anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx) + print_anchor( + myitem.item_id.expect_def_id(), + myitem.name.unwrap(), + cx + ) )?; } } @@ -439,7 +443,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i clean::ImportItem(ref import) => { 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() + print_extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string() }); let id = match import.kind { @@ -497,7 +501,9 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i write!( w, "
\ - {name}\ + \ + {name}\ + \ {visibility_and_hidden}\ {unsafety_flag}\ {stab_tags}\ @@ -505,11 +511,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i {docs_before}{docs}{docs_after}", name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()), visibility_and_hidden = visibility_and_hidden, - stab_tags = extra_info_tags(tcx, myitem, item, None), + stab_tags = print_extra_info_tags(tcx, myitem, item, None), class = myitem.type_(), unsafety_flag = unsafety_flag, - href = item_path(myitem.type_(), myitem.name.unwrap().as_str()), - title = format_args!("{} {}", myitem.type_(), full_path(cx, myitem)), + href = print_item_path(myitem.type_(), myitem.name.unwrap().as_str()), + title1 = myitem.type_(), + title2 = full_path(cx, myitem), )?; } } @@ -524,7 +531,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i /// Render the stability, deprecation and portability tags that are displayed in the item's summary /// at the module level. -fn extra_info_tags( +fn print_extra_info_tags( tcx: TyCtxt<'_>, item: &clean::Item, parent: &clean::Item, @@ -639,7 +646,7 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display { fmt::from_fn(|w| { let tcx = cx.tcx(); - let bounds = bounds(&t.bounds, false, cx); + let bounds = print_bounds(&t.bounds, false, cx); let required_types = t.items.iter().filter(|m| m.is_required_associated_type()).collect::>(); let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); @@ -652,7 +659,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: let count_types = required_types.len() + provided_types.len(); let count_consts = required_consts.len() + provided_consts.len(); let count_methods = required_methods.len() + provided_methods.len(); - let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone(); + let must_implement_one_of_functions = &tcx.trait_def(t.def_id).must_implement_one_of; // Output the trait definition wrap_item(w, |mut w| { @@ -1088,7 +1095,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: it, &implementor_dups, &collect_paths_for_type( - implementor.inner_impl().for_.clone(), + &implementor.inner_impl().for_, &cx.shared.cache, ), ) @@ -1236,7 +1243,7 @@ fn item_trait_alias( attrs = render_attributes_in_pre(it, "", cx), name = it.name.unwrap(), generics = t.generics.print(cx), - bounds = bounds(&t.bounds, true, cx), + bounds = print_bounds(&t.bounds, true, cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(), ) @@ -1278,94 +1285,58 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { - let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); let enum_def_id = ty.ty_adt_def().unwrap().did(); - wrap_item(w, |w| { - let variants_len = variants.len(); - let variants_count = variants_iter().count(); - let has_stripped_entries = variants_len != variants_count; - - write!( - w, - "enum {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_enum_fields( - cx, - Some(&t.generics), - variants, - variants_count, - has_stripped_entries, - *is_non_exhaustive, - enum_def_id, - ) - ) - })?; - write!(w, "{}", item_variants(cx, it, variants, enum_def_id))?; + DisplayEnum { + variants, + generics: &t.generics, + is_non_exhaustive: *is_non_exhaustive, + def_id: enum_def_id, + } + .render_into(cx, it, true, w)?; } clean::TypeAliasInnerType::Union { fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let union_def_id = ty.ty_adt_def().unwrap().did(); - write!( - w, - "union {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - None, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + ItemUnion { + cx, + it, + fields, + generics: &t.generics, + is_type_alias: true, + def_id: union_def_id, + } + .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let struct_def_id = ty.ty_adt_def().unwrap().did(); - write!( - w, - "struct {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - *ctor_kind, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + DisplayStruct { + ctor_kind: *ctor_kind, + generics: &t.generics, + fields, + def_id: struct_def_id, + } + .render_into(cx, it, true, w)?; } } + } else { + let def_id = it.item_id.expect_def_id(); + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + write!( + w, + "{}{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All), + document_type_layout(cx, def_id) + )?; } - let def_id = it.item_id.expect_def_id(); - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - write!( - w, - "{}{}", - render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) - )?; - // [RUSTDOCIMPL] type.impl // // Include type definitions from the alias target type. @@ -1463,50 +1434,83 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> }) } -fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { - item_template!( - #[template(path = "item_union.html")] - struct ItemUnion<'a, 'cx> { - cx: &'a Context<'cx>, - it: &'a clean::Item, - s: &'a clean::Union, - }, - methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] - ); +item_template!( + #[template(path = "item_union.html")] + struct ItemUnion<'a, 'cx> { + cx: &'a Context<'cx>, + it: &'a clean::Item, + fields: &'a [clean::Item], + generics: &'a clean::Generics, + is_type_alias: bool, + def_id: DefId, + }, + methods = [document, document_type_layout, render_assoc_items] +); - impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { - fn render_union(&self) -> impl Display { - render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx) - } - - fn document_field(&self, field: &'a clean::Item) -> impl Display { - document(self.cx, field, Some(self.it), HeadingOffset::H3) - } - - fn stability_field(&self, field: &clean::Item) -> Option { - field.stability_class(self.cx.tcx()) - } - - fn print_ty(&self, ty: &'a clean::Type) -> impl Display { - ty.print(self.cx) - } - - fn fields_iter( - &self, - ) -> iter::Peekable> { - self.s - .fields - .iter() - .filter_map(|f| match f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable() - } +impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { + fn render_union(&self) -> impl Display { + render_union(self.it, Some(&self.generics), &self.fields, self.cx) } + fn document_field(&self, field: &'a clean::Item) -> impl Display { + document(self.cx, field, Some(self.it), HeadingOffset::H3) + } + + fn stability_field(&self, field: &clean::Item) -> Option { + field.stability_class(self.cx.tcx()) + } + + fn print_ty(&self, ty: &'a clean::Type) -> impl Display { + ty.print(self.cx) + } + + // FIXME (GuillaumeGomez): When is implemented, + // we can replace the returned value with: + // + // `iter::Peekable>` + // + // And update `item_union.html`. + fn fields_iter(&self) -> impl Iterator { + self.fields.iter().filter_map(|f| match f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + } + + fn render_attributes_in_pre(&self) -> impl fmt::Display { + fmt::from_fn(move |f| { + if self.is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. + if let Some(repr) = clean::repr_attributes( + self.cx.tcx(), + self.cx.cache(), + self.def_id, + ItemType::Union, + false, + ) { + writeln!(f, "{repr}")?; + }; + } else { + for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { + writeln!(f, "{a}")?; + } + } + Ok(()) + }) + } +} + +fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, s }.render_into(w).unwrap(); + ItemUnion { + cx, + it, + fields: &s.fields, + generics: &s.generics, + is_type_alias: false, + def_id: it.def_id().unwrap(), + } + .render_into(w)?; Ok(()) }) } @@ -1533,41 +1537,81 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa }) } -fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { - fmt::from_fn(|w| { - let count_variants = e.variants().count(); +struct DisplayEnum<'clean> { + variants: &'clean IndexVec, + generics: &'clean clean::Generics, + is_non_exhaustive: bool, + def_id: DefId, +} + +impl<'clean> DisplayEnum<'clean> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + is_type_alias: bool, + w: &mut W, + ) -> fmt::Result { + let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); + let variants_len = self.variants.len(); + let has_stripped_entries = variants_len != non_stripped_variant_count; + wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + } else { + render_attributes_in_code(w, it, cx); + } write!( w, "{}enum {}{}{}", visibility_print_with_space(it, cx), it.name.unwrap(), - e.generics.print(cx), + self.generics.print(cx), render_enum_fields( cx, - Some(&e.generics), - &e.variants, - count_variants, - e.has_stripped_entries(), - it.is_non_exhaustive(), - it.def_id().unwrap(), + Some(self.generics), + self.variants, + non_stripped_variant_count, + has_stripped_entries, + self.is_non_exhaustive, + self.def_id, ), ) })?; - write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; - - if count_variants != 0 { - write!(w, "{}", item_variants(cx, it, &e.variants, it.def_id().unwrap()))?; - } let def_id = it.item_id.expect_def_id(); + let layout_def_id = if is_type_alias { + self.def_id + } else { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + // We don't return the same `DefId` since the layout size of the type alias might be + // different since we might have more information on the generics. + def_id + }; + + if non_stripped_variant_count != 0 { + write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; + } write!( w, "{}{}", render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) + document_type_layout(cx, layout_def_id) ) + } +} + +fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayEnum { + variants: &e.variants, + generics: &e.generics, + is_non_exhaustive: it.is_non_exhaustive(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, false, w) }) } @@ -1955,27 +1999,59 @@ fn item_constant( }) } -fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { - fmt::from_fn(|w| { +struct DisplayStruct<'a> { + ctor_kind: Option, + generics: &'a clean::Generics, + fields: &'a [clean::Item], + def_id: DefId, +} + +impl<'a> DisplayStruct<'a> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + is_type_alias: bool, + w: &mut W, + ) -> fmt::Result { wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + } else { + render_attributes_in_code(w, it, cx); + } write!( w, "{}", - render_struct(it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx) + render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx) ) })?; - let def_id = it.item_id.expect_def_id(); + if !is_type_alias { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + } + let def_id = it.item_id.expect_def_id(); write!( w, - "{}{}{}{}", - document(cx, it, None, HeadingOffset::H2), - item_fields(cx, it, &s.fields, s.ctor_kind), + "{}{}{}", + item_fields(cx, it, self.fields, self.ctor_kind), render_assoc_items(cx, it, def_id, AssocItemRender::All), document_type_layout(cx, def_id), ) + } +} + +fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayStruct { + ctor_kind: s.ctor_kind, + generics: &s.generics, + fields: s.fields.as_slice(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, false, w) }) } @@ -2185,14 +2261,18 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { s } -pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display { +pub(super) fn print_item_path(ty: ItemType, name: &str) -> impl Display { fmt::from_fn(move |f| match ty { ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)), _ => write!(f, "{ty}.{name}.html"), }) } -fn bounds(bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> impl Display { +fn print_bounds( + bounds: &[clean::GenericBound], + trait_alias: bool, + cx: &Context<'_>, +) -> impl Display { (!bounds.is_empty()) .then_some(fmt::from_fn(move |f| { let has_lots_of_bounds = bounds.len() > 2; diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index cd0c9775f5c9..91540e06e339 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::cmp::Ordering; +use std::fmt; use askama::Template; use rustc_data_structures::fx::FxHashSet; @@ -126,7 +127,7 @@ pub(crate) mod filters { use askama::filters::Safe; use crate::html::escape::EscapeBodyTextWithWbr; - pub(crate) fn wrapped(v: T) -> askama::Result> + pub(crate) fn wrapped(v: T, _: V) -> askama::Result> where T: Display, { @@ -135,7 +136,11 @@ pub(crate) mod filters { } } -pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut String) { +pub(super) fn print_sidebar( + cx: &Context<'_>, + it: &clean::Item, + mut buffer: impl fmt::Write, +) -> fmt::Result { let mut ids = IdMap::new(); let mut blocks: Vec> = docblock_toc(cx, it, &mut ids).into_iter().collect(); let deref_id_map = cx.deref_id_map.borrow(); @@ -195,7 +200,8 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Str blocks, path, }; - sidebar.render_into(buffer).unwrap(); + sidebar.render_into(&mut buffer)?; + Ok(()) } fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec> { @@ -593,7 +599,7 @@ fn sidebar_enum<'a>( deref_id_map: &'a DefIdMap, ) { let mut variants = e - .variants() + .non_stripped_variants() .filter_map(|v| v.name) .map(|name| Link::new(format!("variant.{name}"), name.to_string())) .collect::>(); diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index b2bbf4614bf4..33738f7a242f 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -607,68 +607,68 @@ impl TypeAliasPart { let cx = type_impl_collector.cx; let aliased_types = type_impl_collector.aliased_types; for aliased_type in aliased_types.values() { - let impls = aliased_type - .impl_ - .values() - .flat_map(|AliasedTypeImpl { impl_, type_aliases }| { - let mut ret = Vec::new(); - let trait_ = impl_ - .inner_impl() - .trait_ - .as_ref() - .map(|trait_| format!("{:#}", trait_.print(cx))); + let impls = aliased_type.impl_.values().filter_map( + |AliasedTypeImpl { impl_, type_aliases }| { + let mut ret: Option = None; // render_impl will filter out "impossible-to-call" methods // to make that functionality work here, it needs to be called with // each type alias, and if it gives a different result, split the impl for &(type_alias_fqp, type_alias_item) in type_aliases { cx.id_map.borrow_mut().clear(); cx.deref_id_map.borrow_mut().clear(); - let target_did = impl_ - .inner_impl() - .trait_ - .as_ref() - .map(|trait_| trait_.def_id()) - .or_else(|| impl_.inner_impl().for_.def_id(&cx.shared.cache)); - let provided_methods; - let assoc_link = if let Some(target_did) = target_did { - provided_methods = impl_.inner_impl().provided_trait_methods(cx.tcx()); - AssocItemLink::GotoSource(ItemId::DefId(target_did), &provided_methods) - } else { - AssocItemLink::Anchor(None) - }; - let text = super::render_impl( - cx, - impl_, - type_alias_item, - assoc_link, - RenderMode::Normal, - None, - &[], - ImplRenderingParameters { - show_def_docs: true, - show_default_items: true, - show_non_assoc_items: true, - toggle_open_by_default: true, - }, - ) - .to_string(); let type_alias_fqp = (*type_alias_fqp).iter().join("::"); - if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) { - ret.last_mut() - .expect("already established that ret.last() is Some()") - .aliases - .push(type_alias_fqp); + if let Some(ret) = &mut ret { + ret.aliases.push(type_alias_fqp); } else { - ret.push(AliasSerializableImpl { + let target_did = impl_ + .inner_impl() + .trait_ + .as_ref() + .map(|trait_| trait_.def_id()) + .or_else(|| impl_.inner_impl().for_.def_id(&cx.shared.cache)); + let provided_methods; + let assoc_link = if let Some(target_did) = target_did { + provided_methods = + impl_.inner_impl().provided_trait_methods(cx.tcx()); + AssocItemLink::GotoSource( + ItemId::DefId(target_did), + &provided_methods, + ) + } else { + AssocItemLink::Anchor(None) + }; + let text = super::render_impl( + cx, + impl_, + type_alias_item, + assoc_link, + RenderMode::Normal, + None, + &[], + ImplRenderingParameters { + show_def_docs: true, + show_default_items: true, + show_non_assoc_items: true, + toggle_open_by_default: true, + }, + ) + .to_string(); + // The alternate display prints it as plaintext instead of HTML. + let trait_ = impl_ + .inner_impl() + .trait_ + .as_ref() + .map(|trait_| format!("{:#}", trait_.print(cx))); + ret = Some(AliasSerializableImpl { text, - trait_: trait_.clone(), + trait_, aliases: vec![type_alias_fqp], }) } } ret - }) - .collect::>(); + }, + ); let mut path = PathBuf::from("type.impl"); for component in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] { @@ -681,7 +681,7 @@ impl TypeAliasPart { )); let part = OrderedJson::array_sorted( - impls.iter().map(OrderedJson::serialize).collect::, _>>().unwrap(), + impls.map(|impl_| OrderedJson::serialize(impl_).unwrap()), ); path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part])); } @@ -759,7 +759,7 @@ impl TraitAliasPart { Some(Implementor { text: imp.inner_impl().print(false, cx).to_string(), synthetic: imp.inner_impl().kind.is_auto(), - types: collect_paths_for_type(imp.inner_impl().for_.clone(), cache), + types: collect_paths_for_type(&imp.inner_impl().for_, cache), }) } }) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 095795c711d9..1fa6b5a60f3a 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -11,9 +11,8 @@ use rustc_session::Session; use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, sym}; use tracing::info; -use super::highlight; -use super::layout::{self, BufDisplay}; use super::render::Context; +use super::{highlight, layout}; use crate::clean; use crate::clean::utils::has_doc_flag; use crate::docfs::PathError; @@ -243,16 +242,16 @@ impl SourceCollector<'_, '_> { &shared.layout, &page, "", - BufDisplay(|buf: &mut String| { + fmt::from_fn(|f| { print_src( - buf, + f, contents, file_span, self.cx, &root_path, &highlight::DecorationInfo::default(), &source_context, - ); + ) }), &shared.style_files, ); @@ -331,7 +330,7 @@ pub(crate) fn print_src( root_path: &str, decoration_info: &highlight::DecorationInfo, source_context: &SourceContext<'_>, -) { +) -> fmt::Result { let mut lines = s.lines().count(); let line_info = if let SourceContext::Embedded(info) = source_context { highlight::LineInfo::new_scraped(lines as u32, info.offset as u32) @@ -367,12 +366,10 @@ pub(crate) fn print_src( }, max_nb_digits, } - .render_into(&mut writer) - .unwrap(), + .render_into(&mut writer), SourceContext::Embedded(info) => { - ScrapedSource { info, code_html: code, max_nb_digits } - .render_into(&mut writer) - .unwrap(); + ScrapedSource { info, code_html: code, max_nb_digits }.render_into(&mut writer) } - }; + }?; + Ok(()) } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a81d5c9c49b4..7be83b65fbfa 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2527,9 +2527,12 @@ in src-script.js and main.js z-index: 11; /* Reduce height slightly to account for mobile topbar. */ height: calc(100vh - 45px); - width: 200px; /* resize indicator: hide this when on touch or mobile */ border-right: none; + width: 100%; + } + .sidebar-elems .block li a { + white-space: wrap; } /* The source view uses a different design for the sidebar toggle, and doesn't have a topbar, diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index a7ce2bf9048b..7b1a61a3ffa4 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1179,8 +1179,10 @@ function preLoadCss(cssUrl) { onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { // @ts-expect-error + // Clicking on the summary's contents should not collapse it, + // but links within should still fire. el.addEventListener("click", e => { - if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { + if (!e.target.matches("summary, a, a *")) { e.preventDefault(); } }); diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index b1c1d5a63a03..b5d3367a6a10 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -2,15 +2,16 @@ {{ self.render_attributes_in_pre()|safe }} {{ self.render_union()|safe }} -{{ self.document()|safe }} -{% if self.fields_iter().peek().is_some() %} +{% if !self.is_type_alias %} + {{ self.document()|safe }} +{% endif %} +{% if self.fields_iter().next().is_some() %}

{# #} Fields§ {# #}

{% for (field, ty) in self.fields_iter() %} {% let name = field.name.expect("union field name") %} - {# #} + {# #} § {# #} {{ name }}: {{+ self.print_ty(ty)|safe }} {# #} diff --git a/src/librustdoc/html/tests.rs b/src/librustdoc/html/tests.rs index b568942bbcb9..873462bbeba8 100644 --- a/src/librustdoc/html/tests.rs +++ b/src/librustdoc/html/tests.rs @@ -1,51 +1,51 @@ -use rustc_span::{Symbol, sym}; +use rustc_span::{Symbol, create_default_session_globals_then, sym}; use crate::html::format::href_relative_parts; -fn assert_relative_path(expected: &[Symbol], relative_to_fqp: &[Symbol], fqp: &[Symbol]) { - // No `create_default_session_globals_then` call is needed here because all - // the symbols used are static, and no `Symbol::intern` calls occur. - assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).collect::>()); +fn assert_relative_path(expected: &str, relative_to_fqp: &[Symbol], fqp: &[Symbol]) { + create_default_session_globals_then(|| { + assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).finish()); + }); } #[test] fn href_relative_parts_basic() { let relative_to_fqp = &[sym::std, sym::vec]; let fqp = &[sym::std, sym::iter]; - assert_relative_path(&[sym::dotdot, sym::iter], relative_to_fqp, fqp); + assert_relative_path("../iter", relative_to_fqp, fqp); } #[test] fn href_relative_parts_parent_module() { let relative_to_fqp = &[sym::std, sym::vec]; let fqp = &[sym::std]; - assert_relative_path(&[sym::dotdot], relative_to_fqp, fqp); + assert_relative_path("..", relative_to_fqp, fqp); } #[test] fn href_relative_parts_different_crate() { let relative_to_fqp = &[sym::std, sym::vec]; let fqp = &[sym::core, sym::iter]; - assert_relative_path(&[sym::dotdot, sym::dotdot, sym::core, sym::iter], relative_to_fqp, fqp); + assert_relative_path("../../core/iter", relative_to_fqp, fqp); } #[test] fn href_relative_parts_same_module() { let relative_to_fqp = &[sym::std, sym::vec]; let fqp = &[sym::std, sym::vec]; - assert_relative_path(&[], relative_to_fqp, fqp); + assert_relative_path("", relative_to_fqp, fqp); } #[test] fn href_relative_parts_child_module() { let relative_to_fqp = &[sym::std]; let fqp = &[sym::std, sym::vec]; - assert_relative_path(&[sym::vec], relative_to_fqp, fqp); + assert_relative_path("vec", relative_to_fqp, fqp); } #[test] fn href_relative_parts_root() { let relative_to_fqp = &[]; let fqp = &[sym::std]; - assert_relative_path(&[sym::std], relative_to_fqp, fqp); + assert_relative_path("std", relative_to_fqp, fqp); } diff --git a/src/librustdoc/html/url_parts_builder.rs b/src/librustdoc/html/url_parts_builder.rs index 1e6af6af63cc..9a5338274415 100644 --- a/src/librustdoc/html/url_parts_builder.rs +++ b/src/librustdoc/html/url_parts_builder.rs @@ -14,7 +14,6 @@ pub(crate) struct UrlPartsBuilder { impl UrlPartsBuilder { /// Create an empty buffer. - #[allow(dead_code)] pub(crate) fn new() -> Self { Self { buf: String::new() } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f446c9fbbd8b..bfcb794b89a2 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -6,7 +6,7 @@ use rustc_abi::ExternAbi; use rustc_ast::ast; -use rustc_attr_parsing::DeprecatedSince; +use rustc_attr_data_structures::{self as attrs, DeprecatedSince}; use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_metadata::rendered_const; @@ -40,7 +40,7 @@ impl JsonRenderer<'_> { }) .collect(); let docs = item.opt_doc_value(); - let attrs = item.attributes(self.tcx, self.cache(), true); + let attrs = item.attributes_and_repr(self.tcx, self.cache(), true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); let clean::ItemInner { name, item_id, .. } = *item.inner; @@ -153,8 +153,8 @@ where } } -pub(crate) fn from_deprecation(deprecation: rustc_attr_parsing::Deprecation) -> Deprecation { - let rustc_attr_parsing::Deprecation { since, note, suggestion: _ } = deprecation; +pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation { + let attrs::Deprecation { since, note, suggestion: _ } = deprecation; let since = match since { DeprecatedSince::RustcVersion(version) => Some(version.to_string()), DeprecatedSince::Future => Some("TBD".to_owned()), diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b4003044e207..025c135aff2a 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -3,6 +3,8 @@ html_playground_url = "https://play.rust-lang.org/" )] #![feature(rustc_private)] +#![feature(ascii_char)] +#![feature(ascii_char_variants)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(debug_closure_helpers)] @@ -36,6 +38,7 @@ extern crate pulldown_cmark; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_attr_data_structures; extern crate rustc_attr_parsing; extern crate rustc_data_structures; extern crate rustc_driver; diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index c9f0baaaa4c1..66d8b667a4ca 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -241,7 +241,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { data: hir::VariantData::Tuple(_, _, _), .. }) | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, hir::VariantData::Tuple(_, _, _), _), + kind: hir::ItemKind::Struct(_, _, hir::VariantData::Tuple(_, _, _)), .. }) ) diff --git a/src/librustdoc/passes/check_doc_cfg.rs b/src/librustdoc/passes/check_doc_cfg.rs new file mode 100644 index 000000000000..3284da77a022 --- /dev/null +++ b/src/librustdoc/passes/check_doc_cfg.rs @@ -0,0 +1,76 @@ +use rustc_hir::HirId; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::TyCtxt; +use rustc_span::sym; + +use super::Pass; +use crate::clean::{Attributes, Crate, Item}; +use crate::core::DocContext; +use crate::visit::DocVisitor; + +pub(crate) const CHECK_DOC_CFG: Pass = Pass { + name: "check-doc-cfg", + run: Some(check_doc_cfg), + description: "checks `#[doc(cfg(...))]` for stability feature and unexpected cfgs", +}; + +pub(crate) fn check_doc_cfg(krate: Crate, cx: &mut DocContext<'_>) -> Crate { + let mut checker = DocCfgChecker { cx }; + checker.visit_crate(&krate); + krate +} + +struct RustdocCfgMatchesLintEmitter<'a>(TyCtxt<'a>, HirId); + +impl<'a> rustc_attr_parsing::CfgMatchesLintEmitter for RustdocCfgMatchesLintEmitter<'a> { + fn emit_span_lint( + &self, + sess: &rustc_session::Session, + lint: &'static rustc_lint::Lint, + sp: rustc_span::Span, + builtin_diag: rustc_lint_defs::BuiltinLintDiag, + ) { + self.0.node_span_lint(lint, self.1, sp, |diag| { + rustc_lint::decorate_builtin_lint(sess, Some(self.0), builtin_diag, diag) + }); + } +} + +struct DocCfgChecker<'a, 'tcx> { + cx: &'a mut DocContext<'tcx>, +} + +impl DocCfgChecker<'_, '_> { + fn check_attrs(&mut self, attrs: &Attributes, did: LocalDefId) { + let doc_cfgs = attrs + .other_attrs + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::cfg)); + + for doc_cfg in doc_cfgs { + if let Some([cfg_mi]) = doc_cfg.meta_item_list() { + let _ = rustc_attr_parsing::cfg_matches( + cfg_mi, + &self.cx.tcx.sess, + RustdocCfgMatchesLintEmitter( + self.cx.tcx, + self.cx.tcx.local_def_id_to_hir_id(did), + ), + Some(self.cx.tcx.features()), + ); + } + } + } +} + +impl DocVisitor<'_> for DocCfgChecker<'_, '_> { + fn visit_item(&mut self, item: &'_ Item) { + if let Some(Some(local_did)) = item.def_id().map(|did| did.as_local()) { + self.check_attrs(&item.attrs, local_did); + } + + self.visit_item_recur(item); + } +} diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index f3e2138d1a57..1daaba3b86c5 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -22,6 +22,7 @@ use rustc_resolve::rustdoc::{ MalformedGenerics, has_primitive_or_keyword_docs, prepare_to_doc_link_resolution, source_span_for_markdown_range, strip_generics_from_path, }; +use rustc_session::config::CrateType; use rustc_session::lint::Lint; use rustc_span::BytePos; use rustc_span::hygiene::MacroKind; @@ -59,12 +60,7 @@ fn filter_assoc_items_by_name_and_namespace( ident: Ident, ns: Namespace, ) -> impl Iterator { - let iter: Box> = if !ident.name.is_empty() { - Box::new(tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name)) - } else { - Box::new([].iter()) - }; - iter.filter(move |item| { + tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name).filter(move |item| { item.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of) }) } @@ -1174,7 +1170,6 @@ impl LinkCollector<'_, '_> { #[allow(rustc::potential_query_instability)] pub(crate) fn resolve_ambiguities(&mut self) { let mut ambiguous_links = mem::take(&mut self.ambiguous_links); - for ((item_id, path_str), info_items) in ambiguous_links.iter_mut() { for info in info_items { info.resolved.retain(|(res, _)| match res { @@ -2232,15 +2227,35 @@ fn ambiguity_error( emit_error: bool, ) -> bool { let mut descrs = FxHashSet::default(); - let kinds = candidates + // proc macro can exist in multiple namespaces at once, so we need to compare `DefIds` + // to remove the candidate in the fn namespace. + let mut possible_proc_macro_id = None; + let is_proc_macro_crate = cx.tcx.crate_types() == &[CrateType::ProcMacro]; + let mut kinds = candidates .iter() - .map( - |(res, def_id)| { - if let Some(def_id) = def_id { Res::from_def_id(cx.tcx, *def_id) } else { *res } - }, - ) - .filter(|res| descrs.insert(res.descr())) + .map(|(res, def_id)| { + let r = + if let Some(def_id) = def_id { Res::from_def_id(cx.tcx, *def_id) } else { *res }; + if is_proc_macro_crate && let Res::Def(DefKind::Macro(_), id) = r { + possible_proc_macro_id = Some(id); + } + r + }) .collect::>(); + // In order to properly dedup proc macros, we have to do it in two passes: + // 1. Completing the full traversal to find the possible duplicate in the macro namespace, + // 2. Another full traversal to eliminate the candidate in the fn namespace. + // + // Thus, we have to do an iteration after collection is finished. + // + // As an optimization, we only deduplicate if we're in a proc-macro crate, + // and only if we already found something that looks like a proc macro. + if is_proc_macro_crate && let Some(macro_id) = possible_proc_macro_id { + kinds.retain(|res| !matches!(res, Res::Def(DefKind::Fn, fn_id) if macro_id == *fn_id)); + } + + kinds.retain(|res| descrs.insert(res.descr())); + if descrs.len() == 1 { // There is no way for users to disambiguate at this point, so better return the first // candidate and not show a warning. diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index 1e07277d38e5..3b3ce3e92202 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -18,12 +18,15 @@ use crate::html::markdown::main_body_opts; pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { let report_diag = |cx: &DocContext<'_>, msg: &'static str, range: Range| { - let sp = source_span_for_markdown_range(cx.tcx, dox, &range, &item.attrs.doc_strings) - .unwrap_or_else(|| item.attr_span(cx.tcx)); + let maybe_sp = source_span_for_markdown_range(cx.tcx, dox, &range, &item.attrs.doc_strings); + let sp = maybe_sp.unwrap_or_else(|| item.attr_span(cx.tcx)); cx.tcx.node_span_lint(crate::lint::BARE_URLS, hir_id, sp, |lint| { lint.primary_message(msg) - .note("bare URLs are not automatically turned into clickable links") - .multipart_suggestion( + .note("bare URLs are not automatically turned into clickable links"); + // The fallback of using the attribute span is suitable for + // highlighting where the error is, but not for placing the < and > + if let Some(sp) = maybe_sp { + lint.multipart_suggestion( "use an automatic link instead", vec![ (sp.shrink_to_lo(), "<".to_string()), @@ -31,6 +34,7 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & ], Applicability::MachineApplicable, ); + } }); }; diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 9ba63d34144a..475d05b7d0e7 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -32,6 +32,9 @@ pub(crate) use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS; mod check_doc_test_visibility; pub(crate) use self::check_doc_test_visibility::CHECK_DOC_TEST_VISIBILITY; +mod check_doc_cfg; +pub(crate) use self::check_doc_cfg::CHECK_DOC_CFG; + mod collect_trait_impls; pub(crate) use self::collect_trait_impls::COLLECT_TRAIT_IMPLS; @@ -72,6 +75,7 @@ pub(crate) enum Condition { /// The full list of passes. pub(crate) const PASSES: &[Pass] = &[ + CHECK_DOC_CFG, CHECK_DOC_TEST_VISIBILITY, STRIP_ALIASED_NON_LOCAL, STRIP_HIDDEN, @@ -89,6 +93,7 @@ pub(crate) const PASSES: &[Pass] = &[ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[ ConditionalPass::always(COLLECT_TRAIT_IMPLS), ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY), + ConditionalPass::always(CHECK_DOC_CFG), ConditionalPass::always(STRIP_ALIASED_NON_LOCAL), ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden), ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate), diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index fdab2b087799..7b3da8d7c0fb 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -6,7 +6,7 @@ //! [`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_parsing::{Stability, StabilityLevel}; +use rustc_attr_data_structures::{Stability, StabilityLevel}; use rustc_hir::def_id::CRATE_DEF_ID; use crate::clean::{Crate, Item, ItemId, ItemKind}; diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 64223b5b7589..c091c955ed5e 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -30,7 +30,7 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 45; +pub const FORMAT_VERSION: u32 = 46; /// The root of the emitted JSON blob. /// @@ -180,19 +180,13 @@ pub struct Item { /// /// Does not include `#[deprecated]` attributes: see the [`Self::deprecation`] field instead. /// - /// Some attributes appear in pretty-printed Rust form, regardless of their formatting + /// Attributes appear in pretty-printed Rust form, regardless of their formatting /// in the original source code. For example: /// - `#[non_exhaustive]` and `#[must_use]` are represented as themselves. /// - `#[no_mangle]` and `#[export_name]` are also represented as themselves. /// - `#[repr(C)]` and other reprs also appear as themselves, /// though potentially with a different order: e.g. `repr(i8, C)` may become `repr(C, i8)`. /// Multiple repr attributes on the same item may be combined into an equivalent single attr. - /// - /// Other attributes may appear debug-printed. For example: - /// - `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`. - /// - /// As an internal implementation detail subject to change, this debug-printing format - /// is currently equivalent to the HIR pretty-printing of parsed attributes. pub attrs: Vec, /// Information about the item’s deprecation, if present. pub deprecation: Option, diff --git a/src/stage0 b/src/stage0 index 8ca6860490ca..4cff7bafa5de 100644 --- a/src/stage0 +++ b/src/stage0 @@ -13,466 +13,466 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2025-05-12 +compiler_date=2025-05-26 compiler_version=beta -rustfmt_date=2025-05-12 +rustfmt_date=2025-05-27 rustfmt_version=nightly -dist/2025-05-12/rustc-beta-aarch64-apple-darwin.tar.gz=e5ec8453efc1f51d37d5031d87d45a327647614b00993d1b7f477c7d2e6c7b16 -dist/2025-05-12/rustc-beta-aarch64-apple-darwin.tar.xz=6711902d59079cd57d6f93e951d3028acb5cef0f59a2ab87e1688edee96f6471 -dist/2025-05-12/rustc-beta-aarch64-pc-windows-msvc.tar.gz=7168682081144b8eacab42efe6c9ddb9ee6964712d271988345e63d2d6faac9c -dist/2025-05-12/rustc-beta-aarch64-pc-windows-msvc.tar.xz=5794e0d6bed097d349e138c7602a083f4025604f711328c0a4548e27f191444b -dist/2025-05-12/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=c4d31776d1b74dcc6184c2ed6064667b1ade59c68fb355bee812a805f61234f9 -dist/2025-05-12/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=970b0c910f8ba2b5b470ffa7959466526b0f99211578f7d8ceca8d0aaa23fe1d -dist/2025-05-12/rustc-beta-aarch64-unknown-linux-musl.tar.gz=8e9c80f826b4571136f082d3cadbb4668167f19688a3da91fc732464b5a604b5 -dist/2025-05-12/rustc-beta-aarch64-unknown-linux-musl.tar.xz=09731185aeb15263cfed5786ccc78fee0db70f82aeb5409f8bd8b03b0566d491 -dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=4ae8dec81d8f2d1aff7710a357e3c56323cae56bacd6b014fdb4058c06bb75f0 -dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=4767de7ea81913c6ed33907d93dfb56664d9bce0d095f33f0ca5662b284a94d7 -dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=d2233f4e687bb1bd407593f6d7a8c288581b7209d758be49f0681e1f556083e4 -dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=a8f9778a765d9fa8a0651b7d6fac8fdebcbaa61e903a32e7cbcd88bcd9418bd3 -dist/2025-05-12/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=bd2ee7918df85f24a34911b91a233663b4cf706e7c54784c78fea8e58c12ca91 -dist/2025-05-12/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=9b1a1c4eb35d3c1ec97132e33fc6551ffb280d6b2c9d049bf0392441674d338c -dist/2025-05-12/rustc-beta-i686-pc-windows-gnu.tar.gz=729e4d7a116d8ee2a42489484429b138bafc14b43c87adfedaad442515e61c15 -dist/2025-05-12/rustc-beta-i686-pc-windows-gnu.tar.xz=8272b95f1d99dff28f22161d0181ac0e64e1909d51448f9ba4bcbe09690e79a9 -dist/2025-05-12/rustc-beta-i686-pc-windows-msvc.tar.gz=13a7327d08d26ba1911071c798d520b74422e320f5cc1c41d4e215a5615e692e -dist/2025-05-12/rustc-beta-i686-pc-windows-msvc.tar.xz=0f9ce8fb06bb1ae460ee82601c269b885c109729df342e5b6b05b9dd9b51560a -dist/2025-05-12/rustc-beta-i686-unknown-linux-gnu.tar.gz=82b54be8042baa56e1e6c0346f2044a84c4a50b3df6fe813d45eab21e1fe8935 -dist/2025-05-12/rustc-beta-i686-unknown-linux-gnu.tar.xz=48c9a8181b6ac7b7b6fb4535391c0498965127f5b5ac694de7eb1dba7ed8e9d5 -dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=768156149054211735ec45d0091a8e7dfac16a39c44e122af5b28b316a45fd00 -dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=50a38f72a253bfb8005a9cdd49621289f8b4a2373247957f520f5c5d1f12db29 -dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=f79bb58d8e2c80270a4c9d7076ce8645b2ea3f64db5077b085cb4cc6763f5e17 -dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=eafeaea2813e34ef0606a9f935fe1a104417604686ef9144b899fe97de53aa67 -dist/2025-05-12/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=f557e00500071835712afdc9d91161a95b1cca5cc4e32abebcf5d35a9147eb2b -dist/2025-05-12/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=72fc4d26e06d74349e65415da211429ec92cd479aae78f82e223f3f760b0e63a -dist/2025-05-12/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=a67b7e5e0b30227b07a41829c5e88180d9c404c2ce37fcb10d8df702c2b3c222 -dist/2025-05-12/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=0c4cfeb6555283e58b75533930783e7cc3c838f9c8eb34938fa60656b15568a1 -dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=7e6f02eede8d87cd5bbcd8dcf8235ebabd1237fb294cf1d0dcfaf961f3628d95 -dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=a5e9612d42f999a7b0fe22b2d5d5def21162aeb604c4625fc70259c5ec2b669e -dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=8fc92b9e35110a53458e08b49db1809a23060f8d05e742561cd746fd206085f2 -dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=8b3fc4ac4423bc71b7402554436d1e6e62ff06b36c69f7be724e8ec5ebf96352 -dist/2025-05-12/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=d0777e5ea794a9d19a2a1744acff649a1bac8fc616f6df41410553ac0b3c275d -dist/2025-05-12/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=849039740272c91141862a028f45889d4874ddc83842a66b906df37b7a30f9de -dist/2025-05-12/rustc-beta-s390x-unknown-linux-gnu.tar.gz=3230ab1516a19cf803952138ef7f815ce321d7123539539249b76f6afadcf9ed -dist/2025-05-12/rustc-beta-s390x-unknown-linux-gnu.tar.xz=12c8e476a73d71d58d5438ce94bb2fa822a8d043015b0961af14096d68c52daf -dist/2025-05-12/rustc-beta-x86_64-apple-darwin.tar.gz=01717cd3b5141d29896caeab17ad61a27b8b7af6460745f245d67dd066a09924 -dist/2025-05-12/rustc-beta-x86_64-apple-darwin.tar.xz=fceb7e0f431f84621a22ae50ec9694cd0ecdf90801f953295b1975b0aedb4fff -dist/2025-05-12/rustc-beta-x86_64-pc-windows-gnu.tar.gz=1f7abd7650cab64cd09848ac8de9b7e0047f6c77eb433140fbae8ae8b522c019 -dist/2025-05-12/rustc-beta-x86_64-pc-windows-gnu.tar.xz=4e3e07967e44907cb2b2ccb733b969014ee6efedb82412dc81f95533d2d473be -dist/2025-05-12/rustc-beta-x86_64-pc-windows-msvc.tar.gz=3cc10eb4187e09a48efa5351250e09c83edda4296d605dcb886eb81f9d6580af -dist/2025-05-12/rustc-beta-x86_64-pc-windows-msvc.tar.xz=9ce5c89a9b2e7360c7991c3f976bbbe9bf9685854d1019aa6dc1cc5b9d13eb88 -dist/2025-05-12/rustc-beta-x86_64-unknown-freebsd.tar.gz=3a9e92319e91c0498a3e54ff5ae00f4e1ecfac9b0d4f291885c9feef89d356df -dist/2025-05-12/rustc-beta-x86_64-unknown-freebsd.tar.xz=6930ccd83b6b63d0a876eb5ac52c32a8449fd4cea8666b919494ce6358c6122c -dist/2025-05-12/rustc-beta-x86_64-unknown-illumos.tar.gz=c1a4ad2cfa4b7c3181ea0facc3b18baea7f4138d089825915eb41630e5bac500 -dist/2025-05-12/rustc-beta-x86_64-unknown-illumos.tar.xz=44ae303a09cbc8a198c0cd947f958229b0e605842666a3b0aadb0f1f69f34ffc -dist/2025-05-12/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=fc55fe3f5b2d206417452de880a177f59004762e58fbfa4404f0b59fdd7075dd -dist/2025-05-12/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=a5ce304c4798bbacc998b2350d6ef79e9845a7ffb28bdf0af6066869667a0c86 -dist/2025-05-12/rustc-beta-x86_64-unknown-linux-musl.tar.gz=391cb81e61589377ab0a6780289628a805a5b1d842adc29e66ee5731f36372af -dist/2025-05-12/rustc-beta-x86_64-unknown-linux-musl.tar.xz=7c3ac5df14b28b99e3e2d0072b5aacc59acc08621731fdebaa3199059ccbeb76 -dist/2025-05-12/rustc-beta-x86_64-unknown-netbsd.tar.gz=670beaf2ec21118fb099a1b034b33665e360b8f1920b9cbd5fb58271a8aab9ca -dist/2025-05-12/rustc-beta-x86_64-unknown-netbsd.tar.xz=e4982c3e4b9f757485ff9aee183d973e31b2c485dbb39387c1afe4bee0fdbc30 -dist/2025-05-12/rust-std-beta-aarch64-apple-darwin.tar.gz=c2786d9e874eecea00935c62c58e2e3ddfbe11b4c99f9ce807e2251640c8f7f8 -dist/2025-05-12/rust-std-beta-aarch64-apple-darwin.tar.xz=0f2a6b28befa7d44055f32683d7b9c4de19ffd39c02fe6ce44aeffbdd1d13ea8 -dist/2025-05-12/rust-std-beta-aarch64-apple-ios.tar.gz=3cccf751678cc229a7ca3b39cbee4467230bec235e16b48acc576c825e0be15c -dist/2025-05-12/rust-std-beta-aarch64-apple-ios.tar.xz=9ed9b7f1672d887fac4a0386027440651ef99c682ff21b1bd9c1ddd850934613 -dist/2025-05-12/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=e7e6c0c7d9fa99f268d7601a127c6ce07df620fb27462dbaf933124a5786ef8a -dist/2025-05-12/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=abcecf3ecdb72714f35981847a91190c3f038dd5dce23a68253c7129fa6abf3b -dist/2025-05-12/rust-std-beta-aarch64-apple-ios-sim.tar.gz=c50ac5245e87b5e251fce3ff847ddf7d62df4490843e8a5f592515517b04d406 -dist/2025-05-12/rust-std-beta-aarch64-apple-ios-sim.tar.xz=1c913535759d008327eef49e47870d3afcf609c29aab4a188209c3cfea954682 -dist/2025-05-12/rust-std-beta-aarch64-linux-android.tar.gz=6120c1b159fa4f0279f8952aebf8cf1513f5b843905d64d1efaccaceac79c1f1 -dist/2025-05-12/rust-std-beta-aarch64-linux-android.tar.xz=57ab4652b879df33556cf04596f0f9ad9b0eee832b67e33c8c8cdf812c229a6e -dist/2025-05-12/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=7afcbb49691f8286ac21107598a7a44363a8e385eaa648ab2e7711f87ddedfca -dist/2025-05-12/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=ea8af597e49e924f1e04eb3435afa09720c81f43dc467461de1058265d36dd64 -dist/2025-05-12/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=2d8791f8ebff5f5f679c8b1735fdd1f0a4d7968983a5c2ddc5e036ad35b31f1e -dist/2025-05-12/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=a7f7bb3269dd7312edea5c6fef81d373499a670804259cf7853ef346fff42ee0 -dist/2025-05-12/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=9469cb7871dc724148489180df240dd51c0388cd9bb478adf272934e38916b73 -dist/2025-05-12/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=315f3dea48c50819f925bd32a3a5181591d4370eee4def8e37448828e622ab06 -dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=abfaa164202c7d5d3c7e956b10a5ea612b092ee45d6c05d5c19a097617cfd703 -dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=71ef1275726f6c61113bf1d23099a7557461205b6be243a952fa806ef15d9413 -dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=d651e5e46e1251952e719237dde30ed7ecdb6b95a7cc0398fc635a76b94c552a -dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=0a67ebf159539bc7f5a4e5698a0c74550da3c5e2cb0b5e1dd694ad29e1f35834 -dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=c0f1ecbbdd5234230d2439620c0ebe9b1c3d331388cd174cdeaf48d724172aab -dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=e2ba0a2853d685679422c065f266ee57f269bb5a231c5af5a791559a3609fb25 -dist/2025-05-12/rust-std-beta-aarch64-unknown-none.tar.gz=04b4eaf5910e662364b5ac3ee08ddffc2eda3957892ba99c8c945f5e1a18747a -dist/2025-05-12/rust-std-beta-aarch64-unknown-none.tar.xz=065751b346f9c3d76e164a9edc123f277492ebfaf1d00db61027e4fb17d50f79 -dist/2025-05-12/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=056a135278dfdafb5b22c8f01bfc77b17396511d67b55c1404693d801e584262 -dist/2025-05-12/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=fc086ae7ca3a5c05790cb41dfc382fc65f929c669efd540c07131b851b78a743 -dist/2025-05-12/rust-std-beta-aarch64-unknown-uefi.tar.gz=97a6301cdd34da68d5c6b243cc125f7e34215853e405d9b34bc715aeda3223ab -dist/2025-05-12/rust-std-beta-aarch64-unknown-uefi.tar.xz=3f2055ce638671316dc074595a35b893eea7be596cff218ec1416f3259ff86cb -dist/2025-05-12/rust-std-beta-arm-linux-androideabi.tar.gz=299158c865df15424564be4d72921b8b25993b0671e4d462ff69f49ea29367db -dist/2025-05-12/rust-std-beta-arm-linux-androideabi.tar.xz=6e71d518bf5f4a29b91938ee28b3c9b22509f3d97d4331ddd8ae0c1069192310 -dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=0a00703833d46720e470ed90f81a08d9c20f63932d852e379fe63df955e61c9b -dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=57be85e4c2d4eeb4cbb19f48150693d4e6dd2969d380b1d55feb431c858e4c35 -dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=7b96461125b04d98a550bac5a7c3dad9c1df65ce849758d867c72ffc0b475012 -dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=a66602af671667fe5686c7a4e395d3dca8374ddae10cc9260e23e20f65022549 -dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=deaf5c7ed339c8a7bc2af94888841b647f8118854f698ece4ddbf900df921bd9 -dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=eac2d4d330a5300ee297c2eb61914b86efded3d494c5a73e2f91d989cb2896c4 -dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=479a5941193d14e2d4d50fcdbecb31103f6a143bcd3afae887d068c2ebe14163 -dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=2483258323175c1e338be84ce52d44e15177096643beabba9d806c69cbed23dd -dist/2025-05-12/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=528803fac28b0a0025dc50324a6980a4b561e7e3b99d7428b8ed0a73fd3dd462 -dist/2025-05-12/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=6603b9aa82cfd563d7c462ebe50058c36aff403aa9e3a1d6a305780126aee481 -dist/2025-05-12/rust-std-beta-armebv7r-none-eabi.tar.gz=6ae7f3e39e974e20e9cbfae276fd4995063c5702c41085c2b764f3c37cbbfdec -dist/2025-05-12/rust-std-beta-armebv7r-none-eabi.tar.xz=404ae1fc0f5a6995ced2f66fa863cfff17c863096e99b5a04c841b97e6f0e28f -dist/2025-05-12/rust-std-beta-armebv7r-none-eabihf.tar.gz=bf6aaeeba558ac148b693c4e4d231415f6e72506b50ee06b0a1f987374a08df7 -dist/2025-05-12/rust-std-beta-armebv7r-none-eabihf.tar.xz=ed3f8767f5e824c5b81178e56c6084c45c67653793128d2c08146533333cc0ba -dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=7d2fae89c459d65fe2cd28acaa225f0ccc35b3f49c84ce6aa86e2c40dba38e03 -dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=fc05ce5ee26be4a233181b9841975c0975fc45ad5466d1001a24a01e2a31123b -dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=0335813546a1f905e274135b2bd97c3a0c95f2e0d992d7396bc110b800d3ca8c -dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=7031aeca445d4f8fa351c7ad2e0e06df0386ed11f91080ea65968f1716006bd3 -dist/2025-05-12/rust-std-beta-armv7-linux-androideabi.tar.gz=9abd7fe0b7163a141d758ccdca422bd32ed4ad3618066ac022671b082f4641f9 -dist/2025-05-12/rust-std-beta-armv7-linux-androideabi.tar.xz=2bcdeb652d42755528a17b86a3b64b13b32d1ba9207cd2c9ccb43fa0d7a1c6bc -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=1bad15b2e9806e7858d5d4d58f6b2864c3f04e65d4ecb1cc448efdbf0e0030b0 -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=f2d9039c903e5c309bbd17c7567462d4663665cbb7e1d98154022d98a9883719 -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=13aa4a3ef68a87de259726c7c2a3906cbf013836f753b707a453bf91879f023b -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=8c4f8c044aa4ec6813cec1fed11326f67b0f2db3f20e4b441aba5656af7f0ae3 -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=4c2e5ae8c903577e963af32fdbb39de6180db52907c3f508064a87a21feb9390 -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=c1a12c15792f6b0de81a6e24317d7bea9af023a977ae0558ee3b4598539aa7cb -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=f789db5aebd9395daf198d5248323fee1eec27533f6d95d0f454339cbc997950 -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=0376d2f2ad8f82719eabb378de3404e066da7d603e27ae4e1620509ccd6eb5b6 -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=9312a8d530c6ca1e72ed35ef82700853e1fba8a1f39bcaad61277a86a974ab18 -dist/2025-05-12/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=8c7d99202e5468bbd6fcd818cb832376c00a7c4b09973e5d00b84aa4964b7ff6 -dist/2025-05-12/rust-std-beta-armv7a-none-eabi.tar.gz=c4fb94b25d21802136bc36289eea9b95e50b101f64de925a1e9d8ad8ee70aef6 -dist/2025-05-12/rust-std-beta-armv7a-none-eabi.tar.xz=6ac88ec457fd554268da3307d40664d2926174cf8e89eb173112c7248776e060 -dist/2025-05-12/rust-std-beta-armv7r-none-eabi.tar.gz=c436c2c58d224e1f9bea4703f8ab57cd3f427c60432cca50eb294dde65994002 -dist/2025-05-12/rust-std-beta-armv7r-none-eabi.tar.xz=220906e1eca686d6e4a76a80417f527d37b0659adbec940566570292496715f8 -dist/2025-05-12/rust-std-beta-armv7r-none-eabihf.tar.gz=eee1788aec77c48c76bc5ba807d42d4bbb7c8f3e9220ba1135764061a9ddf3d9 -dist/2025-05-12/rust-std-beta-armv7r-none-eabihf.tar.xz=136f3486bdd8a7e91d738a3f8c1c3b96b853aa054a76c4e83e427ea56d3eea0d -dist/2025-05-12/rust-std-beta-i586-unknown-linux-gnu.tar.gz=72e3c031fa55d131a206d5815899a48ff7bcb19c9ac4b3dbaeab38a3cc4a3630 -dist/2025-05-12/rust-std-beta-i586-unknown-linux-gnu.tar.xz=91ca56a1e5a07e1c147a8906d366985548bd961af2aa31dfba60938e457ddece -dist/2025-05-12/rust-std-beta-i586-unknown-linux-musl.tar.gz=839ece94670a9295148231c77573f5b2d8ec5fb9727ab6aa45b8f320201f40d5 -dist/2025-05-12/rust-std-beta-i586-unknown-linux-musl.tar.xz=27ec97a6e24184edf4a51de5500d5bb4d4833ad2b7bc771a4506589ce2190062 -dist/2025-05-12/rust-std-beta-i686-linux-android.tar.gz=d8f529a63a46bba2bd358e491d7fe0be10fee6dabf0075c40177402aeeb49721 -dist/2025-05-12/rust-std-beta-i686-linux-android.tar.xz=a061a703858aa0770d51c6c8bcdfca048efe96b561c460464b835b4ccfdca387 -dist/2025-05-12/rust-std-beta-i686-pc-windows-gnu.tar.gz=8b7eb90ad7edb050599dd477c520455ad7e02696426692a0a72094381e189285 -dist/2025-05-12/rust-std-beta-i686-pc-windows-gnu.tar.xz=f99e1d82a3cfaef05e6f088e766932a3860e7df60e1f392162746bb08eb72ddc -dist/2025-05-12/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=5d57965f2a6ffff01619e84acdc0f7d9b2afe3c361e5094eecccfa9893eaa501 -dist/2025-05-12/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=c1b218aac6370cef9564043c98f361a2938c6ebc7784cb49b361aad3a1bfb6f1 -dist/2025-05-12/rust-std-beta-i686-pc-windows-msvc.tar.gz=fdcd4b6f391338fc0f7b72d11fc8dad9df903fb4639b893b57e729de387a9cf9 -dist/2025-05-12/rust-std-beta-i686-pc-windows-msvc.tar.xz=c8faa9123c9df0d764cac59e10e94f1562ec7bc7a792f5c63f9a9decd48a3280 -dist/2025-05-12/rust-std-beta-i686-unknown-freebsd.tar.gz=e8882425b127d01afcf6269e820bb8c4b813619b6d10f0422fea17c87d5921bf -dist/2025-05-12/rust-std-beta-i686-unknown-freebsd.tar.xz=dabd3bb2560a7949f8984e1dcab35aa46f8e46b09e68c7f2ff32894370ed80b7 -dist/2025-05-12/rust-std-beta-i686-unknown-linux-gnu.tar.gz=dd296784ed2199b4c2d85053bce686e01cf867851b175b24781e7e8e6f6ef8bb -dist/2025-05-12/rust-std-beta-i686-unknown-linux-gnu.tar.xz=3bb9069b4456de27cc9fba5dd2b350e5e8215f0460ce9ee375f65856958e4a82 -dist/2025-05-12/rust-std-beta-i686-unknown-linux-musl.tar.gz=008ea77ae8d982461c65c25bfcc0c41642ca51a33007a4c8d1ede8612df8f20f -dist/2025-05-12/rust-std-beta-i686-unknown-linux-musl.tar.xz=fdfeb6df04afe1f4e414ad8292a7b75191c2507d020e69f402f97ee9ab3ccf90 -dist/2025-05-12/rust-std-beta-i686-unknown-uefi.tar.gz=dbca5a983d2eb2bd84aa7779fc54562bccf9043b31a7f52a3043f1e1e59695c8 -dist/2025-05-12/rust-std-beta-i686-unknown-uefi.tar.xz=c0d9abf38ba7b1847fc70b9dbe68f4c27d5a1adb9726dbbee77911f1d271b6aa -dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=c923562d0a1d2830d41212ba140225b9c36087087dde6753e7a891383a095a10 -dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=5ca633c2e218939983d77cbf5738ab7d5fc4aa89093a0d1fb701ab06ed7ecf51 -dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=a38b4748085b3c06f2154376cdda41fcee2154f1fb409ac5137b63034cfe8cab -dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=43601e0aecb02535ee46b0ddd076867248cd8654be302ae6580a81af33660faa -dist/2025-05-12/rust-std-beta-loongarch64-unknown-none.tar.gz=04dc49b516a638589d907f885aeafa19170683b023d0ee1bf5d78f0d91d0b94a -dist/2025-05-12/rust-std-beta-loongarch64-unknown-none.tar.xz=123f388b208842b3ee46a01ae8efab900c0b5b01b97eb896d26b12bb3aecdeaf -dist/2025-05-12/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=a57452e86c5b768f1feb7f903e4ef8e76518e625c09b5f555885e1d9aaf9b76f -dist/2025-05-12/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=e9d8b99bc4686e199f3aeda5cbfd99d49416a7ba104b494c18ae67a8d1133d9d -dist/2025-05-12/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=3a9f4744fc128be61877967586e6c163cd6ef4e017e04578cb9101c8a9a60cdc -dist/2025-05-12/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=0e693f7c27a34876728565152f7b6b407e1773a187742792ea2ac3f53d6c9839 -dist/2025-05-12/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=9d0f6d9cc2b7d1ceff5934a00c780337d2fa77cd9a81cbe9e041e5b18adb43ff -dist/2025-05-12/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=afcd5c9d2e67d6c514630443d9e50d37d36722712e9275e3eaf4f460f7eb779f -dist/2025-05-12/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=9c02e0eb75361a024d25863456c3906b845314481cd9173a6708104a21265e88 -dist/2025-05-12/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=5b0628ca22f762796c9215606314babc1237baea075c990e146ee9f9ba1ed834 -dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=9e12bd3f2b61b8753aca3a1ed117cae0b4bae2267634a6e24afc0c642d998784 -dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=70a6cf1d3e6767656657e5f76e8dd35049bd20a30517f85832c35847c9f63bf7 -dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=e2feb3c8bf2390281c71f3b76f07a5a9700e454236bdd2c8f75403cb2247b252 -dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=0b533328ff7dfffdfb11826811fa9474c36faebe909f176d60898477d5b9d23b -dist/2025-05-12/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=42b46c1d8ebec202131d08aa21fb6ead760a630199822b4fe88c94a5447f0491 -dist/2025-05-12/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=24bb2a24d41bfdb76dfb8817e99759dfd314ce52309d51b294db7a558114f936 -dist/2025-05-12/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=5d50c5a766344cacc6e7ebdabddfe720199fca74d1d4284a80ff5625150d7bcc -dist/2025-05-12/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=f6f6e68c0d495b2833566deacac8a6154a220fe1f92deacd031e6b649a63a04f -dist/2025-05-12/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=a80d128b4d0b3b5bb9316da1297b0c1cfee026eea3e9e23c546d62dda9cebd3d -dist/2025-05-12/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=03e27d02bf685f6eb1281fc48d417dcf9f934587fbc743d6e7aac6e0c3691d5c -dist/2025-05-12/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=67185b764c3423704af10318f44f0f310349191d62785bd8cb85ca2bac7f935a -dist/2025-05-12/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=8ef88ac6044c84815bbbcd2b5ce4128349633addf40bb8c439b9a0a07fc5e179 -dist/2025-05-12/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=2ab3bbb6de6a5281f8aa586e5fc15d575a34b17b4f44908347d7a776c924add2 -dist/2025-05-12/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=321b0167c9481ab88ff44bf920fa15bdb4e07c864a90b6777f3c8dfd0e5c5ec6 -dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=bede2674247df8ff2153808f499ee1c1a7a909ff87600513ebc2998f43c7c1ea -dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=b903c9ca2344fd1323695052f74b9562f6dd3cdde4872f935bcba6c0fb988dce -dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=ad90fed7ed9137d04aa8c41d1c7e856dd8cc57a0f4b7836b22c9b1932a24a769 -dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=78372e3e32174a2cfa12dcd426e36fe29ff76779d8815944e6f6c7be4a3c55fe -dist/2025-05-12/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=a781d0ee95ae3012e3d016ae1b029ca8507ff549a6b1e0a6f052bca6d4afbc7b -dist/2025-05-12/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=23b1cf7192f044a0f46ccedd654aa203dc0e9fad47c5ffc2a1e6717bf6598d69 -dist/2025-05-12/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=dfb15324b8047bd26a58a26d373af441182808203c06a3d4e595d79bca21b757 -dist/2025-05-12/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=432dfeb9231b67537dc5c77941ee26fd73404ea16dc1be4071b98c13680ddcaf -dist/2025-05-12/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=cff18fbbbe323c67779651dd6e3b94a76a573567720985d59a091c26a3c33110 -dist/2025-05-12/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=c5f25038ba5be3ffddb6966e89017de862a0d9f267a57eeaae81b3b2a44d5690 -dist/2025-05-12/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=104d762d5a45fea227880d2395068824f9202e5a7fbd30bea478bb1ee6899ee2 -dist/2025-05-12/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=de1f15d6cfafc275108c4584a294128962dabe54bf5a1f6e81da3508ea9e8a14 -dist/2025-05-12/rust-std-beta-sparcv9-sun-solaris.tar.gz=531562c65d558a993128054fcfb29f0d408a40318ecd5623b5b24636bd7b0a07 -dist/2025-05-12/rust-std-beta-sparcv9-sun-solaris.tar.xz=af866deae0c10ce2b11c0ebe37fdafef79285bc694eaba75213316ab125b198d -dist/2025-05-12/rust-std-beta-thumbv6m-none-eabi.tar.gz=ff623d437bda1c0b8cd8affd2a6bc165b8224a5467894aa54dee63b1b6939fc6 -dist/2025-05-12/rust-std-beta-thumbv6m-none-eabi.tar.xz=d8743e42057014ef2742cec5b93e34d5cde5a658d3ed9e7e738276387985122e -dist/2025-05-12/rust-std-beta-thumbv7em-none-eabi.tar.gz=0b097cef25dfe72f692cd6d9dd2df85a2fc5ea9db87b8c06b8f310c239c74624 -dist/2025-05-12/rust-std-beta-thumbv7em-none-eabi.tar.xz=e7fd61ad7660c7f8c62ae6dbbd238305d997fe7539dfffb8fd0df2205656b5a9 -dist/2025-05-12/rust-std-beta-thumbv7em-none-eabihf.tar.gz=d6b3c40bd84fe352c1a88dfbc3c0f9012dcc1d82b860ce68c1d21a8d452fa662 -dist/2025-05-12/rust-std-beta-thumbv7em-none-eabihf.tar.xz=b2be1305ae382359f81e0bff16341719b6ea7731ff833205dc3fd99e7e978fb9 -dist/2025-05-12/rust-std-beta-thumbv7m-none-eabi.tar.gz=5452dc0f152065e887178423e324bf3082885b922ac57ff22c156cf7c432e184 -dist/2025-05-12/rust-std-beta-thumbv7m-none-eabi.tar.xz=5331de420a79f521351a1ea3dd501cb00b21e1979eb23dfc871ce33abca68dd7 -dist/2025-05-12/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=0b2ffb463dca747f00cf063d8fb07971df80882d3890c34ba82fbf1b77655dd0 -dist/2025-05-12/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=2f7c2bde8ae4b911889dc24a8fbe2d1539685d46c71689e5e8362cf46c391019 -dist/2025-05-12/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=1d29e86aa77e277ce1598313d6851f2f077b023217f1712d59eb76305fc773fb -dist/2025-05-12/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=73dc975b217329d6ad44b8e8b3f72a3396597a207df7d7222d983a155ca05758 -dist/2025-05-12/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=c34c686a62afb45b9e57b3d487dcc1f66396bd7804a9c0d9696def0936a2ba1f -dist/2025-05-12/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=1527843f87588ee28aaedbb0590bb809c24cbde6a5264151ce5fe01baf70176d -dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=8225d6b35a55d7937bbcb7f2e74ab8ec0f23fcd69a48c59391e9016d9863151f -dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=8c59ed4aa0a62ff8999570b60a6b9c468ea52c45642ecfdc515d6f2722fd821b -dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=6a5804a7dc199f696867e4612d1381910ff9a13b5516b2906e651451d8ec23e8 -dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=11205a43892169cd0aad2764f5d7604a52d13292978e7e851cef2d8e65ae6fe5 -dist/2025-05-12/rust-std-beta-wasm32-unknown-emscripten.tar.gz=962e092960bd3074dc966c1928a4adfdc16d6d811060e719dc1a84061132566c -dist/2025-05-12/rust-std-beta-wasm32-unknown-emscripten.tar.xz=5d7fe7b3fe3b022c95d96e4027767b44a7e7980ca5c894839868919a0bb4b5bc -dist/2025-05-12/rust-std-beta-wasm32-unknown-unknown.tar.gz=38afbfef695bad377ac9d3a4d7d9037b500795c3a75f907bf60acd4cac2b4cd4 -dist/2025-05-12/rust-std-beta-wasm32-unknown-unknown.tar.xz=f5f670d35a843cda6f5213ae02a99c3c6d1e30f3ab651be0087bf8e4de0911f2 -dist/2025-05-12/rust-std-beta-wasm32-wasip1.tar.gz=e7faeb24fac65f565e0145166d67a30b02b26f0df20791f3bdc31169846a0e2b -dist/2025-05-12/rust-std-beta-wasm32-wasip1.tar.xz=0136f4434e8a0edbbe050899a17ae2b2825aeb4b98c4fb80f8eb25c9ea6623ab -dist/2025-05-12/rust-std-beta-wasm32-wasip1-threads.tar.gz=2ed823ff5c3704f91048300fa31624cddeea8086cfc654fa2fea4adff58fd901 -dist/2025-05-12/rust-std-beta-wasm32-wasip1-threads.tar.xz=6f156a460db83c271b43c37709ce5724fb8059c44b29e08c2b2da27c32c06e5c -dist/2025-05-12/rust-std-beta-wasm32-wasip2.tar.gz=ecd1ba1fec2e1e87b5f30b341e8228ca98545143adb8acd6ba53c7503f581e34 -dist/2025-05-12/rust-std-beta-wasm32-wasip2.tar.xz=593976f715c77796cecf6f7f2b79fbd4f49c10ded0349762e8312052497f1e28 -dist/2025-05-12/rust-std-beta-wasm32v1-none.tar.gz=396eb4c1e4cd930f045b092bbc8203315f494ea32c836d62e84f63ead124d886 -dist/2025-05-12/rust-std-beta-wasm32v1-none.tar.xz=9b827d1941a1d67a32a255342b476a19f57de06e53a9e6798bf00688b86eb2e0 -dist/2025-05-12/rust-std-beta-x86_64-apple-darwin.tar.gz=2796de44843d68141c6330f0e09fbabb5c3a8f34470d2948f1ed93b1b9dac088 -dist/2025-05-12/rust-std-beta-x86_64-apple-darwin.tar.xz=881e98599e5b2475e8c9f6b81e0ad6a51e8058cb2c7fc893ab57c19cdcc80804 -dist/2025-05-12/rust-std-beta-x86_64-apple-ios.tar.gz=8203faeaf21dc2c86b35d4362413c12d01de33da4524008c6261d3c87be9e51d -dist/2025-05-12/rust-std-beta-x86_64-apple-ios.tar.xz=13a59816008d3d4b0fb20680bfe2f1c2ae8ca7eed0bdf717817e03693724eb25 -dist/2025-05-12/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=5b906fe2d801c572696cd93564723338385eb574587769f79506cb3e6c87452d -dist/2025-05-12/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=1624a408800a895d8fe71bfc71876e52349c3508e9ddabd46d89d3274ede2dd7 -dist/2025-05-12/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=6722e76457289c6551f77fd462058862d7fb8597e1714cf66925b21e5af75c7b -dist/2025-05-12/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=8097f509383cab4e8e444ccbf7f5d91fe35bcd2cd2017ab78bcc692c9fd1ecf4 -dist/2025-05-12/rust-std-beta-x86_64-linux-android.tar.gz=eb3124653c908003185b36aa9829ea983f4b44e11a96da69c2585664a67bfeaf -dist/2025-05-12/rust-std-beta-x86_64-linux-android.tar.xz=4f29c6a0458ed5e37ee7a17643ff7854bd6ed029c46cdd0707019d01523a7a62 -dist/2025-05-12/rust-std-beta-x86_64-pc-solaris.tar.gz=319663b24b449df3f8063f64bd849969999a441b9376c86e6eea15cf3b872e5b -dist/2025-05-12/rust-std-beta-x86_64-pc-solaris.tar.xz=13280470aa4c84ed6ca200664ebf3a6aa084550a82c06505b3178caefe3072ef -dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=d97cf2b52f013b5cfdd9c5a3885ea70accdf52e2f957e086018d88731c8c1964 -dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=a2685ab1c204823b19809e47b00f2c48c5f2cc2faea05ac2df935732a7412441 -dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=50c0f770a938123f704837bd3313dcb12842aba75b687282a9aca6c11b11ba8e -dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=8f0d04c8d55f23235f8dec94c5d5035405afd513b082f00b257bbb86cd481240 -dist/2025-05-12/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=e633aebc178d4846a3d26f796405dde13115560c23bd2955c82afea8ab7c8d7b -dist/2025-05-12/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=8125d5bb9a9205ffab43d0dcd56402320643101169a49098a98ee6ae785c0ed3 -dist/2025-05-12/rust-std-beta-x86_64-unknown-freebsd.tar.gz=c089415c86c9f74a454b82955911e84c9138ad66757e4da689381a1bfbd4cee5 -dist/2025-05-12/rust-std-beta-x86_64-unknown-freebsd.tar.xz=a930b94bc005ce2b09b4d67abf47bfeafad8c7ab6ca5c15acc10e023818e7b25 -dist/2025-05-12/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=f74e77eb803d1ca244e1e97272578ec008e9c373af92887318d9281204a798fa -dist/2025-05-12/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=2fa583fcde17c1ab2f2d148af9467fa65f6bf6a0a1801e957fa15a79e6de4f78 -dist/2025-05-12/rust-std-beta-x86_64-unknown-illumos.tar.gz=20d16ce11adf468da51b30c0b55a46ce3bd030eea9f9fdb3f65f36aa442a3d71 -dist/2025-05-12/rust-std-beta-x86_64-unknown-illumos.tar.xz=dc1f2d3c1a0ae59cbfaa09b2d646645cb4fabb151edbf92975e4c8a0bfa54eba -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=05b2e5ded14501cbdc86c0510faecbf873e30d2d70724013bbb176b6f4039b44 -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=a18281579cb61ea26ae0062428f7a49e51c4a928102a5eba7ff96b0ca38490c0 -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=a5285ae02217d64c7bbddaa3dd1f68c361f2849479a6d75edf1d551751886f7d -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=4c41edc4f4cd1f24107b1b003a1713af3b456ff3e933781c5d4ef21a490df5e7 -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=2d4c1666d456e810353f8b386d0d331812f84d9a17344953e5f4f4370bdccb0f -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=287f51dbc6e4273208869140b9c2e0de2896c0cd40f7492396ec0bbb8989a82b -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=e20af62d1900a5e10cf766ddcda9550176ab5f41111e09d57167e4e23e68d005 -dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=7837f4880ce5d5251213d17867a6c61977504840678388fe245e0433086f409e -dist/2025-05-12/rust-std-beta-x86_64-unknown-netbsd.tar.gz=17d2a43bc24e4e49d54315c7eb0e4952c3118b278b0a564fd588ea4ce0e8d90e -dist/2025-05-12/rust-std-beta-x86_64-unknown-netbsd.tar.xz=ab34b5b10273c639805956665cd6543749cff2748c53980f80342facb9171b2d -dist/2025-05-12/rust-std-beta-x86_64-unknown-none.tar.gz=d8387a8478f6a937944d684f852dee18d584344ab84425d228489dee324c318c -dist/2025-05-12/rust-std-beta-x86_64-unknown-none.tar.xz=6ed8c2c72d547c7cc6b32a6080c346915de02a1ac02f032b6320fc7e3d45e330 -dist/2025-05-12/rust-std-beta-x86_64-unknown-redox.tar.gz=7f3a62578694121ef90fd08ab7a82a8fb27d86f164d7f73edb56a2e360198f41 -dist/2025-05-12/rust-std-beta-x86_64-unknown-redox.tar.xz=44f7ba0ca447050ad3eb7be0a0e41fee304dad2ce359c854848b7430c42b22d8 -dist/2025-05-12/rust-std-beta-x86_64-unknown-uefi.tar.gz=f78e6eca6ff517571480a6bbe20099d170f6a6b2ff0e64544c41dc77588ed890 -dist/2025-05-12/rust-std-beta-x86_64-unknown-uefi.tar.xz=d2a733aad6929be6135676307bd4576eb168e11192c24051e0be4a713b5733c5 -dist/2025-05-12/cargo-beta-aarch64-apple-darwin.tar.gz=43afffa0c5f7287e205a63871b555be144e900f8d8d67e4ed0654b50809b7338 -dist/2025-05-12/cargo-beta-aarch64-apple-darwin.tar.xz=705f051543ed8cc7011d7a866f345c3aa22c9d24f5325bffb9d9676e3c26142b -dist/2025-05-12/cargo-beta-aarch64-pc-windows-msvc.tar.gz=c0911e84ca85de5e8c9550e2be08dd85458ba31516e282044c9149bf8bb56fa1 -dist/2025-05-12/cargo-beta-aarch64-pc-windows-msvc.tar.xz=7335470fc1338b95edc81777eb0975cd5cf5cdcdcaefc7658f356ef3e0c54fda -dist/2025-05-12/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=99071e036041b47f78b71f1ff2ef5699b96a126ea84010ac031ee8d52d7c5873 -dist/2025-05-12/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=a9be2eeeed37905e83beb4265f4f45086675a0f5ff25db0e6bc0c5164257e1e1 -dist/2025-05-12/cargo-beta-aarch64-unknown-linux-musl.tar.gz=b38fa8d68c27b4989b1dc94caaf6bec833cc8e6d4464b859451d495b081c5b1b -dist/2025-05-12/cargo-beta-aarch64-unknown-linux-musl.tar.xz=95a839bd2f928afafbe1058cb185b95e0099ae15d5d3030a3493724f40300ae9 -dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=34cef4599ece9c218c3841ccff9a627a69909eb733c19441c19de5b68841845b -dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=cedfde42e95a0e86c3be841965c20f1c8bcebd20d88f38b2e694017a8afa745e -dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=b8f1a0fca9b32362da6169b41fd58d53af6b02992ac5666cdeed03aa6150dd0c -dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=8f5b040e4099a03418b72b5975419089e7fa15a947b04ce6dd18f450cc21f2b4 -dist/2025-05-12/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=2f526034ad1280d152861e700fad2aef95759eaf17780a3a00d71e8fc6d8520a -dist/2025-05-12/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=b6fdc7a08740d06e29aa678f4f9cb2dfb57fb863605fba1cce67d71ae1c1ace7 -dist/2025-05-12/cargo-beta-i686-pc-windows-gnu.tar.gz=f8b1e0227f5c1c2334cbcf53ebe5e94e01215ce21de2c5c9846e0ea7dce8e777 -dist/2025-05-12/cargo-beta-i686-pc-windows-gnu.tar.xz=149bc0d8cba9924db3b882795b6dd17f3d0a01bedfa75143dfdb7623cc7c4684 -dist/2025-05-12/cargo-beta-i686-pc-windows-msvc.tar.gz=b8462286bb1746bb789f580a14f1c5c37b108037633d9e8fbc5e2e6638e12a5c -dist/2025-05-12/cargo-beta-i686-pc-windows-msvc.tar.xz=f07104b3439e4cfcf5c96dbf6bf4428f677f45449ce2a5595551884ab0a6870a -dist/2025-05-12/cargo-beta-i686-unknown-linux-gnu.tar.gz=f68dece61dc087622d9e622944c4c13cdfb056eecdd93c9527c71637c73a708a -dist/2025-05-12/cargo-beta-i686-unknown-linux-gnu.tar.xz=3272d868a2bc44b80d0ab11d133f66ed7a40b75d00fbb7a341adbee083dfd8c0 -dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=2fa7ef9b0f5247a650c1cf649e7f5514989a22b6c7927fa1df809e54466bc18f -dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=3eddae3525cd8b446a4b31ea933cb859d335b0309900379868230d4a63979afe -dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=88d56208387b4aa9707729f0b9337c32a0516dacc4c891b3c80140874dec6043 -dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=8e4ceefb3d64560d989bf69f3d58cc07ab2e6a68d1f761ef92cb1826351834bb -dist/2025-05-12/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=ed5705fb6dba34981727e4af215d8875de2c39d41b1c3e8653a93cdc06873975 -dist/2025-05-12/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=be618816cd7706709fc13ab268249a74f7b905e7ae6abe6ca1fda336dd38baa2 -dist/2025-05-12/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=8b53a21201661914e3291ebc6912083e1cd86ed5d202d6940c2be15724371bc7 -dist/2025-05-12/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=546260a68ec029f228f280fc439e93dc1f64b3e597cf615ff3915548ab67b435 -dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=343a00f2cc571ac779fd7647560b215650a01e877c9b15f95668cfc33c67ec77 -dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=efc6a23ffb467e1459f3fe5932e8303d0ee550853ad13b3ace12c9aa6514f24c -dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=5c4e53aca46fcfb7d669b74872130fa2b8bf05b09d14bdce34f0322030450e47 -dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=c2e33c9522924cbfde1109f87d12d27225ceb23c7ad801d3a5559a72715ca402 -dist/2025-05-12/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=91d578317c8fa147c22e81728da411fd01c1fcb0bdf2e054948537476b8371e8 -dist/2025-05-12/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=83fc425704b7673943583e38c31a944695984ffabcdaa4ab79b43aea03cef48e -dist/2025-05-12/cargo-beta-s390x-unknown-linux-gnu.tar.gz=dac65289a906a32908ff0af9e9b829111295b49099fd5d9f90b2e454b4ecb422 -dist/2025-05-12/cargo-beta-s390x-unknown-linux-gnu.tar.xz=02a3972bfd62d4097da252fed278d741193f2c4face2e35ce8e84974e42cb1e1 -dist/2025-05-12/cargo-beta-x86_64-apple-darwin.tar.gz=148d0410ec2d3e540cfc27b6756e50d98b7ed214c2e5a702a9f2326e75ec249c -dist/2025-05-12/cargo-beta-x86_64-apple-darwin.tar.xz=65e993adfc14eb7a9c3946a3d1ce35f5aa9767ece65cd759669bb82deda0adc8 -dist/2025-05-12/cargo-beta-x86_64-pc-windows-gnu.tar.gz=a69c23bfe9ec73737c22d0b6ce308a4f19625aab2f1846bc223ec6974cdd9163 -dist/2025-05-12/cargo-beta-x86_64-pc-windows-gnu.tar.xz=56b33a8c9e0bcbbdb2c6be13d7b84d077a896b21d800a3c6da64aa2ef64ecada -dist/2025-05-12/cargo-beta-x86_64-pc-windows-msvc.tar.gz=cfd22dda3987642606f9e869264fa709d87b8ac5894547f809f60abce268ff76 -dist/2025-05-12/cargo-beta-x86_64-pc-windows-msvc.tar.xz=7075d67ef2dbf1e0d3889039d4db66042db538304c53cacd3e983eb9aa9d0275 -dist/2025-05-12/cargo-beta-x86_64-unknown-freebsd.tar.gz=419ce0f856113509f58f2fbccf9e5f864aa56c3c1a2c4029ecdb546464393214 -dist/2025-05-12/cargo-beta-x86_64-unknown-freebsd.tar.xz=d8f73cb808471883a5f6ee8db3dd5165fff5084ae744f4ffdca89fb545faaba8 -dist/2025-05-12/cargo-beta-x86_64-unknown-illumos.tar.gz=69e63b33c7f8d469232504c373a4e35df97016735be633a818023ea21de8f0be -dist/2025-05-12/cargo-beta-x86_64-unknown-illumos.tar.xz=aa86cbf46dd2e35c10bb5725c627dc40ecb33329a866c2b0c5c274728f384ed3 -dist/2025-05-12/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=f77e6d762e13eb95d6369a26971e4108de448eb23690554914f650fadd2898de -dist/2025-05-12/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=8e4b379bd88e8f18e5b6efe6058bad4ee60fb6c2e734ec165fee188f893f948d -dist/2025-05-12/cargo-beta-x86_64-unknown-linux-musl.tar.gz=a04b711f9a07eee991b1ab13ab56e0f9e2c2ba2a16186be6c0d04529ca68af59 -dist/2025-05-12/cargo-beta-x86_64-unknown-linux-musl.tar.xz=587b214ddf5b85697b78d8baa9164a4b81604b8dccc969a03b1bf06ae7c11240 -dist/2025-05-12/cargo-beta-x86_64-unknown-netbsd.tar.gz=81a468f1db3cbdaddf6a1785297457d4780fbec472d0bdfda64fb7a398782a78 -dist/2025-05-12/cargo-beta-x86_64-unknown-netbsd.tar.xz=32212f4273171d78e10170c4a863d6f9990e29e26fdf6857dd3d134eb803161d -dist/2025-05-12/clippy-beta-aarch64-apple-darwin.tar.gz=e5de69a84edb22eeaaeea2d94aafb07ed408508f68fc0989268e6dec8bae6a8e -dist/2025-05-12/clippy-beta-aarch64-apple-darwin.tar.xz=03a9ebedbf11cf151d19f46b9eeb3f8ea765ac779b55356b51db21e83195c610 -dist/2025-05-12/clippy-beta-aarch64-pc-windows-msvc.tar.gz=5a9e27ab31a382ba91f9621508cf28fb4f5d0f2521452369ea2441598d34b2bf -dist/2025-05-12/clippy-beta-aarch64-pc-windows-msvc.tar.xz=951c9f03a6fe0de1e94ab8f064cfc1b29b06606c38e891c2f9f1c550e9d94678 -dist/2025-05-12/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=1a241694ef544259a3c87bf271b1248ebb6fd32ac35b3ac16154e509b80c6e47 -dist/2025-05-12/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=679c8ed606c22490fb0a5a8503d898e61199e3cd17d9dd7a34c121781ca7306a -dist/2025-05-12/clippy-beta-aarch64-unknown-linux-musl.tar.gz=26ba8ec943e4f8cfa27afcde06fd34dcf546c3a5c7668acf703a9b962a1977c8 -dist/2025-05-12/clippy-beta-aarch64-unknown-linux-musl.tar.xz=051112fc6bd906c62cf14d2fa9c7f1505540a6aa86ee0b1889e11b1925274c23 -dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=a44d29c794e49742417de03a955922ff3634ad45a5e6b5799c767f3feb2ae7ea -dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=1650c464df6d87fcf3cea65722a515a1f1625d9e1ad6d27359455ecab849a592 -dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=1c4f6c22361665705334faf35a0a7c17d55fb3fbd2622721e8cd7c76418cfc41 -dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=f75400fc72fd358be80cbedefc53a9002fe6cc22637687e941835acb8c5eced0 -dist/2025-05-12/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=f1a2db6029e9d881dbfe7c6589873b323358d8317865824705c0cd358fa3ef49 -dist/2025-05-12/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=9cc0a2212a36bfb39379008b781304da67c74ab4ce0909da18f8cad50fcbbfd0 -dist/2025-05-12/clippy-beta-i686-pc-windows-gnu.tar.gz=06051eca41cbd1b570725847b4d8b79f29bd20ac06878ef5689167626fd4b137 -dist/2025-05-12/clippy-beta-i686-pc-windows-gnu.tar.xz=857d43d424e718e04714562132802aa5fc9028945a3c40c34508abd165a909c1 -dist/2025-05-12/clippy-beta-i686-pc-windows-msvc.tar.gz=58bf660a2f3ecf4671de4624b12b5a35f1e530d3c16f47eb7e114d1deb1891ad -dist/2025-05-12/clippy-beta-i686-pc-windows-msvc.tar.xz=5a36ec9ff4e35f1a49775e6657ea4f65543b47ebbb776fa1c60fa7898666de62 -dist/2025-05-12/clippy-beta-i686-unknown-linux-gnu.tar.gz=30df536f3cf6fbea2cf745ca8177f88831ed5b5e25d8fbdeee5f300fb35b97fe -dist/2025-05-12/clippy-beta-i686-unknown-linux-gnu.tar.xz=a491efcade35834adcbcfa8f08004b6a181a8d8fbe36f6a1bfd8e092443a82ad -dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=a16579fb92973f609f0eb215d81e1125ad9dfa9e22d5d869236bbe0a7bf8050c -dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=45ff10aa52e6162b015b1a927dd23ef7404fbbec554e5a1b655c085d59a378e7 -dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=37e4ca4776fb278cac2ac05ece43ae569780503d0b122545eebc7a746dca69f3 -dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=9c33b12b9c0a6d94b16a52066e3a1a8a2581db1c7549de002f0d6f4670021f0f -dist/2025-05-12/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=a7939ed010f6cef23e23e17c7ad905c6c0f4e549c85a8ae38d743232fe8de321 -dist/2025-05-12/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=21046d6fe31c0930e4611a18dcd48f5cacdcf3b64b5d035b4449b8b5af417254 -dist/2025-05-12/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=a03df872f97472d9a4310c8097042ef80ca859485fdb95ed9bcd853de3cbe9ec -dist/2025-05-12/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=925ff3b371f6c4ec871920c5e9fa5ab046f203c0af95f10f0996a750bd125582 -dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=5f159a1913f6a5d10b5d5140093c9af4277d8a632db5cc116065a08fc0ff8bb6 -dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=a2385ac96c42af4d77eb84ca70931e005aff1dc0e1ba272483ee82a837d96709 -dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=9c289ed719cd18c8e5b883aeecc03e46f35b6b90d191b4fb0d0b4b6c7fc5073c -dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=1a62cf477d5ad2ce4904a4438ab5756f75b894288a7449ae70c9f63d3b7badda -dist/2025-05-12/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=c1abab08e81632db27613f3ac7036d8ffdeaf92e345b345bf2c3535f4d9c16f0 -dist/2025-05-12/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=611252f8b142af9a86e511ae783f41cc97104d2e5ec5835c7d5006421ff6207c -dist/2025-05-12/clippy-beta-s390x-unknown-linux-gnu.tar.gz=d436be0f0f72db3c4933e8e34fcbb71e33b90ddcca58bc4b4360fe22e7a89404 -dist/2025-05-12/clippy-beta-s390x-unknown-linux-gnu.tar.xz=9f8086f13b6f53d44f03bc53fa3d750a9f4dc13b3612b10dba48958f4b61706d -dist/2025-05-12/clippy-beta-x86_64-apple-darwin.tar.gz=1b4a51c42bcc9e3241ceaceab3fb22bbf8060e9f4c2c55357603c1bf2fbf75f2 -dist/2025-05-12/clippy-beta-x86_64-apple-darwin.tar.xz=42556126bad0e0554dc5464396383c75a1fcb76257249c62ca4e40971129c458 -dist/2025-05-12/clippy-beta-x86_64-pc-windows-gnu.tar.gz=59a2a00a0c4e05cd0900fd119f43d4354b9f6b9df9dd9a9b44a1cfee9c674eb3 -dist/2025-05-12/clippy-beta-x86_64-pc-windows-gnu.tar.xz=35290a11740a2fc0c02d534375ca4ac0392de41f281383d7396179f670ddf309 -dist/2025-05-12/clippy-beta-x86_64-pc-windows-msvc.tar.gz=db01970a436b89d5fe3cb5eb65ea075f7dfd15b649958b35ea8d88835d8fe1c3 -dist/2025-05-12/clippy-beta-x86_64-pc-windows-msvc.tar.xz=9df8c8ed117b2e975bcb0520601c9b4e19e0440b14d9e510d09c9b54b872379f -dist/2025-05-12/clippy-beta-x86_64-unknown-freebsd.tar.gz=736361d62d33e969bda4cb98ea592ee7128e88c047f05b77cc025c982c27acb6 -dist/2025-05-12/clippy-beta-x86_64-unknown-freebsd.tar.xz=72f50e46dd2697c32b20ac2d0ae9ae2ea10485225dfd41dc9fa4e24d3b61a26e -dist/2025-05-12/clippy-beta-x86_64-unknown-illumos.tar.gz=4c856630844d01f655dc9855efb3685c2c30fcf199edfe665d9cf4230774ae0d -dist/2025-05-12/clippy-beta-x86_64-unknown-illumos.tar.xz=70bad50bffa518c4658e44dda7b6723558d68a545511228b97e18efc37a3ad0b -dist/2025-05-12/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=4c1e0fc35732f19effc50e67f637c57699ed7e846e4201db3897740c1e34a43a -dist/2025-05-12/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=fe53a5340c93485ac496453752a15222d323755cb20427b29b952b49f317a4bc -dist/2025-05-12/clippy-beta-x86_64-unknown-linux-musl.tar.gz=c56f80644373fbe9bb87310d26876a86325fccb1756716db30a5bf70293d328c -dist/2025-05-12/clippy-beta-x86_64-unknown-linux-musl.tar.xz=f4597f7ed6d0def07a32e952330cc964e49d42f84d65eead84192a29978c1a41 -dist/2025-05-12/clippy-beta-x86_64-unknown-netbsd.tar.gz=ecbc80189d470c1cc221360b94964fbd26d52b7583ea065cdd52795a48bf6271 -dist/2025-05-12/clippy-beta-x86_64-unknown-netbsd.tar.xz=f08204b9216fcb127934f2ceefeb7abe4338bb2ab79576a3a2e2077201f521e6 -dist/2025-05-12/rustfmt-nightly-aarch64-apple-darwin.tar.gz=269b22b568f60889c4841feff1c11d9c151d2655d134e966f7344f7affc6db57 -dist/2025-05-12/rustfmt-nightly-aarch64-apple-darwin.tar.xz=474f13aa57c73f4f9e3c63edb9a126ca845e63a376b7b8e35b5c6aa8fb0d9573 -dist/2025-05-12/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=9f24753d7abc9aa196a72ac54bb574f5eb375ecd5b2da42d0ed34bf0fb8eb947 -dist/2025-05-12/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=daae34864734810ff8ea563db7bf691f6c0fa56b9087fe285f7a3060247ef6e3 -dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=c21f59bc03b8097f066be7bd3a7d0febe873f321583a4c7a9a0cdf5448d92ced -dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=574fce0d0ff06850db47da008fdc6c6551f2cc459f63f69dcf8edae5e5ff51eb -dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=6379365fb729e0f5d57873ad028f0c2641d60bc19ac5c905a2d1772b6730cb93 -dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=a274c20436d31f74b4144f165a2b383297316f1f96b0d89b2b86bbf38e57be98 -dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=03c3270a78c5d62517ec1b5c61414634ad58e5d4afb914f31bdc12ee0893ff2b -dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=b309c052cdae48b23c2e89dcd7362af97f50181745191dee596ac176c2ade8a0 -dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=300baf318827928f0c824e20ccc8966d3fe9e5b5f62a0d1aeba5feae1d183a11 -dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=09b764e2038499d23b28b8cbdb01c9480f2100a01d864b7f03905bc78412fa00 -dist/2025-05-12/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=47c087899d4155750e71a261a0c93c9f736530d991dfa7e34c1a7bb7f2aedd8b -dist/2025-05-12/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=7e589aaaac2ab2c1211e5f5e1090b2ce1633f8b8682425aff01afd4dbd25e088 -dist/2025-05-12/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=0169fb75018dd644d7ed842472c04a5c82d46f3bfebe6d49931839809d1824b7 -dist/2025-05-12/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=96f3e288c8ccf073b1ea983ba382e341c8f6664135ad9aed7168bc05cf06ac4e -dist/2025-05-12/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=29b1f7a4b1454bb1c6af1e720e05bda846725a8e866266a147335920e99e66a9 -dist/2025-05-12/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=71a2f81ff29fd7e4c8dbdb2ce85bebf5e8ea5889cbb41f98fd3c3816918a6a3d -dist/2025-05-12/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=ae5458b4c0d58bc3e307c289aa44daf82218aaafc7911dadd4a09f4ca7cf6e12 -dist/2025-05-12/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=cf19b582a8336aa3f3959803cb24ad4499bc529bd58cd0766e668af5083de93b -dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=474a34a9566402e313f5fcfaefe29188a6db1c0bd17caa20f186787267ac8e5d -dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=c02f75eaa71f6c4d613a80dc7092d57cd4f6ef8a7de7511711fa818c0612da24 -dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=95b47139ab6e9c16acee5ac78744c3e9ac917a5e811f45adfec4fddd45e98cf3 -dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=fe13340e51d7d81629e03019d375a72874b80f19420c77ea083292a22a9be589 -dist/2025-05-12/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=a95ed14a5bc2f926c2ffb5dfe49813817638154edef7f29522661c57ec2dec09 -dist/2025-05-12/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=d9060c0aa08e0ade2fb54fb5381f0f69dc94166741200b2ed35a46b5d9885036 -dist/2025-05-12/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=060213e707c6b8911517e786b21515e169e062bbbf96302e012a442d260789e1 -dist/2025-05-12/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=f1d4dd54017937490f559a472893fb8a00236b46bf0f57ef9222ec3bbd191004 -dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=38a57b7fac63608992995b3b983643ae213f6fa3d6a1021691334d84a5491542 -dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=f26658ea60a6424707a027b1e36488f99490bce045978c3919c7320638f60d68 -dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=07fbca58abf5fc57560e20fe7aede77137dd3f2f4cf2a6da11a80eaf6672bed3 -dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=f56f7bb1091fbb1a8d1583beb586194e5dd526f7a0268b4ebe997e0ce7c9d9cb -dist/2025-05-12/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=3ec40438a95a086a1c4c522c6ae018393469f605b03d392562fca4926bdf0631 -dist/2025-05-12/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=d7c342edbefe3fc22631961c2aca53cb808bc8f1df17673ec5cafcc56eaf0475 -dist/2025-05-12/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=4c1a2aa84e8e1c67a111b9a622b2c6ed96eebcec9752ccc5e940460ce048f22e -dist/2025-05-12/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=e26a0359223ca793d34ac9e4e5731923c4531dcdbf32aa8789bc9d1bda17013f -dist/2025-05-12/rustfmt-nightly-x86_64-apple-darwin.tar.gz=dbf20af35cbe11baab7ead72ec254717642b01fdf30140589510413058af3e49 -dist/2025-05-12/rustfmt-nightly-x86_64-apple-darwin.tar.xz=7beb25f2df0877ee74231abe03e74a09c6e41a356d0cea27956b2091382dbf47 -dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=527d2d68bfd519d49936fd8941a04d787df1edf8c2c3ecc39103d55d1683a970 -dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=8744bef9d00d6f7397ef2b1b36971ad7af6389e93b5286ca60feb6137c4f6b10 -dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=50f8f2db4f410e60a6cd4ad03a762ea636076d85af05d511f40d2d2ea98bc833 -dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=183f8742c505ab1d0488ca915509c1b0558166c6d19d8dc864d0a1686d66a791 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=f042a8c4ef96911b2cc6cc2228ff832229196b4ab5b1b04b05b22b5b9a90649d -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=9b93acd9cb8c8e062f3e47f5415adb8eae67479318b6201bf66119d467b81e11 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=fe9073a3bbd3b6513ba0fc38005b8ab1d44052e1bb10c1976bc98a62f8df5934 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=4c99f67e351758fe0db0bc7cdfe177018083b9ada2feeee952180b420e2c6ac9 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=c5a5702c66ae7de6b7a10d1c8c39af6c973c6eeebbc1fdba3b427c1ec9588756 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=8da51f6150fa5c53dead4c3db2c2d7493cc46b36d64b978e605a9d5755dfd779 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=3d77d2579fcb53a9bb6d942d44353f7b818b10504b64b790ecc3630d8b17a565 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=7e75748bcb8b25bebeb1b5aeb2afc2fc1c48f38ccff9c624cd002a8e051424b7 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=9c05c902b0db8fd8f8b44d83a95bc8722bb714d333d2a61a2e1ef140092b6d83 -dist/2025-05-12/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=d614cb69e1484f3653bc148280e7518640ec830ab8f02ddf512206ac265d6746 -dist/2025-05-12/rustc-nightly-aarch64-apple-darwin.tar.gz=ac2c35cd19b85e6356bcdb987031314afbb7e41f26418ddb0d943fc3482245c6 -dist/2025-05-12/rustc-nightly-aarch64-apple-darwin.tar.xz=a3c53f15d7b6f7c7e5f1e55c107663ef102cdb123394bcbe8a8c9c32a7e715f5 -dist/2025-05-12/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=29e3bae16967111ce72d00b931d32410ab526617bf1c88bbf90e4d32825ea7dd -dist/2025-05-12/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=116103ab4251b366644239f8ef8d7129ae3d9588d768b8e66671497b1fa36c95 -dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=911acda80c362dd7690e5a4596e166b8ea49425f6dbbfd78ef697e69dc826c85 -dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=9cabea351ef05117d8cdfae0df334c98b12a99c4191d3e4f382c336c326520dc -dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=81c9ed04939e8d363e060ef2808bee8dbd63435b111f37325bc8fd2891726560 -dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=a44b2f887aeafd5ff57ff67d8c4eeaa94cb4edd2f7d5912618ee186a4d609c73 -dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=7a4047a85297d3012c00377241f3daa50b34ddc54d68d67787d76eb45f5db616 -dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=09acd09fbfa3c43738c43c8c423d3fce6dc4451ca4ee8650ab3392279cfc288a -dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=88ffa28a612cfb661a731dd4feeb6d6fae88d7236469ded88ee74a06a1576a8f -dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=7c5747fb16062a786ffba5d00e1bc0e3c81ccf6154f09e21a6aa5b87c2fc9594 -dist/2025-05-12/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=c1bd5074d4664f0ac8019151aea13e051cf2d89b8bd8fa77b9ed3831a1b7c217 -dist/2025-05-12/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=20fa9e5531e4be0e54af97c8d033722c68d54ef984be3619ad84be6b579d0c73 -dist/2025-05-12/rustc-nightly-i686-pc-windows-gnu.tar.gz=fe7511b5bf7830efeec083d3414e389286ec117b53db0501d5c314eba24e3bdd -dist/2025-05-12/rustc-nightly-i686-pc-windows-gnu.tar.xz=95677d845a5c7677b951300f17d810301397df022145f16674a58ebb1cd52a56 -dist/2025-05-12/rustc-nightly-i686-pc-windows-msvc.tar.gz=a8f1e4852ffab09aeab1ccc09fff930444871fd3b490e68a1f9ae504c0dce6ed -dist/2025-05-12/rustc-nightly-i686-pc-windows-msvc.tar.xz=11fd3093a95e379d6472f063bfdccf6f3cf6c44956d68d121adcd1c927812eba -dist/2025-05-12/rustc-nightly-i686-unknown-linux-gnu.tar.gz=d95876f9a84ebcc97033c81dd07fe8852f0f472db94c074f5029458fec512d2e -dist/2025-05-12/rustc-nightly-i686-unknown-linux-gnu.tar.xz=766182f4d375138f4871abba6a8b50c3ca342edb7842b6d4bf7162e466cb32fe -dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=58e41cb37fb5b974a78e7891c7aca2786bdf8153ac9cd134b713fc73771017b3 -dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=ec6990871579f86c0587a6f7262bb53dd7de3a79a39ca55b994475ad96f20f4f -dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=39b7b026e95bdee7eba78804d2f8f3703a141ff37c24ac636deb755fc669f081 -dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=0b066061a1a55836b3b81667c0c35d864055578370f00365db7226fc41f0f11c -dist/2025-05-12/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=040b0718e4f460bb6136628ce24dca390608671b609d8e222e4ccbfedff43d6e -dist/2025-05-12/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=f9206ff2fad2acaab1b3a30e1d7a634384533329f71ceed5ef2fce0bd288bd43 -dist/2025-05-12/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=9c6c12d9d5486c4d26d1f7d9a61625a20e3e7703af79195ec4cb7e7e22358f4e -dist/2025-05-12/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=2cace3cec2973aa8f93f1d5bbe8cdcb36134fc2313b0131c51d2d4885bb18492 -dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=60adf24efc4a8207709ccb39bf45ff5fb08c4a853de816c239a2aec795c22e46 -dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=038e9220451a497885e7886a293986b37b83979a4a6f70b112d42245f9e4a924 -dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=66d700e4a734f1a1a4f2c5d9125fee2c20e400b85a4a72ec4d6963f7d438a591 -dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=1198a73d12b6f556a5016a2181e1c95adf929f24df1be5a17b1ff8cf6635656f -dist/2025-05-12/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=e1b12d459eeed0496a93db5ca6962bd15bd307a400e8bb870623d20479d75aa0 -dist/2025-05-12/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=2339af50b563056c4ad58cff24b1d59198e71e06c85f1860461e9384a0aeac0a -dist/2025-05-12/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=4977999e15a893215a7f86ad55e195249f63c416b7a0bee3423950575a952d1e -dist/2025-05-12/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=5745e2dd22c39abd35b19117b5514ba383058c057265b3003cda3da4aadfa18b -dist/2025-05-12/rustc-nightly-x86_64-apple-darwin.tar.gz=008b5b604e3fb66026eca67f29ed65262f85a2e305286a5ad11642edc8eaee2a -dist/2025-05-12/rustc-nightly-x86_64-apple-darwin.tar.xz=b2c071998e209e6b4989eae799938268dee9d8ada531956d41147e747128f328 -dist/2025-05-12/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=8791712c5513a077d2936dd26c7157b12fd8b4bfc93180f97273eb534461837f -dist/2025-05-12/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=02fd232fa95660aa19665089a191fe350d0dfc44fcee4436be28fad82324fd00 -dist/2025-05-12/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=9f7d67cadca7abf25c5445a9f7c911a3e0a2db2e52c088cc6833e40b52bef0de -dist/2025-05-12/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=ef2853ac4f2a5c6932f16768fb1df277b9edb8d91615869b8cfa574d6bda026a -dist/2025-05-12/rustc-nightly-x86_64-unknown-freebsd.tar.gz=117efae53fc69e481498c1f268bbb12e382f479dc6859ad04fdfc4a84659d677 -dist/2025-05-12/rustc-nightly-x86_64-unknown-freebsd.tar.xz=14b67230c06ed6ec7597e31c6b7385782ab6a1f6bc723c5d2f171defa02c669d -dist/2025-05-12/rustc-nightly-x86_64-unknown-illumos.tar.gz=881a6c5ff0222eaca1fa278fb517963b30f51714c3724956bb2d29c142af0add -dist/2025-05-12/rustc-nightly-x86_64-unknown-illumos.tar.xz=3e708bcafdf8da1ceb92ad0e27407ea210144d91e30ba2486bd6758085153caf -dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=b264a719d90f6842e3cbc8dc7d74ec356328f0a94cca279795ada5f4b22c54ed -dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=fbe22ac8c9995feac7b13f92b8d4c16fc1cdfb4a15c06e127420762db0198443 -dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=48cf6d33fdba4e38dcc19710efd24eb863fe13bbca634e0ca02fc1647255bd6a -dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=7767dd1b6baf7065dfc74b4e9ce4c200616294ecd664243c6fe756522fb4a328 -dist/2025-05-12/rustc-nightly-x86_64-unknown-netbsd.tar.gz=bde7b39870fbce418257278ae56159af3f80f1688efd01d6d52b16127fd0b64a -dist/2025-05-12/rustc-nightly-x86_64-unknown-netbsd.tar.xz=6018c06dda8f5a0ff5ef7754bf2e8692b2dfd48be525d896261aea27d682f4e5 +dist/2025-05-26/rustc-beta-aarch64-apple-darwin.tar.gz=4dbdbac9216bb9a1e277f02d1fbbe6125709456a26440d0b8b852f615c8d0e5e +dist/2025-05-26/rustc-beta-aarch64-apple-darwin.tar.xz=14cfac4d029e4960d3d822d2a02fd5a604b4d545ccf9b2a6c8ce7d1a7fffd2a2 +dist/2025-05-26/rustc-beta-aarch64-pc-windows-msvc.tar.gz=af901ff979cb9ec448ca8d5ae8dd70987b015614265dc7d8c5fbcf0c7d7f06f1 +dist/2025-05-26/rustc-beta-aarch64-pc-windows-msvc.tar.xz=afe5ac02574f8b996a8eb7dd7e95d717655da8a49f652694a31f52fdb39eb044 +dist/2025-05-26/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=f2eaa46be24e7d5504628f05f799e58dd993d8ac3158328c238b834c14b5ad33 +dist/2025-05-26/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=2c4a949aee410d9ed6602c3f95d58895fb0051fe7a3f1d0abd585f3d952a31d7 +dist/2025-05-26/rustc-beta-aarch64-unknown-linux-musl.tar.gz=e8aec1f37de24b6532056f5e3be512f5ddde86e536a9b68dab0baac76df36778 +dist/2025-05-26/rustc-beta-aarch64-unknown-linux-musl.tar.xz=64457bd80c7575c0792a5b998d407dea844d38d102560d1fce824ac8241efa7c +dist/2025-05-26/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=fd5dac6844caaccc15f29bea41b81e9d271f4408057580a86fdd7f5a032f4233 +dist/2025-05-26/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=3095a4442404eb8e958ab9205fca9cfff13ca52cc18602fb322b410d497e6849 +dist/2025-05-26/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=d02a4cd721a8fa1f82f64bd9953f4867e1628dbb9e223e04c3ab7954f7eec055 +dist/2025-05-26/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=84c1a1e5e8dfb796c1c6b643329dfb65f53df372455fc70f4f3abd5dc8f614d8 +dist/2025-05-26/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=86b675fa61c7cfd494e7e6ed514e9ccf6beab425238c236f8425052df7800724 +dist/2025-05-26/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=078b1d00b8c6a37823d724b7993e7b2bcc73f433230a25bcbeb191eb2a0e8714 +dist/2025-05-26/rustc-beta-i686-pc-windows-gnu.tar.gz=08b47eca900f48b51ad4da927dca1a29b761e4e62b8e8eed7486cb150101afe1 +dist/2025-05-26/rustc-beta-i686-pc-windows-gnu.tar.xz=da0701faa92f4cfab71a78e707068d4840fb79393a00b14984a2bb37c24d99f5 +dist/2025-05-26/rustc-beta-i686-pc-windows-msvc.tar.gz=35de6670fbf76f3455be1630c8a3f628baea46473a69f0281e0dee20121b44be +dist/2025-05-26/rustc-beta-i686-pc-windows-msvc.tar.xz=45bd16224593ae586358343ceb5c845af01b053800bc0dd9ddb3fca352abeb09 +dist/2025-05-26/rustc-beta-i686-unknown-linux-gnu.tar.gz=1de0f032ca7755c2b2c7d79d048bb8e25279a728619b9bec65f8e373ef58ff0f +dist/2025-05-26/rustc-beta-i686-unknown-linux-gnu.tar.xz=00506ca8eeca547c844c48165e80afc71fa5bc9ad5734c2b90ebd9d6184540f5 +dist/2025-05-26/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=3067489dbd5bd1713e0d256a13061f484d662b4dad46e502a0d7507db69506c4 +dist/2025-05-26/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=09337bbecb0933162c1dcd5c85a5fa430b85c4b541b70f01ba77a82d5e64cbdb +dist/2025-05-26/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=aac2f6ca44fd4541ec6acdb658631e7907365f27b874c5cb14a15bd1fd23ee78 +dist/2025-05-26/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=51505bc92660d2e206ea35218b682e23155f5d006ab200cbb1398f6a23c63fcf +dist/2025-05-26/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=35e4fa7207810cd8490e3d7ba181356d55e946d740a7a4f36e18d38e8a3b35a2 +dist/2025-05-26/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=4cc9f12877323f5ebcf7f3d2878800dbc4d0930615b9baeb40e0a2824536d5d9 +dist/2025-05-26/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=9d8b217d6733c5f0375eaf6a38aa1a1b596ac5ef979f9440ff51ec7e7df25b08 +dist/2025-05-26/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=b32b303840f35d6a2f42751cada1f833b4c55b7d83634b1cc6d469902539d168 +dist/2025-05-26/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=2fb8b2e97e7d1adea24302e2d2cf47b04200c6ad0299498d07b4ab59b6d4df08 +dist/2025-05-26/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=33c763eeedd8f3a3ca885f94ade5c3a2355a479a0186ddae33e4cb068529de72 +dist/2025-05-26/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=6f59fecc08d6e84616bb89c2ee73b2f285c4bb2ebdfb122538c49b2fda41d1f9 +dist/2025-05-26/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=4723d0b463897004959e91675aa50aff0c9a9beca943267d77d11d5beee257cb +dist/2025-05-26/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=96a0400a43b8bc948619b51a9b8dbe778584b4225baf11f97bb59a443dfad1bb +dist/2025-05-26/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=cee9ff6682f8c87d5b81082dd6dd7eea26c59c246ef34c70c934b07a2d520817 +dist/2025-05-26/rustc-beta-s390x-unknown-linux-gnu.tar.gz=fa4d770e564aa0863825d5111a4b6c01d8486c65e8b9ef06db25ef778a448813 +dist/2025-05-26/rustc-beta-s390x-unknown-linux-gnu.tar.xz=945070094b80ac73cb013d3f556767caf6437258121f76921227e44d18249678 +dist/2025-05-26/rustc-beta-x86_64-apple-darwin.tar.gz=cf87e17e8f2fd18d9146671a393f31ab40ccfaf4c781bb81cdf02dff8bab5435 +dist/2025-05-26/rustc-beta-x86_64-apple-darwin.tar.xz=7cf73955adfb107f454829d3503d6cf91430e4cf5c4640466073c2c0f8a42732 +dist/2025-05-26/rustc-beta-x86_64-pc-windows-gnu.tar.gz=12b7528b31d971ccd36a44fff62ccc377dfa322a22af85fbcc7dcf2c8f2e0539 +dist/2025-05-26/rustc-beta-x86_64-pc-windows-gnu.tar.xz=21a305e0b085d73db5d79dabb61e1aad213b623f12708f94ff448a2db60d7651 +dist/2025-05-26/rustc-beta-x86_64-pc-windows-msvc.tar.gz=444aa1eea6824d1b73c0f653a2703806bd04154da160f96b9700c39b9e201dc3 +dist/2025-05-26/rustc-beta-x86_64-pc-windows-msvc.tar.xz=16c6000c46bab4f46ec2084d7e920d2099b8759870057e62bf0e8df8eb4ccb9f +dist/2025-05-26/rustc-beta-x86_64-unknown-freebsd.tar.gz=6ad9c67484aa6598c4f0f70799980f57e4749560306ce1190dcb38476006247d +dist/2025-05-26/rustc-beta-x86_64-unknown-freebsd.tar.xz=b8f921568dbca553484936adb267d384b8ce6bfd40efa0b54d22cd98a6638c43 +dist/2025-05-26/rustc-beta-x86_64-unknown-illumos.tar.gz=14083e187d62529058dc0de8657024f5dc2ac5af37986053fc21f2334e1217af +dist/2025-05-26/rustc-beta-x86_64-unknown-illumos.tar.xz=2410d5423581ec2d205a47bfeb3c95bf3071303b5b71343254492d53fa27cd48 +dist/2025-05-26/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=6561e72c72b5a2a10ef97632c0af2ce8112fe0faf6d12d83da0ec9f0b347b88f +dist/2025-05-26/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=14b944631444b666019e0c2f3590b78b3de3dcd499c0f7254dd22a95703e8585 +dist/2025-05-26/rustc-beta-x86_64-unknown-linux-musl.tar.gz=cd09f6ef2c26b2f192cf1a05badd3603e8cab4141e120ec98c1afbcda7036aa5 +dist/2025-05-26/rustc-beta-x86_64-unknown-linux-musl.tar.xz=53745c050956b886e5e3f523b1a77f40c6c73c1df867505cb9f1ec2cb5026f56 +dist/2025-05-26/rustc-beta-x86_64-unknown-netbsd.tar.gz=d88ccdea31b269ad513cd8106c0aec60124ee1ec50c839dbc7218dc1d2b80e0a +dist/2025-05-26/rustc-beta-x86_64-unknown-netbsd.tar.xz=d10add7b925f1492d2b1c9ecd76df2065bac118fa6a27fde7b73d5ec55f30c0c +dist/2025-05-26/rust-std-beta-aarch64-apple-darwin.tar.gz=4076b5062f1e3f098c0b5ce5cacbaed784afcae6f7db740c0939fcf3a58025e6 +dist/2025-05-26/rust-std-beta-aarch64-apple-darwin.tar.xz=91c94ea57ca9eebf103d89532c6b1b24f3a2529f3a755b1c29ae0897b759dec6 +dist/2025-05-26/rust-std-beta-aarch64-apple-ios.tar.gz=8b89d32f731c103945efedaf2d9050d96e525d50f066239175f629cc0e3b944c +dist/2025-05-26/rust-std-beta-aarch64-apple-ios.tar.xz=b139ea399a96514732007ba26488cc6f75cd86e710938638dc3b1c7913a8b103 +dist/2025-05-26/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=7bb6dd559ef08d5e8bbfee75243eca8760d411f952ff646356ce4fe74564dc2a +dist/2025-05-26/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=c17c372f13bddd9d0629fc1bab0dac6004f7356752db20b196da0e46860b174d +dist/2025-05-26/rust-std-beta-aarch64-apple-ios-sim.tar.gz=a2a01f111d234d89b820591428b036cc6de2b74e6c0e96c32211b085e537b4f6 +dist/2025-05-26/rust-std-beta-aarch64-apple-ios-sim.tar.xz=06380c88a781e584feea822c91b1b6f98412ed5699d4d00d65e5ef7075dbf4c4 +dist/2025-05-26/rust-std-beta-aarch64-linux-android.tar.gz=1ab07c597044c1eed2224aa72893e14055494d8fcf0e746426578055ee881087 +dist/2025-05-26/rust-std-beta-aarch64-linux-android.tar.xz=c55be970bcde2866cb2d66830476cd7fd52692a791fbe3f198e84913a4247a4b +dist/2025-05-26/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=7445b8979d6d325a1a6714e28048ce965159b7fa326c9b7d663c771129f81a7b +dist/2025-05-26/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=f3fe861b5d81b54c7861663519324a5d305f3300c733cb7f146b6a93770fb73b +dist/2025-05-26/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=57be8b656ae0f4fa9164803155c9af5eafdd961bac752ff3833e5ceba4accb18 +dist/2025-05-26/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=e64807cbdf82117a127d85a35a1303683dbe3c95366bf469c13a2781ed66916b +dist/2025-05-26/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=d8d4737d031170d5c6f8bb1aede6c863a3ad6c32007de2e1b7ff03242710ab17 +dist/2025-05-26/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=27998ee47c29ba860a5c56155332091275e7e9669e035fcf11a02c9089b6e8f2 +dist/2025-05-26/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=9dc717d80d91e833580748c831c2a80bca2f8d7bce9af51d21f0375c44cce395 +dist/2025-05-26/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=88ffb04366bc06c331113026ea64b04ce970f01f13a0e80736fd59676e4e974a +dist/2025-05-26/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=5ac4a726e875af3387a2214f722b676e21e3d0afbfbcf1969107c34c6eeb1706 +dist/2025-05-26/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=7fe9cd0d76f9c4778d53b5fba083616ac06d04b622e9e783e3e0fd3e0d3756e8 +dist/2025-05-26/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=5813b23bccf3fe10ec2ab540149bed4739668d9f9b198a476300e50e53721d52 +dist/2025-05-26/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=383a1758e3f2f1d20b23b8095f3772f79ea5377312602cd577a2b047ed062115 +dist/2025-05-26/rust-std-beta-aarch64-unknown-none.tar.gz=3589b1e3c4367b369050062e9df8838a797059056c98662e47d1cf71ecdcd787 +dist/2025-05-26/rust-std-beta-aarch64-unknown-none.tar.xz=47e290959699c9bc9093cd6603bf3357492ef7a05a5c2335393d41ef94955c30 +dist/2025-05-26/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=e690ad47757a726cfe621e936dd0ac76d96dd4b838ba3e5dd582565d5b2c3618 +dist/2025-05-26/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=2d17e86eaaddc93e81b3bd39d3faeed3ccb1765a9a13144d4bab726d384f8eba +dist/2025-05-26/rust-std-beta-aarch64-unknown-uefi.tar.gz=a5f8b8e8ee3859d877430760132180ef956e597de3ab539e980aa48c937e3e10 +dist/2025-05-26/rust-std-beta-aarch64-unknown-uefi.tar.xz=225b50bb3500d46291bdf877cdce57d8bf8133f1302b98c3b8d07d34a91cfadc +dist/2025-05-26/rust-std-beta-arm-linux-androideabi.tar.gz=aa93b2e198a1ea5f586851ecde2b1fc56a0e28e2c16e7210cd77b4357e327196 +dist/2025-05-26/rust-std-beta-arm-linux-androideabi.tar.xz=00e020d2654c6324b6c8070c9ac32b0838470ad74df711ea7c9940a4bdcbb71f +dist/2025-05-26/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=c0ed57ac290b57f654419e35f7950c6b2b2594638bfca5a060a49dff6febd2de +dist/2025-05-26/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=bcb7a661e83d7973ac0cf9ead4588595cbcf39e71867d1e2fb3e6e94f2506d39 +dist/2025-05-26/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=baf206be5648992a7280836ed7520b5fd6ca59a28d9aecfbf3769022ececc753 +dist/2025-05-26/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=a86cb2309e93b7a0ff92199e47e0a36f192221eb376966b1e4a49c8d5d5de7bd +dist/2025-05-26/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=703b6baab5a33e6ed1acaad52781c1779146e1fa5d5974d19d354dc2279ccec6 +dist/2025-05-26/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=55e56cda5e2af49b0ccab4baeb0e8051c416c0321832742609edde9b1ebae8ee +dist/2025-05-26/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=7080221a8911393a012306c934043df6aa4d483411c90ca275d2539704d38120 +dist/2025-05-26/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=8d9ed7e57a37f328aeb4004f043887edd980e7ac0df8ff714566b0bf474bce99 +dist/2025-05-26/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=85cd1a37bc2977c116264a6e84f2c578c7a698e26bf7bd99e0713b14ec6c74c5 +dist/2025-05-26/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=6ee7ea565266040310050a4cf3e2afb1e85f956e0ad54a9642291949e1b376bc +dist/2025-05-26/rust-std-beta-armebv7r-none-eabi.tar.gz=8c41876321a8bec594d028460c93396786739ed82f79ed8703cf54dfc1e7d51b +dist/2025-05-26/rust-std-beta-armebv7r-none-eabi.tar.xz=fb1a88a048e1945dc2bb84aa47e027455c5a94acf416865c5ef26c6a1616414f +dist/2025-05-26/rust-std-beta-armebv7r-none-eabihf.tar.gz=61561a8324bced38f5ee6c1c65348750162655315ddb5eb0f0142a48872faa8c +dist/2025-05-26/rust-std-beta-armebv7r-none-eabihf.tar.xz=3e57bdf2dfb8cbcdd4b4f7e2523cd7946dd0d64d0ee17cf794b5a04dab26a46b +dist/2025-05-26/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=c2586abead3190b936f1cdb46c472ee4c4b6bdfeb9c33165765922d3da0151a0 +dist/2025-05-26/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=24a64cbdabd8074219f7a27f266f695109bcd7fc3646cc78652cf2f3bc2ea099 +dist/2025-05-26/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=a6b7e5ec522eb29582c7ca2d9a2a79f1658058c4db6b0442ccfae40e1403ff07 +dist/2025-05-26/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=94f88e141605997ae9578b062de3f750761648847d0ed6fd18394d8dbb81afff +dist/2025-05-26/rust-std-beta-armv7-linux-androideabi.tar.gz=e50086fac3b8e433d1a59724349122bde2172bc22db1175c7826635d978973c0 +dist/2025-05-26/rust-std-beta-armv7-linux-androideabi.tar.xz=2700b25ce1e6db9eadd2b3f63582fc3759963449781799216239b9e2daa9e51b +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=e507fd70d155f17fc3cd5e1b38b97a871d51090d4dd7069366d3e3e2a48c0355 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=8988863479cb0280851e1765de2810eebcfc1592100b052e88c51386a88c4c87 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=519cfee0178123e15bea3544113ac3f4914a72fd619fdf11044ccc1b8b31c848 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=5cf09c2f66482863ca9586782f56aa453c3fbe8ab48d80c18d5570b1d7a80455 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=9949d9684b83610bcba9fd91cc66587d6216dc850cc7b6413a7dc0f80dc4ca2b +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=cd87009d6362cc5777a664e537261227fda510a5e9ac1080a42b0f2fa7b4d364 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=a9a5280fc64bcb40dbd43f3fae1ee01dfffb06d4bbf1a635b79b923343f97060 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=bf1e9ef03fc1d727b59ffcd991c207c503db884b14134154ff674b6435dd7c92 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=9171f328fc67817f3f2e10209e0122ec7e9f0138ad8ffb4b19b64d21a72b9414 +dist/2025-05-26/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=9d9974ccdd87fb741a568ca8966e21a044f8c40415b4e58bcf359605c6c3d8fb +dist/2025-05-26/rust-std-beta-armv7a-none-eabi.tar.gz=402f7b7b4c132ec6a1d51c0528608291a423e16090b9f4327e447fdef5d8e771 +dist/2025-05-26/rust-std-beta-armv7a-none-eabi.tar.xz=9a8d857299c24a54a5c49b25d5b07bdd9eb77b4a4be853d6d12e5df4ac404c56 +dist/2025-05-26/rust-std-beta-armv7r-none-eabi.tar.gz=c8fc641d53bc04751d16283d50696ade8925e22d530cdd4f97facd7e2e4f878c +dist/2025-05-26/rust-std-beta-armv7r-none-eabi.tar.xz=4773a4a02fa088d3c4231695cc698edeef39c2a0ac3f4d3a0eb272d7b8663705 +dist/2025-05-26/rust-std-beta-armv7r-none-eabihf.tar.gz=4ffc1f544f164f5e4a803fb9aacff693e00abcead950bde2d7065739e189058a +dist/2025-05-26/rust-std-beta-armv7r-none-eabihf.tar.xz=e64d238b9468ecc3df0e3a20cbc0c2b3bd5a12500fad4b7194382106ee5c4aa3 +dist/2025-05-26/rust-std-beta-i586-unknown-linux-gnu.tar.gz=82ac2e07233a629ff1ea556be728fb1617ce085f5909ae3f3b9d8a792f73baca +dist/2025-05-26/rust-std-beta-i586-unknown-linux-gnu.tar.xz=2c54755349f8d94511b15b29b527533be186f6865ee1b2022e5345f42b00f5bf +dist/2025-05-26/rust-std-beta-i586-unknown-linux-musl.tar.gz=5debce1f9edbbbf0b8e2e8055525296d2d7c4a14bf561c7bc05e7eb953b5a034 +dist/2025-05-26/rust-std-beta-i586-unknown-linux-musl.tar.xz=e559611ce05ba79894c4e3bd9066701ea851708ca44c0189c93607fe8721640a +dist/2025-05-26/rust-std-beta-i686-linux-android.tar.gz=17be4d733c597bc7ff5aefe1b5944df0d2f3f2274773f2691e863fcf18877251 +dist/2025-05-26/rust-std-beta-i686-linux-android.tar.xz=2603b3e9fa2b8a2f769ea3558037e4a20b1bd13101c518f05a5800935bb5b02b +dist/2025-05-26/rust-std-beta-i686-pc-windows-gnu.tar.gz=3c2c9283b1fb0196bfedd8f87f1aaf00dbcadd52ca8fb40382262f86e21701d9 +dist/2025-05-26/rust-std-beta-i686-pc-windows-gnu.tar.xz=3067d576128325449174c9220d10c543644e94d6045764e32bfccd30cd94e94b +dist/2025-05-26/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=6df448b2a49f2f9f3f4fd0cb0ef758f31f00fbf2008b4a9ae2a1cc5818bbb21c +dist/2025-05-26/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=537a695b2377b1aa4ed31970b8170ddc575c18c180faec5602febe71bc44088d +dist/2025-05-26/rust-std-beta-i686-pc-windows-msvc.tar.gz=d45afe32951d926702d62d5a94b5343c3cd2f1d04497af3412dc6d1ccb97be05 +dist/2025-05-26/rust-std-beta-i686-pc-windows-msvc.tar.xz=bd8a25921b63d3d4fe13219422a324a538a08284dac681153d960cd25fd02c6c +dist/2025-05-26/rust-std-beta-i686-unknown-freebsd.tar.gz=e8ad2a8eae371b8a99251bc3337732f3e47457a8d740a9609f96206291589f43 +dist/2025-05-26/rust-std-beta-i686-unknown-freebsd.tar.xz=e2edeb9636cab8ba8489d28e829c039d4e547ccdbfd4023f98943341f5cad96c +dist/2025-05-26/rust-std-beta-i686-unknown-linux-gnu.tar.gz=9b319357a2c6d75d0140bad5241a72e4d858e06962c024414449299f83bce9b4 +dist/2025-05-26/rust-std-beta-i686-unknown-linux-gnu.tar.xz=b4447663123e42a950943829f661283d6b36f3da14750c811d6704a2edc64b40 +dist/2025-05-26/rust-std-beta-i686-unknown-linux-musl.tar.gz=4391070bdb67171c4063df3d801dc66e16c097165873c5c219659e1e706028fb +dist/2025-05-26/rust-std-beta-i686-unknown-linux-musl.tar.xz=48e566d549dff7bde2a1cb07c55ee0a2537147f66e4ca8a7bc9ac9ebe104de28 +dist/2025-05-26/rust-std-beta-i686-unknown-uefi.tar.gz=3176685b3b5b3d0f44655f5449b328ff8c2e4a577d1076a2692f2e7062004e3d +dist/2025-05-26/rust-std-beta-i686-unknown-uefi.tar.xz=2c0983eaec9d5cca76fdf0fceb3461960e5bcb162c2409169d0c6ddfc5497f8a +dist/2025-05-26/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=17e49779ef3931acbc2828a5a674e1d9032f08ca6166cb62a9ba7dc156b06ee8 +dist/2025-05-26/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=7411b776b6fb10d0a10e3de19183caeb9980f523aa014a5b320bb4422f2301a8 +dist/2025-05-26/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=1a54d2d8b31a84693738a896068e38506b9d8e94f660368c6674ca66d38fee89 +dist/2025-05-26/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=1023907e072bf083168497dea8801544d2f407cdba1ad3157977032c92e7c4d8 +dist/2025-05-26/rust-std-beta-loongarch64-unknown-none.tar.gz=86135dbb69909d33a0d6bf0caeb35f190299f594b062238e3d18ec7ffab26150 +dist/2025-05-26/rust-std-beta-loongarch64-unknown-none.tar.xz=3e9733daceb421f6a2e00a1c8d7ba2ad87332127ca0fedfc391bd0715836da2f +dist/2025-05-26/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=6190b439b569ef1a5aa832445ac60bb5b4ef605ff8f41a0ad1cdc5f5cb0de28b +dist/2025-05-26/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=8e5a7224d8abd8010a4278f7e767aecdcde7060ffb16e0a5c8de579c5d86e21b +dist/2025-05-26/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=2006ea99dcecccabf97d74f3f582f8a29c4e69caf27fa9a689a28702a6d9b1ad +dist/2025-05-26/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=7baffc71f0e9279439775b95280206411ef04bea7eb4ad38096a11a38e38dd47 +dist/2025-05-26/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=9650a6a1812492df104c0df20c5db422678e2f7a84e33a83aedf1d9c602369d6 +dist/2025-05-26/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=4a265f557aca1b2cc3d377be693bf9fe90c4b0ced99e77a406849aaaf3ad7eff +dist/2025-05-26/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=6aedb018d3758c3f1e8d5d3e69f0c996c35fba18338346b43ad7e9099621d714 +dist/2025-05-26/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=3886265ec1bd94ae47146db67bc2a72fdd9d70b64c74878fc1ef9c50498b9f23 +dist/2025-05-26/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=60c8355e9f6c59a329b111b4abd7b8feeb493dc0e4d8d4ec1b07809a9ac6923e +dist/2025-05-26/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=0c687a9b04b1b36a14c972913dfb9f0d230551f301a895593f1623677b59a26d +dist/2025-05-26/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=29fc686f26c6258a542e118a035924167c3c6b2b007980f89101cd50ca3543f4 +dist/2025-05-26/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=a6d643c2c794a405ee6bfa7e8f3c19cf2b94a17c61d743763dd6939ed8641d0e +dist/2025-05-26/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=781b81baa8dfd9a5e7eb346176e9cac1e83cba42d1943677b08c689d535debd4 +dist/2025-05-26/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=ca46997729ba43239372bda073a20c1a5d6d25c00cfd79105dd44ff634cacf84 +dist/2025-05-26/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=d0173b18212cf346c5477b8fa1292f51db7fa7275455417ede405f6e822548b1 +dist/2025-05-26/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=6357a65bf47d918d8375e0f7f0f3cfa137026a3be7123a2ae1ae827a71e96c31 +dist/2025-05-26/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=dc0b7a21b4e597320bcf63c84812ae9caab0aafd215aafa10187820ba91d6490 +dist/2025-05-26/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=f42b4e807be249463cb9e4f3781b45c45492c0badc45ee6baacb50d61fd3fd42 +dist/2025-05-26/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=78cfa47f2376a73f7247ac69968ce0752129cc57f7961fe0c12509b27411e232 +dist/2025-05-26/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=2b2ba34b8c6992e4f253e30d80df578e9ac8063264f61cfec0b0fedb9b823ef6 +dist/2025-05-26/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=27ef3d52ca7b2e1294506f3e105fb5753f468e5a29f1a84804213a055a885719 +dist/2025-05-26/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=71b0a54a82cf5f69da177559494b2601c55c3e3bd0bea539ce69e7499c3150a1 +dist/2025-05-26/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=d11ccac57c31274b7b3dac4b1761a04a806ddf368c00e7a16644390ba221154a +dist/2025-05-26/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=f636c9cee16b8e676597b31956d6827e1bac7ccd83c730653953affce9724a96 +dist/2025-05-26/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=806dab8bfe031f0043706992a9426e8580e82a92df11ef5728e6a00da13cb40a +dist/2025-05-26/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=b48f6e9641db567e8728db48a062b0cc570ee2712af7adfc64dcb4f4ab355396 +dist/2025-05-26/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=8d779f261e4c0e62e8c6a616f2635dc74cb4637366386b0857f31fb015fcf152 +dist/2025-05-26/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=7d12366c1c1166e50d2a78f1ac3fe3166935b4920eac8f64e878a6b5f8653a6a +dist/2025-05-26/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=6c01c064a5812f55004ecce5c514c1aeead262dda2525fc7225bbc68d0020d5b +dist/2025-05-26/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=1be564ed4d96e15ed8eaad48a0b64112d05a9096a2fc56245e38ef9fc3098865 +dist/2025-05-26/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=9329f4c55d1c9216ddbe684d0b246f65724d0919a009d066987052bb27a867db +dist/2025-05-26/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=3e164f2c3af1850902a7852d5b043f6ac2065b02d0786912b66c83c1371f71e6 +dist/2025-05-26/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=613cd5db53ae3f2c69eddbbbf39957a6931b9b5ab1766135b150e01396cff220 +dist/2025-05-26/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=03d9f75b9a4126c81b1896dcfafadecbbb6f4e81fdc774023f81acc28ed125c9 +dist/2025-05-26/rust-std-beta-sparcv9-sun-solaris.tar.gz=fd5aad29a1ac50b469629fa7ead7d503060909f2640750084f0a72f8d9699456 +dist/2025-05-26/rust-std-beta-sparcv9-sun-solaris.tar.xz=942a34da917069072ac610dbc3667cd847dfadfe3df09d6ff3aebefd49311041 +dist/2025-05-26/rust-std-beta-thumbv6m-none-eabi.tar.gz=91024331bd867a1ed71b687509fe694f924bef58047a9137c7a759ae783179f3 +dist/2025-05-26/rust-std-beta-thumbv6m-none-eabi.tar.xz=e3b5096e07f3587c87fd6832b852882f7d63fbc6b8819de452ce5883e26742e1 +dist/2025-05-26/rust-std-beta-thumbv7em-none-eabi.tar.gz=40f0dcf8654464732add7e8b3e1a1d898699fbf50be74b116650c8251209f913 +dist/2025-05-26/rust-std-beta-thumbv7em-none-eabi.tar.xz=6193c72b9c4c7ca27b12da0f13dca0ac1d11b1cb2a8868a7f2e7f24ab3e7a78f +dist/2025-05-26/rust-std-beta-thumbv7em-none-eabihf.tar.gz=a008f4debee859f5c5b90aa4000774cdaae045f8161b8e7fbfaab68db7f2341e +dist/2025-05-26/rust-std-beta-thumbv7em-none-eabihf.tar.xz=bb96feab15991aea5fa38f22848a715422c0b1da060b34a7273f3ec077d4a4e3 +dist/2025-05-26/rust-std-beta-thumbv7m-none-eabi.tar.gz=46e8c8f891c22cee04de8c02aa1e0f5604a9bcd33219cfd1a299c43b4005f93f +dist/2025-05-26/rust-std-beta-thumbv7m-none-eabi.tar.xz=d4dbed9d96c6b57e185ede566a3dc610f03d854ff696466e9c68815b85dee963 +dist/2025-05-26/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=222d18565a0d40edc3d6cb051f2647b5416b417933fcdcd25bba54fc55de625f +dist/2025-05-26/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=4190060ea541ad1f1f5eb91815027ba475d0e1281ded77ee3116561660919550 +dist/2025-05-26/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=bf99b1d2a8997d36f994f31bcb48482ec3d48f962ed66beb8642025859c53d97 +dist/2025-05-26/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=477fe3a269b43ca2f795fef83b50697600021129678aa6af1594858bfeb9479d +dist/2025-05-26/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=bbd981f00bdad617fdaf823d78cd6f500807b742e050a3d4cbd42ed2966ac7d7 +dist/2025-05-26/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=8a8c004818d5fe2eb9f99d77f65810f577bc1e8cbba0ba2ec6e045234c6a700b +dist/2025-05-26/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=7e015ea2920c65337b61d5fc1a5f619cfef6589483a4e6c09d8dfbe1529972b2 +dist/2025-05-26/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=37b2b934afd57a81c4007cb4b8b901fe7b15334588625e98d79b037876f18725 +dist/2025-05-26/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=919db1cc7cbe3e52a6e33f03fe4e79504bef2825ffe58895e24130907bad7b6b +dist/2025-05-26/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=59b8ced330f72dc06635aff3de2cc7c796baee86320ff8458d8dc995ba11b909 +dist/2025-05-26/rust-std-beta-wasm32-unknown-emscripten.tar.gz=5d5298f0a3d9a8a8cee3ad9003c409adeed1b3fcacac449a5827f14099b052a6 +dist/2025-05-26/rust-std-beta-wasm32-unknown-emscripten.tar.xz=e9b8868b05e0d210419f3b041839f60e0d4fdd7bd54eca6b0f96b5f90148cdf9 +dist/2025-05-26/rust-std-beta-wasm32-unknown-unknown.tar.gz=35a70b6c8369ca487d93c9cf445a0011ff13dd5cea485a4d01558167473130f0 +dist/2025-05-26/rust-std-beta-wasm32-unknown-unknown.tar.xz=5243ebcffd4880cecae8c3a90c9c76bae1da188478d5e572a8a71cf4442ca991 +dist/2025-05-26/rust-std-beta-wasm32-wasip1.tar.gz=f39ae9fc833c1e0d6b3aa3a3782f7dd1e547c7f6b0141c52e0c7cb5b6fa30def +dist/2025-05-26/rust-std-beta-wasm32-wasip1.tar.xz=caa1d6e9eb7663ba34dc7db1d47bbd972b41f22f458afd95a6a0aaa0d7e26f59 +dist/2025-05-26/rust-std-beta-wasm32-wasip1-threads.tar.gz=9f0de2858a6ee7de500562fb9639e68cdebc45a6181778ffb41be77af74fdead +dist/2025-05-26/rust-std-beta-wasm32-wasip1-threads.tar.xz=98523168355e334dbcf0730c314ad9fe901751eefd61d567878c34f21c30ec98 +dist/2025-05-26/rust-std-beta-wasm32-wasip2.tar.gz=8cfdd1d718208fead76aaebd54ad44e9f98145664e475914f7b9951372c1813d +dist/2025-05-26/rust-std-beta-wasm32-wasip2.tar.xz=64fed08a0d9b09097a9370ee4b013332d19587b5de67b0f0af49bc09625c765c +dist/2025-05-26/rust-std-beta-wasm32v1-none.tar.gz=ff212273e3d5c4e465d8a3d8838716f2b2e2380f82c158d13dba997df4a8fb0b +dist/2025-05-26/rust-std-beta-wasm32v1-none.tar.xz=8300711696bc8988e6e00baea2d15012f373525436f1415f54037e10511acd83 +dist/2025-05-26/rust-std-beta-x86_64-apple-darwin.tar.gz=77d89abf4d00195e240b8f55ab2bc5558d21f0f1fee33bf419d14a3e3f2b3ab1 +dist/2025-05-26/rust-std-beta-x86_64-apple-darwin.tar.xz=817dc01f12d7a2c388b001bd708aab2967afce42a11aecfa278a40235800e1cf +dist/2025-05-26/rust-std-beta-x86_64-apple-ios.tar.gz=cdcbeb20884c7782e080ae38ec6dd955706fa2e924ddfd235d775e39be2cb446 +dist/2025-05-26/rust-std-beta-x86_64-apple-ios.tar.xz=378d04709658b08a24edff046bbc6b3fbee7d0127fff93818e92cda0234e0837 +dist/2025-05-26/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=81d68c70d385f38f526856d021aa1b5b25e9bddff52b8098d76a87c53721784f +dist/2025-05-26/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=e9cd869473a379a72364b1246fee3007d9b45801e40a3cd2eecc7831edba5bb4 +dist/2025-05-26/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=49c18132004107623e554264b000d07ea0a1dc51ee1e21d02b833b9fdb07b855 +dist/2025-05-26/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=be2269500b5522f677eebf74c0a201751c74481568ba235200716fe368f443e2 +dist/2025-05-26/rust-std-beta-x86_64-linux-android.tar.gz=25801215e0bfa5af3b2b84aa0b881fa723cef308872de58e36d2de943746e51b +dist/2025-05-26/rust-std-beta-x86_64-linux-android.tar.xz=f692f2f3c69c8fa62d5344faa75375fd2a48a1b81284a7fbfe4b41f575c7263f +dist/2025-05-26/rust-std-beta-x86_64-pc-solaris.tar.gz=7d79359bc70414d23174aac81b1e5c341b541f6a8361142a3d48ddfc956dc7fd +dist/2025-05-26/rust-std-beta-x86_64-pc-solaris.tar.xz=ec42340a3bbaeb9723ec77a48b4c49496ee61a928ae20c1bdf3ca33859a3fa52 +dist/2025-05-26/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=6ad0c0a19a92af2749dc8680dcc14bc1fa9fc1d3f4bcf8e28b0646c3bb50859b +dist/2025-05-26/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=e3e1ecbf40411fa9216a43573b765c526652205f2978409b5488c25b19e98375 +dist/2025-05-26/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=a62671dc133e7cc059ed4ad004946e9030e8b882094ddd81b282c56363b0644a +dist/2025-05-26/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=16aac5f5f78f067dd4e2106d62a31fff071757bebf53742eb72f25a5abb2d7af +dist/2025-05-26/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=d2c5c7a89dc122a7edf1120c3765e55001b3ecca57fb8077b6f4e6085c9fb6af +dist/2025-05-26/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=14b0a62b8dca999d96ff6cbc7fa8dfc3486555be9aae4d509dd95fba01db8f65 +dist/2025-05-26/rust-std-beta-x86_64-unknown-freebsd.tar.gz=fc3ead4c4599e5371668c610658538dc2bab3b3db2ca9aa1645da087649df131 +dist/2025-05-26/rust-std-beta-x86_64-unknown-freebsd.tar.xz=9e7477e05192ce11190e9b1291a5e171a9cd9da9ca2f4c53d08b98025a697255 +dist/2025-05-26/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=b21552a046715dabb5b14d82fc9707c6622073b013b839f1b08e0463dcf536ac +dist/2025-05-26/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=1ed92b1672f0d18ff0d6f8365326478cdcf60af25837924f54d3c7459a4dcdf4 +dist/2025-05-26/rust-std-beta-x86_64-unknown-illumos.tar.gz=14d1ffa188c1e4b64d9ac941698922d2a46d0fab78498c6499d5782edd529968 +dist/2025-05-26/rust-std-beta-x86_64-unknown-illumos.tar.xz=41cca6d938e5c39e4032f94256ecb4efdd76c1569e29f84191d58be5d3c0773a +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=444f4875970842d935d1a42a46ac524b6bac248d4bb5993e5ac578ee88f519cf +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=354c0e9396e7b241a7e8c69e3670d98a42ed8bb7359d4f06deefa4fdf81df675 +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=efb7bc1afab8d3f3f6de3fbf41bfb3ae17bb3e193644ae40be5d497aba20d1cb +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=c717608c4378ecf44cf3ef9d3ab8c5e6bc29db0b1c9b04054b42c60fb5109ff0 +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=1e4f8c03396b130a284a3a11c20da15185d3a9cbbb6d9a219a76e0e192cbd2a0 +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=b5639086009cc35a8fd493fc885cebbf2dc68b4d4fc956b00bd5061ec4ed75b1 +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=cd577082d0fb089e42ea31509f92321b40221b54f73edb0f80510f6e170acc6d +dist/2025-05-26/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=2257286555b3208b43390284db141d2db7282a7d2d4c438fd3b217de3ede790a +dist/2025-05-26/rust-std-beta-x86_64-unknown-netbsd.tar.gz=61205a7f309f18f9e1a8c12c9b74902f014475ea357fdadf728858f9f81d6e73 +dist/2025-05-26/rust-std-beta-x86_64-unknown-netbsd.tar.xz=d11ae113c9d4156ef31561c002a9b65ef702302c89b0dd2b3005bef57ba92d01 +dist/2025-05-26/rust-std-beta-x86_64-unknown-none.tar.gz=8583686c6aa8eaeb57b64dbc288d564fdaf4939d6f552143e7cdc0641147192d +dist/2025-05-26/rust-std-beta-x86_64-unknown-none.tar.xz=64a654c790902abf4f936a3194757eb6885d88a68cbd8de19767a8e7a0f21335 +dist/2025-05-26/rust-std-beta-x86_64-unknown-redox.tar.gz=b80e62b6982c684d5550233c15299533fa709e540e53bf20e7ed06fc09e396d1 +dist/2025-05-26/rust-std-beta-x86_64-unknown-redox.tar.xz=63a195aab6fe29882c7a0688ca2368a611127381320349c7cb1097dcde2b4603 +dist/2025-05-26/rust-std-beta-x86_64-unknown-uefi.tar.gz=3f285485b3a7bd957ad69cb76ff717d7987ad0bc50368aeb1b813f9b2d3b5af5 +dist/2025-05-26/rust-std-beta-x86_64-unknown-uefi.tar.xz=dc1e9a9952adbb65711ebcb79b7afc149ac1c9f73b69b97f6b059a53ac71067c +dist/2025-05-26/cargo-beta-aarch64-apple-darwin.tar.gz=368b6cb43f573346237012bdc23e5c44d476db779795464cecc2065f6fda8b8f +dist/2025-05-26/cargo-beta-aarch64-apple-darwin.tar.xz=4bbf76bc026d740269c09658a3218eee2dfcc7422fe233db192cfee4dc4a371e +dist/2025-05-26/cargo-beta-aarch64-pc-windows-msvc.tar.gz=afd53341d1c84f86ddcb4ee85affc0413715c05bd43c075ef193306e86126488 +dist/2025-05-26/cargo-beta-aarch64-pc-windows-msvc.tar.xz=2cf06c20e07ab905f329c8ffcf275b7cd528488893c7014a1cc03faafb13d2c6 +dist/2025-05-26/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=8ca26bf35ed608b6b6ebdff67f7949bf8219f32edb1ece3ca9f8d49a182cd8ed +dist/2025-05-26/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=61a8743d62dd505d7d76b2ffd282e2b41653b0b30eb96e7f950f144201270a21 +dist/2025-05-26/cargo-beta-aarch64-unknown-linux-musl.tar.gz=8cd37cda7f2f2c323ebda896fc2fb8d8a83b30f2b9c102a1305bf724c261566c +dist/2025-05-26/cargo-beta-aarch64-unknown-linux-musl.tar.xz=fa5f4fa4da574b2bc79db4dd37969ba5549b32acb65554e35735b55913ab6e53 +dist/2025-05-26/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=30a29adc0c331a3cdff5c1f1d8b541f720212a3b8b2c96795e95f96ffb5982b2 +dist/2025-05-26/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=069071883eee8226003b1cd8501868b3aa51ec8d0f0637e4538b30b920d05823 +dist/2025-05-26/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=0151634d3a2a1757b9671cf9d48cbfd5fa34df77744ffeac02d8cb5f6949cdc1 +dist/2025-05-26/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=524386061e1b6b212cd9f94d9d7baf2cd1eb9b2ee105c334aaffcc192cb38e19 +dist/2025-05-26/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=d392c9fac6521b2377928305d87e1d65f70e6a5472d4ded3475e08118186f2b1 +dist/2025-05-26/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=b1ff6f50dd621d7af5224dce74a25ae895e6b06216fe8e1501ff4199c04f0374 +dist/2025-05-26/cargo-beta-i686-pc-windows-gnu.tar.gz=52ebc95b1c29d3c0714c66505f0ef838c13d12d9436f1d2c2291cf027a38697f +dist/2025-05-26/cargo-beta-i686-pc-windows-gnu.tar.xz=c6acb26d5e9a4f8c51c13f8b92560849cc4df822a80d04b0e61b1407e93555d5 +dist/2025-05-26/cargo-beta-i686-pc-windows-msvc.tar.gz=af54473c85c035105c429138cfc0d5ab30dcc1b13ea01a3e4d12a8342c309e98 +dist/2025-05-26/cargo-beta-i686-pc-windows-msvc.tar.xz=3a44659128f07fe5953659506c1b6c93fbea96a327401064dbe0393ddb28542d +dist/2025-05-26/cargo-beta-i686-unknown-linux-gnu.tar.gz=39632af7bcf55760161ddd4ebfe40a3c9a49b6191ec88d1b1d66390668d09905 +dist/2025-05-26/cargo-beta-i686-unknown-linux-gnu.tar.xz=190e8b6bda864b4316f530e5d693e779074de8665a5abe6a4f5cbd01ce8fe6b7 +dist/2025-05-26/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=87d25663fa5b4b09fff9ea02c07bdddf760873bad7c425015d6e1750a24f66a4 +dist/2025-05-26/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=b0789d6fe6d8f7e07f0858211e59ae9278adee7d14dee64fc359b3079773993d +dist/2025-05-26/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=d0f43421313fb6d43ec9b165dc2c9f6be91daee61f201eaea6735fa6ddaadda7 +dist/2025-05-26/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=3e2de8fe7062494c260d0560253e03fc45baa680f9a62171350c5caf2e5fb426 +dist/2025-05-26/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=93a901615aeaa14dcaa0ccd2fe870ccd29bb4f52601cb7ff3b2da7bc6c3e1b22 +dist/2025-05-26/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=28ab251856c6a252beb72a5db0e18e68e40b342fcbd903dd75811ba393b44194 +dist/2025-05-26/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=6c1f5cb9ec7787cf004c3efa6da81a93155ff3b5319ba7c6ffd29ba631a0feb2 +dist/2025-05-26/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=9a09c0f8d027310b26909c193227466402ef616c27b943ec16cd5a7eabca5ca9 +dist/2025-05-26/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=c5c81cbf63206e47989c5a11953289f99e72647aff4d876d18fb8d2c99a54d1a +dist/2025-05-26/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=d187d131e679bebcdae5a7b9e828285f55b61cbc124e72d233725e4e0f2dbc39 +dist/2025-05-26/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=883c45f3a2a659560187cbc7696a3132163d6385dd155007c4d5fd2fb068dfb7 +dist/2025-05-26/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=f9d36abf952bed0e4df94dbeab0ef732ed731e6f8740c5be0ff96f603c46f4c1 +dist/2025-05-26/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=23e25b899432df81b660105786f038e95bbddb3bab60a7917e4ca077d5b7520a +dist/2025-05-26/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=885a2c1e30eb3d489a1234034663131c8762645ec1c03ce94198053a21debfa7 +dist/2025-05-26/cargo-beta-s390x-unknown-linux-gnu.tar.gz=f9b69f26f868a5a2173de74424be26cb0d4e6db6867e1a8c32db799e9fb03ede +dist/2025-05-26/cargo-beta-s390x-unknown-linux-gnu.tar.xz=bf3d01fc0202bcdea158e22821e1ffb8b07e4324ce487be96cde2cf1f1e5eaf6 +dist/2025-05-26/cargo-beta-x86_64-apple-darwin.tar.gz=65b4ee4359e402e06cee2c574a03389e36acb4e1caee4aa83cb281f95c48576a +dist/2025-05-26/cargo-beta-x86_64-apple-darwin.tar.xz=2906bd00506ada8cffb743f355aa918531273f45f449616410dd0c3f913013b3 +dist/2025-05-26/cargo-beta-x86_64-pc-windows-gnu.tar.gz=7b4c8ad29c72d619c2977f5d79cb5c959bdd8acaae2364495962db5473478609 +dist/2025-05-26/cargo-beta-x86_64-pc-windows-gnu.tar.xz=0dfddbc3218d921ac75affe9d3b8595c8d49df9a98d91fe0f92341754f2b6296 +dist/2025-05-26/cargo-beta-x86_64-pc-windows-msvc.tar.gz=43a110d4e7cd3c8a764e4a2836fe368a347ba7fdfd40c8f565969244964d20c1 +dist/2025-05-26/cargo-beta-x86_64-pc-windows-msvc.tar.xz=703d2cce50711a9753c5b7a72c9468d73144a8f6015db913920795590c54ac97 +dist/2025-05-26/cargo-beta-x86_64-unknown-freebsd.tar.gz=9236099e0ffff060c483cc8996e66ca2e906a2c030941aa49163bdc4dfb7bd3b +dist/2025-05-26/cargo-beta-x86_64-unknown-freebsd.tar.xz=ff50d29e650cf85f6aadee0618ffef15ac4f3c9b30f02f9a678129e9bf8f5ad3 +dist/2025-05-26/cargo-beta-x86_64-unknown-illumos.tar.gz=ea5bd3cd42867e5174f7661fb5254d2f3effadcf0551cf3cbe6fa60d718f48ae +dist/2025-05-26/cargo-beta-x86_64-unknown-illumos.tar.xz=e3249f14d4467644a73b950d3d9a4f5ac20146923c961bdec3689bbbd4330a38 +dist/2025-05-26/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=e0c5dc6ee9250c0ddbd7db218878fffc5a38641fc773ded5dc28d92a1750eed6 +dist/2025-05-26/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=53921721c33e20275eb7a912ae80af22aa3e888a232360baa3f00f272114833f +dist/2025-05-26/cargo-beta-x86_64-unknown-linux-musl.tar.gz=18ea106ff675f15b112c4f4dabcb068857a54c6dbd25e9e8661184b2ee3db556 +dist/2025-05-26/cargo-beta-x86_64-unknown-linux-musl.tar.xz=ea4db9c40954dd72896ef9323767384f2da48064465f8961b775f87c8944d0a8 +dist/2025-05-26/cargo-beta-x86_64-unknown-netbsd.tar.gz=05e8398cb96e2c5ebc2db71edd0965c6da755bd14b1598197f5d375d2b0c1cf3 +dist/2025-05-26/cargo-beta-x86_64-unknown-netbsd.tar.xz=ede3da14f0e405398aa9cfe3743d568a37b1adf62aa2a16489b710d773b1744f +dist/2025-05-26/clippy-beta-aarch64-apple-darwin.tar.gz=43cbc31dce5ca5abc1efcf87fc4609d148d456429d41836c502f217de50aaaab +dist/2025-05-26/clippy-beta-aarch64-apple-darwin.tar.xz=1ea6c9615a8c3101acb36585d12ec3a61ba55ec069155324675aeb0005738bf4 +dist/2025-05-26/clippy-beta-aarch64-pc-windows-msvc.tar.gz=eec62be5aaa28c856954a2d5e3fbdce10377bd164929ea6d18e43c085ff5044f +dist/2025-05-26/clippy-beta-aarch64-pc-windows-msvc.tar.xz=76e60d581deb1989f93ec88e94fc984568c69486c9b50c88e1059f18560cf649 +dist/2025-05-26/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=be69d0e6ac05d624dece95d6e6f9a90f8f8e52be2c1fde4b5d64fd9da8d89c7b +dist/2025-05-26/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=198c679a62e71d108688d3b64a5be76ecd6462f3301c0ee411943678bae640ce +dist/2025-05-26/clippy-beta-aarch64-unknown-linux-musl.tar.gz=c9012719c15ed4fddd04d4ac3618018a1c194f048e9879acbbb580346a72bda9 +dist/2025-05-26/clippy-beta-aarch64-unknown-linux-musl.tar.xz=7f10e2a9164ae2cd916e82ef569a1f729853ecdc8edfd92012c63e03ff9b5786 +dist/2025-05-26/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=8388777a665a098add359f5dfb10c2e85e6d5ff344b71844267750c589d9dd9f +dist/2025-05-26/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=ecbd41ae30412624507a6338c25b398b34153762d127bcb413321510334b7036 +dist/2025-05-26/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=47b420620eae76da5db8fbb2e0a7f23988b436bfce22d17cd44885daac011d7a +dist/2025-05-26/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=21fe8e08a556bc6c70bff20e13ea2b54fc6f97656911b960f27c665c6d9d45d2 +dist/2025-05-26/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=ded5cec25a893481d0735228946518b3bbf22becb298d5bd72ffa80c338d423b +dist/2025-05-26/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=26ce2e33c7434e2da0d3dd48ea2d57d2cb08eb52d041ff6539bc7d6e6f6ec13c +dist/2025-05-26/clippy-beta-i686-pc-windows-gnu.tar.gz=e38aa8d7ee0d74e73ed07ebd173549be67654ecf3e781e8386d47c11175d150e +dist/2025-05-26/clippy-beta-i686-pc-windows-gnu.tar.xz=e35de2fd0152277780464f80bed8aa78feb2e1e64b818888b32522d36ddc6ef2 +dist/2025-05-26/clippy-beta-i686-pc-windows-msvc.tar.gz=b4d259b042439f1324c91580b5d050eebfab04afb86715bc7571f17174f7622f +dist/2025-05-26/clippy-beta-i686-pc-windows-msvc.tar.xz=7183a9094ebe14baf68a42e7879953c8d433febdad5b32153371d21c65dd86ca +dist/2025-05-26/clippy-beta-i686-unknown-linux-gnu.tar.gz=d42ba45cc7f6ecec2cfad85fd15d69b17a84d19206fa5c33f1016d135ee29e6f +dist/2025-05-26/clippy-beta-i686-unknown-linux-gnu.tar.xz=ef98b85f80e434c49b0c19eca16baab3d10345237642c7252b3ab5ddeb494fba +dist/2025-05-26/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=6d2e11bbe7c0a1a9249146193e6879176460b90bb9b7909ec01065c02a20f801 +dist/2025-05-26/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=1eeb173bc287d7fba22091a7083c472aeace48679aae3450c77435376a5285b5 +dist/2025-05-26/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=3d75d8f697609fd3ff857d9aba4947d5efcbe1791994a5a29204d87c625ad3b1 +dist/2025-05-26/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=70ee5bd276113f98b2913c72564c0bf0d364167986d7db776669fb6e4e08e9e7 +dist/2025-05-26/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=010fcc2d0e9724d6162383002d0c63039b9e24c0cb6e2d5187edbb869bc7e1b0 +dist/2025-05-26/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=6b0bcca51760ec121f7ec64e2f6eaf91eeebb9da0318642a115f6852647ae806 +dist/2025-05-26/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=3b2306a5b60fd2f67eb805189457e1dc0350854eb3a47ae9dd53cc89df9f668d +dist/2025-05-26/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=ccafe9b4403c6bcb87a244eb6afadcbab799e65dc105f60551a8a3b6153c31d9 +dist/2025-05-26/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=d70a86b08254f64cb2c4d37e911f70aaa0c22f464e1c906d63e61a6b29d39184 +dist/2025-05-26/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=2db59bb48172923ad3db736f51ccf1226bdb8ebc76daa29e220007897d76bf6d +dist/2025-05-26/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=cf6915d74c6e8789380f5b986c2ed1b17e8709c2a41abd4cfe89033b45cd8642 +dist/2025-05-26/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=60c647b9fe5ab19522ef94dc5d5e6a03ce58e922ac55dd85feded92812b40879 +dist/2025-05-26/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=36614d7f77357fbdcdaf35bebb4222e41617cb684a3daf69e2a1cbfe46ea60d1 +dist/2025-05-26/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=0876fae91f3d54a745a73876b901d6551089264b408b7d1954475d3e6195f72b +dist/2025-05-26/clippy-beta-s390x-unknown-linux-gnu.tar.gz=72247770c08147d59d93ece7d6fc97f46c091fc71c65d3a215f682608aecb2ba +dist/2025-05-26/clippy-beta-s390x-unknown-linux-gnu.tar.xz=12d12c1c6a277af5c203ad0fbf6dcb4b04ea74614740734466ea7754f13696f6 +dist/2025-05-26/clippy-beta-x86_64-apple-darwin.tar.gz=a9e255811a75cba14ee0789c2263655407b8d293273252217a4fd7d0de813cec +dist/2025-05-26/clippy-beta-x86_64-apple-darwin.tar.xz=8bac948774490e48e4193eef0415fd02ce0b7e6855d6cc59314e0f6234da927c +dist/2025-05-26/clippy-beta-x86_64-pc-windows-gnu.tar.gz=fc5d7c3712d8be85f3992f01e2ade695e6c443983b46b4e1eaa3bbad9bc951a8 +dist/2025-05-26/clippy-beta-x86_64-pc-windows-gnu.tar.xz=0e20c9f824ac305c0fa0370376f977f5fd27aff485223ae1ce32c3de0e12b119 +dist/2025-05-26/clippy-beta-x86_64-pc-windows-msvc.tar.gz=c85932e32236b3365351b775cd382744fb47b3bb3117a65cee537ad79fc78881 +dist/2025-05-26/clippy-beta-x86_64-pc-windows-msvc.tar.xz=b4198fac7d359f3fea4240ab81b2f4f013c938e520a350ca21878c84c5ec16f5 +dist/2025-05-26/clippy-beta-x86_64-unknown-freebsd.tar.gz=e3e5d327a35c467ad44151db010a10ad61b0377d8f5c1844d79678d9388cd6e5 +dist/2025-05-26/clippy-beta-x86_64-unknown-freebsd.tar.xz=94637cf9f7715155e530fc9c295fb41555ebbac18255a8750255b13e2f691641 +dist/2025-05-26/clippy-beta-x86_64-unknown-illumos.tar.gz=8ea5ed861bbc11d47b8f6710b95b29acdeaf6d7a80562216a5e8094bfe440d90 +dist/2025-05-26/clippy-beta-x86_64-unknown-illumos.tar.xz=b14302a36f11794b181125c22af92eb5777f7f5f898c73194e82361ddbfacacb +dist/2025-05-26/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=611504bffef243a1ac873c7d18c42731d6e24caa6d4b370be1ab1858603bb201 +dist/2025-05-26/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=52f4b0a2bd2a5d0bdbccfc1a8ad9cf24572c103c8713911e121fde4935c22854 +dist/2025-05-26/clippy-beta-x86_64-unknown-linux-musl.tar.gz=7af7df177e64881dd68fa1e8207fb4a0bd7ba4e642468024fa34fc3d5c839df8 +dist/2025-05-26/clippy-beta-x86_64-unknown-linux-musl.tar.xz=f2f9575cbd3e3f067aeda5f9b7ab424e0dc119448d12872692cb7c6669f61ae0 +dist/2025-05-26/clippy-beta-x86_64-unknown-netbsd.tar.gz=5e1dc30da47902c52ab1fbfa2216a6952385184b44854c47b8eb988bdd1b040d +dist/2025-05-26/clippy-beta-x86_64-unknown-netbsd.tar.xz=23f21905caa5824a463fac01e18e0055009cecdfd406da76b838105eb78127e7 +dist/2025-05-27/rustfmt-nightly-aarch64-apple-darwin.tar.gz=5a3b21df1d525a049b9bd1fca0e32eb5aad1a82a2800e094af80f121e90878c0 +dist/2025-05-27/rustfmt-nightly-aarch64-apple-darwin.tar.xz=3f7edd6997839f12d70246edb672a13d808bd871bfaa4bda66bb4db4fb1158fc +dist/2025-05-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=99ecb24920480c06482d8b10f6dbc23247c66033991ad807f8228dff35626fac +dist/2025-05-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=fc716e83a694792c0b2355457cbe6f0a820ed85be725d6b3e742b257cc9cd245 +dist/2025-05-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=cc6bbe1ea77372ea927329aeb6e4d7602829b307a407466d9c6a3417c62b6ce0 +dist/2025-05-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=2018c51986de7be37f11ae05aa101b50f2d8f0e06f7ed8e3c6e4891b580a122f +dist/2025-05-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=11cbb36b62563209127c1c8b1f4c32ec1ebc6ca04f18a8e067333402120da00b +dist/2025-05-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=5bcdcf88ece597956dea20d63cf568a92cb841df529fb0c0b277f469c58bc742 +dist/2025-05-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=507753792ca1668ffb7ea4e4467f2ecbfee8753e269a29050cd4e22b1ff20b33 +dist/2025-05-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=3e2b0b89c373dc935dc6c0a882b7723d252792d831a6a81889f77df0964df819 +dist/2025-05-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=40c728833bee43b25bf81eea8e9e6e3330d455729ec34c6b1c45d6c8b04f3ff4 +dist/2025-05-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=c8dc03a4b1c1ed9f853f4c548d94d44b87fcdf52095e7b84d9ddd3be7be7a11a +dist/2025-05-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=fefed8cce0ab0b90b7b58e1a417e031b0969148a427dbbf2f29a9170fb386646 +dist/2025-05-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=77b787221ec106ceb20e58e684f348bc5542bac506fc4a3084d4d2931164878a +dist/2025-05-27/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=3cd4ed08fe7dd4d921d60f15e7593d71db450c9e2e6d5a1f4fca3f409dabe8fe +dist/2025-05-27/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=df210bf84e04e83ff77cad6acd156393d687e89fd74fff4c288762edfa0853de +dist/2025-05-27/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=1f3b532d841f5c78fbdb5d0a1c513ab45bd942de27ce650dfca459e33db9b27c +dist/2025-05-27/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=e9e52af5658861dfa2d1caed954b078a2630b42de08205727b368098348fa0dd +dist/2025-05-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=c81689ec620c0fbdd4a4daed7a3de6ecbc0b13b98fa06dd1f2d1beb5cc98d5c8 +dist/2025-05-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=a9cb725755e64fff80dfcd2fddf8cb3a62508dee90c6b6aa6786d8e03a2dd232 +dist/2025-05-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=96fae51d3f3443e28f2789a7117510840f24a3270f8a77cf3103c6e7100079b7 +dist/2025-05-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=78a3298fa4f70001326aec0d080873fd8c64b18beca91c8eb93f2d2786b27b5e +dist/2025-05-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=a87af95c57af0edacceb7072fb81291f9e935d548fa5c68afa58d2d5f53d4497 +dist/2025-05-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=924b5fbbec1a00b714787b7489ab592a6c0ec9c72d57f06f3ac4ff9960a610a5 +dist/2025-05-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=c11fc36cf2ae84737ca5d1fc441acbf755053eba26fd962f12a9b1a76a0a52ec +dist/2025-05-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=6f2fa0295e91031a1f9f1e6c344435021a6b18212c2e21c50a46baafd6694071 +dist/2025-05-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=40e7a2d6986084a630161809132213cf3a2858f04c60902fa09eedbf9caa8bb0 +dist/2025-05-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=112d9a416fb43ed6dcc8b066133ded75354977ea9e2d14e6d8310b35bfdf338e +dist/2025-05-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=6c30107bfcc9c5b72be06570efa37d356ba9ee9a14385fb84e392095533a8352 +dist/2025-05-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=6e80b773b5e2353bad7a5e01e3b330dd569133aae505bceaf605864fda12d55c +dist/2025-05-27/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=b250ceb2e2360bf0366a91f6533aff91e17d7c9f3ca48fe448ca18008da3aedb +dist/2025-05-27/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=62cebf6541b0d3b2f247f3e43492160750eabb227be9ca98b34714538e176cbc +dist/2025-05-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=ac31569b7367b4a44fd64c6cc778849196a7d02ca4b413c08568a4318100246d +dist/2025-05-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=0ef0aa7518da52dcea41e979aba1e4e93268bfc43140a83e00dff566ea2ee0e1 +dist/2025-05-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=811c1024ca9b92a9512105c6589f557ddc409df6ce7dda3f1ad537f0b5e5520c +dist/2025-05-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=f0deb894b1f9921ab401c8e4fe3a1eb2cef4c2b51352f54c87fad0dc8689d927 +dist/2025-05-27/rustfmt-nightly-x86_64-apple-darwin.tar.gz=18b3231a7df8e5ab2fa961de699880878aa234f56cff9d7a1126c17b8f249846 +dist/2025-05-27/rustfmt-nightly-x86_64-apple-darwin.tar.xz=74a69eb74ebd5ae965f2f7fd251743ad81efc2e6e5684886d47427414d39b2e7 +dist/2025-05-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=815169fe5a0bced72ae2a7a187597d56bfc402cd5c318f9258d5576c178a19e2 +dist/2025-05-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=0849e77b370332530f51bc486cb3b67a26a13369267e1978aeb895e66d8c62a1 +dist/2025-05-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=b003015eb6622e2ee16a4471e9d1b6908556b4f544ee8574d793e94e866258b9 +dist/2025-05-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=2fb0722be5ecec129175e74928464f57b4595208e87b5295186f163389aee8c3 +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=d780af4579941b6a1d1825a3d512cf541486cd365699243634f134af1af80661 +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=7109ac35f05e8d0f013eaa530c6271cc943ae8076cb7056383b93422329dbc0a +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=9e3d61435933b25f5e489cfd97cc3d9737fc99403e72fd2b2c302a2850d6e7ac +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=8a8dfcea26c974826693c776a64e89b3ef9104f61772e84918c780c92c5a13a5 +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=c4b37e59ef93c647a1533bb7427cfc97a3766a40dd551ae8eb3668a44702e1df +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=5ad72eb343c31b8da7edd7c1fe56e9920a4f7662190fab6e20dfb258e3c38c60 +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=f99a565ea5a7d2238f7cd79364c39fdd2b83559f4cc668cee10c0e3564a5420c +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=a470498028404e7c64cb5fb77f88dfac560772320fd6aa620eb25c37bf879c9a +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=e9c10350ba54a7d8a190a32aa913cc581e918cfdda14c12553e0278db8e78239 +dist/2025-05-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=63e5effaf0b5bfaf8fc9350d1bc0eb30cf0e8076da85c805fbb4988fff1b8f3c +dist/2025-05-27/rustc-nightly-aarch64-apple-darwin.tar.gz=16ca9b794c74a72cf9ca68fff71b9f56db1e832feb919c3ff95b65133718719d +dist/2025-05-27/rustc-nightly-aarch64-apple-darwin.tar.xz=52c42f611e409b50e857c3ce2857afd5f45f19d30f0c8ca1d0a7e1add6fadcbe +dist/2025-05-27/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=687337020aca4e3e97a5313aafecbbce548cd54fe599c6d62b07f530c39ea755 +dist/2025-05-27/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=2d558352e8f441d1eba929dd5598db5f717a5dec3f813dcc34c4c43da169aea2 +dist/2025-05-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=1eab60c4ce6e7e8e0e245a5928f26ab0b76dc9a4545eb89e481eae0673bc9c84 +dist/2025-05-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=be47b529deb209ae5120a359047b6353114e7a7cceeee5b038a2fa1464fc9c14 +dist/2025-05-27/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=a9985e558669e2f8f6142c3ae25010b834a4d9069344abeb69e4d4cf5c244777 +dist/2025-05-27/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=ce8e9f473ef4247d011055ac6787a9b92b2fb0e932a8b0f08278c8db2529655e +dist/2025-05-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=709f050c2c73b2788d0c5bbe388296c94db9bc014996015e41688d119b42b0cd +dist/2025-05-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=010b92069ba7a9de01966e54f09bda22b9ff436929a2e17ea1e9f5b379114780 +dist/2025-05-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=0685aa646c5bcf6c2b6a70e6384023bd79571f1f87bf85c74c452ea7adbcab32 +dist/2025-05-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=f3cb40e7a13f75e40c36dea7b916ed245976e9d82a37299c94b6d6245e697f66 +dist/2025-05-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=a4c876c4d4c8829ec4755f71ac8efa69baa2875782a371a9aa6ae84d13270ae1 +dist/2025-05-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=5e64186cdb993c1ff56fbe10ee1fed73fe71384db6bfdfc57e15cc93924df816 +dist/2025-05-27/rustc-nightly-i686-pc-windows-gnu.tar.gz=92b706e06dc7b656038437b0c281436a28d8508ef13081b61438133d2fe952ef +dist/2025-05-27/rustc-nightly-i686-pc-windows-gnu.tar.xz=fb8d2d68ac3600befba96f67acbeefec581e2b1eada7c33887fb792101eb2dda +dist/2025-05-27/rustc-nightly-i686-pc-windows-msvc.tar.gz=8322ce000c9660d86b5a376da11b7da482b78e7e47a56f1fa2fe151b597d8024 +dist/2025-05-27/rustc-nightly-i686-pc-windows-msvc.tar.xz=4612835d7ba71cfe226d04d55c06222bd8b2dd56a8dcba07133dd02cac69fb16 +dist/2025-05-27/rustc-nightly-i686-unknown-linux-gnu.tar.gz=8663c35fe6371fe7ae91c391933a7165cefd5f9f26fe75bcedf6f9977cb4ad0f +dist/2025-05-27/rustc-nightly-i686-unknown-linux-gnu.tar.xz=17c937f85f59fa7a876540ea60ecd5729c85409a64655041707865fd5f7cc849 +dist/2025-05-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=639e0b5ed5b0afa3d8304189ed674e9d39b742dc61cc267043628b4c458ba157 +dist/2025-05-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=faa19a69d37059f67afe8f031b8f743823422dc23939513877125cc2c4a9db2c +dist/2025-05-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=3dffa8b59899fd9f4d0d7a999212a1737745883f6b6d1a15cee7ff75d7dda417 +dist/2025-05-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=1d5368b7d616d42b81b82d0f46e5ddfc2b7033bc13e06b06520dca4489631388 +dist/2025-05-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=66b1ef67218c651844ffa481e8a9dbbb81a2ef4b40e673bcde2a0c9612eaac95 +dist/2025-05-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=48306f0162b762424e5e7da9e8920c1be982e6e0989536f2d89e922cf7fe7d64 +dist/2025-05-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=7d7557024f84240fa7cb0d42bbe223c49615eeadcff6c757a755a2e500f8f623 +dist/2025-05-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=62ebda3f8a44be8c1498defb5059b93add29e95f867e2e7cfd648185fc21b6e5 +dist/2025-05-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=5f3579be74da3329e3af32b034fcae002c781f7933b522638cb84876e00efa56 +dist/2025-05-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=6191553b2216ef7e9f43763736708a50c9ba972ae51997676244c52795ac5486 +dist/2025-05-27/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=70fe263d30c9ed08e00d4d10f9bcbdfda571e5468dcda304a137f7d980e027ac +dist/2025-05-27/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=973c090d6f72c9962fec065d99c02a79f857243306cc6b34a2f77f9c8f7f567c +dist/2025-05-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=bcf3f416152378bac430f88da0fc79e34a7fcbb65e7e06ac890b8de9f2793e98 +dist/2025-05-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=cf0cce7680f97a7c248869e44c5571dcc46c5b85e8f00c567efbf9ca3c4af80e +dist/2025-05-27/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=e6e6094edf1a44f7f08e9d2cb814d3023f0261d5595be89d968b75b0ba0e368c +dist/2025-05-27/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=0f4b4c5d07b8cc6815094f49ad53e8520245da428afd80e0497676a0863764cf +dist/2025-05-27/rustc-nightly-x86_64-apple-darwin.tar.gz=c9c5ff52a78d80c74ce0c40c0a2947dedfe99b195f06885d0e405c7f5b6bde28 +dist/2025-05-27/rustc-nightly-x86_64-apple-darwin.tar.xz=1cc3250c923e8647d6668c6e8ee14f2c92c50a73b080a2991768e3a88a9a99ca +dist/2025-05-27/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=04d2f910571ce2e2e32ab655589989538516cfc023cb6401c605973465054927 +dist/2025-05-27/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=a420f9a4cc7fd01da0b86e56ed4d72f45cfd10c725f381d047dd701bd4e84178 +dist/2025-05-27/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=ea454055258e3ccb6710ba86fc58e1d629c807aa52353d48d754eafe6e4f3522 +dist/2025-05-27/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=9570ad0c65bc3226e3ec05185b01dbf5a1d9822de9aeabedcb4921cc8fbc2639 +dist/2025-05-27/rustc-nightly-x86_64-unknown-freebsd.tar.gz=08f400e47513fe7b8a3d3f5fb86510e28f87d5bfbd661fa8b106b16c0e22b444 +dist/2025-05-27/rustc-nightly-x86_64-unknown-freebsd.tar.xz=5c6467a38bff56ca4fa1722b092a157d0e258eb037bd5f784fae0827af842088 +dist/2025-05-27/rustc-nightly-x86_64-unknown-illumos.tar.gz=f007908e9cbc7defab2719a4f734f6f327952d59d6939b0e85ccb36dca670e0c +dist/2025-05-27/rustc-nightly-x86_64-unknown-illumos.tar.xz=620be77081b1564ff626b1926d8242d8fc2e6f2c0308002f01cc214f8843701b +dist/2025-05-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=8749217fd22d81ee2f380b1af63116e4c540fd11f617752e552f66568d50868c +dist/2025-05-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=545ff3e0ac1c7c303b47bc062d029033a3d8de77c6fb54bad39a6a34b099c711 +dist/2025-05-27/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=d146af52aa7fad3b198b9dd5242793bfc2dc8aad81642bf34702e409d5ae7f3b +dist/2025-05-27/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=d72ed1096917a5789f26564ddc920c3fdcd29056cf97452371e5141bcc2c8a8e +dist/2025-05-27/rustc-nightly-x86_64-unknown-netbsd.tar.gz=94b608796d12feff92c54f942318e711879d86b1a3114a710b8366b7415ae025 +dist/2025-05-27/rustc-nightly-x86_64-unknown-netbsd.tar.xz=7d870360a35a34dffede096d62734d97a7bf60d0661e638f73d913cb93bd49ec diff --git a/src/tools/cargo b/src/tools/cargo index 47c911e9e6f6..64a12460708c 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 47c911e9e6f6461f90ce19142031fe16876a3b95 +Subproject commit 64a12460708cf146e16cc61f28aba5dc2463bbb4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 28147dfbea3e..3a98217f625a 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6440,6 +6440,7 @@ Released 2018-09-13 [`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_concat`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_concat [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 45ba2f078be7..72ab08792d37 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -51,7 +51,7 @@ Clippy team directly by mentioning them in the issue or over on [Zulip]. All currently active team members can be found [here](https://github.com/rust-lang/rust-clippy/blob/master/triagebot.toml#L18) -Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy +Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues. You can use `@rustbot claim` to assign the issue to yourself. There are also some abandoned PRs, marked with [`S-inactive-closed`]. @@ -70,7 +70,7 @@ To figure out how this syntax structure is encoded in the AST, it is recommended Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. But we can make it nest-less by using [let chains], [like this][nest-less]. -[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. Note that [`E-medium`] issues may require some knowledge of Clippy internals or some debugging to find the actual problem behind the issue. @@ -79,7 +79,7 @@ debugging to find the actual problem behind the issue. lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of an AST expression). -[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue +[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed [`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST [`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index c7c06afb6129..f69e5bee4bb2 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy" -# begin autogenerated version version = "0.1.89" -# end autogenerated version 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/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index cdbbe76bdb08..fc405249bcfe 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -151,7 +151,7 @@ toolchain called `clippy` by default, see `cargo dev setup toolchain --help` for other options. ```terminal -cargo dev setup toolcahin +cargo dev setup toolchain ``` Now you may run `cargo +clippy clippy` in any project using the new toolchain. diff --git a/src/tools/clippy/book/src/development/the_team.md b/src/tools/clippy/book/src/development/the_team.md index da5d084ed97f..d22123231868 100644 --- a/src/tools/clippy/book/src/development/the_team.md +++ b/src/tools/clippy/book/src/development/the_team.md @@ -33,7 +33,7 @@ this group to help with triaging, which can include: 1. **Labeling issues** - For the `good-first-issue` label, it can still be good to use `@rustbot` to + For the `good first issue` label, it can still be good to use `@rustbot` to subscribe to the issue and help interested parties, if they post questions in the comments. diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 0db4182dbcdb..9809e32de8a4 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -836,6 +836,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) * [`manual_slice_fill`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill) +* [`manual_slice_size_calculation`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) * [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 1134b0e97af2..0606245f990c 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_config" -# begin autogenerated version version = "0.1.89" -# end autogenerated version edition = "2024" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index ad0aea39d41a..4ce8d001c2f0 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -739,6 +739,7 @@ define_Conf! { manual_repeat_n, manual_retain, manual_slice_fill, + manual_slice_size_calculation, manual_split_once, manual_str_repeat, manual_strip, diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 47b7b3758613..10c08dba50b9 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -5,13 +5,11 @@ version = "0.0.1" edition = "2024" [dependencies] -aho-corasick = "1.0" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.7" -shell-escape = "0.1" walkdir = "2.3" [package.metadata.rust-analyzer] diff --git a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs index bf0e77710469..3bdc5b277232 100644 --- a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs +++ b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs @@ -1,6 +1,4 @@ -use crate::update_lints::{ - DeprecatedLint, DeprecatedLints, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints, -}; +use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints}; use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; @@ -16,28 +14,34 @@ use std::{fs, io}; /// /// If a file path could not read from or written to pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { - let prefixed_name = if name.starts_with("clippy::") { - name.to_owned() - } else { - format!("clippy::{name}") - }; - let stripped_name = &prefixed_name[8..]; + if let Some((prefix, _)) = name.split_once("::") { + panic!("`{name}` should not contain the `{prefix}` prefix"); + } let mut lints = find_lint_decls(); - let DeprecatedLints { - renamed: renamed_lints, - deprecated: mut deprecated_lints, - file: mut deprecated_file, - contents: mut deprecated_contents, - deprecated_end, - .. - } = read_deprecated_lints(); + let (mut deprecated_lints, renamed_lints) = read_deprecated_lints(); - let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { + let Some(lint) = lints.iter().find(|l| l.name == name) else { eprintln!("error: failed to find lint `{name}`"); return; }; + let prefixed_name = String::from_iter(["clippy::", name]); + match deprecated_lints.binary_search_by(|x| x.name.cmp(&prefixed_name)) { + Ok(_) => { + println!("`{name}` is already deprecated"); + return; + }, + Err(idx) => deprecated_lints.insert( + idx, + DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + version: clippy_version.rust_display().to_string(), + }, + ), + } + let mod_path = { let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); if mod_path.is_dir() { @@ -48,24 +52,7 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { mod_path }; - if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - deprecated_contents.insert_str( - deprecated_end as usize, - &format!( - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - clippy_version.rust_display(), - prefixed_name, - reason, - ), - ); - deprecated_file.replace_contents(deprecated_contents.as_bytes()); - drop(deprecated_file); - - deprecated_lints.push(DeprecatedLint { - name: prefixed_name, - reason: reason.into(), - }); - + if remove_lint_declaration(name, &mod_path, &mut lints).unwrap_or(false) { generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); println!("info: `{name}` has successfully been deprecated"); println!("note: you must run `cargo uitest` to update the test results"); diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index b4c13213f552..13d6b1285dcd 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,19 +1,18 @@ +use crate::utils::{ + ClippyInfo, ErrAction, FileUpdater, UpdateMode, UpdateStatus, panic_action, run_with_args_split, run_with_output, +}; use itertools::Itertools; use rustc_lexer::{TokenKind, tokenize}; -use shell_escape::escape; -use std::ffi::{OsStr, OsString}; +use std::fmt::Write; +use std::fs; +use std::io::{self, Read}; use std::ops::ControlFlow; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::{self, Command, Stdio}; -use std::{fs, io}; use walkdir::WalkDir; pub enum Error { - CommandFailed(String, String), Io(io::Error), - RustfmtNotInstalled, - WalkDir(walkdir::Error), - IntellijSetupActive, Parse(PathBuf, usize, String), CheckFailed, } @@ -24,37 +23,15 @@ impl From for Error { } } -impl From for Error { - fn from(error: walkdir::Error) -> Self { - Self::WalkDir(error) - } -} - impl Error { fn display(&self) { match self { Self::CheckFailed => { eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); }, - Self::CommandFailed(command, stderr) => { - eprintln!("error: command `{command}` failed!\nstderr: {stderr}"); - }, Self::Io(err) => { eprintln!("error: {err}"); }, - Self::RustfmtNotInstalled => { - eprintln!("error: rustfmt nightly is not installed."); - }, - Self::WalkDir(err) => { - eprintln!("error: {err}"); - }, - Self::IntellijSetupActive => { - eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ - Not formatting because that would format the local repo as well!\n\ - Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." - ); - }, Self::Parse(path, line, msg) => { eprintln!("error parsing `{}:{line}`: {msg}", path.display()); }, @@ -62,12 +39,6 @@ impl Error { } } -struct FmtContext { - check: bool, - verbose: bool, - rustfmt_path: String, -} - struct ClippyConf<'a> { name: &'a str, attrs: &'a str, @@ -257,155 +228,153 @@ fn fmt_conf(check: bool) -> Result<(), Error> { Ok(()) } -fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to - // format because rustfmt would also format the entire rustc repo as it is a local - // dependency - if fs::read_to_string("Cargo.toml") - .expect("Failed to read clippy Cargo.toml") - .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") - { - return Err(Error::IntellijSetupActive); - } - - check_for_rustfmt(context)?; - - cargo_fmt(context, ".".as_ref())?; - cargo_fmt(context, "clippy_dev".as_ref())?; - cargo_fmt(context, "rustc_tools_util".as_ref())?; - cargo_fmt(context, "lintcheck".as_ref())?; - - let chunks = WalkDir::new("tests") - .into_iter() - .filter_map(|entry| { - let entry = entry.expect("failed to find tests"); - let path = entry.path(); - if path.extension() != Some("rs".as_ref()) - || path - .components() - .nth_back(1) - .is_some_and(|c| c.as_os_str() == "syntax-error-recovery") - || entry.file_name() == "ice-3891.rs" - { - None +/// Format the symbols list +fn fmt_syms(update_mode: UpdateMode) { + FileUpdater::default().update_file_checked( + "cargo dev fmt", + update_mode, + "clippy_utils/src/sym.rs", + &mut |_, text: &str, new_text: &mut String| { + let (pre, conf) = text.split_once("generate! {\n").expect("can't find generate! call"); + let (conf, post) = conf.split_once("\n}\n").expect("can't find end of generate! call"); + let mut lines = conf + .lines() + .map(|line| { + let line = line.trim(); + line.strip_suffix(',').unwrap_or(line).trim_end() + }) + .collect::>(); + lines.sort_unstable(); + write!( + new_text, + "{pre}generate! {{\n {},\n}}\n{post}", + lines.join(",\n "), + ) + .unwrap(); + if text == new_text { + UpdateStatus::Unchanged } else { - Some(entry.into_path().into_os_string()) + UpdateStatus::Changed } - }) - .chunks(250); + }, + ); +} - for chunk in &chunks { - rustfmt(context, chunk)?; +fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) { + let mut rustfmt_path = String::from_utf8(run_with_output( + "rustup which rustfmt", + Command::new("rustup").args(["which", "rustfmt"]), + )) + .expect("invalid rustfmt path"); + rustfmt_path.truncate(rustfmt_path.trim_end().len()); + + let mut cargo_path = String::from_utf8(run_with_output( + "rustup which cargo", + Command::new("rustup").args(["which", "cargo"]), + )) + .expect("invalid cargo path"); + cargo_path.truncate(cargo_path.trim_end().len()); + + // Start all format jobs first before waiting on the results. + let mut children = Vec::with_capacity(16); + for &path in &[ + ".", + "clippy_config", + "clippy_dev", + "clippy_lints", + "clippy_lints_internal", + "clippy_utils", + "rustc_tools_util", + "lintcheck", + ] { + let mut cmd = Command::new(&cargo_path); + cmd.current_dir(clippy.path.join(path)) + .args(["fmt"]) + .env("RUSTFMT", &rustfmt_path) + .stdout(Stdio::null()) + .stdin(Stdio::null()) + .stderr(Stdio::piped()); + if update_mode.is_check() { + cmd.arg("--check"); + } + match cmd.spawn() { + Ok(x) => children.push(("cargo fmt", x)), + Err(ref e) => panic_action(&e, ErrAction::Run, "cargo fmt".as_ref()), + } + } + + run_with_args_split( + || { + let mut cmd = Command::new(&rustfmt_path); + if update_mode.is_check() { + cmd.arg("--check"); + } + cmd.stdout(Stdio::null()) + .stdin(Stdio::null()) + .stderr(Stdio::piped()) + .args(["--config", "show_parse_errors=false"]); + cmd + }, + |cmd| match cmd.spawn() { + Ok(x) => children.push(("rustfmt", x)), + Err(ref e) => panic_action(&e, ErrAction::Run, "rustfmt".as_ref()), + }, + WalkDir::new("tests") + .into_iter() + .filter_entry(|p| p.path().file_name().is_none_or(|x| x != "skip_rustfmt")) + .filter_map(|e| { + let e = e.expect("error reading `tests`"); + e.path() + .as_os_str() + .as_encoded_bytes() + .ends_with(b".rs") + .then(|| e.into_path().into_os_string()) + }), + ); + + for (name, child) in &mut children { + match child.wait() { + Ok(status) => match (update_mode, status.exit_ok()) { + (UpdateMode::Check | UpdateMode::Change, Ok(())) => {}, + (UpdateMode::Check, Err(_)) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); + process::exit(1); + }, + (UpdateMode::Change, Err(e)) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + panic_action(&e, ErrAction::Run, name.as_ref()); + }, + }, + Err(ref e) => panic_action(e, ErrAction::Run, name.as_ref()), + } } - Ok(()) } // the "main" function of cargo dev fmt -pub fn run(check: bool, verbose: bool) { - let output = Command::new("rustup") - .args(["which", "rustfmt"]) - .stderr(Stdio::inherit()) - .output() - .expect("error running `rustup which rustfmt`"); - if !output.status.success() { - eprintln!("`rustup which rustfmt` did not execute successfully"); - process::exit(1); +pub fn run(clippy: &ClippyInfo, update_mode: UpdateMode) { + if clippy.has_intellij_hook { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ + Not formatting because that would format the local repo as well!\n\ + Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." + ); + return; } - let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path"); - rustfmt_path.truncate(rustfmt_path.trim_end().len()); - - let context = FmtContext { - check, - verbose, - rustfmt_path, - }; - if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) { + run_rustfmt(clippy, update_mode); + fmt_syms(update_mode); + if let Err(e) = fmt_conf(update_mode.is_check()) { e.display(); process::exit(1); } } - -fn format_command(program: impl AsRef, dir: impl AsRef, args: &[impl AsRef]) -> String { - let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect(); - - format!( - "cd {} && {} {}", - escape(dir.as_ref().to_string_lossy()), - escape(program.as_ref().to_string_lossy()), - arg_display.join(" ") - ) -} - -fn exec_fmt_command( - context: &FmtContext, - program: impl AsRef, - dir: impl AsRef, - args: &[impl AsRef], -) -> Result<(), Error> { - if context.verbose { - println!("{}", format_command(&program, &dir, args)); - } - - let output = Command::new(&program) - .env("RUSTFMT", &context.rustfmt_path) - .current_dir(&dir) - .args(args.iter()) - .output() - .unwrap(); - let success = output.status.success(); - - match (context.check, success) { - (_, true) => Ok(()), - (true, false) => Err(Error::CheckFailed), - (false, false) => { - let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); - Err(Error::CommandFailed( - format_command(&program, &dir, args), - String::from(stderr), - )) - }, - } -} - -fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> { - let mut args = vec!["fmt", "--all"]; - if context.check { - args.push("--check"); - } - exec_fmt_command(context, "cargo", path, &args) -} - -fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> { - let program = "rustfmt"; - let dir = std::env::current_dir()?; - let args = &["--version"]; - - if context.verbose { - println!("{}", format_command(program, &dir, args)); - } - - let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?; - - if output.status.success() { - Ok(()) - } else if std::str::from_utf8(&output.stderr) - .unwrap_or("") - .starts_with("error: 'rustfmt' is not installed") - { - Err(Error::RustfmtNotInstalled) - } else { - Err(Error::CommandFailed( - format_command(program, &dir, args), - std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), - )) - } -} - -fn rustfmt(context: &FmtContext, paths: impl Iterator) -> Result<(), Error> { - let mut args = Vec::new(); - if context.check { - args.push(OsString::from("--check")); - } - args.extend(paths); - exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args) -} diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index e237a05b2530..3361443196ab 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,4 +1,12 @@ -#![feature(rustc_private, if_let_guard, let_chains)] +#![feature( + rustc_private, + exit_status_error, + if_let_guard, + let_chains, + os_str_slice, + os_string_truncate, + slice_split_once +)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 5dce0be742b2..ebcd8611d78c 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -26,7 +26,7 @@ fn main() { allow_staged, allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), - DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), + DevCommand::Fmt { check } => fmt::run(&clippy, utils::UpdateMode::from_check(check)), DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), DevCommand::NewLint { pass, @@ -125,9 +125,6 @@ enum DevCommand { #[arg(long)] /// Use the rustfmt --check option check: bool, - #[arg(short, long)] - /// Echo commands run - verbose: bool, }, #[command(name = "update_lints")] /// Updates lint registration and information from the source code diff --git a/src/tools/clippy/clippy_dev/src/release.rs b/src/tools/clippy/clippy_dev/src/release.rs index d3b1a7ff3201..62c1bee81850 100644 --- a/src/tools/clippy/clippy_dev/src/release.rs +++ b/src/tools/clippy/clippy_dev/src/release.rs @@ -1,4 +1,4 @@ -use crate::utils::{FileUpdater, Version, update_text_region_fn}; +use crate::utils::{FileUpdater, UpdateStatus, Version, parse_cargo_package}; use std::fmt::Write; static CARGO_TOML_FILES: &[&str] = &[ @@ -13,15 +13,17 @@ pub fn bump_version(mut version: Version) { let mut updater = FileUpdater::default(); for file in CARGO_TOML_FILES { - updater.update_file( - file, - &mut update_text_region_fn( - "# begin autogenerated version\n", - "# end autogenerated version", - |dst| { - writeln!(dst, "version = \"{}\"", version.toml_display()).unwrap(); - }, - ), - ); + updater.update_file(file, &mut |_, src, dst| { + let package = parse_cargo_package(src); + if package.version_range.is_empty() { + dst.push_str(src); + UpdateStatus::Unchanged + } else { + dst.push_str(&src[..package.version_range.start]); + write!(dst, "\"{}\"", version.toml_display()).unwrap(); + dst.push_str(&src[package.version_range.end..]); + UpdateStatus::from_changed(src.get(package.version_range.clone()) != dst.get(package.version_range)) + } + }); } } diff --git a/src/tools/clippy/clippy_dev/src/rename_lint.rs b/src/tools/clippy/clippy_dev/src/rename_lint.rs index 9e7e5d97f021..be8b27c7a9e9 100644 --- a/src/tools/clippy/clippy_dev/src/rename_lint.rs +++ b/src/tools/clippy/clippy_dev/src/rename_lint.rs @@ -1,9 +1,11 @@ -use crate::update_lints::{ - DeprecatedLints, RenamedLint, find_lint_decls, gen_renamed_lints_test_fn, generate_lint_files, - read_deprecated_lints, +use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::utils::{ + FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, + try_rename_dir, try_rename_file, }; -use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, try_rename_file}; -use std::ffi::OsStr; +use rustc_lexer::TokenKind; +use std::ffi::OsString; +use std::fs; use std::path::Path; use walkdir::WalkDir; @@ -22,7 +24,7 @@ use walkdir::WalkDir; /// * If either lint name has a prefix /// * If `old_name` doesn't name an existing lint. /// * If `old_name` names a deprecated or renamed lint. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { if let Some((prefix, _)) = old_name.split_once("::") { panic!("`{old_name}` should not contain the `{prefix}` prefix"); @@ -33,162 +35,369 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b let mut updater = FileUpdater::default(); let mut lints = find_lint_decls(); - let DeprecatedLints { - renamed: mut renamed_lints, - deprecated: deprecated_lints, - file: mut deprecated_file, - contents: mut deprecated_contents, - renamed_end, - .. - } = read_deprecated_lints(); + let (deprecated_lints, mut renamed_lints) = read_deprecated_lints(); - let mut old_lint_index = None; - let mut found_new_name = false; - for (i, lint) in lints.iter().enumerate() { - if lint.name == old_name { - old_lint_index = Some(i); - } else if lint.name == new_name { - found_new_name = true; - } - } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); + let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else { + panic!("could not find lint `{old_name}`"); + }; + let lint = &lints[lint_idx]; - let lint = RenamedLint { - old_name: format!("clippy::{old_name}"), - new_name: if uplift { - new_name.into() - } else { - format!("clippy::{new_name}") - }, + let old_name_prefixed = String::from_iter(["clippy::", old_name]); + let new_name_prefixed = if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) }; - // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in - // case. - assert!( - !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{old_name}` has already been renamed" - ); - assert!( - !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{old_name}` has already been deprecated" - ); - - // Update all lint level attributes. (`clippy::lint_name`) - let replacements = &[(&*lint.old_name, &*lint.new_name)]; - let replacer = StringReplacer::new(replacements); - for file in WalkDir::new(".").into_iter().map(Result::unwrap).filter(|f| { - let name = f.path().file_name(); - let ext = f.path().extension(); - (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) - && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("deprecated_lints.rs")) - }) { - updater.update_file(file.path(), &mut replacer.replace_ident_fn()); + for lint in &mut renamed_lints { + if lint.new_name == old_name_prefixed { + lint.new_name.clone_from(&new_name_prefixed); + } + } + match renamed_lints.binary_search_by(|x| x.old_name.cmp(&old_name_prefixed)) { + Ok(_) => { + println!("`{old_name}` already has a rename registered"); + return; + }, + Err(idx) => { + renamed_lints.insert( + idx, + RenamedLint { + old_name: old_name_prefixed, + new_name: if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) + }, + version: clippy_version.rust_display().to_string(), + }, + ); + }, } - deprecated_contents.insert_str( - renamed_end as usize, - &format!( - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - clippy_version.rust_display(), - lint.old_name, - lint.new_name, - ), - ); - deprecated_file.replace_contents(deprecated_contents.as_bytes()); - drop(deprecated_file); + // Some tests are named `lint_name_suffix` which should also be renamed, + // but we can't do that if the renamed lint's name overlaps with another + // lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists. + let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); - renamed_lints.push(lint); - renamed_lints.sort_by(|lhs, rhs| { - lhs.new_name - .starts_with("clippy::") - .cmp(&rhs.new_name.starts_with("clippy::")) - .reverse() - .then_with(|| lhs.old_name.cmp(&rhs.old_name)) - }); + let mut mod_edit = ModEdit::None; + if uplift { + let is_unique_mod = lints[..lint_idx].iter().any(|l| l.module == lint.module) + || lints[lint_idx + 1..].iter().any(|l| l.module == lint.module); + if is_unique_mod { + if delete_file_if_exists(lint.path.as_ref()) { + mod_edit = ModEdit::Delete; + } + } else { + updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { + let mut start = &src[..lint.declaration_range.start]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + let mut end = &src[lint.declaration_range.end..]; + if end.starts_with("\n\n") { + end = &end[1..]; + } + dst.push_str(start); + dst.push_str(end); + UpdateStatus::Changed + }); + } + delete_test_files(old_name, change_prefixed_tests); + lints.remove(lint_idx); + } else if lints.binary_search_by(|x| x.name.as_str().cmp(new_name)).is_err() { + let lint = &mut lints[lint_idx]; + if lint.module.ends_with(old_name) + && lint + .path + .file_stem() + .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) + { + let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + new_path.push(".rs"); + if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + mod_edit = ModEdit::Rename; + } + + let mod_len = lint.module.len(); + lint.module.truncate(mod_len - old_name.len()); + lint.module.push_str(new_name); + } + rename_test_files(old_name, new_name, change_prefixed_tests); + new_name.clone_into(&mut lints[lint_idx].name); + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Since `{new_name}` already exists the existing code has not been changed"); + return; + } + + let mut update_fn = file_update_fn(old_name, new_name, mod_edit); + for file in WalkDir::new(".").into_iter().filter_entry(|e| { + // Skip traversing some of the larger directories. + e.path() + .as_os_str() + .as_encoded_bytes() + .get(2..) + .is_none_or(|x| x != "target".as_bytes() && x != ".git".as_bytes()) + }) { + let file = file.expect("error reading clippy directory"); + if file.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { + updater.update_file(file.path(), &mut update_fn); + } + } + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); if uplift { - updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); - println!( - "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." - ); - } else if found_new_name { - updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); - println!( - "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." - ); - } else { - // Rename the lint struct and source files sharing a name with the lint. - let lint = &mut lints[old_lint_index]; - let old_name_upper = old_name.to_uppercase(); - let new_name_upper = new_name.to_uppercase(); - lint.name = new_name.into(); - - // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. - if try_rename_file( - Path::new(&format!("tests/ui/{old_name}.rs")), - Path::new(&format!("tests/ui/{new_name}.rs")), - ) { - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.stderr")), - Path::new(&format!("tests/ui/{new_name}.stderr")), - ); - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.fixed")), - Path::new(&format!("tests/ui/{new_name}.fixed")), - ); - } - - // Try to rename the file containing the lint if the file name matches the lint's name. - let replacements; - let replacements = if lint.module == old_name - && try_rename_file( - Path::new(&format!("clippy_lints/src/{old_name}.rs")), - Path::new(&format!("clippy_lints/src/{new_name}.rs")), - ) { - // Edit the module name in the lint list. Note there could be multiple lints. - for lint in lints.iter_mut().filter(|l| l.module == old_name) { - lint.module = new_name.into(); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else if !lint.module.contains("::") - // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` - && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), - Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), - ) - { - // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{old_name}", lint.module); - for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{new_name}", lint.module); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() + println!("Uplifted `clippy::{old_name}` as `{new_name}`"); + if matches!(mod_edit, ModEdit::None) { + println!("Only the rename has been registered, the code will need to be edited manually"); } else { - replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; - &replacements[0..1] - }; + println!("All the lint's code has been deleted"); + println!("Make sure to inspect the results as some things may have been missed"); + } + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("All code referencing the old name has been updated"); + println!("Make sure to inspect the results as some things may have been missed"); + } + println!("note: `cargo uibless` still needs to be run to update the test results"); +} - // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being - // renamed. - let replacer = StringReplacer::new(replacements); - for file in WalkDir::new("clippy_lints/src") { - let file = file.expect("error reading `clippy_lints/src`"); - if file - .path() - .as_os_str() - .to_str() - .is_some_and(|x| x.ends_with("*.rs") && x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") - { - updater.update_file(file.path(), &mut replacer.replace_ident_fn()); +#[derive(Clone, Copy)] +enum ModEdit { + None, + Delete, + Rename, +} + +fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { + let e = e.expect("error reading `tests/ui`"); + let name = e.file_name(); + if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') { + if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) { + dst.push((name, true)); + } + } else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len()) + { + dst.push((name, false)); + } + } +} + +fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + if rename_prefixed { + for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { + let e = e.expect("error reading `tests/ui-toml`"); + let name = e.file_name(); + if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) { + dst.push((name, false)); } } + } else { + dst.push((lint.into(), false)); + } +} - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{old_name} has been successfully renamed"); +/// Renames all test files for the given lint. +/// +/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix. +fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut old_buf = OsString::from("tests/ui/"); + let mut new_buf = OsString::from("tests/ui/"); + collect_ui_test_names(old_name, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + if is_file { + try_rename_file(old_buf.as_ref(), new_buf.as_ref()); + } else { + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + } + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); } - println!("note: `cargo uitest` still needs to be run to update the test results"); + tests.clear(); + old_buf.truncate("tests/ui".len()); + new_buf.truncate("tests/ui".len()); + old_buf.push("-toml/"); + new_buf.push("-toml/"); + collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests); + for (name, _) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } +} + +fn delete_test_files(lint: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut buf = OsString::from("tests/ui/"); + collect_ui_test_names(lint, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + buf.push(name); + if is_file { + delete_file_if_exists(buf.as_ref()); + } else { + delete_dir_if_exists(buf.as_ref()); + } + buf.truncate("tests/ui/".len()); + } + + buf.truncate("tests/ui".len()); + buf.push("-toml/"); + + tests.clear(); + collect_ui_toml_test_names(lint, rename_prefixed, &mut tests); + for (name, _) in &tests { + buf.push(name); + delete_dir_if_exists(buf.as_ref()); + buf.truncate("tests/ui/".len()); + } +} + +fn snake_to_pascal(s: &str) -> String { + let mut dst = Vec::with_capacity(s.len()); + let mut iter = s.bytes(); + || -> Option<()> { + dst.push(iter.next()?.to_ascii_uppercase()); + while let Some(c) = iter.next() { + if c == b'_' { + dst.push(iter.next()?.to_ascii_uppercase()); + } else { + dst.push(c); + } + } + Some(()) + }(); + String::from_utf8(dst).unwrap() +} + +#[expect(clippy::too_many_lines)] +fn file_update_fn<'a, 'b>( + old_name: &'a str, + new_name: &'b str, + mod_edit: ModEdit, +) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus { + let old_name_pascal = snake_to_pascal(old_name); + let new_name_pascal = snake_to_pascal(new_name); + let old_name_upper = old_name.to_ascii_uppercase(); + let new_name_upper = new_name.to_ascii_uppercase(); + move |_, src, dst| { + let mut copy_pos = 0u32; + let mut changed = false; + let mut searcher = RustSearcher::new(src); + let mut capture = ""; + loop { + match searcher.peek() { + TokenKind::Eof => break, + TokenKind::Ident => { + let match_start = searcher.pos(); + let text = searcher.peek_text(); + searcher.step(); + match text { + // clippy::line_name or clippy::lint-name + "clippy" => { + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + } + }, + // mod lint_name + "mod" => { + if !matches!(mod_edit, ModEdit::None) + && searcher.match_tokens(&[Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + match mod_edit { + ModEdit::Rename => { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + ModEdit::Delete if searcher.match_tokens(&[Token::Semi], &mut []) => { + let mut start = &src[copy_pos as usize..match_start as usize]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + dst.push_str(start); + copy_pos = searcher.pos(); + if src[copy_pos as usize..].starts_with("\n\n") { + copy_pos += 1; + } + changed = true; + }, + ModEdit::Delete | ModEdit::None => {}, + } + } + }, + // lint_name:: + name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { + let name_end = searcher.pos(); + if searcher.match_tokens(&[Token::DoubleColon], &mut []) { + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(new_name); + copy_pos = name_end; + changed = true; + } + }, + // LINT_NAME or LintName + name => { + let replacement = if name == old_name_upper { + &new_name_upper + } else if name == old_name_pascal { + &new_name_pascal + } else { + continue; + }; + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(replacement); + copy_pos = searcher.pos(); + changed = true; + }, + } + }, + // //~ lint_name + TokenKind::LineComment { doc_style: None } => { + let text = searcher.peek_text(); + if text.starts_with("//~") + && let Some(text) = text.strip_suffix(old_name) + && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize + text.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos() + searcher.peek_len(); + changed = true; + } + searcher.step(); + }, + // ::lint_name + TokenKind::Colon + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name => + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + _ => searcher.step(), + } + } + + dst.push_str(&src[copy_pos as usize..]); + UpdateStatus::from_changed(changed) + } } diff --git a/src/tools/clippy/clippy_dev/src/sync.rs b/src/tools/clippy/clippy_dev/src/sync.rs index c699b0d7b959..98fd72fc0bd1 100644 --- a/src/tools/clippy/clippy_dev/src/sync.rs +++ b/src/tools/clippy/clippy_dev/src/sync.rs @@ -4,15 +4,22 @@ use std::fmt::Write; pub fn update_nightly() { let date = Utc::now().format("%Y-%m-%d").to_string(); - let update = &mut update_text_region_fn( + let toolchain_update = &mut update_text_region_fn( "# begin autogenerated nightly\n", "# end autogenerated nightly", |dst| { writeln!(dst, "channel = \"nightly-{date}\"").unwrap(); }, ); + let readme_update = &mut update_text_region_fn( + "\n", + "", + |dst| { + writeln!(dst, "```\nnightly-{date}\n```").unwrap(); + }, + ); let mut updater = FileUpdater::default(); - updater.update_file("rust-toolchain.toml", update); - updater.update_file("clippy_utils/README.md", update); + updater.update_file("rust-toolchain.toml", toolchain_update); + updater.update_file("clippy_utils/README.md", readme_update); } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 0c861b729356..25ba2c720490 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,12 +1,11 @@ use crate::utils::{ - File, FileAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_file, update_text_region_fn, + ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_action, update_text_region_fn, }; use itertools::Itertools; use std::collections::HashSet; use std::fmt::Write; -use std::fs::OpenOptions; use std::ops::Range; -use std::path::Path; +use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ @@ -26,12 +25,11 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// Panics if a file path could not read from or then written to pub fn update(update_mode: UpdateMode) { let lints = find_lint_decls(); - let DeprecatedLints { - renamed, deprecated, .. - } = read_deprecated_lints(); + let (deprecated, renamed) = read_deprecated_lints(); generate_lint_files(update_mode, &lints, &deprecated, &renamed); } +#[expect(clippy::too_many_lines)] pub fn generate_lint_files( update_mode: UpdateMode, lints: &[Lint], @@ -93,6 +91,40 @@ pub fn generate_lint_files( dst.push_str("];\n"); UpdateStatus::from_changed(src != dst) }), + ("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| { + let mut searcher = RustSearcher::new(src); + assert!( + searcher.find_token(Token::Ident("declare_with_version")) + && searcher.find_token(Token::Ident("declare_with_version")), + "error reading deprecated lints" + ); + dst.push_str(&src[..searcher.pos() as usize]); + dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); + for lint in deprecated { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.name, lint.reason, + ) + .unwrap(); + } + dst.push_str( + "]}\n\n\ + #[rustfmt::skip]\n\ + declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ + ", + ); + for lint in renamed { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.old_name, lint.new_name, + ) + .unwrap(); + } + dst.push_str("]}\n"); + UpdateStatus::from_changed(src != dst) + }), ("tests/ui/deprecated.rs", &mut |_, src, dst| { dst.push_str(GENERATED_FILE_COMMENT); for lint in deprecated { @@ -101,7 +133,24 @@ pub fn generate_lint_files( dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) }), - ("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(renamed)), + ("tests/ui/rename.rs", &mut move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for lint in renamed { + if seen_lints.insert(&lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in renamed { + if seen_lints.insert(&lint.old_name) { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + } + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), ], ); } @@ -111,44 +160,25 @@ fn round_to_fifty(count: usize) -> usize { } /// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug)] pub struct Lint { pub name: String, pub group: String, pub module: String, + pub path: PathBuf, pub declaration_range: Range, } -#[derive(Clone, PartialEq, Eq, Debug)] pub struct DeprecatedLint { pub name: String, pub reason: String, + pub version: String, } pub struct RenamedLint { pub old_name: String, pub new_name: String, -} - -pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { - move |_, src, dst| { - let mut seen_lints = HashSet::new(); - dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in lints { - if seen_lints.insert(&lint.new_name) { - writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); - } - } - seen_lints.clear(); - for lint in lints { - if seen_lints.insert(&lint.old_name) { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); - } - } - dst.push_str("\nfn main() {}\n"); - UpdateStatus::from_changed(src != dst) - } + pub version: String, } /// Finds all lint declarations (`declare_clippy_lint!`) @@ -158,6 +188,7 @@ pub fn find_lint_decls() -> Vec { let mut contents = String::new(); for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { parse_clippy_lint_decls( + file.path(), File::open_read_to_cleared_string(file.path(), &mut contents), &module, &mut lints, @@ -172,7 +203,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator e, - Err(ref e) => panic_file(e, FileAction::Read, src_root), + Err(ref e) => panic_action(e, ErrAction::Read, src_root), }; let path = e.path().as_os_str().as_encoded_bytes(); if let Some(path) = path.strip_suffix(b".rs") @@ -202,17 +233,17 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator) { +fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec) { #[allow(clippy::enum_glob_use)] use Token::*; #[rustfmt::skip] - static DECL_TOKENS: &[Token] = &[ + static DECL_TOKENS: &[Token<'_>] = &[ // !{ /// docs - Bang, OpenBrace, AnyDoc, + Bang, OpenBrace, AnyComment, // #[clippy::version = "version"] Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, // pub NAME, GROUP, - Ident("pub"), CaptureIdent, Comma, CaptureIdent, Comma, + Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, ]; let mut searcher = RustSearcher::new(contents); @@ -224,55 +255,42 @@ fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec) name: name.to_lowercase(), group: group.into(), module: module.into(), + path: path.into(), declaration_range: start..searcher.pos() as usize, }); } } } -pub struct DeprecatedLints { - pub file: File<'static>, - pub contents: String, - pub deprecated: Vec, - pub renamed: Vec, - pub deprecated_end: u32, - pub renamed_end: u32, -} - #[must_use] -pub fn read_deprecated_lints() -> DeprecatedLints { +pub fn read_deprecated_lints() -> (Vec, Vec) { #[allow(clippy::enum_glob_use)] use Token::*; #[rustfmt::skip] - static DECL_TOKENS: &[Token] = &[ + static DECL_TOKENS: &[Token<'_>] = &[ // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, // ("first", "second"), OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, ]; #[rustfmt::skip] - static DEPRECATED_TOKENS: &[Token] = &[ + static DEPRECATED_TOKENS: &[Token<'_>] = &[ // !{ DEPRECATED(DEPRECATED_VERSION) = [ Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, ]; #[rustfmt::skip] - static RENAMED_TOKENS: &[Token] = &[ + static RENAMED_TOKENS: &[Token<'_>] = &[ // !{ RENAMED(RENAMED_VERSION) = [ Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, ]; let path = "clippy_lints/src/deprecated_lints.rs"; - let mut res = DeprecatedLints { - file: File::open(path, OpenOptions::new().read(true).write(true)), - contents: String::new(), - deprecated: Vec::with_capacity(30), - renamed: Vec::with_capacity(80), - deprecated_end: 0, - renamed_end: 0, - }; + let mut deprecated = Vec::with_capacity(30); + let mut renamed = Vec::with_capacity(80); + let mut contents = String::new(); + File::open_read_to_cleared_string(path, &mut contents); - res.file.read_append_to_string(&mut res.contents); - let mut searcher = RustSearcher::new(&res.contents); + let mut searcher = RustSearcher::new(&contents); // First instance is the macro definition. assert!( @@ -281,36 +299,38 @@ pub fn read_deprecated_lints() -> DeprecatedLints { ); if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + let mut version = ""; let mut name = ""; let mut reason = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut reason]) { - res.deprecated.push(DeprecatedLint { + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { + deprecated.push(DeprecatedLint { name: parse_str_single_line(path.as_ref(), name), reason: parse_str_single_line(path.as_ref(), reason), + version: parse_str_single_line(path.as_ref(), version), }); } } else { panic!("error reading deprecated lints"); } - // position of the closing `]}` of `declare_with_version` - res.deprecated_end = searcher.pos(); if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + let mut version = ""; let mut old_name = ""; let mut new_name = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut old_name, &mut new_name]) { - res.renamed.push(RenamedLint { + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { + renamed.push(RenamedLint { old_name: parse_str_single_line(path.as_ref(), old_name), new_name: parse_str_single_line(path.as_ref(), new_name), + version: parse_str_single_line(path.as_ref(), version), }); } } else { panic!("error reading renamed lints"); } - // position of the closing `]}` of `declare_with_version` - res.renamed_end = searcher.pos(); - res + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); + (deprecated, renamed) } /// Removes the line splices and surrounding quotes from a string literal @@ -366,7 +386,7 @@ mod tests { } "#; let mut result = Vec::new(); - parse_clippy_lint_decls(CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } @@ -376,12 +396,14 @@ mod tests { name: "ptr_arg".into(), group: "style".into(), module: "module_name".into(), + path: PathBuf::new(), declaration_range: Range::default(), }, Lint { name: "doc_markdown".into(), group: "pedantic".into(), module: "module_name".into(), + path: PathBuf::new(), declaration_range: Range::default(), }, ]; diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index ae2eabc45dd0..255e36afe69c 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -1,13 +1,14 @@ -use aho_corasick::{AhoCorasick, AhoCorasickBuilder}; use core::fmt::{self, Display}; +use core::ops::Range; use core::slice; use core::str::FromStr; use rustc_lexer::{self as lexer, FrontmatterAllowed}; use std::env; +use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; -use std::process::{self, ExitStatus}; +use std::process::{self, Command, ExitStatus, Stdio}; #[cfg(not(windows))] static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; @@ -15,14 +16,16 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; #[derive(Clone, Copy)] -pub enum FileAction { +pub enum ErrAction { Open, Read, Write, Create, Rename, + Delete, + Run, } -impl FileAction { +impl ErrAction { fn as_str(self) -> &'static str { match self { Self::Open => "opening", @@ -30,13 +33,15 @@ impl FileAction { Self::Write => "writing", Self::Create => "creating", Self::Rename => "renaming", + Self::Delete => "deleting", + Self::Run => "running", } } } #[cold] #[track_caller] -pub fn panic_file(err: &impl Display, action: FileAction, path: &Path) -> ! { +pub fn panic_action(err: &impl Display, action: ErrAction, path: &Path) -> ! { panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) } @@ -52,7 +57,7 @@ impl<'a> File<'a> { let path = path.as_ref(); match options.open(path) { Ok(inner) => Self { inner, path }, - Err(e) => panic_file(&e, FileAction::Open, path), + Err(e) => panic_action(&e, ErrAction::Open, path), } } @@ -63,7 +68,7 @@ impl<'a> File<'a> { match options.open(path) { Ok(inner) => Some(Self { inner, path }), Err(e) if e.kind() == io::ErrorKind::NotFound => None, - Err(e) => panic_file(&e, FileAction::Open, path), + Err(e) => panic_action(&e, ErrAction::Open, path), } } @@ -81,7 +86,7 @@ impl<'a> File<'a> { pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { match self.inner.read_to_string(dst) { Ok(_) => {}, - Err(e) => panic_file(&e, FileAction::Read, self.path), + Err(e) => panic_action(&e, ErrAction::Read, self.path), } dst } @@ -103,7 +108,7 @@ impl<'a> File<'a> { Err(e) => Err(e), }; if let Err(e) = res { - panic_file(&e, FileAction::Write, self.path); + panic_action(&e, ErrAction::Write, self.path); } } } @@ -165,9 +170,83 @@ impl Version { } } +enum TomlPart<'a> { + Table(&'a str), + Value(&'a str, &'a str), +} + +fn toml_iter(s: &str) -> impl Iterator)> { + let mut pos = 0; + s.split('\n') + .map(move |s| { + let x = pos; + pos += s.len() + 1; + (x, s) + }) + .filter_map(|(pos, s)| { + if let Some(s) = s.strip_prefix('[') { + s.split_once(']').map(|(name, _)| (pos, TomlPart::Table(name))) + } else if matches!(s.bytes().next(), Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')) { + s.split_once('=').map(|(key, value)| (pos, TomlPart::Value(key, value))) + } else { + None + } + }) +} + +pub struct CargoPackage<'a> { + pub name: &'a str, + pub version_range: Range, + pub not_a_platform_range: Range, +} + +#[must_use] +pub fn parse_cargo_package(s: &str) -> CargoPackage<'_> { + let mut in_package = false; + let mut in_platform_deps = false; + let mut name = ""; + let mut version_range = 0..0; + let mut not_a_platform_range = 0..0; + for (offset, part) in toml_iter(s) { + match part { + TomlPart::Table(name) => { + if in_platform_deps { + not_a_platform_range.end = offset; + } + in_package = false; + in_platform_deps = false; + + match name.trim() { + "package" => in_package = true, + "target.'cfg(NOT_A_PLATFORM)'.dependencies" => { + in_platform_deps = true; + not_a_platform_range.start = offset; + }, + _ => {}, + } + }, + TomlPart::Value(key, value) if in_package => match key.trim_end() { + "name" => name = value.trim(), + "version" => { + version_range.start = offset + (value.len() - value.trim().len()) + key.len() + 1; + version_range.end = offset + key.len() + value.trim_end().len() + 1; + }, + _ => {}, + }, + TomlPart::Value(..) => {}, + } + } + CargoPackage { + name, + version_range, + not_a_platform_range, + } +} + pub struct ClippyInfo { pub path: PathBuf, pub version: Version, + pub has_intellij_hook: bool, } impl ClippyInfo { #[must_use] @@ -177,35 +256,21 @@ impl ClippyInfo { loop { path.push("Cargo.toml"); if let Some(mut file) = File::open_if_exists(&path, OpenOptions::new().read(true)) { - let mut in_package = false; - let mut is_clippy = false; - let mut version: Option = None; - - // Ad-hoc parsing to avoid dependencies. We control all the file so this - // isn't actually a problem - for line in file.read_to_cleared_string(&mut buf).lines() { - if line.starts_with('[') { - in_package = line.starts_with("[package]"); - } else if in_package && let Some((name, value)) = line.split_once('=') { - match name.trim() { - "name" => is_clippy = value.trim() == "\"clippy\"", - "version" - if let Some(value) = value.trim().strip_prefix('"') - && let Some(value) = value.strip_suffix('"') => - { - version = value.parse().ok(); - }, - _ => {}, - } + file.read_to_cleared_string(&mut buf); + let package = parse_cargo_package(&buf); + if package.name == "\"clippy\"" { + if let Some(version) = buf[package.version_range].strip_prefix('"') + && let Some(version) = version.strip_suffix('"') + && let Ok(version) = version.parse() + { + path.pop(); + return ClippyInfo { + path, + version, + has_intellij_hook: !package.not_a_platform_range.is_empty(), + }; } - } - - if is_clippy { - let Some(version) = version else { - panic!("error reading clippy version from {}", file.path.display()); - }; - path.pop(); - return ClippyInfo { path, version }; + panic!("error reading clippy version from `{}`", file.path.display()); } } @@ -258,6 +323,11 @@ impl UpdateMode { pub fn from_check(check: bool) -> Self { if check { Self::Check } else { Self::Change } } + + #[must_use] + pub fn is_check(self) -> bool { + matches!(self, Self::Check) + } } #[derive(Default)] @@ -366,53 +436,11 @@ pub fn update_text_region_fn( move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) } -#[must_use] -pub fn is_ident_char(c: u8) -> bool { - matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') -} - -pub struct StringReplacer<'a> { - searcher: AhoCorasick, - replacements: &'a [(&'a str, &'a str)], -} -impl<'a> StringReplacer<'a> { - #[must_use] - pub fn new(replacements: &'a [(&'a str, &'a str)]) -> Self { - Self { - searcher: AhoCorasickBuilder::new() - .match_kind(aho_corasick::MatchKind::LeftmostLongest) - .build(replacements.iter().map(|&(x, _)| x)) - .unwrap(), - replacements, - } - } - - /// Replace substrings if they aren't bordered by identifier characters. - pub fn replace_ident_fn(&self) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { - move |_, src, dst| { - let mut pos = 0; - let mut changed = false; - for m in self.searcher.find_iter(src) { - if !is_ident_char(src.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(src.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - changed = true; - dst.push_str(&src[pos..m.start()]); - dst.push_str(self.replacements[m.pattern()].1); - pos = m.end(); - } - } - dst.push_str(&src[pos..]); - UpdateStatus::from_changed(changed) - } - } -} - #[derive(Clone, Copy)] -pub enum Token { - /// Matches any number of doc comments. - AnyDoc, - Ident(&'static str), +pub enum Token<'a> { + /// Matches any number of comments / doc comments. + AnyComment, + Ident(&'a str), CaptureIdent, LitStr, CaptureLitStr, @@ -431,29 +459,26 @@ pub enum Token { OpenBracket, OpenParen, Pound, + Semi, + Slash, } pub struct RustSearcher<'txt> { text: &'txt str, cursor: lexer::Cursor<'txt>, pos: u32, - - // Either the next token or a zero-sized whitespace sentinel. next_token: lexer::Token, } impl<'txt> RustSearcher<'txt> { #[must_use] + #[expect(clippy::inconsistent_struct_constructor)] pub fn new(text: &'txt str) -> Self { + let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); Self { text, - cursor: lexer::Cursor::new(text, FrontmatterAllowed::Yes), pos: 0, - - // Sentinel value indicating there is no read token. - next_token: lexer::Token { - len: 0, - kind: lexer::TokenKind::Whitespace, - }, + next_token: cursor.advance_token(), + cursor, } } @@ -462,6 +487,11 @@ impl<'txt> RustSearcher<'txt> { &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] } + #[must_use] + pub fn peek_len(&self) -> u32 { + self.next_token.len + } + #[must_use] pub fn peek(&self) -> lexer::TokenKind { self.next_token.kind @@ -485,37 +515,15 @@ impl<'txt> RustSearcher<'txt> { /// Consumes the next token if it matches the requested value and captures the value if /// requested. Returns true if a token was matched. - fn read_token(&mut self, token: Token, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + fn read_token(&mut self, token: Token<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { loop { match (token, self.next_token.kind) { - // Has to be the first match arm so the empty sentinel token will be handled. - // This will also skip all whitespace/comments preceding any tokens. - ( - _, - lexer::TokenKind::Whitespace - | lexer::TokenKind::LineComment { doc_style: None } - | lexer::TokenKind::BlockComment { - doc_style: None, - terminated: true, - }, - ) => { - self.step(); - if self.at_end() { - // `AnyDoc` always matches. - return matches!(token, Token::AnyDoc); - } - }, - ( - Token::AnyDoc, + (_, lexer::TokenKind::Whitespace) + | ( + Token::AnyComment, lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, - ) => { - self.step(); - if self.at_end() { - // `AnyDoc` always matches. - return true; - } - }, - (Token::AnyDoc, _) => return true, + ) => self.step(), + (Token::AnyComment, _) => return true, (Token::Bang, lexer::TokenKind::Bang) | (Token::CloseBrace, lexer::TokenKind::CloseBrace) | (Token::CloseBracket, lexer::TokenKind::CloseBracket) @@ -529,6 +537,8 @@ impl<'txt> RustSearcher<'txt> { | (Token::OpenBracket, lexer::TokenKind::OpenBracket) | (Token::OpenParen, lexer::TokenKind::OpenParen) | (Token::Pound, lexer::TokenKind::Pound) + | (Token::Semi, lexer::TokenKind::Semi) + | (Token::Slash, lexer::TokenKind::Slash) | ( Token::LitStr, lexer::TokenKind::Literal { @@ -569,7 +579,7 @@ impl<'txt> RustSearcher<'txt> { } #[must_use] - pub fn find_token(&mut self, token: Token) -> bool { + pub fn find_token(&mut self, token: Token<'_>) -> bool { let mut capture = [].iter_mut(); while !self.read_token(token, &mut capture) { self.step(); @@ -581,7 +591,7 @@ impl<'txt> RustSearcher<'txt> { } #[must_use] - pub fn find_capture_token(&mut self, token: Token) -> Option<&'txt str> { + pub fn find_capture_token(&mut self, token: Token<'_>) -> Option<&'txt str> { let mut res = ""; let mut capture = &mut res; let mut capture = slice::from_mut(&mut capture).iter_mut(); @@ -595,7 +605,7 @@ impl<'txt> RustSearcher<'txt> { } #[must_use] - pub fn match_tokens(&mut self, tokens: &[Token], captures: &mut [&mut &'txt str]) -> bool { + pub fn match_tokens(&mut self, tokens: &[Token<'_>], captures: &mut [&mut &'txt str]) -> bool { let mut captures = captures.iter_mut(); tokens.iter().all(|&t| self.read_token(t, &mut captures)) } @@ -606,21 +616,107 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_file(&e, FileAction::Create, new_name), + Err(ref e) => panic_action(e, ErrAction::Create, new_name), } match fs::rename(old_name, new_name) { Ok(()) => true, - Err(e) => { + Err(ref e) => { drop(fs::remove_file(new_name)); - if e.kind() == io::ErrorKind::NotFound { + // `NotADirectory` happens on posix when renaming a directory to an existing file. + // Windows will ignore this and rename anyways. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { false } else { - panic_file(&e, FileAction::Rename, old_name); + panic_action(e, ErrAction::Rename, old_name); + } + }, + } +} + +#[expect(clippy::must_use_candidate)] +pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { + match fs::create_dir(new_name) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(ref e) => panic_action(e, ErrAction::Create, new_name), + } + // Windows can't reliably rename to an empty directory. + #[cfg(windows)] + drop(fs::remove_dir(new_name)); + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(ref e) => { + // Already dropped earlier on windows. + #[cfg(not(windows))] + drop(fs::remove_dir(new_name)); + // `NotADirectory` happens on posix when renaming a file to an existing directory. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { + false + } else { + panic_action(e, ErrAction::Rename, old_name); } }, } } pub fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_file(&e, FileAction::Write, path)); + fs::write(path, contents).unwrap_or_else(|e| panic_action(&e, ErrAction::Write, path)); +} + +#[must_use] +pub fn run_with_output(path: &(impl AsRef + ?Sized), cmd: &mut Command) -> Vec { + fn f(path: &Path, cmd: &mut Command) -> Vec { + match cmd + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .output() + { + Ok(x) => match x.status.exit_ok() { + Ok(()) => x.stdout, + Err(ref e) => panic_action(e, ErrAction::Run, path), + }, + Err(ref e) => panic_action(e, ErrAction::Run, path), + } + } + f(path.as_ref(), cmd) +} + +pub fn run_with_args_split( + mut make_cmd: impl FnMut() -> Command, + mut run_cmd: impl FnMut(&mut Command), + args: impl Iterator>, +) { + let mut cmd = make_cmd(); + let mut len = 0; + for arg in args { + len += arg.as_ref().len(); + cmd.arg(arg); + // Very conservative limit + if len > 10000 { + run_cmd(&mut cmd); + cmd = make_cmd(); + len = 0; + } + } + if len != 0 { + run_cmd(&mut cmd); + } +} + +#[expect(clippy::must_use_candidate)] +pub fn delete_file_if_exists(path: &Path) -> bool { + match fs::remove_file(path) { + Ok(()) => true, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::IsADirectory) => false, + Err(ref e) => panic_action(e, ErrAction::Delete, path), + } +} + +pub fn delete_dir_if_exists(path: &Path) { + match fs::remove_dir_all(path) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) => {}, + Err(ref e) => panic_action(e, ErrAction::Delete, path), + } } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 7e3cb4042479..39e4e2e365ea 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_lints" -# begin autogenerated version version = "0.1.89" -# end autogenerated version 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/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 9ae746c13b26..852e48cbcaee 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; -use rustc_attr_parsing::RustcVersion; +use rustc_attr_data_structures::RustcVersion; use rustc_hir::{HirId, Lit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 272444475c0c..59a0c7c88684 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -6,9 +6,10 @@ use clippy_config::types::{ }; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::is_cfg_test; +use rustc_attr_data_structures::AttributeKind; use rustc_hir::{ - AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, Variant, - VariantData, + AssocItemKind, Attribute, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, + Variant, VariantData, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -28,6 +29,11 @@ declare_clippy_lint! { /// implemented in the code. Sometimes this will be referred to as /// "bikeshedding". /// + /// The content of items with a representation clause attribute, such as + /// `#[repr(C)]` will not be checked, as the order of their fields or + /// variants might be dictated by an external API (application binary + /// interface). + /// /// ### Default Ordering and Configuration /// /// As there is no generally applicable rule, and each project may have @@ -256,8 +262,17 @@ impl ArbitrarySourceItemOrdering { impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if cx + .tcx + .hir_attrs(item.hir_id()) + .iter() + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr(..)))) + { + // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API. + return; + } match &item.kind { - ItemKind::Enum(_, enum_def, _generics) if self.enable_ordering_for_enum => { + ItemKind::Enum(_, _generics, enum_def) if self.enable_ordering_for_enum => { let mut cur_v: Option<&Variant<'_>> = None; for variant in enum_def.variants { if variant.span.in_external_macro(cx.sess().source_map()) { @@ -273,7 +288,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { cur_v = Some(variant); } }, - ItemKind::Struct(_, VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => { + ItemKind::Struct(_, _generics, VariantData::Struct { fields, .. }) if self.enable_ordering_for_struct => { let mut cur_f: Option<&FieldDef<'_>> = None; for field in *fields { if field.span.in_external_macro(cx.sess().source_map()) { diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs index 2643f850879c..9e09fb5bb439 100644 --- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs +++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs @@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { - !matches!(arg.unpack(), GenericArgKind::Type(ty) if matches!(ty.kind(), ty::Param(_))) + !matches!(arg.kind(), GenericArgKind::Type(ty) if matches!(ty.kind(), ty::Param(_))) }) && let Some(send) = cx.tcx.get_diagnostic_item(sym::Send) && let Some(sync) = cx.tcx.lang_items().sync_trait() diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index c073dee855e2..6f2a6a36a38b 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -3,14 +3,13 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{is_expr_final_block_expr, path_res}; +use clippy_utils::{is_expr_final_block_expr, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; 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::sym; declare_clippy_lint! { /// ### What it does @@ -68,11 +67,11 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { return; } } - let (message, replacement) = match method_segment.ident.as_str() { - "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => { + let (message, replacement) = match method_segment.ident.name { + sym::is_ok if type_suitable_to_unwrap(cx, args.type_at(1)) => { ("called `assert!` with `Result::is_ok`", "unwrap") }, - "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => { + sym::is_err if type_suitable_to_unwrap(cx, args.type_at(0)) => { ("called `assert!` with `Result::is_err`", "unwrap_err") }, _ => return, diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs index df01c7fde181..05d8a8c26d1c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr}; +use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr}; use rustc_hir::Attribute; use rustc_lint::LateContext; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index bc6ba84772b3..7c6fd91ca67f 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_ast::ast::LitKind; -use rustc_attr_parsing::RustcVersion; +use rustc_attr_data_structures::RustcVersion; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index e92879b853d7..4120e5c8cb7d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -56,7 +56,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b if signed { return nbits; } - let max_bits = if method.ident.as_str() == "min" { + let max_bits = if method.ident.name == sym::min { get_constant_bits(cx, right) } else { None @@ -64,7 +64,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX)) }, ExprKind::MethodCall(method, _, [lo, hi], _) => { - if method.ident.as_str() == "clamp" + if method.ident.name == sym::clamp //FIXME: make this a diagnostic item && let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index c8abf9dac9af..9a1ad8a74738 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -4,10 +4,11 @@ use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{method_chain_args, sext}; +use clippy_utils::{method_chain_args, sext, sym}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::Symbol; use super::CAST_SIGN_LOSS; @@ -16,24 +17,24 @@ use super::CAST_SIGN_LOSS; /// /// Methods that can overflow and return a negative value must not be included in this list, /// because casting their return values can still result in sign loss. -const METHODS_RET_POSITIVE: &[&str] = &[ - "checked_abs", - "saturating_abs", - "isqrt", - "checked_isqrt", - "rem_euclid", - "checked_rem_euclid", - "wrapping_rem_euclid", +const METHODS_RET_POSITIVE: &[Symbol] = &[ + sym::checked_abs, + sym::saturating_abs, + sym::isqrt, + sym::checked_isqrt, + sym::rem_euclid, + sym::checked_rem_euclid, + sym::wrapping_rem_euclid, ]; /// A list of methods that act like `pow()`. See `pow_call_result_sign()` for details. /// /// Methods that can overflow and return a negative value must not be included in this list, /// because casting their return values can still result in sign loss. -const METHODS_POW: &[&str] = &["pow", "saturating_pow", "checked_pow"]; +const METHODS_POW: &[Symbol] = &[sym::pow, sym::saturating_pow, sym::checked_pow]; /// A list of methods that act like `unwrap()`, and don't change the sign of the inner value. -const METHODS_UNWRAP: &[&str] = &["unwrap", "unwrap_unchecked", "expect", "into_ok"]; +const METHODS_UNWRAP: &[Symbol] = &[sym::unwrap, sym::unwrap_unchecked, sym::expect, sym::into_ok]; pub(super) fn check<'cx>( cx: &LateContext<'cx>, @@ -129,7 +130,7 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i // Calling on methods that always return non-negative values. if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind { - let mut method_name = path.ident.name.as_str(); + let mut method_name = path.ident.name; // Peel unwrap(), expect(), etc. while let Some(&found_name) = METHODS_UNWRAP.iter().find(|&name| &method_name == name) @@ -138,7 +139,7 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i { // The original type has changed, but we can't use `ty` here anyway, because it has been // moved. - method_name = inner_path.ident.name.as_str(); + method_name = inner_path.ident.name; expr = recv; } diff --git a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs index 31cdd078f45a..769cc120c950 100644 --- a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::Symbol; use rustc_span::def_id::DefId; -use rustc_span::{Symbol, sym}; use super::CONFUSING_METHOD_TO_NUMERIC_CAST; @@ -25,7 +26,6 @@ fn get_const_name_and_ty_name( method_def_id: DefId, generics: &[GenericArg<'_>], ) -> Option<(&'static str, &'static str)> { - let method_name = method_name.as_str(); let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id); let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) { @@ -39,14 +39,21 @@ fn get_const_name_and_ty_name( } } else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id) && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) - && ["min", "max", "minimum", "maximum", "min_value", "max_value"].contains(&method_name) + && matches!( + method_name, + sym::min | sym::max | sym::minimum | sym::maximum | sym::min_value | sym::max_value + ) { ty_name } else { return None; }; - let const_name = if method_name.starts_with("max") { "MAX" } else { "MIN" }; + let const_name = if matches!(method_name, sym::max | sym::maximum | sym::max_value) { + "MAX" + } else { + "MIN" + }; Some((const_name, ty_name)) } diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index 2471c7355518..c0c0a47f8551 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; +use clippy_utils::{std_or_core, sym}; use rustc_errors::Applicability; 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; @@ -78,9 +77,9 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) && 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", + && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) { + (Some(sym::ptr_null), sym::cast_mut) => "null_mut", + (Some(sym::ptr_null_mut), sym::cast_const) => "null", _ => return, } && let Some(prefix) = std_or_core(cx) diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 1d44c7e9c88b..5c64216dd92c 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -3,14 +3,14 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn}; +use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym}; use core::ops::ControlFlow; use rustc_hir::intravisit::FnKind; use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -104,7 +104,7 @@ impl CognitiveComplexity { FnKind::Closure => { let header_span = body_span.with_hi(decl.output.span().lo()); #[expect(clippy::range_plus_one)] - if let Some(range) = header_span.map_range(cx, |src, range| { + if let Some(range) = header_span.map_range(cx, |_, src, range| { let mut idxs = src.get(range.clone())?.match_indices('|'); Some(range.start + idxs.next()?.0..range.start + idxs.next()?.0 + 1) }) { @@ -157,9 +157,9 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { } fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity"); + self.limit.push_attrs(cx.sess(), attrs, sym::cognitive_complexity); } fn check_attributes_post(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); + self.limit.pop_attrs(cx.sess(), attrs, sym::cognitive_complexity); } } diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 9c3009a86cdc..238ebd4a444c 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -75,11 +75,15 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } // Check that there exists at least one explicit else condition - let (conds, _) = if_sequence(expr); + let (conds, blocks) = if_sequence(expr); if conds.len() < 2 { return; } + if blocks.len() < 3 { + return; + } + for cond in conds.windows(2) { if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) = (&cond[0].kind, &cond[1].kind) @@ -125,6 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else { unreachable!(); }; + let lhs = Sugg::hir(cx, lhs, "..").maybe_paren(); let rhs = Sugg::hir(cx, rhs, "..").addr(); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 42fbe6438d4e..2467fc95fd05 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; use clippy_utils::visitors::for_each_expr_without_closures; @@ -258,7 +258,7 @@ fn lint_branches_sharing_code<'tcx>( let span = span.with_hi(last_block.span.hi()); // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) let span = span - .map_range(cx, |src, range| { + .map_range(cx, |_, src, range| { (range.start > 4 && src.get(range.start - 4..range.start)? == " ") .then_some(range.start - 4..range.end) }) @@ -567,7 +567,7 @@ fn method_caller_is_mutable<'tcx>( /// Implementation of `IFS_SAME_COND`. fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for (i, j) in search_same( + for group in search_same( conds, |e| hash_expr(cx, e), |lhs, rhs| { @@ -584,14 +584,8 @@ fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mu } }, ) { - span_lint_and_note( - cx, - IFS_SAME_COND, - j.span, - "this `if` has the same condition as a previous `if`", - Some(i.span), - "same as this", - ); + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); } } @@ -609,14 +603,13 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { SpanlessEq::new(cx).eq_expr(lhs, rhs) }; - for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) { - span_lint_and_note( + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( cx, SAME_FUNCTIONS_IN_IF_CONDITION, - j.span, - "this `if` has the same function call as a previous `if`", - Some(i.span), - "same as this", + spans, + "these `if` branches have the same function call", ); } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index bb825c7655f8..5fcb851dfebc 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -764,6 +764,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO, crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO, crate::use_self::USE_SELF_INFO, + crate::useless_concat::USELESS_CONCAT_INFO, crate::useless_conversion::USELESS_CONVERSION_INFO, crate::vec::USELESS_VEC_INFO, crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index 7c64bf46e7bd..615421f3a40d 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr}; +use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr}; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 946515386690..5204f73ea0a9 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -14,36 +14,36 @@ macro_rules! declare_with_version { #[rustfmt::skip] declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ - #[clippy::version = "pre 1.29.0"] - ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), - #[clippy::version = "pre 1.29.0"] - ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), - #[clippy::version = "pre 1.29.0"] - ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), - #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), - #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), - #[clippy::version = "pre 1.29.0"] - ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), #[clippy::version = "1.30.0"] ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] - ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), - #[clippy::version = "1.39.0"] - ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), - #[clippy::version = "1.44.0"] - ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), - #[clippy::version = "1.47.0"] - ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), - #[clippy::version = "1.54.0"] - ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), - #[clippy::version = "1.54.0"] - ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), - #[clippy::version = "1.86.0"] - ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), + ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), #[clippy::version = "1.86.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), + #[clippy::version = "pre 1.29.0"] + ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), + #[clippy::version = "1.86.0"] + ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), + #[clippy::version = "1.54.0"] + ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), + #[clippy::version = "pre 1.29.0"] + ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), + #[clippy::version = "1.47.0"] + ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), + #[clippy::version = "1.44.0"] + ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), + #[clippy::version = "pre 1.29.0"] + ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), + #[clippy::version = "pre 1.29.0"] + ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), + #[clippy::version = "pre 1.29.0"] + ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), + #[clippy::version = "pre 1.29.0"] + ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), + #[clippy::version = "1.39.0"] + ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), + #[clippy::version = "1.54.0"] + ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), ]} #[rustfmt::skip] @@ -61,6 +61,12 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::box_vec", "clippy::box_collection"), #[clippy::version = ""] + ("clippy::cast_ref_to_mut", "invalid_reference_casting"), + #[clippy::version = ""] + ("clippy::clone_double_ref", "suspicious_double_ref_op"), + #[clippy::version = ""] + ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = ""] ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), #[clippy::version = ""] ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), @@ -70,15 +76,35 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::disallowed_method", "clippy::disallowed_methods"), #[clippy::version = ""] ("clippy::disallowed_type", "clippy::disallowed_types"), + #[clippy::version = "1.86.0"] + ("clippy::double_neg", "double_negations"), + #[clippy::version = ""] + ("clippy::drop_bounds", "drop_bounds"), + #[clippy::version = ""] + ("clippy::drop_copy", "dropping_copy_types"), + #[clippy::version = ""] + ("clippy::drop_ref", "dropping_references"), #[clippy::version = ""] ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - #[clippy::version = "1.51.0"] - ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = "1.53.0"] ("clippy::filter_map", "clippy::manual_filter_map"), + #[clippy::version = "1.51.0"] + ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = ""] ("clippy::fn_address_comparisons", "unpredictable_function_pointer_comparisons"), #[clippy::version = ""] + ("clippy::fn_null_check", "useless_ptr_null_checks"), + #[clippy::version = ""] + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::forget_copy", "forgetting_copy_types"), + #[clippy::version = ""] + ("clippy::forget_ref", "forgetting_references"), + #[clippy::version = ""] ("clippy::identity_conversion", "clippy::useless_conversion"), #[clippy::version = "pre 1.29.0"] ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"), @@ -91,7 +117,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), #[clippy::version = ""] + ("clippy::into_iter_on_array", "array_into_iter"), + #[clippy::version = ""] + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + #[clippy::version = "CURRENT_RUSTC_VERSION"] + ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), + #[clippy::version = ""] + ("clippy::invalid_ref", "invalid_value"), + #[clippy::version = ""] + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), + #[clippy::version = ""] + ("clippy::let_underscore_drop", "let_underscore_drop"), + #[clippy::version = ""] ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), + #[clippy::version = "1.80.0"] + ("clippy::maybe_misused_cfg", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + #[clippy::version = "1.80.0"] + ("clippy::mismatched_target_os", "unexpected_cfgs"), #[clippy::version = ""] ("clippy::new_without_default_derive", "clippy::new_without_default"), #[clippy::version = ""] @@ -107,6 +151,10 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), #[clippy::version = ""] + ("clippy::panic_params", "non_fmt_panics"), + #[clippy::version = ""] + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), + #[clippy::version = ""] ("clippy::ref_in_deref", "clippy::needless_borrow"), #[clippy::version = ""] ("clippy::result_expect_used", "clippy::expect_used"), @@ -115,67 +163,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::result_unwrap_used", "clippy::unwrap_used"), #[clippy::version = ""] + ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + #[clippy::version = ""] ("clippy::single_char_push_str", "clippy::single_char_add_str"), #[clippy::version = ""] ("clippy::stutter", "clippy::module_name_repetitions"), #[clippy::version = ""] + ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = ""] ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), #[clippy::version = ""] ("clippy::to_string_in_display", "clippy::recursive_format_impl"), - #[clippy::version = ""] - ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), - #[clippy::version = ""] - ("clippy::zero_width_space", "clippy::invisible_characters"), - #[clippy::version = ""] - ("clippy::cast_ref_to_mut", "invalid_reference_casting"), - #[clippy::version = ""] - ("clippy::clone_double_ref", "suspicious_double_ref_op"), - #[clippy::version = ""] - ("clippy::cmp_nan", "invalid_nan_comparisons"), - #[clippy::version = "CURRENT_RUSTC_VERSION"] - ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), - #[clippy::version = "1.86.0"] - ("clippy::double_neg", "double_negations"), - #[clippy::version = ""] - ("clippy::drop_bounds", "drop_bounds"), - #[clippy::version = ""] - ("clippy::drop_copy", "dropping_copy_types"), - #[clippy::version = ""] - ("clippy::drop_ref", "dropping_references"), - #[clippy::version = ""] - ("clippy::fn_null_check", "useless_ptr_null_checks"), - #[clippy::version = ""] - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::forget_copy", "forgetting_copy_types"), - #[clippy::version = ""] - ("clippy::forget_ref", "forgetting_references"), - #[clippy::version = ""] - ("clippy::into_iter_on_array", "array_into_iter"), - #[clippy::version = ""] - ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), - #[clippy::version = ""] - ("clippy::invalid_ref", "invalid_value"), - #[clippy::version = ""] - ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), - #[clippy::version = ""] - ("clippy::let_underscore_drop", "let_underscore_drop"), - #[clippy::version = "1.80.0"] - ("clippy::maybe_misused_cfg", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), - #[clippy::version = "1.80.0"] - ("clippy::mismatched_target_os", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::panic_params", "non_fmt_panics"), - #[clippy::version = ""] - ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), - #[clippy::version = ""] - ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_float_to_int", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_char", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_float", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] @@ -183,15 +189,9 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::unused_label", "unused_labels"), #[clippy::version = ""] + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), + #[clippy::version = ""] ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), #[clippy::version = ""] - ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_int_to_float", "unnecessary_transmutes"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_int_to_char", "unnecessary_transmutes"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_float_to_int", "unnecessary_transmutes"), - #[clippy::version = "1.88.0"] - ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), + ("clippy::zero_width_space", "clippy::invisible_characters"), ]} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 5edb5c235703..a22a2ee66d25 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -252,13 +253,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } let typeck = cx.typeck_results(); - let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { + let Some((kind, sub_expr, skip_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { report(cx, expr, state, data, typeck); } return; }; + self.skip_expr = skip_expr; match (self.state.take(), kind) { (None, kind) => { @@ -671,42 +673,38 @@ fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, -) -> Option<(RefOp, &'tcx Expr<'tcx>)> { - let (is_ufcs, def_id, arg) = match expr.kind { - ExprKind::MethodCall(_, arg, [], _) => (false, typeck.type_dependent_def_id(expr.hir_id)?, arg), +) -> Option<(RefOp, &'tcx Expr<'tcx>, Option)> { + let (call_path_id, def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, arg, [], _) => (None, typeck.type_dependent_def_id(expr.hir_id)?, arg), ExprKind::Call( - Expr { - kind: ExprKind::Path(path), + &Expr { + kind: ExprKind::Path(QPath::Resolved(None, path)), hir_id, .. }, [arg], - ) => (true, typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ) => (Some(hir_id), path.res.opt_def_id()?, arg), ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_raw_ptr() => { - return Some((RefOp::Deref, sub_expr)); + return Some((RefOp::Deref, sub_expr, None)); + }, + ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => { + return Some((RefOp::AddrOf(mutability), sub_expr, None)); }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)), _ => return None, }; - if tcx.is_diagnostic_item(sym::deref_method, def_id) { - Some(( - RefOp::Method { - mutbl: Mutability::Not, - is_ufcs, - }, - arg, - )) - } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { - Some(( - RefOp::Method { - mutbl: Mutability::Mut, - is_ufcs, - }, - arg, - )) - } else { - None - } + let mutbl = match tcx.get_diagnostic_name(def_id) { + Some(sym::deref_method) => Mutability::Not, + Some(sym::deref_mut_method) => Mutability::Mut, + _ => return None, + }; + Some(( + RefOp::Method { + mutbl, + is_ufcs: call_path_id.is_some(), + }, + arg, + call_path_id, + )) } // Checks if the adjustments contains a deref of `ManuallyDrop<_>` @@ -944,7 +942,7 @@ fn report<'tcx>( mutbl, } => { let mut app = Applicability::MachineApplicable; - let (expr_str, _expr_is_macro_call) = + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); let (_, ref_count) = peel_middle_ty_refs(ty); @@ -968,20 +966,11 @@ fn report<'tcx>( "&" }; - // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's - // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. - /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix { + let expr_str = if !expr_is_macro_call && is_ufcs && expr.precedence() < ExprPrecedence::Prefix { Cow::Owned(format!("({expr_str})")) } else { expr_str }; - */ - - // Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`. - if is_ufcs { - return; - } span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 3443b36eb4f3..062f7cef3a72 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -345,7 +345,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if ty_adt.repr().packed() && ty_subs .iter() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) { return; } diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 039937e0207b..9ee32fced8c4 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -113,7 +113,8 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { } // check for `unwrap` and `expect` for both `Option` and `Result` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or_else(|| method_chain_args(expr, &["expect"])) + if let Some(arglists) = + method_chain_args(expr, &[sym::unwrap]).or_else(|| method_chain_args(expr, &[sym::expect])) && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() && matches!( get_type_diagnostic_name(cx, receiver_ty), diff --git a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs index 9637546b8680..ebfc9972aefd 100644 --- a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs +++ b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::AttrStyle; use rustc_ast::token::CommentKind; -use rustc_attr_parsing::AttributeKind; +use rustc_attr_data_structures::AttributeKind; use rustc_errors::Applicability; use rustc_hir::Attribute; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index dc6cbb425430..7f7224ecfc65 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::AttributeKind; +use rustc_attr_data_structures::AttributeKind; use rustc_errors::Applicability; use rustc_hir::{Attribute, Item, ItemKind}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index a38d6df89f2b..4414aebbf9a3 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -92,7 +92,8 @@ impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VA impl LateLintPass<'_> for EmptyWithBrackets { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Struct(ident, var_data, _) = &item.kind + if let ItemKind::Struct(ident, _, var_data) = &item.kind + && !item.span.from_expansion() && has_brackets(var_data) && let span_after_ident = item.span.with_lo(ident.span.hi()) && has_no_fields(cx, var_data, span_after_ident) diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index ec81294624ef..098571a53512 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { if cx.tcx.data_layout.pointer_size.bits() != 64 { return; } - if let ItemKind::Enum(_, def, _) = &item.kind { + if let ItemKind::Enum(_, _, def) = &item.kind { for var in def.variants { if let Some(anon_const) = &var.disr_expr { let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body); diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 645f93068496..6ed7c87915b2 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -306,7 +306,7 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<' return true; } for (from_arg, to_arg) in to_subs.iter().zip(from_subs) { - match (from_arg.unpack(), to_arg.unpack()) { + match (from_arg.kind(), to_arg.kind()) { (GenericArgKind::Lifetime(from_region), GenericArgKind::Lifetime(to_region)) => { if check_region(from_region, to_region) { return true; @@ -354,5 +354,5 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<' fn ty_has_static(ty: Ty<'_>) -> bool { ty.walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if re.is_static())) + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if re.is_static())) } diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 38d115b878c7..686dc5c3c4fc 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -127,7 +127,7 @@ fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>, sp: Span, max: u64) { impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Struct(_, variant_data, _) = &item.kind + if let ItemKind::Struct(_, _, variant_data) = &item.kind && variant_data.fields().len() as u64 > self.max_struct_bools && has_n_bools( variant_data.fields().iter().map(|field| field.ty), diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 5a74e97c97c5..1fb0e4d24d06 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -76,7 +76,7 @@ impl LateLintPass<'_> for ExhaustiveItems { "exported enums should not be exhaustive", [].as_slice(), ), - ItemKind::Struct(_, v, ..) => ( + ItemKind::Struct(_, _, v) => ( EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive", v.fields(), diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index a5a4e05b3a6d..085ee4448a4e 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, path_def_id}; +use clippy_utils::{is_expn_of, path_def_id, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{ExpnId, sym}; +use rustc_span::ExpnId; declare_clippy_lint! { /// ### What it does @@ -72,9 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { }; // ordering is important here, since `writeln!` uses `write!` internally - let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() { + let calling_macro = if is_expn_of(write_call.span, sym::writeln).is_some() { Some("writeln") - } else if is_expn_of(write_call.span, "write").is_some() { + } else if is_expn_of(write_call.span, sym::write).is_some() { Some("write") } else { None 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 6a217b6182ce..c0b0fd88d9e1 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 @@ -273,7 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { // Only lint on inherent methods, not trait methods. if let ImplItemKind::Fn(.., body_id) = item.kind && !item.generics.params.is_empty() - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() && !is_empty_body(cx, body_id) && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) && !item.span.in_external_macro(cx.sess().source_map()) diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index c868b782f43c..68d0cd19c8a6 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -82,7 +82,7 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl } // check for `unwrap` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs index aae8291905d3..dfb0b4f103c5 100644 --- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -51,7 +51,7 @@ declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MO impl EarlyLintPass for FieldScopedVisibilityModifiers { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(_, ref st, _) = item.kind else { + let ItemKind::Struct(_, _, ref st) = item.kind else { return; }; for field in st.fields() { diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 012ad8e1a229..c51267567d01 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { }, ); } - } else if digits > max as usize && float_str.len() < sym_str.len() { + } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) { span_lint_and_then( cx, EXCESSIVE_PRECISION, diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index e653a57196d6..3c7e83b06972 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -294,8 +294,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: && let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) - && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind - && method_name.as_str() == "sqrt" + && let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = grandparent.kind + && method.name == sym::sqrt && detect_hypot(cx, receiver).is_some() { return; @@ -375,24 +375,10 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { } // check if expression of the form x.powi(2) + y.powi(2) - if let ExprKind::MethodCall( - PathSegment { - ident: lmethod_name, .. - }, - largs_0, - [largs_1, ..], - _, - ) = &add_lhs.kind - && let ExprKind::MethodCall( - PathSegment { - ident: rmethod_name, .. - }, - rargs_0, - [rargs_1, ..], - _, - ) = &add_rhs.kind - && lmethod_name.as_str() == "powi" - && rmethod_name.as_str() == "powi" + if let ExprKind::MethodCall(PathSegment { ident: lmethod, .. }, largs_0, [largs_1, ..], _) = &add_lhs.kind + && let ExprKind::MethodCall(PathSegment { ident: rmethod, .. }, rargs_0, [rargs_1, ..], _) = &add_rhs.kind + && lmethod.name == sym::powi + && rmethod.name == sym::powi && let ecx = ConstEvalCtxt::new(cx) && let Some(lvalue) = ecx.eval(largs_1) && let Some(rvalue) = ecx.eval(rargs_1) @@ -482,8 +468,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { ) = &expr.kind { if let Some(parent) = get_parent_expr(cx, expr) - && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind - && method_name.as_str() == "sqrt" + && let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind + && method.name == sym::sqrt && detect_hypot(cx, receiver).is_some() { return; @@ -623,27 +609,13 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { } fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { - if let ExprKind::MethodCall( - PathSegment { - ident: method_name_a, .. - }, - _, - args_a, - _, - ) = expr_a.kind - && let ExprKind::MethodCall( - PathSegment { - ident: method_name_b, .. - }, - _, - args_b, - _, - ) = expr_b.kind + if let ExprKind::MethodCall(PathSegment { ident: method_a, .. }, _, args_a, _) = expr_a.kind + && let ExprKind::MethodCall(PathSegment { ident: method_b, .. }, _, args_b, _) = expr_b.kind { - return method_name_a.as_str() == method_name_b.as_str() + return method_a.name == method_b.name && args_a.len() == args_b.len() - && (["ln", "log2", "log10"].contains(&method_name_a.as_str()) - || method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); + && (matches!(method_a.name, sym::ln | sym::log2 | sym::log10) + || method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); } false diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 8a3f8e1c5874..a26e736c7ae3 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -15,7 +15,7 @@ use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, FormatPlaceholder, FormatTrait, }; -use rustc_attr_parsing::RustcVersion; +use rustc_attr_data_structures::RustcVersion; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index c3e0d5e8b694..70655838b6af 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -55,7 +55,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); - } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() { check_must_use_candidate( cx, sig.decl, diff --git a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs index aba0fbcb9feb..106202d00d40 100644 --- a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs +++ b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs @@ -17,7 +17,7 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a && is_type_diagnostic_item(cx, *opt_ty, sym::Option) && let ty::Adt(_, opt_gen_args) = opt_ty.kind() && let [gen_arg] = opt_gen_args.as_slice() - && let GenericArgKind::Type(gen_ty) = gen_arg.unpack() + && let GenericArgKind::Type(gen_ty) = gen_arg.kind() && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index 07a92a4ed70c..bb98ae826111 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -55,7 +55,7 @@ pub(super) fn check_impl_item<'tcx>( // Don't lint if method is a trait's implementation, we can't do anything about those if let hir::ImplItemKind::Fn(ref sig, _) = item.kind && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() { if cx.effective_visibilities.is_exported(item.owner_id.def_id) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -103,7 +103,7 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty .did() .as_local() && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_def_id) - && let hir::ItemKind::Enum(_, ref def, _) = item.kind + && let hir::ItemKind::Enum(_, _, ref def) = item.kind { let variants_size = AdtVariantInfo::new(cx, *adt, subst); if let Some((first_variant, variants)) = variants_size.split_first() diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 4c17834c3adf..cab7a9fb7099 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { } let generics_suggestion_span = impl_.generics.span.substitute_dummy({ - let range = (item.span.lo()..target.span().lo()).map_range(cx, |src, range| { + let range = (item.span.lo()..target.span().lo()).map_range(cx, |_, src, range| { Some(src.get(range.clone())?.find("impl")? + 4..range.end) }); if let Some(range) = range { @@ -165,11 +165,12 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { continue; } let generics_suggestion_span = generics.span.substitute_dummy({ - let range = (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |src, range| { - let (pre, post) = src.get(range.clone())?.split_once("fn")?; - let pos = post.find('(')? + pre.len() + 2; - Some(pos..pos) - }); + let range = + (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |_, src, range| { + let (pre, post) = src.get(range.clone())?.split_once("fn")?; + let pos = post.find('(')? + pre.len() + 2; + Some(pos..pos) + }); if let Some(range) = range { range.with_ctxt(item.span.ctxt()) } else { diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index e55edb1fcaa8..5d0bd3e8ca30 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; -use rustc_attr_parsing::{RustcVersion, StabilityLevel, StableSince}; +use rustc_attr_data_structures::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 989997d69f7c..3d131a7825af 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; +use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local, sym}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -12,7 +12,6 @@ use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::Span; use rustc_span::symbol::Ident; @@ -72,7 +71,7 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) - && (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) + && (!expr.span.from_expansion() || is_expn_of(expr.span, sym::if_chain).is_some()) && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && let found_slices = find_slice_values(cx, let_pat) && !found_slices.is_empty() @@ -109,11 +108,11 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap], call_span: Span) -> Option { impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(("open", mut receiver, [_arg], _, _)) = method_call(expr) else { + let Some((sym::open, mut receiver, [_arg], _, _)) = method_call(expr) else { return; }; let receiver_ty = cx.typeck_results().expr_ty(receiver); @@ -70,9 +70,9 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { let mut write = None; while let Some((name, recv, args, _, span)) = method_call(receiver) { - if name == "append" { + if name == sym::append { append = index_if_arg_is_boolean(args, span); - } else if name == "write" { + } else if name == sym::write { write = index_if_arg_is_boolean(args, span); } receiver = recv; diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index c4e10837bf19..bf3eafe09b3d 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -4,6 +4,7 @@ use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -119,33 +120,33 @@ use self::Heuristic::{All, Always, Any, First}; /// returns an infinite or possibly infinite iterator. The finiteness /// is an upper bound, e.g., some methods can return a possibly /// infinite iterator at worst, e.g., `take_while`. -const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [ - ("zip", 1, All, Infinite), - ("chain", 1, Any, Infinite), - ("cycle", 0, Always, Infinite), - ("map", 1, First, Infinite), - ("by_ref", 0, First, Infinite), - ("cloned", 0, First, Infinite), - ("rev", 0, First, Infinite), - ("inspect", 0, First, Infinite), - ("enumerate", 0, First, Infinite), - ("peekable", 1, First, Infinite), - ("fuse", 0, First, Infinite), - ("skip", 1, First, Infinite), - ("skip_while", 0, First, Infinite), - ("filter", 1, First, Infinite), - ("filter_map", 1, First, Infinite), - ("flat_map", 1, First, Infinite), - ("unzip", 0, First, Infinite), - ("take_while", 1, First, MaybeInfinite), - ("scan", 2, First, MaybeInfinite), +const HEURISTICS: [(Symbol, usize, Heuristic, Finiteness); 19] = [ + (sym::zip, 1, All, Infinite), + (sym::chain, 1, Any, Infinite), + (sym::cycle, 0, Always, Infinite), + (sym::map, 1, First, Infinite), + (sym::by_ref, 0, First, Infinite), + (sym::cloned, 0, First, Infinite), + (sym::rev, 0, First, Infinite), + (sym::inspect, 0, First, Infinite), + (sym::enumerate, 0, First, Infinite), + (sym::peekable, 1, First, Infinite), + (sym::fuse, 0, First, Infinite), + (sym::skip, 1, First, Infinite), + (sym::skip_while, 0, First, Infinite), + (sym::filter, 1, First, Infinite), + (sym::filter_map, 1, First, Infinite), + (sym::flat_map, 1, First, Infinite), + (sym::unzip, 0, First, Infinite), + (sym::take_while, 1, First, MaybeInfinite), + (sym::scan, 2, First, MaybeInfinite), ]; fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { for &(name, len, heuristic, cap) in &HEURISTICS { - if method.ident.name.as_str() == name && args.len() == len { + if method.ident.name == name && args.len() == len { return (match heuristic { Always => Infinite, First => is_infinite(cx, receiver), @@ -183,36 +184,36 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { /// the names and argument lengths of methods that *may* exhaust their /// iterators -const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [ - ("find", 1), - ("rfind", 1), - ("position", 1), - ("rposition", 1), - ("any", 1), - ("all", 1), +const POSSIBLY_COMPLETING_METHODS: [(Symbol, usize); 6] = [ + (sym::find, 1), + (sym::rfind, 1), + (sym::position, 1), + (sym::rposition, 1), + (sym::any, 1), + (sym::all, 1), ]; /// the names and argument lengths of methods that *always* exhaust /// their iterators -const COMPLETING_METHODS: [(&str, usize); 12] = [ - ("count", 0), - ("fold", 2), - ("for_each", 1), - ("partition", 1), - ("max", 0), - ("max_by", 1), - ("max_by_key", 1), - ("min", 0), - ("min_by", 1), - ("min_by_key", 1), - ("sum", 0), - ("product", 0), +const COMPLETING_METHODS: [(Symbol, usize); 12] = [ + (sym::count, 0), + (sym::fold, 2), + (sym::for_each, 1), + (sym::partition, 1), + (sym::max, 0), + (sym::max_by, 1), + (sym::max_by_key, 1), + (sym::min, 0), + (sym::min_by, 1), + (sym::min_by_key, 1), + (sym::sum, 0), + (sym::product, 0), ]; fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { - let method_str = method.ident.name.as_str(); + let method_str = method.ident.name; for &(name, len) in &COMPLETING_METHODS { if method_str == name && args.len() == len { return is_infinite(cx, receiver); diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 1d582fb0223e..7f2e25367a6a 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -106,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { // Check if return type is String && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) // Filters instances of to_string which are required by a trait - && trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, impl_item.owner_id).is_none() { show_lint(cx, impl_item); } diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 3d4dcd020702..9c91cf680851 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -535,10 +535,10 @@ impl LateLintPass<'_> for ItemNameRepetitions { if span_is_local(item.span) { match item.kind { - ItemKind::Enum(_, def, _) => { + ItemKind::Enum(_, _, def) => { self.check_variants(cx, item, &def); }, - ItemKind::Struct(_, VariantData::Struct { fields, .. }, _) => { + ItemKind::Struct(_, _, VariantData::Struct { fields, .. }) => { self.check_fields(cx, item, fields); }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index 394005e99129..cee8ca1261e5 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -48,7 +48,7 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Const(ident, _, generics, _) = &item.kind + if let ItemKind::Const(ident, generics, _, _) = &item.kind // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs index d08efa0ec9cc..e85d779b4880 100644 --- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs +++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs @@ -73,7 +73,7 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - if let ItemKind::Enum(ident, ref def, _) = item.kind + if let ItemKind::Enum(ident, _, ref def) = item.kind && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Adt(adt, subst) = ty.kind() && adt.variants().len() > 1 diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 916191b2a7b0..b72e14246db7 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { && !local.span.in_external_macro(cx.tcx.sess.source_map()) { let init_ty = cx.typeck_results().expr_ty(init); - let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { + let contains_sync_guard = init_ty.walk().any(|inner| match inner.kind() { GenericArgKind::Type(inner_ty) => inner_ty .ty_adt_def() .is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 006145cc623c..92eb3d7a7c59 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -35,7 +35,7 @@ extern crate rustc_abi; extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr_parsing; +extern crate rustc_attr_data_structures; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; @@ -393,6 +393,7 @@ mod unwrap; mod unwrap_in_result; mod upper_case_acronyms; mod use_self; +mod useless_concat; mod useless_conversion; mod vec; mod vec_init_then_push; @@ -866,7 +867,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); store.register_late_pass(move |_| Box::new(lines_filter_map_ok::LinesFilterMapOk::new(conf))); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); - store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); + store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))); store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); @@ -937,6 +938,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new())); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(|_| Box::new(useless_concat::UselessConcat)); store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 9a64226b1ed9..8fe0c9d60f96 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -159,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if let ImplItemKind::Fn(ref sig, id) = item.kind { - let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); + let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id).is_none(); check_fn_inner( cx, sig, diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index d8af44233d3e..14ccb6fce223 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id}; +use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::Symbol; pub struct LinesFilterMapOk { msrv: Msrv, @@ -74,17 +74,17 @@ impl LateLintPass<'_> for LinesFilterMapOk { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind && is_trait_method(cx, expr, sym::Iterator) - && let fm_method_str = fm_method.ident.as_str() - && matches!(fm_method_str, "filter_map" | "flat_map" | "flatten") + && let fm_method_name = fm_method.ident.name + && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) - && should_lint(cx, fm_args, fm_method_str) + && should_lint(cx, fm_args, fm_method_name) && self.msrv.meets(cx, msrvs::MAP_WHILE) { span_lint_and_then( cx, LINES_FILTER_MAP_OK, fm_span, - format!("`{fm_method_str}()` will run forever if the iterator repeatedly produces an `Err`",), + format!("`{fm_method_name}()` will run forever if the iterator repeatedly produces an `Err`",), |diag| { diag.span_note( fm_receiver.span, @@ -101,9 +101,9 @@ impl LateLintPass<'_> for LinesFilterMapOk { } } -fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> bool { +fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> bool { match args { - [] => method_str == "flatten", + [] => method_name == sym::flatten, [fm_arg] => { match &fm_arg.kind { // Detect `Result::ok` @@ -120,7 +120,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo && path_to_local_id(receiver, param.pat.hir_id) && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { - is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok" + is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok } else { false } diff --git a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 8916454ada16..a702e60f1c27 100644 --- a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -3,12 +3,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{eq_expr_value, higher, path_to_local_id}; +use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{Expr, ExprKind, LangItem, Node, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use super::CHAR_INDICES_AS_BYTE_INDICES; @@ -16,22 +16,22 @@ use super::CHAR_INDICES_AS_BYTE_INDICES; // Note: `String` also has methods that work with byte indices, // but they all take `&mut self` and aren't worth considering since the user couldn't have called // them while the chars iterator is live anyway. -const BYTE_INDEX_METHODS: &[&str] = &[ - "is_char_boundary", - "floor_char_boundary", - "ceil_char_boundary", - "get", - "index", - "index_mut", - "get_mut", - "get_unchecked", - "get_unchecked_mut", - "slice_unchecked", - "slice_mut_unchecked", - "split_at", - "split_at_mut", - "split_at_checked", - "split_at_mut_checked", +const BYTE_INDEX_METHODS: &[Symbol] = &[ + sym::ceil_char_boundary, + sym::floor_char_boundary, + sym::get, + sym::get_mut, + sym::get_unchecked, + sym::get_unchecked_mut, + sym::index, + sym::index_mut, + sym::is_char_boundary, + sym::slice_mut_unchecked, + sym::slice_unchecked, + sym::split_at, + sym::split_at_checked, + sym::split_at_mut, + sym::split_at_mut_checked, ]; const CONTINUE: ControlFlow = ControlFlow::Continue(()); @@ -88,7 +88,7 @@ fn check_index_usage<'tcx>( // (contrary to the `ExprKind::Index` case which needs to handle both with `is_string_like` because `String` implements // `Index` directly and no deref to `str` would happen in that case). if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str() - && BYTE_INDEX_METHODS.contains(&segment.ident.name.as_str()) + && BYTE_INDEX_METHODS.contains(&segment.ident.name) && eq_expr_value(cx, chars_recv, recv) => { "passing a character position to a method that expects a byte index" diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs index d5ddc33e928d..4aa1c2e211d3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,7 +1,7 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -76,7 +76,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr< }; let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); + let object = snippet_with_context(cx, self_arg.span, call_expr.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( cx, EXPLICIT_INTO_ITER_LOOP, diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index d0b26c91ffaf..010652e1cb90 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,7 +1,7 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sym; use clippy_utils::ty::{ implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, @@ -36,7 +36,7 @@ pub(super) fn check( } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); + let object = snippet_with_context(cx, self_arg.span, call_expr.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( cx, EXPLICIT_ITER_LOOP, diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 56d2bef2305a..01c36b8cb12f 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -25,8 +25,8 @@ mod while_let_loop; mod while_let_on_iterator; use clippy_config::Conf; -use clippy_utils::higher; use clippy_utils::msrvs::Msrv; +use clippy_utils::{higher, sym}; use rustc_ast::Label; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; @@ -909,15 +909,17 @@ impl Loops { } fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { - if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { - match method.ident.as_str() { - "iter" | "iter_mut" => { + if !arg.span.from_expansion() + && let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind + { + match method.ident.name { + sym::iter | sym::iter_mut => { explicit_iter_loop::check(cx, self_arg, arg, self.msrv, self.enforce_iter_loop_reborrow); }, - "into_iter" => { + sym::into_iter => { explicit_into_iter_loop::check(cx, self_arg, arg); }, - "next" => { + sym::next => { iter_next_loop::check(cx, arg); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs index b4cd988329d3..4439a28763a2 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; -use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal}; +use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -103,7 +103,7 @@ fn count_ones_receiver<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio } else { return None; }; - (method.ident.as_str() == "count_ones" && matches!(ty.kind(), ty::Uint(_))).then_some(receiver) + (method.ident.name == sym::count_ones && matches!(ty.kind(), ty::Uint(_))).then_some(receiver) } /// Return `greater` if `smaller == greater - 1` diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 067b92cd46ee..3562b1ff5cce 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { } match item.kind { - ItemKind::Enum(_, def, _) if def.variants.len() > 1 => { + 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)) @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); } }, - ItemKind::Struct(_, variant_data, _) => { + ItemKind::Struct(_, _, variant_data) => { let fields = variant_data.fields(); let private_fields = fields .iter() diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs index 2dad0fa4925e..0c09a47c9651 100644 --- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs +++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs @@ -1,11 +1,13 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -36,20 +38,33 @@ declare_clippy_lint! { complexity, "manual slice size calculation" } -declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]); +impl_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]); + +pub struct ManualSliceSizeCalculation { + msrv: Msrv, +} + +impl ManualSliceSizeCalculation { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Binary(ref op, left, right) = expr.kind && BinOpKind::Mul == op.node && !expr.span.from_expansion() - // Does not apply inside const because size_of_val is not cost in stable. - && !is_in_const_context(cx) && let Some((receiver, refs_count)) = simplify(cx, left, right) + && (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_SIZE_OF_VAL)) { let ctxt = expr.span.ctxt(); let mut app = Applicability::MachineApplicable; - let deref = "*".repeat(refs_count - 1); + let deref = if refs_count > 0 { + "*".repeat(refs_count - 1) + } else { + "&".into() + }; let val_name = snippet_with_context(cx, receiver.span, ctxt, "slice", &mut app).0; let Some(sugg) = std_or_core(cx) else { return }; diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs index 7ca3b7120667..73ee1c3c78ab 100644 --- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs +++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs @@ -5,7 +5,7 @@ use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym, symbol}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -67,7 +67,7 @@ impl LateLintPass<'_> for ManualStringNew { fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool { if let ExprKind::Lit(lit) = expr_kind && let LitKind::Str(value, _) = lit.node - && value == symbol::kw::Empty + && value == sym::empty { return true; } diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index b607f8117eb8..af6a1b07a492 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -255,7 +255,7 @@ impl LateLintPass<'_> for MapUnit { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { if let hir::StmtKind::Semi(expr) = stmt.kind && !stmt.span.from_expansion() - && let Some(arglists) = method_chain_args(expr, &["map"]) + && let Some(arglists) = method_chain_args(expr, &[sym::map]) { lint_map_unit_fn(cx, stmt, expr, arglists[0]); } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 3ac2c9fc2b36..8c3f52542d91 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -109,7 +109,7 @@ fn handle( && implements_trait(cx, expr_type, default_trait_id, &[]) // We check if the initial condition implements Default. && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) - && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() + && let GenericArgKind::Type(condition_ty) = condition_ty.kind() && implements_trait(cx, condition_ty, default_trait_id, &[]) && is_default_equivalent(cx, peel_blocks(body_none)) { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index a21597ffb93d..dbb29ee776b1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; +use itertools::Itertools; use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -110,57 +111,68 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { && check_same_body() }; - let mut appl = Applicability::MaybeIncorrect; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { - if matches!(arm2.pat.kind, PatKind::Wild) { - if !cx.tcx.features().non_exhaustive_omitted_patterns_lint() - || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id) - { - let arm_span = adjusted_arm_span(cx, arm1.span); - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - arm1.hir_id, - arm_span, - "this match arm has an identical body to the `_` wildcard arm", - |diag| { - diag.span_suggestion(arm_span, "try removing the arm", "", appl) - .help("or try changing either arm body") - .span_note(arm2.span, "`_` wildcard arm here"); - }, - ); - } - } else { - let back_block = backwards_blocking_idxs[j]; - let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) { - (arm1, arm2) - } else { - (arm2, arm1) - }; + for mut group in search_same(&indexed_arms, hash, eq) { + // Filter out (and fulfill) `#[allow]`ed and `#[expect]`ed arms + group.retain(|(_, arm)| !fulfill_or_allowed(cx, MATCH_SAME_ARMS, [arm.hir_id])); - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - keep_arm.hir_id, - keep_arm.span, - "this match arm has an identical body to another arm", - |diag| { - let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "", &mut appl); - let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "", &mut appl); - - diag.multipart_suggestion( - "or try merging the arm patterns and removing the obsolete arm", - vec![ - (keep_arm.pat.span, format!("{keep_pat_snip} | {move_pat_snip}")), - (adjusted_arm_span(cx, move_arm.span), String::new()), - ], - appl, - ) - .help("try changing either arm body"); - }, - ); + if group.len() < 2 { + continue; } + + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + group.iter().map(|(_, arm)| arm.span).collect_vec(), + "these match arms have identical bodies", + |diag| { + diag.help("if this is unintentional make the arms return different values"); + + if let [prev @ .., (_, last)] = group.as_slice() + && is_wildcard_arm(last.pat) + && is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, last.hir_id) + { + diag.span_label(last.span, "the wildcard arm"); + + let s = if prev.len() > 1 { "s" } else { "" }; + diag.multipart_suggestion_verbose( + format!("otherwise remove the non-wildcard arm{s}"), + prev.iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect(), + Applicability::MaybeIncorrect, + ); + } else if let &[&(first_idx, _), .., &(last_idx, _)] = group.as_slice() { + let back_block = backwards_blocking_idxs[last_idx]; + let split = if back_block < first_idx + || (back_block == 0 && forwards_blocking_idxs[first_idx] <= last_idx) + { + group.split_first() + } else { + group.split_last() + }; + + if let Some(((_, dest), src)) = split + && let Some(pat_snippets) = group + .iter() + .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .collect::>>() + { + let mut suggs = src + .iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect_vec(); + + suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); + diag.multipart_suggestion_verbose( + "otherwise merge the patterns into a single arm", + suggs, + Applicability::MaybeIncorrect, + ); + } + } + }, + ); } } @@ -450,3 +462,11 @@ fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool { pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.swap_remove(&id)); result && ids.is_empty() } + +fn is_wildcard_arm(pat: &Pat<'_>) -> bool { + match pat.kind { + PatKind::Wild => true, + PatKind::Or([.., last]) => matches!(last.kind, PatKind::Wild), + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index c6ebd6144c76..c128fc40b733 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -28,7 +28,7 @@ use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::walk_span_to_context; use clippy_utils::{ - higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, + higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym, }; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -1053,13 +1053,13 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_direct_expn_of(expr.span, "matches").is_none() && expr.span.in_external_macro(cx.sess().source_map()) { + if is_direct_expn_of(expr.span, sym::matches).is_none() && expr.span.in_external_macro(cx.sess().source_map()) { return; } let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { - if is_direct_expn_of(expr.span, "matches").is_some() + if is_direct_expn_of(expr.span, sym::matches).is_some() && let [arm, _] = arms { redundant_pattern_match::check_match(cx, expr, ex, arms); diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 6c5d7cab2036..b04db03f8d2e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -74,7 +74,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) } if let PatKind::Wild = arm.pat.kind { - if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) { + if !eq_expr_value(cx, match_expr, arm_expr) { return false; } } else if !pat_same_as_expr(arm.pat, arm_expr) { @@ -103,27 +103,18 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool if matches!(else_expr.kind, ExprKind::Block(..)) { return false; } - let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { - return is_res_lang_ctor(cx, path_res(cx, ret), OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); + return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) + || eq_expr_value(cx, if_let.let_expr, else_expr); } - return eq_expr_value(cx, if_let.let_expr, ret); + return eq_expr_value(cx, if_let.let_expr, else_expr); } } false } -/// Strip `return` keyword if the expression type is `ExprKind::Ret`. -fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { - if let ExprKind::Ret(Some(ret)) = expr.kind { - ret - } else { - expr - } -} - /// Manually check for coercion casting by checking if the type of the match operand or let expr /// differs with the assigned local variable or the function return type. fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { @@ -161,7 +152,6 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> } fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - let expr = strip_return(expr); match (&pat.kind, &expr.kind) { // Example: `Some(val) => Some(val)` (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 9bbef8da0a46..7c6d45e42400 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -3,14 +3,14 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{is_in_const_context, path_to_local}; +use clippy_utils::{is_in_const_context, path_to_local, sym}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -95,7 +95,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: } else if let ExprKind::MethodCall(path, recv, args, ..) = guard.kind && let Some(binding) = get_pat_binding(cx, recv, outer_arm) { - check_method_calls(cx, outer_arm, path.ident.name.as_str(), recv, args, guard, &binding); + check_method_calls(cx, outer_arm, path.ident.name, recv, args, guard, &binding); } } } @@ -103,7 +103,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: fn check_method_calls<'tcx>( cx: &LateContext<'tcx>, arm: &Arm<'tcx>, - method: &str, + method: Symbol, recv: &Expr<'_>, args: &[Expr<'_>], if_expr: &Expr<'_>, @@ -112,7 +112,7 @@ fn check_method_calls<'tcx>( let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let slice_like = ty.is_slice() || ty.is_array(); - let sugg = if method == "is_empty" { + let sugg = if method == sym::is_empty { // `s if s.is_empty()` becomes "" // `arr if arr.is_empty()` becomes [] @@ -137,9 +137,9 @@ fn check_method_calls<'tcx>( if needles.is_empty() { sugg.insert_str(1, ".."); - } else if method == "starts_with" { + } else if method == sym::starts_with { sugg.insert_str(sugg.len() - 1, ", .."); - } else if method == "ends_with" { + } else if method == sym::ends_with { sugg.insert_str(1, ".., "); } else { return; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index db20be40f27e..c936c96f9719 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -108,7 +108,7 @@ fn find_match_true<'tcx>( fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { if let ty::Adt(_, subs) = ty.kind() && let Some(sub) = subs.get(index) - && let GenericArgKind::Type(sub_ty) = sub.unpack() + && let GenericArgKind::Type(sub_ty) = sub.kind() { Some(sub_ty) } else { @@ -273,7 +273,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { - let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span)); + let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index d7dc7604088f..88b4d9b7d544 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -4,7 +4,7 @@ use crate::FxHashSet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{first_line_of_span, indent_of, snippet}; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; -use clippy_utils::{get_attr, is_lint_allowed}; +use clippy_utils::{get_attr, is_lint_allowed, sym}; use itertools::Itertools; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; @@ -186,7 +186,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { && get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), - "has_significant_drop", + sym::has_significant_drop, ) .count() > 0 @@ -208,12 +208,12 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { // (to avoid false positive on `Ref<'a, MutexGuard>`) || (args .iter() - .all(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + .all(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_))) // some generic parameter has significant drop // (to avoid false negative on `Box>`) && args .iter() - .filter_map(|arg| match arg.unpack() { + .filter_map(|arg| match arg.kind() { GenericArgKind::Type(ty) => Some(ty), _ => None, }) diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index de22514c37c6..02fc09170e59 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; @@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E if let Some(parent) = clippy_utils::get_parent_expr(cx, expr) && let Some((name, _, _, _, _)) = method_call(parent) - && name == "unwrap" + && name == sym::unwrap { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs index 4ae0aeea2d1c..de27a45ba4d9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -5,12 +5,13 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; use rustc_middle::ty; +use rustc_span::Symbol; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. pub(super) fn check( cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, - chain_methods: &[&str], + chain_methods: &[Symbol], lint: &'static Lint, suggest: &str, ) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 9c45ec2e56cb..1c72a973cfa1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -5,12 +5,13 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; +use rustc_span::Symbol; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. pub(super) fn check( cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, - chain_methods: &[&str], + chain_methods: &[Symbol], lint: &'static Lint, suggest: &str, ) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs index 2efff4c3c549..8729e91d191f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs @@ -1,13 +1,14 @@ use crate::methods::chars_cmp; +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp::check(cx, info, &[sym::chars, sym::last], CHARS_LAST_CMP, "ends_with") { true } else { - chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") + chars_cmp::check(cx, info, &[sym::chars, sym::next_back], CHARS_LAST_CMP, "ends_with") } } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs index 5b8713f7d790..027d0a3947bf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -1,13 +1,26 @@ use crate::methods::chars_cmp_with_unwrap; +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::last, sym::unwrap], + CHARS_LAST_CMP, + "ends_with", + ) { true } else { - chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") + chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::next_back, sym::unwrap], + CHARS_LAST_CMP, + "ends_with", + ) } } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs index b631fecab972..2438843bf3ab 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs @@ -1,8 +1,9 @@ +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") + crate::methods::chars_cmp::check(cx, info, &[sym::chars, sym::next], CHARS_NEXT_CMP, "starts_with") } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs index caf21d3ff3bc..9b3609f19d72 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -1,8 +1,15 @@ +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") + crate::methods::chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::next, sym::unwrap], + CHARS_NEXT_CMP, + "starts_with", + ) } diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs index f7bf8764bdeb..6d0b944df55d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{eq_expr_value, get_parent_expr}; +use clippy_utils::{eq_expr_value, get_parent_expr, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) + && let Some((sym::replace, _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -47,7 +47,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr_without_closures(expr, |e| { - if let Some(("replace", _, [from, to], _, _)) = method_call(e) { + if let Some((sym::replace, _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index f5688e370a47..82e5a6d5a412 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; use super::EXPECT_FUN_CALL; @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, - name: &str, + name: Symbol, receiver: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) { @@ -114,7 +114,7 @@ pub(super) fn check<'tcx>( } } - if args.len() != 1 || name != "expect" || !is_call(&args[0].kind) { + if args.len() != 1 || name != sym::expect || !is_call(&args[0].kind) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs index 460ec7b3640a..db60061904f6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use super::EXTEND_WITH_DRAIN; @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: if is_type_diagnostic_item(cx, ty, sym::Vec) //check source object && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind - && src_method.ident.as_str() == "drain" + && src_method.ident.name == sym::drain && let src_ty = cx.typeck_results().expr_ty(drain_vec) //check if actual src type is mutable for code suggestion && let immutable = src_ty.is_mutable_ptr() diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 519091406ccf..9724463f0c08 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -1,15 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs}; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; use super::IMPLICIT_CLONE; -pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) @@ -43,12 +43,12 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv /// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call /// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g., /// `is_to_owned_like` in `unnecessary_to_owned.rs`. -pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir::def_id::DefId) -> bool { +pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { match method_name { - "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), - "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), - "to_vec" => cx + sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), + sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), + sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), + sym::to_vec => cx .tcx .impl_of_method(method_def_id) .filter(|&impl_did| { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs index 17cc07b91c5d..b4ab313fe98d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs @@ -5,11 +5,16 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_CLONED_COLLECT; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + method_name: Symbol, + expr: &hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, +) { let expr_ty = cx.typeck_results().expr_ty(expr); if is_type_diagnostic_item(cx, expr_ty, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs index 209cf2fcc0a4..6b64cc8b50ae 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs @@ -5,11 +5,11 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_COUNT; -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: &str) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: Symbol) { let ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 3ac9299ba915..c88462129af0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -1,12 +1,12 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::pat_is_wild; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; /// lint use of: /// @@ -16,13 +16,13 @@ use rustc_span::sym; /// on `HashMaps` and `BTreeMaps` in std pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - map_type: &'tcx str, // iter / into_iter + map_type: Symbol, // iter / into_iter expr: &'tcx Expr<'tcx>, // .iter().map(|(_, v_| v)) recv: &'tcx Expr<'tcx>, // hashmap m_arg: &'tcx Expr<'tcx>, // |(_, v)| v msrv: Msrv, ) { - if map_type == "into_iter" && !msrv.meets(cx, msrvs::INTO_KEYS) { + if map_type == sym::into_iter && !msrv.meets(cx, msrvs::INTO_KEYS) { return; } if !expr.span.from_expansion() @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>( { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); - let into_prefix = if map_type == "into_iter" { "into_" } else { "" }; + let into_prefix = if map_type == sym::into_iter { "into_" } else { "" }; if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind && let [local_ident] = path.segments diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs index 82bda5d95122..1fdbd81bf240 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use super::ITER_NTH; @@ -12,7 +12,7 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_recv: &'tcx hir::Expr<'tcx>, - iter_method: &str, + iter_method: Symbol, iter_span: Span, nth_span: Span, ) -> bool { @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( expr.span, format!("called `.{iter_method}().nth()` on a {caller_type}"), |diag| { - let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" }; + let get_method = if iter_method == sym::iter_mut { "get_mut" } else { "get" }; diag.span_suggestion_verbose( iter_span.to(nth_span), format!("`{get_method}` is equivalent but more concise"), diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 9d562f5e51d2..c0366765234f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -2,7 +2,7 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; +use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -10,6 +10,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirId; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; +use rustc_span::Symbol; use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS}; @@ -51,7 +52,7 @@ fn is_arg_ty_unified_in_fn<'tcx>( .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args))) } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: &str, recv: &'tcx Expr<'tcx>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, recv: &'tcx Expr<'tcx>) { let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), @@ -60,9 +61,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method _ => return, }; let iter_type = match method_name { - "iter" => IterType::Iter, - "iter_mut" => IterType::IterMut, - "into_iter" => IterType::IntoIter, + sym::iter => IterType::Iter, + sym::iter_mut => IterType::IterMut, + sym::into_iter => IterType::IntoIter, _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index 7bb625222ec0..f5fe4316eb0d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -8,7 +8,7 @@ use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, Pl use rustc_lint::LateContext; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_OVEREAGER_CLONED; use crate::redundant_clone::REDUNDANT_CLONE; @@ -26,7 +26,7 @@ pub(super) enum Op<'a> { // later `.cloned()` // and add `&` to the parameter of closure parameter // e.g. `find` `filter` - FixClosure(&'a str, &'a Expr<'a>), + FixClosure(Symbol, &'a Expr<'a>), // later `.cloned()` // e.g. `skip` `take` diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs index 3fa83cd39d1d..a8445b68dd60 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs @@ -168,7 +168,7 @@ fn rewrite_as_cstr(cx: &LateContext<'_>, span: Span) -> Option { fn get_cast_target<'tcx>(e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { match &e.kind { - ExprKind::MethodCall(method, receiver, [], _) if method.ident.as_str() == "cast" => Some(receiver), + ExprKind::MethodCall(method, receiver, [], _) if method.ident.name == sym::cast => Some(receiver), ExprKind::Cast(expr, _) => Some(expr), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index 173ebcb7020b..21f2ce8b7c90 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -3,18 +3,18 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_span::{DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use super::MANUAL_INSPECT; #[expect(clippy::too_many_lines)] -pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: &str, name_span: Span, msrv: Msrv) { +pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: Symbol, name_span: Span, msrv: Msrv) { if let ExprKind::Closure(c) = arg.kind && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() @@ -101,7 +101,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: UseKind::Return(s) => edits.push((s.with_leading_whitespace(cx).with_ctxt(s.ctxt()), String::new())), UseKind::Borrowed(s) => { #[expect(clippy::range_plus_one)] - let range = s.map_range(cx, |src, range| { + let range = s.map_range(cx, |_, src, range| { let src = src.get(range.clone())?; let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']); trimmed.starts_with('&').then(|| { @@ -168,8 +168,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: edits.extend(addr_of_edits); } let edit = match name { - "map" => "inspect", - "map_err" => "inspect_err", + sym::map => "inspect", + sym::map_err => "inspect_err", _ => return, }; edits.push((name_span, edit.to_string())); diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 053601446573..98def66ca149 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -5,7 +5,7 @@ use rustc_ast::BindingMode; use rustc_errors::Applicability; use rustc_hir::{self as hir, Node, PatKind}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use super::MAP_IDENTITY; @@ -14,7 +14,7 @@ pub(super) fn check( expr: &hir::Expr<'_>, caller: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, - name: &str, + name: Symbol, _map_span: Span, ) { let caller_ty = cx.typeck_results().expr_ty(caller); diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index e0e6a1a59b62..d2d59f0013c0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -150,7 +150,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; @@ -159,7 +159,7 @@ use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, kw}; declare_clippy_lint! { /// ### What it does @@ -4711,17 +4711,15 @@ impl_lint_pass!(Methods => [ /// Extracts a method call name, args, and `Span` of the method name. /// This ensures that neither the receiver nor any of the arguments /// come from expansion. -pub fn method_call<'tcx>( - recv: &'tcx Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { +pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind && !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { - let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span, call_span)); + Some((path.ident.name, receiver, args, path.ident.span, call_span)) + } else { + None } - None } impl<'tcx> LateLintPass<'tcx> for Methods { @@ -4743,13 +4741,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); + or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args); expect_fun_call::check( cx, &self.format_args, expr, method_span, - method_call.ident.as_str(), + method_call.ident.name, receiver, args, ); @@ -4778,7 +4776,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } - let name = impl_item.ident.name.as_str(); + let name = impl_item.ident.name; let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir_expect_item(parent); let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); @@ -4851,7 +4849,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if name == "new" && ret_ty != self_ty { + if name == sym::new && ret_ty != self_ty { span_lint( cx, NEW_RET_NO_SELF, @@ -4881,7 +4879,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, - item.ident.name.as_str(), + item.ident.name, self_ty, first_arg_ty, first_arg_hir_ty.span, @@ -4912,14 +4910,17 @@ impl Methods { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { - ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { + ( + sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub, + [_arg], + ) => { zst_offset::check(cx, expr, recv); }, - ("all", [arg]) => { + (sym::all, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, true); match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => { + Some((sym::cloned, recv2, [], _, _)) => { iter_overeager_cloned::check( cx, expr, @@ -4929,13 +4930,13 @@ impl Methods { false, ); }, - Some(("map", _, [map_arg], _, map_call_span)) => { + Some((sym::map, _, [map_arg], _, map_call_span)) => { map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all"); }, _ => {}, } }, - ("and_then", [arg]) => { + (sym::and_then, [arg]) => { let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { @@ -4945,11 +4946,11 @@ impl Methods { } } }, - ("any", [arg]) => { + (sym::any, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, false); match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -4957,80 +4958,79 @@ impl Methods { iter_overeager_cloned::Op::NeedlessMove(arg), false, ), - Some(("chars", recv, _, _, _)) + Some((sym::chars, recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind && let body = cx.tcx.hir_body(arg.body) && let [param] = body.params => { string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), self.msrv); }, - Some(("map", _, [map_arg], _, map_call_span)) => { + Some((sym::map, _, [map_arg], _, map_call_span)) => { map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any"); }, - Some(("iter", iter_recv, ..)) => { + Some((sym::iter, iter_recv, ..)) => { manual_contains::check(cx, expr, iter_recv, arg); }, _ => {}, } }, - ("arg", [arg]) => { + (sym::arg, [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); }, - ("as_deref" | "as_deref_mut", []) => { + (sym::as_deref | sym::as_deref_mut, []) => { needless_option_as_deref::check(cx, expr, recv, name); }, - ("as_bytes", []) => { - if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { + (sym::as_bytes, []) => { + if let Some((sym::as_str, recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } sliced_string_as_bytes::check(cx, expr, recv); }, - ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), - ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, self.msrv), - ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), - ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("bytes", []) => unbuffered_bytes::check(cx, expr, recv), - ("cloned", []) => { + (sym::as_mut | sym::as_ref, []) => useless_asref::check(cx, expr, name, recv), + (sym::as_ptr, []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, self.msrv), + (sym::assume_init, []) => uninit_assumed_init::check(cx, expr, recv), + (sym::bytes, []) => unbuffered_bytes::check(cx, expr, recv), + (sym::cloned, []) => { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); option_as_ref_cloned::check(cx, recv, span); }, - ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + (sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, - Some(("map", m_recv, [m_arg], m_ident_span, _)) => { + Some((sym::map, m_recv, [m_arg], m_ident_span, _)) => { map_collect_result_unit::check(cx, expr, m_recv, m_arg); format_collect::check(cx, expr, m_arg, m_ident_span); }, - Some(("take", take_self_arg, [take_arg], _, _)) => { + Some((sym::take, take_self_arg, [take_arg], _, _)) => { if self.msrv.meets(cx, msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, - Some(("drain", recv, args, ..)) => { + Some((sym::drain, recv, args, ..)) => { drain_collect::check(cx, args, expr, recv); }, _ => {}, } }, - ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => { + (sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { + Some((sym::cloned, recv2, [], _, _)) => { iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false); }, - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { + Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, - ("min" | "max", [arg]) => { + (sym::min | sym::max, [arg]) => { unnecessary_min_or_max::check(cx, expr, name, recv, arg); }, - ("drain", ..) => { + (sym::drain, ..) => { if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.parent_hir_node(expr.hir_id) && matches!(kind, StmtKind::Semi(_)) && args.len() <= 1 @@ -5040,31 +5040,31 @@ impl Methods { iter_with_drain::check(cx, expr, recv, span, arg); } }, - ("ends_with", [arg]) => { + (sym::ends_with, [arg]) => { if let ExprKind::MethodCall(.., span) = expr.kind { case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg, self.msrv); } path_ends_with_ext::check(cx, recv, arg, expr, self.msrv, &self.allowed_dotfiles); }, - ("expect", [_]) => { + (sym::expect, [_]) => { match method_call(recv) { - Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span, _)) => { + Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some((sym::err, recv, [], err_span, _)) => { err_expect::check(cx, expr, recv, span, err_span, self.msrv); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("expect_err", [_]) | ("unwrap_err" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => { + (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("extend", [arg]) => { + (sym::extend, [arg]) => { string_extend_chars::check(cx, expr, recv, arg); extend_with_drain::check(cx, expr, recv, arg); }, - ("filter", [arg]) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::filter, [arg]) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { // if `arg` has side-effect, the semantic will change iter_overeager_cloned::check( cx, @@ -5080,8 +5080,8 @@ impl Methods { iter_filter::check(cx, expr, arg, span); } }, - ("find", [arg]) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::find, [arg]) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { // if `arg` has side-effect, the semantic will change iter_overeager_cloned::check( cx, @@ -5093,26 +5093,26 @@ impl Methods { ); } }, - ("filter_map", [arg]) => { + (sym::filter_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, - ("find_map", [arg]) => { + (sym::find_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); }, - ("flat_map", [arg]) => { + (sym::flat_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); }, - ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span, _)) => { + (sym::flatten, []) => match method_call(recv) { + Some((sym::map, recv, [map_arg], map_span, _)) => { map_flatten::check(cx, expr, recv, map_arg, map_span); }, - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5122,15 +5122,15 @@ impl Methods { ), _ => {}, }, - ("fold", [init, acc]) => { + (sym::fold, [init, acc]) => { manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv); unnecessary_fold::check(cx, expr, init, acc, span); }, - ("for_each", [arg]) => { + (sym::for_each, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); match method_call(recv) { - Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5141,44 +5141,44 @@ impl Methods { _ => {}, } }, - ("get", [arg]) => { + (sym::get, [arg]) => { get_first::check(cx, expr, recv, arg); get_last_with_len::check(cx, expr, recv, arg); }, - ("get_or_insert_with", [arg]) => { + (sym::get_or_insert_with, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); }, - ("hash", [arg]) => { + (sym::hash, [arg]) => { unit_hash::check(cx, expr, recv, arg); }, - ("is_empty", []) => { + (sym::is_empty, []) => { match method_call(recv) { - Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => { - needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span); + Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, prev_method, name, prev_recv, expr.span); }, - Some(("as_str", recv, [], as_str_span, _)) => { + Some((sym::as_str, recv, [], as_str_span, _)) => { redundant_as_str::check(cx, expr, recv, as_str_span, span); }, _ => {}, } is_empty::check(cx, expr, recv); }, - ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), - ("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false), - ("is_some", []) => check_is_some_is_none(cx, expr, recv, call_span, true), - ("iter" | "iter_mut" | "into_iter", []) => { + (sym::is_file, []) => filetype_is_file::check(cx, expr, recv), + (sym::is_digit, [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + (sym::is_none, []) => check_is_some_is_none(cx, expr, recv, call_span, false), + (sym::is_some, []) => check_is_some_is_none(cx, expr, recv, call_span, true), + (sym::iter | sym::iter_mut | sym::into_iter, []) => { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, - ("join", [join_arg]) => { - if let Some(("collect", _, _, span, _)) = method_call(recv) { + (sym::join, [join_arg]) => { + if let Some((sym::collect, _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } else { join_absolute_paths::check(cx, recv, join_arg, expr.span); } }, - ("last", []) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::last, []) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5190,24 +5190,24 @@ impl Methods { } double_ended_iterator_last::check(cx, expr, recv, call_span); }, - ("len", []) => { - if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) { - needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span); + (sym::len, []) => { + if let Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, prev_method, sym::len, prev_recv, expr.span); } }, - ("lock", []) => { + (sym::lock, []) => { mut_mutex_lock::check(cx, expr, recv, span); }, - (name @ ("map" | "map_err"), [m_arg]) => { - if name == "map" { + (name @ (sym::map | sym::map_err), [m_arg]) => { + if name == sym::map { unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, self.msrv); map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span); match method_call(recv) { - Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => { + Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => { iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv); }, - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5222,12 +5222,12 @@ impl Methods { } if let Some((name, recv2, args, span2, _)) = method_call(recv) { match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), - ("filter", [f_arg]) => { + (sym::as_mut, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), + (sym::as_ref, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + (sym::filter, [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, - ("find", [f_arg]) => { + (sym::find, [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true); }, _ => {}, @@ -5237,22 +5237,22 @@ impl Methods { manual_inspect::check(cx, expr, m_arg, name, span, self.msrv); crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); }, - ("map_break" | "map_continue", [m_arg]) => { + (sym::map_break | sym::map_continue, [m_arg]) => { crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); }, - ("map_or", [def, map]) => { + (sym::map_or, [def, map]) => { option_map_or_none::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map); unnecessary_map_or::check(cx, expr, recv, def, map, span, self.msrv); }, - ("map_or_else", [def, map]) => { + (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, - ("next", []) => { + (sym::next, []) => { if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { - ("cloned", []) => iter_overeager_cloned::check( + (sym::cloned, []) => iter_overeager_cloned::check( cx, expr, recv, @@ -5260,19 +5260,19 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), - ("iter", []) => iter_next_slice::check(cx, expr, recv2), - ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), - ("skip_while", [_]) => skip_while_next::check(cx, expr), - ("rev", []) => manual_next_back::check(cx, expr, recv, recv2), + (sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg), + (sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + (sym::iter, []) => iter_next_slice::check(cx, expr, recv2), + (sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg), + (sym::skip_while, [_]) => skip_while_next::check(cx, expr), + (sym::rev, []) => manual_next_back::check(cx, expr, recv, recv2), _ => {}, } } }, - ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + (sym::nth, [n_arg]) => match method_call(recv) { + Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5280,54 +5280,54 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - Some((iter_method @ ("iter" | "iter_mut"), iter_recv, [], iter_span, _)) => { + Some((iter_method @ (sym::iter | sym::iter_mut), iter_recv, [], iter_span, _)) => { if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) { iter_nth_zero::check(cx, expr, recv, n_arg); } }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, - ("ok_or_else", [arg]) => { + (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); }, - ("open", [_]) => { + (sym::open, [_]) => { open_options::check(cx, expr, recv); }, - ("or_else", [arg]) => { + (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, - ("push", [arg]) => { + (sym::push, [arg]) => { path_buf_push_overwrite::check(cx, expr, arg); }, - ("read_to_end", [_]) => { + (sym::read_to_end, [_]) => { verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG); }, - ("read_to_string", [_]) => { + (sym::read_to_string, [_]) => { verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG); }, - ("read_line", [arg]) => { + (sym::read_line, [arg]) => { read_line_without_trim::check(cx, expr, recv, arg); }, - ("repeat", [arg]) => { + (sym::repeat, [arg]) => { repeat_once::check(cx, expr, recv, arg); }, - (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => { + (name @ (sym::replace | sym::replacen), [arg1, arg2] | [arg1, arg2, _]) => { no_effect_replace::check(cx, expr, arg1, arg2); // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint if self.msrv.meets(cx, msrvs::PATTERN_TRAIT_CHAR_ARRAY) - && name == "replace" - && let Some(("replace", ..)) = method_call(recv) + && name == sym::replace + && let Some((sym::replace, ..)) = method_call(recv) { collapsible_str_replace::check(cx, expr, arg1, arg2); } }, - ("resize", [count_arg, default_arg]) => { + (sym::resize, [count_arg, default_arg]) => { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, - ("seek", [arg]) => { + (sym::seek, [arg]) => { if self.msrv.meets(cx, msrvs::SEEK_FROM_CURRENT) { seek_from_current::check(cx, expr, recv, arg); } @@ -5335,11 +5335,11 @@ impl Methods { seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); } }, - ("skip", [arg]) => { + (sym::skip, [arg]) => { iter_skip_zero::check(cx, expr, arg); iter_out_of_bounds::check_skip(cx, expr, recv, arg); - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5350,34 +5350,34 @@ impl Methods { ); } }, - ("sort", []) => { + (sym::sort, []) => { stable_sort_primitive::check(cx, expr, recv); }, - ("sort_by", [arg]) => { + (sym::sort_by, [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, false); }, - ("sort_unstable_by", [arg]) => { + (sym::sort_unstable_by, [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, true); }, - ("split", [arg]) => { + (sym::split, [arg]) => { str_split::check(cx, expr, recv, arg); }, - ("splitn" | "rsplitn", [count_arg, pat_arg]) => { + (sym::splitn | sym::rsplitn, [count_arg, pat_arg]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); } }, - ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { + (sym::splitn_mut | sym::rsplitn_mut, [count_arg, _]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, - ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", [arg]) => { + (sym::step_by, [arg]) => iterator_step_by_zero::check(cx, expr, arg), + (sym::take, [arg]) => { iter_out_of_bounds::check_take(cx, expr, recv, arg); manual_repeat_n::check(cx, expr, recv, arg, self.msrv); - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5388,74 +5388,89 @@ impl Methods { ); } }, - ("take", []) => needless_option_take::check(cx, expr, recv), - ("then", [arg]) => { + (sym::take, []) => needless_option_take::check(cx, expr, recv), + (sym::then, [arg]) => { if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { return; } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - ("try_into", []) if is_trait_method(cx, expr, sym::TryInto) => { + (sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, - ("to_owned", []) => { + (sym::to_owned, []) => { if !suspicious_to_owned::check(cx, expr, recv) { implicit_clone::check(cx, name, expr, recv); } }, - ("to_os_string" | "to_path_buf" | "to_vec", []) => { + (sym::to_os_string | sym::to_path_buf | sym::to_vec, []) => { implicit_clone::check(cx, name, expr, recv); }, - ("type_id", []) => { + (sym::type_id, []) => { type_id_on_box::check(cx, recv, expr.span); }, - ("unwrap", []) => { + (sym::unwrap, []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _, _)) => { + Some((sym::get, recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _, _)) => { + Some((sym::get_mut, recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span, _)) => { + Some((sym::or, recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or", [u_arg]) => { + (sym::unwrap_or, [u_arg]) => { match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { - manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); + Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => { + manual_saturating_arithmetic::check( + cx, + expr, + lhs, + rhs, + u_arg, + &arith.as_str()[const { "checked_".len() }..], + ); }, - Some(("map", m_recv, [m_arg], span, _)) => { + Some((sym::map, m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, "unwrap_or"); + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or_default", []) => { + (sym::unwrap_or_default, []) => { match method_call(recv) { - Some(("map", m_recv, [arg], span, _)) => { + Some((sym::map, m_recv, [arg], span, _)) => { manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); }, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, None, then_method, "unwrap_or_default"); + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + None, + then_method, + sym::unwrap_or_default, + ); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or_else", [u_arg]) => { + (sym::unwrap_or_else, [u_arg]) => { match method_call(recv) { - Some(("map", recv, [map_arg], _, _)) + Some((sym::map, recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { obfuscated_if_else::check( cx, expr, @@ -5463,7 +5478,7 @@ impl Methods { t_arg, Some(u_arg), then_method, - "unwrap_or_else", + sym::unwrap_or_else, ); }, _ => { @@ -5472,13 +5487,13 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("wake", []) => { + (sym::wake, []) => { waker_clone_wake::check(cx, expr, recv); }, - ("write", []) => { + (sym::write, []) => { readonly_write_lock::check(cx, expr, recv); }, - ("zip", [arg]) => { + (sym::zip, [arg]) => { if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind && name.ident.name == sym::iter { @@ -5490,8 +5505,8 @@ impl Methods { } // Handle method calls whose receiver and arguments may come from expansion if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { - match (path.ident.name.as_str(), args) { - ("expect", [_]) if !matches!(method_call(recv), Some(("ok" | "err", _, [], _, _))) => { + match (path.ident.name, args) { + (sym::expect, [_]) if !matches!(method_call(recv), Some((sym::ok | sym::err, _, [], _, _))) => { unwrap_expect_used::check( cx, expr, @@ -5502,7 +5517,7 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, - ("expect_err", [_]) => { + (sym::expect_err, [_]) => { unwrap_expect_used::check( cx, expr, @@ -5513,7 +5528,7 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, - ("unwrap", []) => { + (sym::unwrap, []) => { unwrap_expect_used::check( cx, expr, @@ -5524,7 +5539,7 @@ impl Methods { unwrap_expect_used::Variant::Unwrap, ); }, - ("unwrap_err", []) => { + (sym::unwrap_err, []) => { unwrap_expect_used::check( cx, expr, @@ -5543,13 +5558,13 @@ impl Methods { fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, call_span: Span, is_some: bool) { match method_call(recv) { - Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) => { + Some((name @ (sym::find | sym::position | sym::rposition), f_recv, [arg], span, _)) => { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); }, - Some(("get", f_recv, [arg], _, _)) => { + Some((sym::get, f_recv, [arg], _, _)) => { unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some); }, - Some(("first", f_recv, [], _, _)) => { + Some((sym::first, f_recv, [], _, _)) => { unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some); }, _ => {}, @@ -5593,7 +5608,7 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { struct ShouldImplTraitCase { trait_name: &'static str, - method_name: &'static str, + method_name: Symbol, param_count: usize, fn_header: hir::FnHeader, // implicit self kind expected (none, self, &self, ...) @@ -5606,7 +5621,7 @@ struct ShouldImplTraitCase { impl ShouldImplTraitCase { const fn new( trait_name: &'static str, - method_name: &'static str, + method_name: Symbol, param_count: usize, fn_header: hir::FnHeader, self_kind: SelfKind, @@ -5639,36 +5654,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs index 7c9f7bae9906..635d06330e05 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs @@ -4,11 +4,11 @@ use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use super::NEEDLESS_AS_BYTES; -pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) { +pub fn check(cx: &LateContext<'_>, prev_method: Symbol, method: Symbol, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 4c1ed6a1d833..2b75d6a8248a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -508,7 +508,7 @@ fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet { match ty.kind() { ty::Adt(_, generics) => { for generic in *generics { - if let GenericArgKind::Type(ty) = generic.unpack() { + if let GenericArgKind::Type(ty) = generic.kind() { get_captured_ids_recursive(cx, ty, set); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index 538aa9097a40..d77d044340dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,22 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_res; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; +use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_hir::def::Res; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; use super::NEEDLESS_OPTION_AS_DEREF; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: Symbol) { let typeck = cx.typeck_results(); let outer_ty = typeck.expr_ty(expr); if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { - if name == "as_deref_mut" && recv.is_syntactic_place_expr() { + if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { let Res::Local(binding_id) = path_res(cx, recv) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs index cd1b97f3c51b..1544a12e6ba8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::NEEDLESS_OPTION_TAKE; @@ -42,20 +42,20 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// When this function is called, we are reasonably certain that the `ExprKind` is either /// `Call` or `MethodCall` because we already checked that the expression is not /// `is_syntactic_place_expr()`. -fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> { +fn source_of_temporary_value(expr: &Expr<'_>) -> Option { match expr.peel_borrows().kind { ExprKind::Call(function, _) => { if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind && !func_path.segments.is_empty() { - return Some(func_path.segments[0].ident.name.as_str()); + return Some(func_path.segments[0].ident.name); } if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind { - return Some(func_path_segment.ident.name.as_str()); + return Some(func_path_segment.ident.name); } None }, - ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name.as_str()), + ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs index 1cc56de48763..604b48656aea 100644 --- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,13 +1,14 @@ use super::OBFUSCATED_IF_ELSE; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_eager_eval; -use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; +use clippy_utils::{get_parent_expr, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind; use rustc_lint::LateContext; +use rustc_span::Symbol; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -15,8 +16,8 @@ pub(super) fn check<'tcx>( then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, unwrap_arg: Option<&'tcx hir::Expr<'_>>, - then_method_name: &str, - unwrap_method_name: &str, + then_method_name: Symbol, + unwrap_method_name: Symbol, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); @@ -31,25 +32,25 @@ pub(super) fn check<'tcx>( }; let if_then = match then_method_name { - "then" if let ExprKind::Closure(closure) = then_arg.kind => { + sym::then if let ExprKind::Closure(closure) = then_arg.kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + sym::then_some => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), _ => return, }; // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol let els = match unwrap_method_name { - "unwrap_or" => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), - "unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { + sym::unwrap_or => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), + sym::unwrap_or_else if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { + sym::unwrap_or_else if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" }, - "unwrap_or_default" => "Default::default()".into(), + sym::unwrap_or_default => "Default::default()".into(), _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs index 9b22494888fb..3c38deca6cd1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -1,14 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { - if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) + if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = + method_call(cloned_recv) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index b78b082e460e..38cb4d51ca0f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -12,7 +12,7 @@ use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; -use rustc_span::symbol::{self, Symbol}; +use rustc_span::Symbol; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_span: Span, - name: &str, + name: Symbol, receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], ) { @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. fn check_unwrap_or_default( cx: &LateContext<'_>, - name: &str, + name: Symbol, receiver: &hir::Expr<'_>, fun: &hir::Expr<'_>, call_expr: Option<&hir::Expr<'_>>, @@ -66,8 +66,8 @@ pub(super) fn check<'tcx>( }; let sugg = match (name, call_expr.is_some()) { - ("unwrap_or", true) | ("unwrap_or_else", false) => sym::unwrap_or_default, - ("or_insert", true) | ("or_insert_with", false) => sym::or_default, + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, _ => return false, }; @@ -126,7 +126,7 @@ pub(super) fn check<'tcx>( #[expect(clippy::too_many_arguments)] fn check_or_fn_call<'tcx>( cx: &LateContext<'tcx>, - name: &str, + name: Symbol, method_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, @@ -137,11 +137,16 @@ pub(super) fn check<'tcx>( fun_span: Option, ) -> bool { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ - (sym::BTreeEntry, false, &["or_insert"], "with"), - (sym::HashMapEntry, false, &["or_insert"], "with"), - (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (sym::Result, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 4] = [ + (sym::BTreeEntry, false, &[sym::or_insert], "with"), + (sym::HashMapEntry, false, &[sym::or_insert], "with"), + ( + sym::Option, + false, + &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], + "else", + ), + (sym::Result, true, &[sym::or, sym::unwrap_or], "else"), ]; if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) @@ -260,7 +265,7 @@ fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) && ident.name == sym::to_string && let hir::Expr { kind, .. } = self_arg && let hir::ExprKind::Lit(lit) = kind - && let ast::LitKind::Str(symbol::kw::Empty, _) = lit.node + && let ast::LitKind::Str(rustc_span::sym::empty, _) = lit.node { return true; } diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index 97c8ce2bcdd2..855babb797a2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -2,14 +2,13 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs}; +use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use super::SEARCH_IS_SOME; @@ -19,7 +18,7 @@ use super::SEARCH_IS_SOME; pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, - search_method: &str, + search_method: Symbol, is_some: bool, search_recv: &hir::Expr<'_>, search_arg: &'tcx hir::Expr<'_>, @@ -35,7 +34,7 @@ pub(super) fn check<'tcx>( // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` let mut applicability = Applicability::MachineApplicable; - let any_search_snippet = if search_method == "find" + let any_search_snippet = if search_method == sym::find && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind && let closure_body = cx.tcx.hir_body(body) && let Some(closure_arg) = closure_body.params.first() @@ -107,7 +106,7 @@ pub(super) fn check<'tcx>( } } // lint if `find()` is called by `String` or `&str` - else if search_method == "find" { + else if search_method == sym::find { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); if is_type_lang_item(cx, self_ty, hir::LangItem::String) { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index fb4ac7b3613d..479064a0671e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -15,7 +15,7 @@ pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &' // or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind - && trim_method_name.ident.as_str() == "trim" + && trim_method_name.ident.name == sym::trim && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index c8efb600f576..6935ae1f391f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, path_to_local_id, paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -12,13 +12,13 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, Symbol, SyntaxContext, sym}; +use rustc_span::{Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; pub(super) fn check( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, @@ -45,9 +45,9 @@ pub(super) fn check( } } -fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { +fn lint_needless(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { let mut app = Applicability::MachineApplicable; - let r = if method_name == "splitn" { "" } else { "r" }; + let r = if method_name == sym::splitn { "" } else { "r" }; span_lint_and_sugg( cx, @@ -66,14 +66,14 @@ fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_ fn check_manual_split_once( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, usage: &IterUsage, ) { let ctxt = expr.span.ctxt(); - let (msg, reverse) = if method_name == "splitn" { + let (msg, reverse) = if method_name == sym::splitn { ("manual implementation of `split_once`", false) } else { ("manual implementation of `rsplit_once`", true) @@ -121,7 +121,7 @@ fn check_manual_split_once( /// ``` fn check_manual_split_once_indirect( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, @@ -143,7 +143,7 @@ fn check_manual_split_once_indirect( && first.name != second.name && !local_used_after_expr(cx, iter_binding_id, second.init_expr) { - let (r, lhs, rhs) = if method_name == "splitn" { + let (r, lhs, rhs) = if method_name == sym::splitn { ("", first.name, second.name) } else { ("r", second.name, first.name) diff --git a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs index c7885f689d75..f11a41f90f1a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{method_chain_args, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { return; } - if let Some(arglists) = method_chain_args(arg, &["chars"]) { + if let Some(arglists) = method_chain_args(arg, &[sym::chars]) { let target = &arglists[0].0; let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if self_ty.is_str() { diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs index ff5c1d1a4019..f8b6d4349fbe 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_note; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_span::Symbol; use rustc_span::source_map::Spanned; use super::SUSPICIOUS_SPLITN; -pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { +pub(super) fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { if count <= 1 && let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(call_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index 79ed352193fd..d260e0ef6e19 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -9,10 +9,16 @@ use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_span::Symbol; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + name: Symbol, +) { if !is_trait_method(cx, expr, sym::Iterator) { return; } @@ -38,7 +44,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { // Check if the closure is .filter_map(|x| Some(x)) - if name == "filter_map" + if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) && let hir::ExprKind::Path(_) = args[0].kind @@ -51,7 +57,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a ); return; } - if name == "filter_map" { + if name == sym::filter_map { "map(..)" } else { "map(..).next()" @@ -61,7 +67,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => { - if name == "filter_map" { "filter(..)" } else { "find(..)" } + if name == sym::filter_map { + "filter(..)" + } else { + "find(..)" + } }, _ => return, } @@ -70,7 +80,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a }; span_lint( cx, - if name == "filter_map" { + if name == sym::filter_map { UNNECESSARY_FILTER_MAP } else { UNNECESSARY_FIND_MAP diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index fa3a29e36670..cc4448192d3e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res}; +use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_span::Symbol; use super::UNNECESSARY_LITERAL_UNWRAP; @@ -25,7 +26,7 @@ pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, - method: &str, + method: Symbol, args: &[hir::Expr<'_>], ) { let init = clippy_utils::expr_or_init(cx, recv); @@ -42,17 +43,17 @@ pub(super) fn check( let res = cx.qpath_res(qpath, call.hir_id()); if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { - ("Some", call_args, get_ty_from_args(args, 0)) + (sym::Some, call_args, get_ty_from_args(args, 0)) } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) { - ("Ok", call_args, get_ty_from_args(args, 0)) + (sym::Ok, call_args, get_ty_from_args(args, 0)) } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) { - ("Err", call_args, get_ty_from_args(args, 1)) + (sym::Err, call_args, get_ty_from_args(args, 1)) } else { return; } } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; - ("None", call_args, None) + (sym::None, call_args, None) } else { return; }; @@ -62,12 +63,12 @@ pub(super) fn check( span_lint_and_then(cx, UNNECESSARY_LITERAL_UNWRAP, expr.span, help_message, |diag| { let suggestions = match (constructor, method, ty) { - ("None", "unwrap", _) => Some(vec![(expr.span, "panic!()".to_string())]), - ("None", "expect", _) => Some(vec![ + (sym::None, sym::unwrap, _) => Some(vec![(expr.span, "panic!()".to_string())]), + (sym::None, sym::expect, _) => Some(vec![ (expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()), (expr.span.with_lo(args[0].span.hi()), ")".to_string()), ]), - ("Some" | "Ok", "unwrap_unchecked", _) | ("Err", "unwrap_err_unchecked", _) => { + (sym::Some | sym::Ok, sym::unwrap_unchecked, _) | (sym::Err, sym::unwrap_err_unchecked, _) => { let mut suggs = vec![ (recv.span.with_hi(call_args[0].span.lo()), String::new()), (expr.span.with_lo(call_args[0].span.hi()), String::new()), @@ -83,7 +84,7 @@ pub(super) fn check( } Some(suggs) }, - ("None", "unwrap_or_default", _) => { + (sym::None, sym::unwrap_or_default, _) => { let ty = cx.typeck_results().expr_ty(expr); let default_ty_string = if let ty::Adt(def, ..) = ty.kind() { with_forced_trimmed_paths!(format!("{}", cx.tcx.def_path_str(def.did()))) @@ -92,11 +93,11 @@ pub(super) fn check( }; Some(vec![(expr.span, format!("{default_ty_string}::default()"))]) }, - ("None", "unwrap_or", _) => Some(vec![ + (sym::None, sym::unwrap_or, _) => Some(vec![ (expr.span.with_hi(args[0].span.lo()), String::new()), (expr.span.with_lo(args[0].span.hi()), String::new()), ]), - ("None", "unwrap_or_else", _) => match args[0].kind { + (sym::None, sym::unwrap_or_else, _) => match args[0].kind { hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![ (expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()), String::new()), (expr.span.with_lo(args[0].span.hi()), String::new()), @@ -105,14 +106,14 @@ pub(super) fn check( }, _ if call_args.is_empty() => None, (_, _, Some(_)) => None, - ("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![ + (sym::Ok, sym::unwrap_err, None) | (sym::Err, sym::unwrap, None) => Some(vec![ ( recv.span.with_hi(call_args[0].span.lo()), "panic!(\"{:?}\", ".to_string(), ), (expr.span.with_lo(call_args[0].span.hi()), ")".to_string()), ]), - ("Ok", "expect_err", None) | ("Err", "expect", None) => Some(vec![ + (sym::Ok, sym::expect_err, None) | (sym::Err, sym::expect, None) => Some(vec![ ( recv.span.with_hi(call_args[0].span.lo()), "panic!(\"{1}: {:?}\", ".to_string(), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs index 7d01bdc2269b..413881d5ec99 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -5,16 +5,17 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, - name: &str, + name: Symbol, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, ) { @@ -47,10 +48,10 @@ pub(super) fn check<'tcx>( } } -fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, name: &str, lhs: Span, rhs: Span, order: Ordering) { +fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, name: Symbol, lhs: Span, rhs: Span, order: Ordering) { let cmp_str = if order.is_ge() { "smaller" } else { "greater" }; - let suggested_value = if (name == "min" && order.is_ge()) || (name == "max" && order.is_le()) { + let suggested_value = if (name == sym::min && order.is_ge()) || (name == sym::max && order.is_le()) { snippet(cx, rhs, "..") } else { snippet(cx, lhs, "..") diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index fb4984914eb0..dbff08bc51c9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -188,7 +188,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(_))) } pub(super) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 87bb8d46a1d6..768bbebccd4a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -608,7 +608,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< } fn has_lifetime(ty: Ty<'_>) -> bool { - ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_))) + ty.walk().any(|t| matches!(t.kind(), GenericArgKind::Lifetime(_))) } /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. @@ -619,7 +619,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, method_name.as_str(), method_def_id) + is_clone_like(cx, method_name, method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) } @@ -643,7 +643,7 @@ fn is_to_string_on_string_like<'a>( if let Some(args) = cx.typeck_results().node_args_opt(call_expr.hir_id) && let [generic_arg] = args.as_slice() - && let GenericArgKind::Type(ty) = generic_arg.unpack() + && let GenericArgKind::Type(ty) = generic_arg.kind() && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) && (cx.get_associated_type(ty, deref_trait_id, sym::Target) == Some(cx.tcx.types.str_) @@ -655,11 +655,18 @@ fn is_to_string_on_string_like<'a>( } } -fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashSet) - || is_type_diagnostic_item(cx, ty, sym::HashMap) - || is_type_diagnostic_item(cx, ty, sym::BTreeMap) - || is_type_diagnostic_item(cx, ty, sym::BTreeSet) +fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) + if matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::BTreeMap | sym::BTreeSet | sym::HashMap | sym::HashSet) + ) => + { + Some(args.type_at(0)) + }, + _ => None, + } } fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { @@ -679,11 +686,11 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = arg.kind && let ExprKind::MethodCall(method_path, caller, &[], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let method_name = method_path.ident.name.as_str() + && let method_name = method_path.ident.name && match method_name { - "to_owned" => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id), - "to_string" => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id), - "to_vec" => cx + sym::to_owned => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id), + sym::to_string => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id), + sym::to_vec => cx .tcx .impl_of_method(method_def_id) .filter(|&impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice()) @@ -721,6 +728,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx // 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`). +// 4. The key to the "map type" is not a reference. 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) @@ -738,7 +746,9 @@ fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { }) && let caller_ty = cx.typeck_results().expr_ty(caller) // For now we limit it to "map types". - && is_a_std_map_type(cx, caller_ty) + && let Some(key_ty) = std_map_key(cx, caller_ty) + // We need to check that the key type is not a reference. + && !key_ty.is_ref() { check_if_applicable_to_argument(cx, &arg); } diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index 17e2620d9dd4..d30c12e0c483 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -7,7 +7,7 @@ use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use core::ops::ControlFlow; @@ -39,7 +39,7 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option> { } /// Checks for the `USELESS_ASREF` lint. -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbol, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { @@ -79,9 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } - } else if let Some(impl_id) = cx.tcx.opt_parent(def_id) + } else if let Some(impl_id) = cx.tcx.impl_of_method(def_id) && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() - && (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did())) + && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); @@ -161,7 +161,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { } } -fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: &str) { +fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: Symbol) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 7384e534ed7d..ad9b3c364542 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::fmt; use super::WRONG_SELF_CONVENTION; @@ -83,17 +83,18 @@ impl fmt::Display for Convention { #[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - item_name: &str, + item_name: Symbol, self_ty: Ty<'tcx>, first_arg_ty: Ty<'tcx>, first_arg_span: Span, implements_trait: bool, is_trait_item: bool, ) { + let item_name_str = item_name.as_str(); if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { convs .iter() - .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) + .all(|conv| conv.check(cx, self_ty, item_name_str, implements_trait, is_trait_item)) }) { // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032) if implements_trait diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index f3e24044fb6c..a6be7581c9a3 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -7,7 +7,7 @@ use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; -use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind}; +use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind, OwnerId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { } }, FnKind::Method(_, sig, ..) => { - if already_const(sig.header) || trait_ref_of_method(cx, def_id).is_some() { + if already_const(sig.header) || trait_ref_of_method(cx, OwnerId { def_id }).is_some() { return; } }, diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index be7dd74fd62b..d4d33029dbdb 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -225,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { && let typeck_results = cx.tcx.typeck_body(*body_id) && should_lint(cx, typeck_results, block) // we intentionally only lint structs, see lint description - && let ItemKind::Struct(_, data, _) = &self_item.kind + && let ItemKind::Struct(_, _, data) = &self_item.kind { check_struct(cx, typeck_results, block, self_ty, item, data); } diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index a45031ce22b9..98a9a98d281a 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() { self.check_sig(cx, item.owner_id.def_id, sig.decl); } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index f768e11a4a2b..3ed4b1c2ea94 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, + is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, sym, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -320,7 +320,7 @@ fn check_comparison<'a, 'tcx>( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), ); - if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() { + if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() { return; } if l_ty.is_bool() && r_ty.is_bool() { diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index e579dd5947d7..2efb55b9880c 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -269,7 +269,7 @@ fn needless_borrow_count<'tcx>( .tcx .is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) && let ty::Param(param_ty) = trait_predicate.self_ty().kind() - && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() + && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].kind() && ty.is_array() && !msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) { diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 275d710c76a9..95623467b815 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -212,7 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_diagnostic_item(cx, ty, sym::Vec) - && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]) + && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path .segments @@ -253,8 +253,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_lang_item(cx, ty, LangItem::String) - && let Some(clone_spans) = - get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) + && let Some(clone_spans) = get_spans( + cx, + Some(body.id()), + idx, + &[(sym::clone, ".to_string()"), (sym::as_str, "")], + ) { diag.span_suggestion( input.span, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 7ab7976d5697..02c48166131e 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -221,10 +221,16 @@ fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } +/// Checks if dropping `expr` might have a visible side effect. +fn expr_ty_has_significant_drop(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + ty.has_significant_drop(cx.tcx, cx.typing_env()) +} + fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Lit(..) | ExprKind::Closure { .. } => true, - ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), + ExprKind::Path(..) => !expr_ty_has_significant_drop(cx, expr), ExprKind::Index(a, b, _) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)), ExprKind::Repeat(inner, _) @@ -233,8 +239,8 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) | ExprKind::AddrOf(_, _, inner) => has_no_effect(cx, inner), - ExprKind::Struct(_, fields, ref base) => { - !has_drop(cx, cx.typeck_results().expr_ty(expr)) + ExprKind::Struct(_, fields, base) => { + !expr_ty_has_significant_drop(cx, expr) && fields.iter().all(|field| has_no_effect(cx, field.expr)) && match &base { StructTailExpr::None | StructTailExpr::DefaultFields(_) => true, @@ -252,7 +258,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) ); if def_matched || is_range_literal(expr) { - !has_drop(cx, cx.typeck_results().expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg)) + !expr_ty_has_significant_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg)) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 6d3e77b6b6e9..a27c6aa75e36 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -1,56 +1,78 @@ -use std::ptr; +// Implementation for lints detecting interior mutability in constants. +// +// For `declare_interior_mutable_const` there are three strategies used to +// determine if a value has interior mutability: +// * A type-based check. This is the least accurate, but can always run. +// * A const-eval based check. This is the most accurate, but this requires that the value is +// defined and does not work with generics. +// * A HIR-tree based check. This is less accurate than const-eval, but it can be applied to generic +// values. +// +// For `borrow_interior_mutable_const` the same three strategies are applied +// when checking a constant's value, but field and array index projections at +// the borrow site are taken into account as well. As an example: `FOO.bar` may +// have interior mutability, but `FOO.baz` may not. When borrowing `FOO.baz` no +// warning will be issued. +// +// No matter the lint or strategy, a warning should only be issued if a value +// definitely contains interior mutability. use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::{InteriorMut, implements_trait}; -use rustc_abi::VariantIdx; +use clippy_utils::paths::{PathNS, lookup_path_str}; +use clippy_utils::ty::{get_field_idx_by_name, implements_trait}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ - BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, + Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr, TraitItem, TraitItemKind, UnOp, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::mir::{ConstValue, UnevaluatedConst}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::{ + self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, + TypeckResults, TypingEnv, }; -use rustc_lint::{LateContext, LateLintPass, Lint}; -use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, sym}; +use std::collections::hash_map::Entry; -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks for declaration of `const` items which is interior - /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). + /// Checks for the declaration of named constant which contain interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. /// - /// The `const` should better be replaced by a `static` item if a global - /// variable is wanted, or replaced by a `const fn` if a constructor is wanted. + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's used. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` + /// + /// If shared modification of the value is desired, a `static` item is needed instead. + /// If that is not desired, a `const fn` constructor should be used to make it obvious + /// at the use site that a new value is created. /// /// ### Known problems - /// A "non-constant" const item is a legacy way to supply an - /// initialized value to downstream `static` items (e.g., the - /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, - /// and this lint should be suppressed. + /// Prior to `const fn` stabilization this was the only way to provide a value which + /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In + /// this case the use of `const` is required and this lint should be suppressed. /// - /// Even though the lint avoids triggering on a constant whose type has enums that have variants - /// with interior mutability, and its value uses non interior mutable variants (see - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); - /// it complains about associated constants without default values only based on its types; - /// which might not be preferable. - /// There're other enums plus associated constants cases that the lint cannot handle. - /// - /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issue - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -74,20 +96,44 @@ declare_clippy_lint! { "declaring `const` with interior mutability" } -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks if `const` items which is interior mutable (e.g., - /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. + /// Checks for a borrow of a named constant with interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. /// - /// The `const` value should be stored inside a `static` item. + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's borrowed + /// // for use by `fetch_add`. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` + /// + /// ### Known problems + /// This lint does not, and cannot in general, determine if the borrow of the constant + /// is used in a way which causes a mutation. e.g. + /// + /// ```rust + /// use core::cell::Cell; + /// const CELL: Cell = Cell::new(0); + /// fn get_cell() -> Cell { + /// // This is fine. It borrows a copy of `CELL`, but never mutates it through the + /// // borrow. + /// CELL.clone() + /// } + /// ``` + /// + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -113,60 +159,101 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -#[derive(Copy, Clone)] -enum Source<'tcx> { - Item { item: Span, ty: Ty<'tcx> }, - Assoc { item: Span }, - Expr { expr: Span }, +#[derive(Clone, Copy)] +enum IsFreeze { + /// The type and all possible values are `Freeze` + Yes, + /// The type itself is non-`Freeze`, but not all values are. + Maybe, + /// The type and all possible values are non-`Freeze` + No, +} +impl IsFreeze { + /// Merges the variants of a sum type (i.e. an enum). + fn from_variants(iter: impl Iterator) -> Self { + iter.fold(Self::Yes, |x, y| match (x, y) { + (Self::Maybe, _) | (_, Self::Maybe) | (Self::No, Self::Yes) | (Self::Yes, Self::No) => Self::Maybe, + (Self::No, Self::No) => Self::No, + (Self::Yes, Self::Yes) => Self::Yes, + }) + } + + /// Merges the fields of a product type (e.g. a struct or tuple). + fn from_fields(mut iter: impl Iterator) -> Self { + iter.try_fold(Self::Yes, |x, y| match (x, y) { + (Self::No, _) | (_, Self::No) => None, + (Self::Maybe, _) | (_, Self::Maybe) => Some(Self::Maybe), + (Self::Yes, Self::Yes) => Some(Self::Yes), + }) + .unwrap_or(Self::No) + } + + /// Checks if this is definitely `Freeze`. + fn is_freeze(self) -> bool { + matches!(self, Self::Yes) + } + + /// Checks if this is definitely not `Freeze`. + fn is_not_freeze(self) -> bool { + matches!(self, Self::No) + } } -impl Source<'_> { - #[must_use] - fn lint(&self) -> (&'static Lint, &'static str, Span) { +/// What operation caused a borrow to occur. +#[derive(Clone, Copy)] +enum BorrowCause { + Borrow, + Deref, + Index, + AutoDeref, + AutoBorrow, + AutoDerefField, +} +impl BorrowCause { + fn note(self) -> Option<&'static str> { match self { - Self::Item { item, .. } | Self::Assoc { item, .. } => ( - DECLARE_INTERIOR_MUTABLE_CONST, - "a `const` item should not be interior mutable", - *item, - ), - Self::Expr { expr } => ( - BORROW_INTERIOR_MUTABLE_CONST, - "a `const` item with interior mutability should not be borrowed", - *expr, - ), + Self::Borrow => None, + Self::Deref => Some("this deref expression is a call to `Deref::deref`"), + Self::Index => Some("this index expression is a call to `Index::index`"), + Self::AutoDeref => Some("there is a compiler inserted call to `Deref::deref` here"), + Self::AutoBorrow => Some("there is a compiler inserted borrow here"), + Self::AutoDerefField => { + Some("there is a compiler inserted call to `Deref::deref` when accessing this field") + }, } } } -fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { - let (lint, msg, span) = source.lint(); - span_lint_and_then(cx, lint, span, msg, |diag| { - if span.from_expansion() { - return; // Don't give suggestions into macros. - } - match source { - Source::Item { ty, .. } => { - let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { - return; - }; - if implements_trait(cx, ty, sync_trait, &[]) { - diag.help("consider making this a static item"); - } else { - diag.help( - "consider making this `Sync` so that it can go in a static item or using a `thread_local`", - ); - } - }, - Source::Assoc { .. } => (), - Source::Expr { .. } => { - diag.help("assign this const to a local or static variable, and use the variable here"); - }, - } - }); +/// The source of a borrow. Both what caused it and where. +struct BorrowSource<'tcx> { + expr: &'tcx Expr<'tcx>, + cause: BorrowCause, +} +impl<'tcx> BorrowSource<'tcx> { + fn new(tcx: TyCtxt<'tcx>, expr: &'tcx Expr<'tcx>, cause: BorrowCause) -> Self { + // Custom deref and index impls will always have an auto-borrow inserted since we + // never work with reference types. + let (expr, cause) = if matches!(cause, BorrowCause::AutoBorrow) + && let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + { + match parent.kind { + ExprKind::Unary(UnOp::Deref, _) => (parent, BorrowCause::Deref), + ExprKind::Index(..) => (parent, BorrowCause::Index), + ExprKind::Field(..) => (parent, BorrowCause::AutoDerefField), + _ => (expr, cause), + } + } else { + (expr, cause) + }; + Self { expr, cause } + } } pub struct NonCopyConst<'tcx> { - interior_mut: InteriorMut<'tcx>, + ignore_tys: DefIdSet, + // Cache checked types. We can recurse through a type multiple times so this + // can be hit quite frequently. + freeze_tys: FxHashMap, IsFreeze>, } impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); @@ -174,332 +261,629 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE impl<'tcx> NonCopyConst<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability), + ignore_tys: conf + .ignore_interior_mutability + .iter() + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) + .collect(), + freeze_tys: FxHashMap::default(), } } - fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { - // No branch that we check (yet) should continue if val isn't a branch - let Some(branched_val) = val.try_to_branch() else { - return false; - }; - match *ty.kind() { - // the fact that we have to dig into every structs to search enums - // leads us to the point checking `UnsafeCell` directly is the only option. - ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, - // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the - // contained value. - ty::Adt(def, ..) if def.is_union() => false, - ty::Array(ty, _) => branched_val - .iter() - .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Adt(def, args) if def.is_enum() => { - let Some((&variant_valtree, fields)) = branched_val.split_first() else { - return false; - }; - let variant_index = variant_valtree.unwrap_leaf(); - let variant_index = VariantIdx::from_u32(variant_index.to_u32()); - fields - .iter() - .copied() - .zip( - def.variants()[variant_index] + /// Checks if a value of the given type is `Freeze`, or may be depending on the value. + fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.freeze_tys.entry(ty) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let e = e.insert(IsFreeze::Yes); + if ty.is_freeze(tcx, typing_env) { + return IsFreeze::Yes; + } + let is_freeze = match *ty.kind() { + ty::Adt(adt, _) if adt.is_unsafe_cell() => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + ty::Adt(adt, _) if self.ignore_tys.contains(&adt.did()) => return IsFreeze::Yes, + ty::Adt(adt, args) if adt.is_enum() => IsFreeze::from_variants(adt.variants().iter().map(|v| { + IsFreeze::from_fields( + v.fields + .iter() + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ) + })), + // Workaround for `ManuallyDrop`-like unions. + ty::Adt(adt, args) + if adt.is_union() + && adt.non_enum_variant().fields.iter().any(|f| { + tcx.layout_of(typing_env.as_query_input(f.ty(tcx, args))) + .is_ok_and(|l| l.layout.size().bytes() == 0) + }) => + { + return IsFreeze::Yes; + }, + // Rust doesn't have the concept of an active union field so we have + // to treat all fields as active. + ty::Adt(adt, args) => IsFreeze::from_fields( + adt.non_enum_variant() .fields .iter() - .map(|field| field.ty(cx.tcx, args)), - ) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ), + ty::Array(ty, _) | ty::Pat(ty, _) => self.is_ty_freeze(tcx, typing_env, ty), + ty::Tuple(tys) => { + IsFreeze::from_fields(tys.iter().map(|ty| self.is_ty_freeze(tcx, typing_env, ty))) + }, + // Treat type parameters as though they were `Freeze`. + ty::Param(_) | ty::Alias(..) => return IsFreeze::Yes, + // TODO: check other types. + _ => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + }; + if !is_freeze.is_freeze() { + self.freeze_tys.insert(ty, is_freeze); + } + is_freeze }, - ty::Adt(def, args) => branched_val - .iter() - .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Tuple(tys) => branched_val - .iter() - .zip(tys) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) { - Ok(normalized_ty) if ty != normalized_ty => Self::is_value_unfrozen_raw_inner(cx, val, normalized_ty), - _ => false, - }, - _ => false, } } - fn is_value_unfrozen_raw( - cx: &LateContext<'tcx>, - result: Result, Ty<'tcx>>, ErrorHandled>, - ty: Ty<'tcx>, - ) -> bool { - result.map_or_else( - |err| { - // Consider `TooGeneric` cases as being unfrozen. - // This causes a false positive where an assoc const whose type is unfrozen - // have a value that is a frozen variant with a generic param (an example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). - // However, it prevents a number of false negatives that is, I think, important: - // 1. assoc consts in trait defs referring to consts of themselves (an example is - // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). - // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait - // defs (i.e. without substitute for `Self`). (e.g. borrowing - // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) - // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no - // enums. (An example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). - // One might be able to prevent these FNs correctly, and replace this with `false`; - // e.g. implementing `has_frozen_variant` described above, and not running this function - // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd - // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, - // similar to 2., but with a frozen variant) (e.g. borrowing - // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). - // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). - matches!(err, ErrorHandled::TooGeneric(..)) - }, - |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)), - ) - } - - fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { - let def_id = body_id.hir_id.owner.to_def_id(); - let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::new_raw(def_id, args); - let cid = GlobalId { - instance, - promoted: None, - }; - let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id); - let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP); - Self::is_value_unfrozen_raw(cx, result, ty) - } - - fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { - let args = cx.typeck_results().node_args(hir_id); - - let result = Self::const_eval_resolve( - cx.tcx, - cx.typing_env(), - ty::UnevaluatedConst::new(def_id, args), - DUMMY_SP, - ); - Self::is_value_unfrozen_raw(cx, result, ty) - } - - pub fn const_eval_resolve( + /// Checks if the given constant value is `Freeze`. Returns `Err` if the constant + /// cannot be read, but the result depends on the value. + fn is_value_freeze( + &mut self, tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> EvalToValTreeResult<'tcx> { - match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) { - Ok(Some(instance)) => { - let cid = GlobalId { - instance, - promoted: None, - }; - tcx.const_eval_global_id_for_typeck(typing_env, cid, span) + typing_env: TypingEnv<'tcx>, + ty: Ty<'tcx>, + val: ConstValue<'tcx>, + ) -> Result { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => Ok(true), + IsFreeze::Maybe if matches!(ty.kind(), ty::Adt(..) | ty::Array(..) | ty::Tuple(..)) => { + for &(val, ty) in tcx + .try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + { + if !self.is_value_freeze(tcx, typing_env, ty, val)? { + return Ok(false); + } + } + Ok(true) }, - Ok(None) => Err(ErrorHandled::TooGeneric(span)), - Err(err) => Err(ErrorHandled::Reported( - ReportedErrorInfo::non_const_eval_error(err), - span, - )), + IsFreeze::Maybe | IsFreeze::No => Ok(false), + } + } + + /// Checks if the given expression creates a value which is `Freeze`. + /// + /// This will return `true` if the type is maybe `Freeze`, but it cannot be + /// determined for certain from the value. + /// + /// `typing_env` and `gen_args` are from the constant's use site. + /// `typeck` and `e` are from the constant's definition site. + fn is_init_expr_freeze( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + gen_args: GenericArgsRef<'tcx>, + e: &'tcx Expr<'tcx>, + ) -> bool { + // Make sure to instantiate all types coming from `typeck` with `gen_args`. + let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args); + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => true, + IsFreeze::No => false, + IsFreeze::Maybe => match e.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(e) = b.expr => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e) + }, + ExprKind::Path(ref p) => { + let res = typeck.qpath_res(p, e.hir_id); + let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)).instantiate(tcx, gen_args); + match res { + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = + tcx.const_eval_resolve(typing_env, UnevaluatedConst::new(did, gen_args), DUMMY_SP) + && let Ok(is_freeze) = self.is_value_freeze(tcx, typing_env, ty, val) => + { + is_freeze + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((typeck, init)) = get_const_hir_value(tcx, typing_env, did, gen_args) => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, init) + }, + // Either this is a unit constructor, or some unknown value. + // In either case we consider the value to be `Freeze`. + _ => true, + } + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(p) = &callee.kind + && let res = typeck.qpath_res(p, callee.hir_id) + && matches!(res, Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)) => + { + args.iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)) + }, + ExprKind::Struct(_, fields, StructTailExpr::None) => fields + .iter() + .all(|f| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, f.expr)), + ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs + .iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)), + ExprKind::Repeat(e, _) => self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e), + _ => true, + }, + } + } + + /// Checks if the given expression (or a local projection of it) is both borrowed and + /// definitely a non-`Freeze` type. + fn is_non_freeze_expr_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + ) -> Option> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + let ty = typeck.expr_ty(src_expr); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + let is_freeze = self.is_ty_freeze(tcx, typing_env, ty); + if is_freeze.is_freeze() { + return None; + } + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| is_freeze.is_not_freeze()) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + match use_expr.kind { + ExprKind::Field(..) => {}, + ExprKind::Index(..) if ty.is_array() => {}, + ExprKind::AddrOf(..) if is_freeze.is_not_freeze() => { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, + } + src_expr = use_expr; + } + } + + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. Returns `Err` if the constant cannot be read, but the + /// result depends on the value. + fn is_non_freeze_val_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut val: ConstValue<'tcx>, + ) -> Result>, ()> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + let mut ty = typeck.expr_ty(src_expr); + loop { + // Normalized as we need to check if this is an array later. + ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + let res = if let Some(cause) = does_adjust_borrow(adjust) + && !self.is_value_freeze(tcx, typing_env, ty, val)? + { + Some(BorrowSource::new(tcx, src_expr, cause)) + } else { + None + }; + return Ok(res); + } + // Check only the type here as the result gets cached for each type. + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return Ok(None); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return Ok(None); + }; + let next_val = match use_expr.kind { + ExprKind::Field(_, name) => { + if let Some(idx) = get_field_idx_by_name(ty, name.name) { + tcx.try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + .get(idx) + } else { + return Ok(None); + } + }, + ExprKind::Index(_, idx, _) if ty.is_array() => { + let val = tcx.try_destructure_mir_constant_for_user_output(val, ty).ok_or(())?; + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + val.fields.get(idx as usize) + } else { + // It's some value in the array so check all of them. + for &(val, _) in val.fields { + if let Some(src) = + self.is_non_freeze_val_borrowed(tcx, typing_env, typeck, use_expr, val)? + { + return Ok(Some(src)); + } + } + return Ok(None); + } + }, + ExprKind::AddrOf(..) if !self.is_value_freeze(tcx, typing_env, ty, val)? => { + return Ok(Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow))); + }, + // All other expressions use the value. + _ => return Ok(None), + }; + src_expr = use_expr; + if let Some(&(next_val, next_ty)) = next_val { + ty = next_ty; + val = next_val; + } else { + return Ok(None); + } + } + } + + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. + /// + /// `typing_env` and `init_args` are from the constant's use site. + /// `init_typeck` and `init_expr` are from the constant's definition site. + #[expect(clippy::too_many_arguments, clippy::too_many_lines)] + fn is_non_freeze_init_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut init_typeck: &'tcx TypeckResults<'tcx>, + mut init_args: GenericArgsRef<'tcx>, + mut init_expr: &'tcx Expr<'tcx>, + ) -> Option> { + // Make sure to instantiate all types coming from `init_typeck` with `init_args`. + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + // First handle any adjustments since they are cheap to check. + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr)) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); + } + + // Then read through constants and blocks on the init expression before + // applying the next use expression. + loop { + match init_expr.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(next_init) = b.expr => + { + init_expr = next_init; + }, + ExprKind::Path(ref init_path) => { + let next_init_args = + EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)).instantiate(tcx, init_args); + match init_typeck.qpath_res(init_path, init_expr.hir_id) { + Res::Def(DefKind::Ctor(..), _) => return None, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = tcx.const_eval_resolve( + typing_env, + UnevaluatedConst::new(did, next_init_args), + DUMMY_SP, + ) && let Ok(res) = + self.is_non_freeze_val_borrowed(tcx, typing_env, init_typeck, src_expr, val) => + { + return res; + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((next_typeck, value)) = + get_const_hir_value(tcx, typing_env, did, next_init_args) => + { + init_typeck = next_typeck; + init_args = next_init_args; + init_expr = value; + }, + // There's no more that we can read from the init expression. Switch to a + // type based check. + _ => { + return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, src_expr); + }, + } + }, + _ => break, + } + } + + // Then a type check. Note we only check the type here as the result + // gets cached. + let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return None; + } + + // Finally reduce the init expression using the next use expression. + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + init_expr = match &use_expr.kind { + ExprKind::Field(_, name) => match init_expr.kind { + ExprKind::Struct(_, fields, _) + if let Some(field) = fields.iter().find(|f| f.ident.name == name.name) => + { + field.expr + }, + ExprKind::Tup(fields) + if let Ok(idx) = name.as_str().parse::() + && let Some(field) = fields.get(idx) => + { + field + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(callee_path) = &callee.kind + && matches!( + init_typeck.qpath_res(callee_path, callee.hir_id), + Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_) + ) + && let Ok(idx) = name.as_str().parse::() + && let Some(arg) = args.get(idx) => + { + arg + }, + // Revert to a type based check as we don't know the field's value. + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::Index(_, idx, _) if ty.is_array() => match init_expr.kind { + ExprKind::Array(fields) => { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + // If the index is out of bounds it means the code + // unconditionally panics. In that case there is no borrow. + fields.get(idx as usize)? + } else { + // Unknown index, just run the check for all values. + return fields.iter().find_map(|f| { + self.is_non_freeze_init_borrowed( + tcx, + typing_env, + typeck, + use_expr, + init_typeck, + init_args, + f, + ) + }); + } + }, + // Just assume the index expression doesn't panic here. + ExprKind::Repeat(field, _) => field, + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::AddrOf(..) + if !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr) => + { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, + }; + src_expr = use_expr; } } } impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(.., body_id) = it.kind { - let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); - if !ignored_macro(cx, it) - && self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_poly(cx, body_id, ty) - { - lint(cx, Source::Item { item: it.span, ty }); - } - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind { - let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity(); - - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - if self.interior_mut.is_interior_mut_ty(cx, normalized) - // When there's no default value, lint it only according to its type; - // in other words, lint consts whose value *could* be unfrozen, not definitely is. - // This feels inconsistent with how the lint treats generic types, - // which avoids linting types which potentially become unfrozen. - // One could check whether an unfrozen type have a *frozen variant* - // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), - // and do the same as the case of generic types at impl items. - // Note that it isn't sufficient to check if it has an enum - // since all of that enum's variants can be unfrozen: - // i.e. having an enum doesn't necessary mean a type has a frozen variant. - // And, implementing it isn't a trivial task; it'll probably end up - // re-implementing the trait predicate evaluation specific to `Freeze`. - && body_id_opt.is_none_or(|body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized)) - { - lint(cx, Source::Assoc { item: trait_item.span }); - } - } - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(_, body_id) = &impl_item.kind { - let item_def_id = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir_expect_item(item_def_id); - - match &item.kind { - ItemKind::Impl(Impl { - of_trait: Some(of_trait_ref), - .. - }) => { - if let Some(of_trait_def_id) = of_trait_ref.trait_def_id() - // Lint a trait impl item only when the definition is a generic type, - // assuming an assoc const is not meant to be an interior mutable type. - && let Some(of_assoc_item) = cx - .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id - && cx - .tcx - .layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input( - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound at the trait defs; - // and, in that case, the definition is *not* generic. - cx.tcx.normalize_erasing_regions( - ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id), - cx.tcx.type_of(of_assoc_item).instantiate_identity(), - ), - )) - .is_err() - // If there were a function like `has_frozen_variant` described above, - // we should use here as a frozen variant is a potential to be frozen - // similar to unknown layouts. - // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() - && let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty) - && self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); - } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Const(ident, .., body_id) = item.kind + && !ident.is_special() + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Yes => false, + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), }, - ItemKind::Impl(Impl { of_trait: None, .. }) => { - let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); - // Normalize assoc types originated from generic params. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - - if self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); - } - }, - _ => (), } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Path(qpath) = &expr.kind { - // Only lint if we use the const item inside a function. - if is_in_const_context(cx) { - return; - } - - // Make sure it is a const item. - let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { - return; - }; - - // Climb up to resolve any field access and explicit referencing. - let mut cur_expr = expr; - let mut dereferenced_expr = expr; - let mut needs_check_adjustment = true; - loop { - let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id); - if parent_id == cur_expr.hir_id { - break; - } - if let Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) { - match &parent_expr.kind { - ExprKind::AddrOf(..) => { - // `&e` => `e` must be referenced. - needs_check_adjustment = false; - }, - ExprKind::Field(..) => { - needs_check_adjustment = true; - - // Check whether implicit dereferences happened; - // if so, no need to go further up - // because of the same reason as the `ExprKind::Unary` case. - if cx - .typeck_results() - .expr_adjustments(dereferenced_expr) - .iter() - .any(|adj| matches!(adj.kind, Adjust::Deref(_))) - { - break; - } - - dereferenced_expr = parent_expr; - }, - ExprKind::Index(e, _, _) if ptr::eq(&raw const **e, cur_expr) => { - // `e[i]` => desugared to `*Index::index(&e, i)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; - }, - ExprKind::Unary(UnOp::Deref, _) => { - // `*e` => desugared to `*Deref::deref(&e)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; - }, - _ => break, - } - cur_expr = parent_expr; - } else { - break; - } - } - - let ty = if needs_check_adjustment { - let adjustments = cx.typeck_results().expr_adjustments(dereferenced_expr); - if let Some(i) = adjustments - .iter() - .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_))) - { - if i == 0 { - cx.typeck_results().expr_ty(dereferenced_expr) + && !item.span.in_external_macro(cx.sess().source_map()) + // Only needed when compiling `std` + && !is_thread_local(cx, item) + { + span_lint_and_then( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + ident.span, + "named constant with interior mutability", + |diag| { + let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { + return; + }; + if implements_trait(cx, ty, sync_trait, &[]) { + diag.help("did you mean to make this a `static` item"); } else { - adjustments[i - 1].target + diag.help("did you mean to make this a `thread_local!` item"); } - } else { - // No borrow adjustments means the entire const is moved. - return; - } - } else { - cx.typeck_results().expr_ty(dereferenced_expr) - }; + }, + ); + } + } - if self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) - { - lint(cx, Source::Expr { expr: expr.span }); + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Const(_, body_id_opt) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Maybe if let Some(body_id) = body_id_opt => { + match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => { + !is_freeze + }, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + } + }, + IsFreeze::Yes | IsFreeze::Maybe => false, } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Const(_, body_id) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::Yes => false, + IsFreeze::No => { + // If this is a trait impl, check if the trait definition is the source + // of the cell. + if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id()) + && let ItemKind::Impl(impl_block) = parent_item.kind + && let Some(of_trait) = impl_block.of_trait + && let Some(trait_id) = of_trait.trait_def_id() + { + // Replace all instances of `::AssocType` with the + // unit type and check again. If the result is the same then the + // trait definition is the cause. + let ty = (ReplaceAssocFolder { + tcx: cx.tcx, + trait_id, + self_ty: cx.tcx.type_of(parent_item.owner_id).instantiate_identity(), + }) + .fold_ty(cx.tcx.type_of(item.owner_id).instantiate_identity()); + // `ty` may not be normalizable, but that should be fine. + !self.is_ty_freeze(cx.tcx, cx.typing_env(), ty).is_not_freeze() + } else { + true + } + }, + // Even if this is from a trait, there are values which don't have + // interior mutability. + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + }, + } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(qpath) = &e.kind + && let typeck = cx.typeck_results() + && let Res::Def(DefKind::Const | DefKind::AssocConst, did) = typeck.qpath_res(qpath, e.hir_id) + // As of `1.80` constant contexts can't borrow any type with interior mutability + && !is_in_const_context(cx) + && !self.is_ty_freeze(cx.tcx, cx.typing_env(), typeck.expr_ty(e)).is_freeze() + && let Some(borrow_src) = { + // The extra block helps formatting a lot. + if let Ok(val) = cx.tcx.const_eval_resolve( + cx.typing_env(), + UnevaluatedConst::new(did, typeck.node_args(e.hir_id)), + DUMMY_SP, + ) && let Ok(src) = self.is_non_freeze_val_borrowed(cx.tcx, cx.typing_env(), typeck, e, val) + { + src + } else if let init_args = typeck.node_args(e.hir_id) + && let Some((init_typeck, init)) = get_const_hir_value(cx.tcx, cx.typing_env(), did, init_args) + { + self.is_non_freeze_init_borrowed(cx.tcx, cx.typing_env(), typeck, e, init_typeck, init_args, init) + } else { + self.is_non_freeze_expr_borrowed(cx.tcx, cx.typing_env(), typeck, e) + } + } + && !borrow_src.expr.span.in_external_macro(cx.sess().source_map()) + { + span_lint_and_then( + cx, + BORROW_INTERIOR_MUTABLE_CONST, + borrow_src.expr.span, + "borrow of a named constant with interior mutability", + |diag| { + if let Some(note) = borrow_src.cause.note() { + diag.note(note); + } + diag.help("this lint can be silenced by assigning the value to a local variable before borrowing"); + }, + ); } } } -fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { +struct ReplaceAssocFolder<'tcx> { + tcx: TyCtxt<'tcx>, + trait_id: DefId, + self_ty: Ty<'tcx>, +} +impl<'tcx> TypeFolder> for ReplaceAssocFolder<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(AliasTyKind::Projection, ty) = ty.kind() + && ty.trait_def_id(self.tcx) == self.trait_id + && ty.self_ty() == self.self_ty + { + self.tcx.types.unit + } else { + ty.super_fold_with(self) + } + } +} + +fn is_thread_local(cx: &LateContext<'_>, it: &Item<'_>) -> bool { macro_backtrace(it.span).any(|macro_call| { matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), @@ -507,3 +891,42 @@ fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { ) }) } + +/// Checks if the adjustment causes a borrow of the original value. Returns +/// `None` if the value is consumed instead of borrowed. +fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option { + match adjust.kind { + Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), + // Custom deref calls `::deref(&x)` resulting in a borrow. + Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + // All other adjustments read the value. + _ => None, + } +} + +/// Attempts to get the value of a constant as a HIR expression. Also gets the +/// `TypeckResults` associated with the constant's body. +fn get_const_hir_value<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + did: DefId, + args: GenericArgsRef<'tcx>, +) -> Option<(&'tcx TypeckResults<'tcx>, &'tcx Expr<'tcx>)> { + let did = did.as_local()?; + let (did, body_id) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::Item(item) if let ItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(_) + if let Ok(Some(inst)) = Instance::try_resolve(tcx, typing_env, did.into(), args) + && let Some(did) = inst.def_id().as_local() => + { + match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(item) if let TraitItemKind::Const(.., Some(body_id)) = item.kind => (did, body_id), + _ => return None, + } + }, + _ => return None, + }; + Some((tcx.typeck(did), tcx.hir_body(body_id).value)) +} diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs index 9542fed38759..8ff78ec7c580 100644 --- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -172,7 +172,7 @@ impl NonSendField<'_> { /// Example: `MyStruct>` => `vec![P, Q, R]` fn collect_generic_params(ty: Ty<'_>) -> Vec> { ty.walk() - .filter_map(|inner| match inner.unpack() { + .filter_map(|inner| match inner.kind() { GenericArgKind::Type(inner_ty) => Some(inner_ty), _ => None, }) @@ -208,7 +208,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t ty::Adt(_, args) => { if contains_pointer_like(cx, ty) { // descends only if ADT contains any raw pointers - args.iter().all(|generic_arg| match generic_arg.unpack() { + args.iter().all(|generic_arg| match generic_arg.kind() { GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), // Lifetimes and const generics are not solid part of ADT and ignored GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, @@ -226,7 +226,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t /// Checks if the type contains any pointer-like types in args (including nested ones) fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool { for ty_node in target_ty.walk() { - if let GenericArgKind::Type(inner_ty) = ty_node.unpack() { + if let GenericArgKind::Type(inner_ty) = ty_node.kind() { match inner_ty.kind() { ty::RawPtr(_, _) => { return true; diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index f66b9519317b..abee3c44c5a3 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -187,7 +187,7 @@ struct LazyInfo { impl LazyInfo { fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option { // Check if item is a `once_cell:sync::Lazy` static. - if let ItemKind::Static(_, ty, _, body_id) = item.kind + if let ItemKind::Static(_, _, ty, body_id) = item.kind && let Some(path_def_id) = path_def_id(cx, ty) && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 6de203e068b7..ba8f6354d976 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -385,7 +385,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { fn has_matching_args(kind: FnKind, args: GenericArgsRef<'_>) -> bool { match kind { FnKind::Fn => true, - FnKind::TraitFn => args.iter().enumerate().all(|(idx, subst)| match subst.unpack() { + FnKind::TraitFn => args.iter().enumerate().all(|(idx, subst)| match subst.kind() { GenericArgKind::Lifetime(_) => true, GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx), GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx), diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 03b907ebdf4d..4be42267b14b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( let rty = cx.typeck_results().expr_ty(rhs); if let Some((_, lang_item)) = binop_traits(op.node) && let Some(trait_id) = cx.tcx.lang_items().get(lang_item) - && let parent_fn = cx.tcx.hir_get_parent_item(e.hir_id).def_id + && let parent_fn = cx.tcx.hir_get_parent_item(e.hir_id) && trait_ref_of_method(cx, parent_fn).is_none_or(|t| t.path.res.def_id() != trait_id) && implements_trait(cx, ty, trait_id, &[rty.into()]) { diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index e3029f8438e5..6c9be7c5e90b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -1,11 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::sym; use super::DURATION_SUBSEC; @@ -21,9 +21,9 @@ pub(crate) fn check<'tcx>( && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) { - let suggested_fn = match (method_path.ident.as_str(), divisor) { - ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", - ("subsec_nanos", 1_000) => "subsec_micros", + let suggested_fn = match (method_path.ident.name, divisor) { + (sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis", + (sym::subsec_nanos, 1_000) => "subsec_micros", _ => return, }; let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs index d16f5f8e112c..64ad92b1ebb5 100644 --- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs @@ -37,7 +37,7 @@ impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind && matches!(seg.ident.name, sym::expect | sym::unwrap) - && is_direct_expn_of(receiver.span, "option_env").is_some() + && is_direct_expn_of(receiver.span, sym::option_env).is_some() { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs index cda752d003fa..65e93af9420d 100644 --- a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs +++ b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs @@ -41,7 +41,7 @@ declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); impl EarlyLintPass for PartialPubFields { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(_, ref st, _) = item.kind else { + let ItemKind::Struct(_, _, ref st) = item.kind else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs index e4a9bf7a8481..66c59cb70d36 100644 --- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -58,7 +58,7 @@ impl PubUnderscoreFields { impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { // This lint only pertains to structs. - let ItemKind::Struct(_, variant_data, _) = &item.kind else { + let ItemKind::Struct(_, _, variant_data) = &item.kind else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 122d97fdf819..6bc5af268ff8 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -423,7 +423,14 @@ fn check_final_expr<'tcx>( _ => return, } - emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id); + emit_return_lint( + cx, + peeled_drop_expr.span, + ret_span, + semi_spans, + &replacement, + expr.hir_id, + ); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -448,6 +455,7 @@ fn check_final_expr<'tcx>( fn emit_return_lint( cx: &LateContext<'_>, + lint_span: Span, ret_span: Span, semi_spans: Vec, replacement: &RetReplacement<'_>, @@ -457,7 +465,7 @@ fn emit_return_lint( cx, NEEDLESS_RETURN, at, - ret_span, + lint_span, "unneeded `return` statement", |diag| { let suggestions = std::iter::once((ret_span, replacement.to_string())) @@ -479,7 +487,7 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) .skip_binder() .output() .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index ccb1209c6fcb..521754f7bab7 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; +use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Span}; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -169,7 +169,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { let mut iter = get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), - "has_significant_drop", + sym::has_significant_drop, ); if iter.next().is_some() { return true; @@ -184,7 +184,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { } } for generic_arg in *b { - if let GenericArgKind::Type(ty) = generic_arg.unpack() + if let GenericArgKind::Type(ty) = generic_arg.kind() && self.has_sig_drop_attr(ty, depth) { return true; diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index 835ec1e4ca1c..3623039aece1 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::sym; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -62,17 +63,17 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { - const METHODS: [&str; 10] = [ - "copy_to", - "copy_from", - "copy_to_nonoverlapping", - "copy_from_nonoverlapping", - "add", - "wrapping_add", - "sub", - "wrapping_sub", - "offset", - "wrapping_offset", + const METHODS: [Symbol; 10] = [ + sym::copy_to, + sym::copy_from, + sym::copy_to_nonoverlapping, + sym::copy_from_nonoverlapping, + sym::add, + sym::wrapping_add, + sym::sub, + sym::wrapping_sub, + sym::offset, + sym::wrapping_offset, ]; if let ExprKind::Call(func, [.., count]) = expr.kind @@ -97,7 +98,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} - && let method_ident = method_path.ident.as_str() + && let method_ident = method_path.ident.name && METHODS.contains(&method_ident) // Get the pointee type diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 30a5fe4db27e..f497d0700b8e 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -266,7 +266,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // If we have a size expression, check that it is equal to what's passed to `resize` SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity) } else { self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); true @@ -288,7 +288,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // Check that len expression is equals to `with_capacity` expression return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity"); + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity); } self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index d68ac8bab128..3d39386ecf90 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; -use rustc_attr_parsing::{StabilityLevel, StableSince}; +use rustc_attr_data_structures::{StabilityLevel, StableSince}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 5c95dfe83473..f63e6b3087b9 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -5,9 +5,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::path_to_local_id; use clippy_utils::source::{snippet, str_literal_to_char_literal}; use clippy_utils::visitors::{Descend, for_each_expr}; +use clippy_utils::{path_to_local_id, sym}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -15,7 +15,7 @@ use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -83,29 +83,29 @@ impl StringPatterns { impl_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]); -const PATTERN_METHODS: [(&str, usize); 22] = [ - ("contains", 0), - ("starts_with", 0), - ("ends_with", 0), - ("find", 0), - ("rfind", 0), - ("split", 0), - ("split_inclusive", 0), - ("rsplit", 0), - ("split_terminator", 0), - ("rsplit_terminator", 0), - ("splitn", 1), - ("rsplitn", 1), - ("split_once", 0), - ("rsplit_once", 0), - ("matches", 0), - ("rmatches", 0), - ("match_indices", 0), - ("rmatch_indices", 0), - ("trim_start_matches", 0), - ("trim_end_matches", 0), - ("replace", 0), - ("replacen", 0), +const PATTERN_METHODS: [(Symbol, usize); 22] = [ + (sym::contains, 0), + (sym::starts_with, 0), + (sym::ends_with, 0), + (sym::find, 0), + (sym::rfind, 0), + (sym::split, 0), + (sym::split_inclusive, 0), + (sym::rsplit, 0), + (sym::split_terminator, 0), + (sym::rsplit_terminator, 0), + (sym::splitn, 1), + (sym::rsplitn, 1), + (sym::split_once, 0), + (sym::rsplit_once, 0), + (sym::matches, 0), + (sym::rmatches, 0), + (sym::match_indices, 0), + (sym::rmatch_indices, 0), + (sym::trim_start_matches, 0), + (sym::trim_end_matches, 0), + (sym::replace, 0), + (sym::replacen, 0), ]; fn check_single_char_pattern_lint(cx: &LateContext<'_>, arg: &Expr<'_>) { @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns { && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() && ty.is_str() - && let method_name = method.ident.name.as_str() + && let method_name = method.ident.name && let Some(&(_, pos)) = PATTERN_METHODS .iter() .find(|(array_method_name, _)| *array_method_name == method_name) diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index 1ada7094dc55..33856c750d7e 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_libc_symbol; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::is_expr_unsafe; +use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -44,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { && let ExprKind::Call(func, [recv]) = expr.kind && let ExprKind::Path(path) = &func.kind && let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id() - && match_libc_symbol(cx, did, "strlen") + && match_libc_symbol(cx, did, sym::strlen) && let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind && !recv.span.from_expansion() && path.ident.name == sym::as_ptr diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 83241f97a99a..edb7600b7c06 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -80,7 +80,7 @@ fn check_expr_inner<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, bin && let hir::Node::ImplItem(impl_item) = cx.tcx.hir_node_by_def_id(parent_fn) && let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind && let body = cx.tcx.hir_body(body_id) - && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id).def_id + && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id) && let Some(trait_ref) = trait_ref_of_method(cx, parent_fn) && let trait_id = trait_ref.path.res.def_id() && ![binop_trait_id, op_assign_trait_id].contains(&trait_id) diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs index 20bf3a0bff1c..75a82770af04 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } fn is_struct_with_trailing_zero_sized_array<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { - if let ItemKind::Struct(_, data, _) = &item.kind + if let ItemKind::Struct(_, _, data) = &item.kind && let Some(last_field) = data.fields().last() && let field_ty = cx.tcx.normalize_erasing_regions( cx.typing_env(), diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 95ce19975c7e..3c21d194b81d 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -66,7 +66,7 @@ impl LateLintPass<'_> for TupleArrayConversions { } fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) { - let (ty::Array(ty, _) | ty::Slice(ty)) = cx.typeck_results().expr_ty(expr).kind() else { + let Some(ty) = cx.typeck_results().expr_ty(expr).builtin_index() else { unreachable!("`expr` must be an array or slice due to `ExprKind::Array`"); }; @@ -85,7 +85,7 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & ExprKind::Path(_) => Some(elements.iter().collect()), _ => None, }) - && all_bindings_are_for_conv(cx, &[*ty], expr, elements, &locals, ToType::Array) + && all_bindings_are_for_conv(cx, &[ty], expr, elements, &locals, ToType::Array) && !is_from_proc_macro(cx, expr) { span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index c1c7cc516565..515be5adeed0 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -447,7 +447,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { - ItemKind::Static(_, ty, _, _) | ItemKind::Const(_, ty, _, _) => self.check_ty( + ItemKind::Static(_, _, ty, _) | ItemKind::Const(_, _, ty, _) => self.check_ty( cx, ty, CheckTyContext { diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index bcd05cceca9c..54a7efc090ad 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; @@ -78,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { fn_kind: FnKind<'tcx>, fn_decl: &FnDecl<'tcx>, body: &Body<'tcx>, - span: Span, + _span: Span, def_id: LocalDefId, ) { // Abort if public function/method or closure. @@ -147,19 +149,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { "remove the return type...".to_string(), // FIXME: we should instead get the span including the `->` and suggest an // empty string for this case. - "()".to_string(), - "...and then remove returned values", + Cow::Borrowed("()"), + Cow::Borrowed("...and then remove returned values"), ) } else { + let wrapper = if lang_item == OptionSome { "Some" } else { "Ok" }; ( format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), format!("remove `{return_type_label}` from the return type..."), - inner_type.to_string(), - "...and then change returning expressions", + Cow::Owned(inner_type.to_string()), + Cow::Owned(format!( + "...and then remove the surrounding `{wrapper}()` from returning expressions" + )), ) }; - span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg, |diag| { + span_lint_and_then(cx, UNNECESSARY_WRAPS, cx.tcx.def_span(def_id), lint_msg, |diag| { diag.span_suggestion( fn_decl.output.span(), return_type_sugg_msg, diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index 7487e273caa7..1f5351e32aac 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; +use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::ops::ControlFlow; declare_clippy_lint! { @@ -150,10 +149,10 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { remaining_args, _, ) => { - let method_name = method_name_ident.name.as_str(); + let method_name = method_name_ident.name; // `Peekable` methods - if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq") + if matches!(method_name, sym::peek | sym::peek_mut | sym::next_if | sym::next_if_eq) && arg_is_mut_peekable(self.cx, self_arg) { return ControlFlow::Break(()); @@ -167,7 +166,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { } // foo.by_ref(), keep checking for `peek` - if method_name == "by_ref" { + if method_name == sym::by_ref { continue; } diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs index 3e5afec541c4..c21004b5362d 100644 --- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs +++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -30,19 +32,20 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> { +fn is_useless_rounding(cx: &EarlyContext<'_>, expr: &Expr) -> Option<(Symbol, String)> { if let ExprKind::MethodCall(box MethodCall { seg: name_ident, receiver, .. }) = &expr.kind - && let method_name = name_ident.ident.name.as_str() - && (method_name == "ceil" || method_name == "round" || method_name == "floor") + && let method_name = name_ident.ident.name + && matches!(method_name, sym::ceil | sym::floor | sym::round) && let ExprKind::Lit(token_lit) = &receiver.kind && token_lit.is_semantic_float() && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::() + && f.fract() == 0.0 { - (f.fract() == 0.0).then(|| (method_name, snippet(cx, receiver.span, "..").to_string())) + Some((method_name, snippet(cx, receiver.span, "..").into())) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index f870eb71e19b..7bec212a23ca 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -79,7 +79,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc let mut result = Vec::new(); let _: Option = for_each_expr(cx, body.value, |e| { // check for `expect` - if let Some(arglists) = method_chain_args(e, &["expect"]) { + if let Some(arglists) = method_chain_args(e, &[sym::expect]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(cx, receiver_ty, sym::Option) || is_type_diagnostic_item(cx, receiver_ty, sym::Result) @@ -89,7 +89,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc } // check for `unwrap` - if let Some(arglists) = method_chain_args(e, &["unwrap"]) { + if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(cx, receiver_ty, sym::Option) || is_type_diagnostic_item(cx, receiver_ty, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs index 8922478e7183..02281b9e9223 100644 --- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs +++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs @@ -134,7 +134,7 @@ impl LateLintPass<'_> for UpperCaseAcronyms { ItemKind::TyAlias(ident, ..) | ItemKind::Struct(ident, ..) | ItemKind::Trait(_, _, ident, ..) => { check_ident(cx, &ident, it.hir_id(), self.upper_case_acronyms_aggressive); }, - ItemKind::Enum(ident, ref enumdef, _) => { + ItemKind::Enum(ident, _, ref enumdef) => { check_ident(cx, &ident, it.hir_id(), self.upper_case_acronyms_aggressive); // check enum variants separately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 743f54ca993a..aeda864b7eb5 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -319,7 +319,7 @@ fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { args_a .iter() .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { + .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { // TODO: Handle inferred lifetimes (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), @@ -337,7 +337,7 @@ fn has_no_lifetime(ty: MiddleTy<'_>) -> bool { &Adt(_, args) => !args .iter() // TODO: Handle inferred lifetimes - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(..))), + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))), _ => true, } } diff --git a/src/tools/clippy/clippy_lints/src/useless_concat.rs b/src/tools/clippy/clippy_lints/src/useless_concat.rs new file mode 100644 index 000000000000..1ed1fbb3b9c6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/useless_concat.rs @@ -0,0 +1,104 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::CONCAT; +use clippy_utils::source::snippet_opt; +use clippy_utils::tokenize_with_text; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lexer::TokenKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks that the `concat!` macro has at least two arguments. + /// + /// ### Why is this bad? + /// If there are less than 2 arguments, then calling the macro is doing nothing. + /// + /// ### Example + /// ```no_run + /// let x = concat!("a"); + /// ``` + /// Use instead: + /// ```no_run + /// let x = "a"; + /// ``` + #[clippy::version = "1.89.0"] + pub USELESS_CONCAT, + complexity, + "checks that the `concat` macro has at least two arguments" +} + +declare_lint_pass!(UselessConcat => [USELESS_CONCAT]); + +impl LateLintPass<'_> for UselessConcat { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // Check that the expression is generated by a macro. + if expr.span.from_expansion() + // Check that it's a string literal. + && let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(lit_s, _) = lit.node + // Get the direct parent of the expression. + && let Some(macro_call) = macro_backtrace(expr.span).next() + // Check if the `concat` macro from the `core` library. + && CONCAT.matches(cx, macro_call.def_id) + // We get the original code to parse it. + && let Some(original_code) = snippet_opt(cx, macro_call.span) + // This check allows us to ensure that the code snippet: + // 1. Doesn't come from proc-macro expansion. + // 2. Doesn't come from foreign macro expansion. + // + // It works as follows: if the snippet we get doesn't contain `concat!(`, then it + // means it's not code written in the current crate so we shouldn't lint. + && let mut parts = original_code.split('!') + && parts.next().is_some_and(|p| p.trim() == "concat") + && parts.next().is_some_and(|p| p.trim().starts_with('(')) + { + let mut literal = None; + let mut nb_commas = 0; + let mut nb_idents = 0; + for (token_kind, token_s, _) in tokenize_with_text(&original_code) { + match token_kind { + TokenKind::Eof => break, + TokenKind::Literal { .. } => { + if literal.is_some() { + return; + } + literal = Some(token_s); + }, + TokenKind::Ident => { + if token_s == "true" || token_s == "false" { + literal = Some(token_s); + } else { + nb_idents += 1; + } + }, + TokenKind::Comma => { + nb_commas += 1; + if nb_commas > 1 { + return; + } + }, + // We're inside a macro definition and we are manipulating something we likely + // shouldn't, so aborting. + TokenKind::Dollar => return, + _ => {}, + } + } + // There should always be the ident of the `concat` macro. + if nb_idents == 1 { + span_lint_and_sugg( + cx, + USELESS_CONCAT, + macro_call.span, + "unneeded use of `concat!` macro", + "replace with", + format!("{lit_s:?}"), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 3a9c997a579d..380ddea4e1e8 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -3,11 +3,11 @@ use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ - get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, + get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; @@ -15,7 +15,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { @@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" { + if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_and_consts(a, b) { @@ -298,6 +298,33 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. if same_type_and_consts(a, b) && !is_copy(cx, b) { + // Below we check if the parent method call meets the following conditions: + // 1. First parameter is `&mut self` (requires mutable reference) + // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) + // For methods satisfying these conditions (like any), .into_iter() must be preserved. + if let Some(parent) = get_parent_expr(cx, e) + && let ExprKind::MethodCall(_, recv, _, _) = parent.kind + && recv.hir_id == e.hir_id + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let sig = cx.tcx.fn_sig(def_id).skip_binder().skip_binder() + && let inputs = sig.inputs() + && inputs.len() >= 2 + && let Some(self_ty) = inputs.first() + && let ty::Ref(_, _, Mutability::Mut) = self_ty.kind() + && let Some(second_ty) = inputs.get(1) + && let predicates = cx.tcx.param_env(def_id).caller_bounds() + && predicates.iter().any(|pred| { + if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() { + trait_pred.self_ty() == *second_ty + && cx.tcx.lang_items().fn_mut_trait() == Some(trait_pred.def_id()) + } else { + false + } + }) + { + return; + } + let sugg = snippet(cx, recv.span, "").into_owned(); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 812c4df4ddde..3a08531cf1c9 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,4 +1,4 @@ -use clippy_utils::{MaybePath, get_attr, higher, path_def_id}; +use clippy_utils::{MaybePath, get_attr, higher, path_def_id, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -826,5 +826,5 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - get_attr(cx.sess(), attrs, "author").count() > 0 + get_attr(cx.sess(), attrs, sym::author).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs index 9910be9bc285..d6cf07fdaf3f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs +++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs @@ -1,4 +1,4 @@ -use clippy_utils::get_attr; +use clippy_utils::{get_attr, sym}; use hir::TraitItem; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -60,5 +60,5 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir { fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - get_attr(cx.sess(), attrs, "dump").count() > 0 + get_attr(cx.sess(), attrs, sym::dump).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 3346b15dae9c..7b6a25123e85 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -8,14 +8,14 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment}; +use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; -use rustc_span::{DesugaringKind, Span, sym}; +use rustc_span::{DesugaringKind, Span}; pub struct UselessVec { too_large_for_stack: u64, @@ -249,10 +249,8 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// that also exists on slices. If this returns true, it means that /// this expression does not actually require a `Vec` and could just work with an array. pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "is_empty"]; - if let ExprKind::MethodCall(path, _, [], _) = e.kind { - ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str()) + matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) } else { is_trait_method(cx, e, sym::IntoIterator) } diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index 24b1381ba458..1550872bca2b 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -51,9 +51,11 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) - // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of - // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 - && !ty.has_escaping_bound_vars() + // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. + // This might happen when computing a reference/pointer metadata on a type for which we + // cannot check if it is `Sized` or not, such as an incomplete associated type in a + // type alias. See an example in `issue14822()` of `tests/ui/zero_sized_hashmap_values.rs`. + && !ty.has_non_region_param() && let Ok(layout) = cx.layout_of(ty) && layout.is_zst() { diff --git a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs index 4fd5ea459a55..311f76fee6b8 100644 --- a/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs +++ b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs @@ -3,8 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use regex::Regex; use rustc_hir::{Attribute, Item, ItemKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_tool_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs index 407deb45db0a..7c9e7286925e 100644 --- a/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs @@ -4,8 +4,7 @@ use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use std::borrow::{Borrow, Cow}; diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs index 43cde86504f5..0c94d100c417 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lib.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -20,6 +20,7 @@ #![allow(clippy::missing_clippy_version_attribute)] extern crate rustc_ast; +extern crate rustc_attr_data_structures; extern crate rustc_attr_parsing; extern crate rustc_data_structures; extern crate rustc_errors; diff --git a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs index 655d8fb8d1bf..0edeef3ab855 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -10,9 +10,8 @@ use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; use rustc_middle::hir::nested_filter; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs index d48d8dc57b24..70b3c03d2bbd 100644 --- a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs @@ -4,9 +4,8 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_lint_defs::declare_tool_lint; use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_tool_lint! { /// ### What it does @@ -38,7 +37,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .type_of(f.did) .instantiate_identity() .walk() - .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) + .filter(|t| matches!(t.kind(), GenericArgKind::Type(_))) .any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty())) }) && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes") diff --git a/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs index 40951443a48a..5cdc66ae9053 100644 --- a/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs @@ -1,12 +1,11 @@ use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_lint_allowed, method_calls}; +use clippy_utils::{is_lint_allowed, method_calls, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; declare_tool_lint! { /// ### What it does @@ -40,8 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { } let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if let ["expn_data", "outer_expn"] = method_names.as_slice() + if let [sym::expn_data, sym::outer_expn] = method_names.as_slice() && let (self_arg, args) = arg_lists[1] && args.is_empty() && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() diff --git a/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs index 14e93dc6d5f1..3a813b4b9a22 100644 --- a/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs +++ b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs @@ -1,8 +1,7 @@ use rustc_ast::ast::NodeId; use rustc_ast::visit::FnKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; declare_tool_lint! { diff --git a/src/tools/clippy/clippy_lints_internal/src/symbols.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs index 5aee545fb0f5..7b5d58824c38 100644 --- a/src/tools/clippy/clippy_lints_internal/src/symbols.rs +++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs @@ -6,10 +6,9 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Lit, Node, Pat, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; use rustc_middle::mir::ConstValue; use rustc_middle::ty; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs index 8e281ecb2ee4..9ca4ae31d455 100644 --- a/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs +++ b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; use rustc_ast::ast::{Crate, ItemKind, ModKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_tool_lint! { /// ### What it does @@ -26,11 +26,11 @@ impl EarlyLintPass for UnsortedClippyUtilsPaths { if let Some(utils) = krate .items .iter() - .find(|item| item.kind.ident().is_some_and(|i| i.name.as_str() == "utils")) + .find(|item| item.kind.ident().is_some_and(|i| i.name == sym::utils)) && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = utils.kind && let Some(paths) = items .iter() - .find(|item| item.kind.ident().is_some_and(|i| i.name.as_str() == "paths")) + .find(|item| item.kind.ident().is_some_and(|i| i.name == sym::paths)) && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = paths.kind { let mut last_name: Option = None; diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index ac970e1c4b0a..615c0995e8b1 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_utils" -# begin autogenerated version version = "0.1.89" -# end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index d4080d06d3ca..c9083f654c4c 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-05-14 +nightly-2025-05-21 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 8996b694ed8f..a0503a699e69 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -436,11 +436,11 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, - (Enum(li, le, lg), Enum(ri, re, rg)) => { - eq_id(*li, *ri) && over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg) + (Enum(li, lg, le), Enum(ri, rg, re)) => { + eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant) }, - (Struct(li, lv, lg), Struct(ri, rv, rg)) | (Union(li, lv, lg), Union(ri, rv, rg)) => { - eq_id(*li, *ri) && eq_variant_data(lv, rv) && eq_generics(lg, rg) + (Struct(li, lg, lv), Struct(ri, rg, rv)) | (Union(li, lg, lv), Union(ri, rg, rv)) => { + eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv) }, ( Trait(box ast::Trait { diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 09de5c055379..8a0ff5323c98 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -5,11 +5,11 @@ use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use std::str::FromStr; use crate::source::SpanRangeExt; -use crate::tokenize_with_text; +use crate::{sym, tokenize_with_text}; /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { @@ -21,17 +21,17 @@ pub enum DeprecationStatus { } #[rustfmt::skip] -pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ - ("author", DeprecationStatus::None), - ("version", DeprecationStatus::None), - ("cognitive_complexity", DeprecationStatus::None), - ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")), - ("dump", DeprecationStatus::None), - ("msrv", DeprecationStatus::None), +pub const BUILTIN_ATTRIBUTES: &[(Symbol, DeprecationStatus)] = &[ + (sym::author, DeprecationStatus::None), + (sym::version, DeprecationStatus::None), + (sym::cognitive_complexity, DeprecationStatus::None), + (sym::cyclomatic_complexity, DeprecationStatus::Replaced("cognitive_complexity")), + (sym::dump, DeprecationStatus::None), + (sym::msrv, DeprecationStatus::None), // The following attributes are for the 3rd party crate authors. // See book/src/attribs.md - ("has_significant_drop", DeprecationStatus::None), - ("format_args", DeprecationStatus::None), + (sym::has_significant_drop, DeprecationStatus::None), + (sym::format_args, DeprecationStatus::None), ]; pub struct LimitStack { @@ -52,11 +52,11 @@ impl LimitStack { pub fn limit(&self) -> u64 { *self.stack.last().expect("there should always be a value in the stack") } - pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { + pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| stack.push(val)); } - pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { + pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val))); } @@ -65,7 +65,7 @@ impl LimitStack { pub fn get_attr<'a, A: AttributeExt + 'a>( sess: &'a Session, attrs: &'a [A], - name: &'static str, + name: Symbol, ) -> impl Iterator { attrs.iter().filter(move |attr| { let Some(attr_segments) = attr.ident_path() else { @@ -75,8 +75,8 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( if attr_segments.len() == 2 && attr_segments[0].name == sym::clippy { BUILTIN_ATTRIBUTES .iter() - .find_map(|&(builtin_name, ref deprecation_status)| { - if attr_segments[1].name.as_str() == builtin_name { + .find_map(|(builtin_name, deprecation_status)| { + if attr_segments[1].name == *builtin_name { Some(deprecation_status) } else { None @@ -108,7 +108,7 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( }, DeprecationStatus::None => { diag.cancel(); - attr_segments[1].as_str() == name + attr_segments[1].name == name }, } }, @@ -119,9 +119,9 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( }) } -fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: &'static str, mut f: F) { +fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) { for attr in get_attr(sess, attrs, name) { - if let Some(ref value) = attr.value_str() { + if let Some(value) = attr.value_str() { if let Ok(value) = FromStr::from_str(value.as_str()) { f(value); } else { @@ -133,7 +133,7 @@ fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: } } -pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: &'static str) -> Option<&'a A> { +pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> { let mut unique_attr: Option<&A> = None; for attr in get_attr(sess, attrs, name) { if let Some(duplicate) = unique_attr { 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 004c840c3310..407e92d88fb0 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -249,7 +249,7 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) { ItemKind::ForeignMod { .. } => (Pat::Str("extern"), 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(_, _, VariantData::Struct { .. }) => (Pat::Str("struct"), Pat::Str("}")), ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")), ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")), ItemKind::Trait(_, Safety::Unsafe, ..) diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index b9928b8eed49..1ec5d11384f5 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -235,9 +235,7 @@ impl Constant<'_> { _ => None, }, (Self::Vec(l), Self::Vec(r)) => { - let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else { - return None; - }; + let cmp_type = cmp_type.builtin_index()?; iter::zip(l, r) .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) .find(|r| r.is_none_or(|o| o != Ordering::Equal)) @@ -487,7 +485,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, "cfg").is_some() { + if is_direct_expn_of(e.span, sym::cfg).is_some() { None } else { Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) @@ -565,7 +563,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }) }, ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, "cfg").is_some() { + if is_direct_expn_of(e.span, sym::cfg).is_some() { None } else { match &lit.node { @@ -654,7 +652,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { span, .. }) = self.tcx.hir_node(body_id.hir_id) - && is_direct_expn_of(*span, "cfg").is_some() + && is_direct_expn_of(*span, sym::cfg).is_some() { return None; } diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 4543a20cc2cd..9d38672efada 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -10,6 +10,7 @@ //! - option-if-let-else use crate::consts::{ConstEvalCtxt, FullInt}; +use crate::sym; use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; @@ -19,7 +20,7 @@ use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use std::{cmp, ops}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -49,14 +50,13 @@ impl ops::BitOrAssign for EagernessSuggestion { /// Determine the eagerness of the given function call. fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let name = name.as_str(); let ty = match cx.tcx.impl_of_method(fn_id) { Some(id) => cx.tcx.type_of(id).instantiate_identity(), None => return Lazy, }; - if (name.starts_with("as_") || name == "len" || name == "is_empty") && have_one_arg { + if (matches!(name, sym::is_empty | sym::len) || name.as_str().starts_with("as_")) && have_one_arg { if matches!( cx.tcx.crate_name(fn_id.krate), sym::std | sym::core | sym::alloc | sym::proc_macro diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index dbb993482902..6971b488013c 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -299,7 +299,7 @@ impl<'a> VecArgs<'a> { pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option> { if let ExprKind::Call(fun, args) = expr.kind && let ExprKind::Path(ref qpath) = fun.kind - && is_expn_of(fun.span, "vec").is_some() + && is_expn_of(fun.span, sym::vec).is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() { return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 0a9c39c41bd8..2020f3d6b5bd 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -25,8 +25,10 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate indexmap; extern crate rustc_abi; extern crate rustc_ast; +extern crate rustc_attr_data_structures; extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; @@ -81,17 +83,16 @@ pub use self::hir_utils::{ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; -use std::hash::BuildHasherDefault; use std::iter::{once, repeat_n}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_abi::Integer; use rustc_ast::ast::{self, LitKind, RangeLimits}; -use rustc_attr_parsing::{AttributeKind, find_attr}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; -use rustc_data_structures::unhash::UnhashMap; +use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; @@ -523,12 +524,8 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> /// } /// } /// ``` -pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> { - // Get the implemented trait for the current function - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - let parent_impl = cx.tcx.hir_get_parent_item(hir_id); - if parent_impl != hir::CRATE_OWNER_ID - && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id) +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> { + if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner)) && let ItemKind::Impl(impl_) = &item.kind { return impl_.of_trait.as_ref(); @@ -1101,13 +1098,13 @@ pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec(expr: &'a Expr<'_>, methods: &[&str]) -> Option, &'a [Expr<'a>])>> { +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option, &'a [Expr<'a>])>> { let mut current = expr; let mut matched = Vec::with_capacity(methods.len()); for method_name in methods.iter().rev() { // method chains are stored last -> first if let ExprKind::MethodCall(path, receiver, args, _) = current.kind { - if path.ident.name.as_str() == *method_name { + if path.ident.name == *method_name { if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) { return None; } @@ -1493,14 +1490,14 @@ pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// macro `name`. /// See also [`is_direct_expn_of`]. #[must_use] -pub fn is_expn_of(mut span: Span, name: &str) -> Option { +pub fn is_expn_of(mut span: Span, name: Symbol) -> Option { loop { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind - && mac_name.as_str() == name + && mac_name == name { return Some(new_span); } @@ -1523,13 +1520,13 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only /// from `bar!` by `is_direct_expn_of`. #[must_use] -pub fn is_direct_expn_of(span: Span, name: &str) -> Option { +pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind - && mac_name.as_str() == name + && mac_name == name { return Some(new_span); } @@ -1793,11 +1790,11 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { } /// Checks if the given `DefId` matches the `libc` item. -pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { +pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool { let path = cx.get_def_path(did); // libc is meant to be used as a flat list of names, but they're all actually defined in different // modules based on the target platform. Ignore everything but crate name and the item name. - path.first().is_some_and(|s| *s == sym::libc) && path.last().is_some_and(|s| s.as_str() == name) + path.first().is_some_and(|s| *s == sym::libc) && path.last().copied() == Some(name) } /// Returns the list of condition expressions and the list of blocks in a @@ -2196,45 +2193,46 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)> +pub fn search_same(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec> where Hash: FnMut(&T) -> u64, Eq: FnMut(&T, &T) -> bool, { match exprs { - [a, b] if eq(a, b) => return vec![(a, b)], + [a, b] if eq(a, b) => return vec![vec![a, b]], _ if exprs.len() <= 2 => return vec![], _ => {}, } - let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - - let mut map: UnhashMap> = - UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + let mut buckets: UnindexMap>> = UnindexMap::default(); for expr in exprs { - match map.entry(hash(expr)) { - Entry::Occupied(mut o) => { - for o in o.get() { - if eq(o, expr) { - match_expr_list.push((o, expr)); - } + match buckets.entry(hash(expr)) { + indexmap::map::Entry::Occupied(mut o) => { + let bucket = o.get_mut(); + match bucket.iter_mut().find(|group| eq(expr, group[0])) { + Some(group) => group.push(expr), + None => bucket.push(vec![expr]), } - o.get_mut().push(expr); }, - Entry::Vacant(v) => { - v.insert(vec![expr]); + indexmap::map::Entry::Vacant(v) => { + v.insert(vec![vec![expr]]); }, } } - match_expr_list + buckets + .into_values() + .flatten() + .filter(|group| group.len() > 1) + .collect() } /// Peels off all references on the pattern. Returns the underlying pattern and the number of @@ -2364,7 +2362,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(& for id in tcx.hir_module_free_items(module) { if matches!(tcx.def_kind(id.owner_id), DefKind::Const) && let item = tcx.hir_item(id) - && let ItemKind::Const(ident, ty, _generics, _body) = item.kind + && let ItemKind::Const(ident, _generics, ty, _body) = item.kind && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind // We could also check for the type name `test::TestDescAndFn` && let Res::Def(DefKind::Struct, _) = path.res @@ -3318,7 +3316,7 @@ pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'t if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) && temporary_ty .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index dfb30b9c2186..ba126fcd05de 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -2,8 +2,8 @@ use std::sync::{Arc, OnceLock}; -use crate::get_unique_attr; use crate::visitors::{Descend, for_each_expr_without_closures}; +use crate::{get_unique_attr, sym}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; @@ -12,7 +12,7 @@ use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; -use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol, sym}; +use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -42,7 +42,7 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { } else { // Allow users to tag any macro as being format!-like // TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method - get_unique_attr(cx.sess(), cx.tcx.get_attrs_unchecked(macro_def_id), "format_args").is_some() + get_unique_attr(cx.sess(), cx.tcx.get_attrs_unchecked(macro_def_id), sym::format_args).is_some() } } @@ -248,10 +248,10 @@ impl<'a> PanicExpn<'a> { let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None; }; - let name = path.segments.last().unwrap().ident.as_str(); + let name = path.segments.last().unwrap().ident.name; // This has no argument - if name == "panic_cold_explicit" { + if name == sym::panic_cold_explicit { return Some(Self::Empty); } @@ -259,18 +259,18 @@ impl<'a> PanicExpn<'a> { return None; }; let result = match name { - "panic" if arg.span.eq_ctxt(expr.span) => Self::Empty, - "panic" | "panic_str" => Self::Str(arg), - "panic_display" | "panic_cold_display" => { + sym::panic if arg.span.eq_ctxt(expr.span) => Self::Empty, + sym::panic | sym::panic_str => Self::Str(arg), + sym::panic_display | sym::panic_cold_display => { let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None; }; Self::Display(e) }, - "panic_fmt" => Self::Format(arg), + sym::panic_fmt => Self::Format(arg), // Since Rust 1.52, `assert_{eq,ne}` macros expand to use: // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));` - "assert_failed" => { + sym::assert_failed => { // It should have 4 arguments in total (we already matched with the first argument, // so we're just checking for 3) if rest.len() != 3 { diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 223a8649eb3c..a5e66ad463bb 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,7 +1,8 @@ use crate::sym; use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; -use rustc_attr_parsing::{RustcVersion, parse_version}; +use rustc_attr_data_structures::RustcVersion; +use rustc_attr_parsing::parse_version; use rustc_lint::LateContext; use rustc_session::Session; use rustc_span::Symbol; @@ -24,7 +25,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,88,0 { LET_CHAINS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT } - 1,85,0 { UINT_FLOAT_MIDPOINT } + 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index e5179e479ccd..9d7f3086b05f 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -129,6 +129,7 @@ path_macros! { // Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of); pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit); +pub static CONCAT: PathLookup = macro_path!(core::concat); pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new); pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other); pub static ITER_STEP: PathLookup = type_path!(core::iter::Step); diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs index 360c6251a57c..5847e916e340 100644 --- a/src/tools/clippy/clippy_utils/src/ptr.rs +++ b/src/tools/clippy/clippy_utils/src/ptr.rs @@ -1,17 +1,17 @@ use crate::source::snippet; use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{path_to_local_id, strip_pat_refs}; +use crate::{path_to_local_id, strip_pat_refs, sym}; use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; pub fn get_spans( cx: &LateContext<'_>, opt_body_id: Option, idx: usize, - replacements: &[(&'static str, &'static str)], + replacements: &[(Symbol, &'static str)], ) -> Option)>> { if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) { if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { @@ -27,7 +27,7 @@ pub fn get_spans( fn extract_clone_suggestions<'tcx>( cx: &LateContext<'tcx>, id: HirId, - replace: &[(&'static str, &'static str)], + replace: &[(Symbol, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { let mut spans = Vec::new(); @@ -35,11 +35,11 @@ fn extract_clone_suggestions<'tcx>( if let ExprKind::MethodCall(seg, recv, [], _) = e.kind && path_to_local_id(recv, id) { - if seg.ident.as_str() == "capacity" { + if seg.ident.name == sym::capacity { return ControlFlow::Break(()); } for &(fn_name, suffix) in replace { - if seg.ident.as_str() == fn_name { + if seg.ident.name == fn_name { spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); return ControlFlow::Continue(Descend::No); } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 5d0401010db6..bb04520c6b7d 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -5,7 +5,7 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; -use rustc_attr_parsing::{RustcVersion, StableSince}; +use rustc_attr_data_structures::{RustcVersion, StableSince}; use rustc_const_eval::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -55,7 +55,7 @@ pub fn is_min_const_fn<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, msrv: Ms fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, msrv: Msrv) -> McfResult { for arg in ty.walk() { - let ty = match arg.unpack() { + let ty = match arg.kind() { GenericArgKind::Type(ty) => ty, // No constraints on lifetimes or constants, except potentially @@ -404,7 +404,7 @@ fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) }) .is_none_or(|const_stab| { - if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level { + if let rustc_attr_data_structures::StabilityLevel::Stable { since, .. } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 8645d5730fed..7f2bf99daff2 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -7,13 +7,14 @@ use std::sync::Arc; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; +use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use rustc_lint::{EarlyContext, LateContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, - hygiene, + BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, + SyntaxContext, hygiene, }; use std::borrow::Cow; use std::fmt; @@ -137,25 +138,25 @@ pub trait SpanRangeExt: SpanRange { fn map_range( self, cx: &impl HasSession, - f: impl for<'a> FnOnce(&'a str, Range) -> Option>, + f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { map_range(cx.sess().source_map(), self.into_range(), f) } #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] - /// Extends the range to include all preceding whitespace characters, unless there - /// are non-whitespace characters left on the same line after `self`. + /// Extends the range to include all preceding whitespace characters. + /// + /// The range will not be expanded if it would cross a line boundary, the line the range would + /// be extended to ends with a line comment and the text after the range contains a + /// non-whitespace character on the same line. e.g. /// - /// This extra condition prevents a problem when removing the '}' in: /// ```ignore - /// ( // There was an opening bracket after the parenthesis, which has been removed - /// // This is a comment - /// }) + /// ( // Some comment + /// foo) /// ``` - /// Removing the whitespaces, including the linefeed, before the '}', would put the - /// closing parenthesis at the end of the `// This is a comment` line, which would - /// make it part of the comment as well. In this case, it is best to keep the span - /// on the '}' alone. + /// + /// When the range points to `foo`, suggesting to remove the range after it's been extended will + /// cause the `)` to be placed inside the line comment as `( // Some comment)`. fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } @@ -254,11 +255,11 @@ fn with_source_text_and_range( fn map_range( sm: &SourceMap, sp: Range, - f: impl for<'a> FnOnce(&'a str, Range) -> Option>, + f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { if let Some(src) = get_source_range(sm, sp.clone()) && let Some(text) = &src.sf.src - && let Some(range) = f(text, src.range.clone()) + && let Some(range) = f(&src.sf, text, src.range.clone()) { debug_assert!( range.start <= text.len() && range.end <= text.len(), @@ -275,20 +276,57 @@ fn map_range( } } +fn ends_with_line_comment_or_broken(text: &str) -> bool { + let Some(last) = tokenize(text).last() else { + return false; + }; + match last.kind { + // Will give the wrong result on text like `" // "` where the first quote ends a string + // started earlier. The only workaround is to lex the whole file which we don't really want + // to do. + TokenKind::LineComment { .. } | TokenKind::BlockComment { terminated: false, .. } => true, + TokenKind::Literal { kind, .. } => matches!( + kind, + LiteralKind::Byte { terminated: false } + | LiteralKind::ByteStr { terminated: false } + | LiteralKind::CStr { terminated: false } + | LiteralKind::Char { terminated: false } + | LiteralKind::RawByteStr { n_hashes: None } + | LiteralKind::RawCStr { n_hashes: None } + | LiteralKind::RawStr { n_hashes: None } + ), + _ => false, + } +} + +fn with_leading_whitespace_inner(lines: &[RelativeBytePos], src: &str, range: Range) -> Option { + debug_assert!(lines.is_empty() || lines[0].to_u32() == 0); + + let start = src.get(..range.start)?.trim_end(); + let next_line = lines.partition_point(|&pos| pos.to_usize() <= start.len()); + if let Some(line_end) = lines.get(next_line) + && line_end.to_usize() <= range.start + && let prev_start = lines.get(next_line - 1).map_or(0, |&x| x.to_usize()) + && ends_with_line_comment_or_broken(&start[prev_start..]) + && let next_line = lines.partition_point(|&pos| pos.to_usize() < range.end) + && let next_start = lines.get(next_line).map_or(src.len(), |&x| x.to_usize()) + && tokenize(src.get(range.end..next_start)?).any(|t| !matches!(t.kind, TokenKind::Whitespace)) + { + Some(range.start) + } else { + Some(start.len()) + } +} + fn with_leading_whitespace(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp, |src, range| { - let non_blank_after = src.len() - src.get(range.end..)?.trim_start().len(); - if src.get(range.end..non_blank_after)?.contains(['\r', '\n']) { - Some(src.get(..range.start)?.trim_end().len()..range.end) - } else { - Some(range) - } + map_range(sm, sp.clone(), |sf, src, range| { + Some(with_leading_whitespace_inner(sf.lines(), src, range.clone())?..range.end) }) - .unwrap() + .unwrap_or(sp) } fn trim_start(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp.clone(), |src, range| { + map_range(sm, sp.clone(), |_, src, range| { let src = src.get(range.clone())?; Some(range.start + (src.len() - src.trim_start().len())..range.end) }) diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 93dec113d31a..f4dfc8f4b5a7 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -941,6 +941,8 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { ProjectionKind::Subslice | // Doesn't have surface syntax. Only occurs in patterns. ProjectionKind::OpaqueCast => (), + // Only occurs in closure captures. + ProjectionKind::UnwrapUnsafeBinder => (), ProjectionKind::Deref => { // Explicit derefs are typically handled later on, but // some items do not need explicit deref, such as array accesses, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 9428262b99aa..f417530be367 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -29,172 +29,330 @@ macro_rules! generate { }; } +// List of extra symbols to be included in Clippy (for example, as `sym::ambiguous_glob_reexports`). +// An alternative content can be specified using a colon after the symbol name. +// +// `cargo dev fmt` ensures that the content of the `generate!()` macro call stays sorted. generate! { - abs, - align_of, - ambiguous_glob_reexports, - as_bytes, - as_deref_mut, - as_deref, - as_mut, AsyncReadExt, AsyncWriteExt, BACKSLASH_SINGLE_QUOTE: r"\'", Binary, - build_hasher, - bytes, - cargo_clippy: "cargo-clippy", - Cargo_toml: "Cargo.toml", - cast, - chars, CLIPPY_ARGS, CLIPPY_CONF_DIR, + CRLF: "\r\n", + Cargo_toml: "Cargo.toml", + Current, + DOUBLE_QUOTE: "\"", + Deserialize, + EarlyLintPass, + ErrorKind, + IntoIter, + Itertools, + LF: "\n", + Lazy, + Lint, + LowerExp, + LowerHex, + MAX, + MIN, + MsrvStack, + Octal, + OpenOptions, + Other, + PathLookup, + Regex, + RegexBuilder, + RegexSet, + Start, + Step, + Symbol, + SyntaxContext, + TBD, + UpperExp, + UpperHex, + V4, + V6, + Visitor, + Weak, + abs, + align_of, + ambiguous_glob_reexports, + append, + arg, + as_bytes, + as_deref, + as_deref_mut, + as_mut, + assert_failed, + author, + borrow, + borrow_mut, + build_hasher, + by_ref, + bytes, + capacity, + cargo_clippy: "cargo-clippy", + cast, + cast_const, + cast_mut, + ceil, + ceil_char_boundary, + chain, + chars, + checked_abs, + checked_add, + checked_isqrt, + checked_mul, + checked_pow, + checked_rem_euclid, + checked_sub, + clamp, clippy_utils, clone_into, cloned, + cognitive_complexity, collect, const_ptr, contains, copied, - CRLF: "\r\n", - Current, + copy_from, + copy_from_nonoverlapping, + copy_to, + copy_to_nonoverlapping, + count_ones, + cycle, + cyclomatic_complexity, de, - Deserialize, diagnostics, disallowed_types, - DOUBLE_QUOTE: "\"", - EarlyLintPass, + drain, + dump, ends_with, enum_glob_use, + enumerate, + err, error, - ErrorKind, exp, + expect_err, + expn_data, extend, - finish_non_exhaustive, + filter, + filter_map, + find, + find_map, finish, + finish_non_exhaustive, + first, flat_map, + flatten, + floor, + floor_char_boundary, + fold, for_each, - from_bytes_with_nul_unchecked, from_bytes_with_nul, + from_bytes_with_nul_unchecked, from_ptr, from_raw, from_ref, + from_str, from_str_radix, fs, + fuse, futures_util, get, + get_mut, + get_or_insert_with, + get_unchecked, + get_unchecked_mut, + has_significant_drop, hidden_glob_reexports, hygiene, + if_chain, insert, + inspect, int_roundings, + into, into_bytes, + into_ok, into_owned, - IntoIter, io, is_ascii, + is_char_boundary, + is_digit, is_empty, is_err, + is_file, is_none, is_ok, is_some, + isqrt, itertools, - Itertools, + join, kw, last, lazy_static, - Lazy, - LF: "\n", - Lint, ln, + lock, lock_api, log, - LowerExp, - LowerHex, + log10, + log2, macro_use_imports, - map_or_else, + map_break, + map_continue, map_or, + map_or_else, + match_indices, + matches, max, - MAX, + max_by, + max_by_key, + max_value, + maximum, mem, min, - MIN, + min_by, + min_by_key, + min_value, + minimum, mode, module_name_repetitions, msrv, msrvs, - MsrvStack, mut_ptr, mutex, needless_return, + next_back, + next_if, + next_if_eq, next_tuple, - Octal, + nth, + ok, + ok_or, once_cell, - OpenOptions, + open, or_default, - Other, + or_else, + or_insert, + or_insert_with, + outer_expn, + panic_cold_display, + panic_cold_explicit, + panic_display, + panic_str, parse, - PathLookup, + partition, paths, + peek, + peek_mut, + peekable, + pow, powf, powi, + product, push, + read_line, + read_to_end, + read_to_string, redundant_pub_crate, regex, - Regex, - RegexBuilder, - RegexSet, + rem_euclid, + repeat, + replace, + replacen, reserve, resize, restriction, - rustc_lint_defs, + rev, + rfind, + rmatch_indices, + rmatches, + round, + rposition, + rsplit, + rsplit_once, + rsplit_terminator, + rsplitn, + rsplitn_mut, rustc_lint, + rustc_lint_defs, rustc_span, rustfmt_skip, rwlock, + saturating_abs, + saturating_pow, + scan, + seek, serde, set_len, set_mode, set_readonly, signum, single_component_path_imports, + skip_while, + slice_mut_unchecked, + slice_unchecked, + sort, + sort_by, + sort_unstable_by, span_lint_and_then, - split_whitespace, split, + split_at, + split_at_checked, + split_at_mut, + split_at_mut_checked, + split_inclusive, + split_once, + split_terminator, + split_whitespace, + splitn, + splitn_mut, sqrt, - Start, - Step, + starts_with, + step_by, + strlen, style, + subsec_micros, + subsec_nanos, + sum, symbol, - Symbol, - SyntaxContext, take, - TBD, + take_while, + then, then_some, to_ascii_lowercase, to_ascii_uppercase, to_digit, to_lowercase, + to_os_string, to_owned, + to_path_buf, to_uppercase, tokio, + trim, + trim_end_matches, + trim_start_matches, unreachable_pub, unsafe_removed_from_name, + unused, unused_braces, unused_extern_crates, unused_import_braces, unused_trait_names, - unused, unwrap_err, + unwrap_err_unchecked, unwrap_or_default, unwrap_or_else, - UpperExp, - UpperHex, - V4, - V6, - Visitor, + unwrap_unchecked, + unzip, + utils, + wake, warnings, - Weak, wildcard_imports, with_capacity, wrapping_offset, + write, + writeln, + zip, } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 26d41cfb4977..61e70b3fa0bc 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -78,7 +78,7 @@ pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt /// constructor. pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { - ty.walk().any(|inner| match inner.unpack() { + ty.walk().any(|inner| match inner.kind() { GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }) @@ -96,7 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' needle: Ty<'tcx>, seen: &mut FxHashSet, ) -> bool { - ty.walk().any(|inner| match inner.unpack() { + ty.walk().any(|inner| match inner.kind() { GenericArgKind::Type(inner_ty) => { if inner_ty == needle { return true; @@ -129,7 +129,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' // For `impl Trait`, it will register a predicate of `::Assoc = U`, // so we check the term for `U`. ty::ClauseKind::Projection(projection_predicate) => { - if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() + if let ty::TermKind::Ty(ty) = projection_predicate.term.kind() && contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { return true; @@ -526,7 +526,7 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { args_a .iter() .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { + .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { same_type_and_consts(type_a, type_b) @@ -996,7 +996,7 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi if let Some((idx, (param, arg))) = params .clone() - .zip(args.iter().map(|&x| x.unpack())) + .zip(args.iter().map(|&x| x.kind())) .enumerate() .find(|(_, (param, arg))| match (param, arg) { (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) @@ -1361,3 +1361,14 @@ pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { || ty.is_array() || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) } + +/// Gets the index of a field by name. +pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { + match *ty.kind() { + ty::Adt(def, _) if def.is_union() || def.is_struct() => { + def.non_enum_variant().fields.iter().position(|f| f.name == name) + }, + ty::Tuple(_) => name.as_str().parse::().ok(), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 6e3586623277..84df36c75bf8 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -326,5 +326,5 @@ fn adt_def_id(ty: Ty<'_>) -> Option { fn contains_param(ty: Ty<'_>, index: u32) -> bool { ty.walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(ty) if ty.is_param(index))) + .any(|arg| matches!(arg.kind(), GenericArgKind::Type(ty) if ty.is_param(index))) } diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index da41bdd27bcb..0b9ebe554f5b 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-05-14" +channel = "nightly-2025-05-21" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index f8acf88cf81c..37adb14169a3 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -96,16 +96,11 @@ fn track_files(psess: &mut ParseSess) { // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt - #[expect( - clippy::collapsible_if, - reason = "Due to a bug in let_chains this if statement can't be collapsed" - )] - if cfg!(debug_assertions) { - if let Ok(current_exe) = env::current_exe() - && let Some(current_exe) = current_exe.to_str() - { - file_depinfo.insert(Symbol::intern(current_exe)); - } + if cfg!(debug_assertions) + && let Ok(current_exe) = env::current_exe() + && let Some(current_exe) = current_exe.to_str() + { + file_depinfo.insert(Symbol::intern(current_exe)); } } diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr index 50567e32b1bb..a3c35a31c331 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr @@ -13,37 +13,37 @@ LL | const SNAKE_CASE: &str = "zzzzzzzz"; = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 | LL | fn main() { | ^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,100 +61,124 @@ LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr index 50567e32b1bb..a3c35a31c331 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr @@ -13,37 +13,37 @@ LL | const SNAKE_CASE: &str = "zzzzzzzz"; = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 | LL | fn main() { | ^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,100 +61,124 @@ LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr index ae5261dcc6df..3fdd706fc627 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr @@ -25,7 +25,19 @@ LL | const SNAKE_CASE: &str = "zzzzzzzz"; | ^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:71:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:64:8 + | +LL | struct EnumWithExternButAtWrongPosition { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `EnumWithoutExtern` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:55:8 + | +LL | struct EnumWithoutExtern { + | ^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:87:1 | LL | / impl CloneSelf for StructOrdered { LL | | @@ -36,7 +48,7 @@ LL | | } | |_^ | note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:61:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:77:1 | LL | / impl Default for StructOrdered { LL | | fn default() -> Self { @@ -47,25 +59,25 @@ LL | | } | |_^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:149:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:167:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:1 | LL | impl BasicEmptyTrait for StructOrdered {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:1 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:168:1 | LL | / impl TraitUnordered for StructUnordered { LL | | const A: bool = false; @@ -76,25 +88,25 @@ LL | | } | |_^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:188:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 | LL | fn main() { | ^^^^ error: incorrect ordering of items (module item groupings specify another order) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:198:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:196:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,112 +136,136 @@ LL | C, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:105:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:104:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:131:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:139:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:137:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:162:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:161:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:173:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:191:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:207:11 | LL | const A: i8 = 1; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:190:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:206:11 | LL | const C: i8 = 0; | ^ -error: aborting due to 18 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs index 90399470d4c0..1cfed9790c12 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs @@ -52,6 +52,22 @@ enum EnumUnorderedAllowed { B, } +struct EnumWithoutExtern { + r: u8, + g: u8, + //~^ arbitrary_source_item_ordering + b: u8, + //~^ arbitrary_source_item_ordering +} + +#[repr(C)] +struct EnumWithExternButAtWrongPosition { + //~[ord_within]^ arbitrary_source_item_ordering + r: u8, + g: u8, + b: u8, +} + struct StructOrdered { a: bool, b: bool, diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed index 6f5cc47ba6c7..f695f9804d59 100644 --- a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed @@ -13,7 +13,7 @@ fn main() { //~^^^^^^ collapsible_if // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" // Inner comment + if x == "hello" // Inner comment && y == "world" { println!("Hello world!"); } @@ -26,7 +26,7 @@ fn main() { } //~^^^^^^ collapsible_if - if x == "hello" /* Inner comment */ + if x == "hello" /* Inner comment */ && y == "world" { println!("Hello world!"); } diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr index 357ce4ad32de..a12c2112f587 100644 --- a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr @@ -32,7 +32,7 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" // Inner comment +LL ~ if x == "hello" // Inner comment LL ~ && y == "world" { LL | println!("Hello world!"); LL ~ } @@ -70,7 +70,7 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" /* Inner comment */ +LL ~ if x == "hello" /* Inner comment */ LL ~ && y == "world" { LL | println!("Hello world!"); LL ~ } diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs index e953a2a4e90b..2a6097fb5795 100644 --- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -11,9 +11,9 @@ fn issue10272() { // should trigger warning let x = Cell::new(true); if x.get() { + //~^ ifs_same_cond } else if !x.take() { } else if x.get() { - //~^ ifs_same_cond } else { } } diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr index d67e7fca6565..adc44358c4c7 100644 --- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr +++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr @@ -1,14 +1,12 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:15:15 - | -LL | } else if x.get() { - | ^^^^^^^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:13:8 | LL | if x.get() { | ^^^^^^^ +... +LL | } else if x.get() { + | ^^^^^^^ + | = note: `-D clippy::ifs-same-cond` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed index 18f0e04a8807..429c20f95e91 100644 --- a/src/tools/clippy/tests/ui/assign_ops.fixed +++ b/src/tools/clippy/tests/ui/assign_ops.fixed @@ -1,7 +1,9 @@ -use core::num::Wrapping; +#![allow(clippy::useless_vec)] +#![warn(clippy::assign_op_pattern)] + +use core::num::Wrapping; +use std::ops::{Mul, MulAssign}; -#[allow(dead_code, unused_assignments, clippy::useless_vec)] -#[warn(clippy::assign_op_pattern)] fn main() { let mut a = 5; a += 1; @@ -39,3 +41,35 @@ fn main() { v[0] = v[0] + v[1]; let _ = || v[0] = v[0] + v[1]; } + +fn cow_add_assign() { + use std::borrow::Cow; + let mut buf = Cow::Owned(String::from("bar")); + let cows = Cow::Borrowed("foo"); + + // this can be linted + buf += cows.clone(); + //~^ assign_op_pattern + + // this should not as cow Add is not commutative + buf = cows + buf; +} + +// check that we don't lint on op assign impls, because that's just the way to impl them + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Wrap(i64); + +impl Mul for Wrap { + type Output = Self; + + fn mul(self, rhs: i64) -> Self { + Wrap(self.0 * rhs) + } +} + +impl MulAssign for Wrap { + fn mul_assign(&mut self, rhs: i64) { + *self = *self * rhs + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs index 8b05c74d8604..480ff07f150e 100644 --- a/src/tools/clippy/tests/ui/assign_ops.rs +++ b/src/tools/clippy/tests/ui/assign_ops.rs @@ -1,7 +1,9 @@ -use core::num::Wrapping; +#![allow(clippy::useless_vec)] +#![warn(clippy::assign_op_pattern)] + +use core::num::Wrapping; +use std::ops::{Mul, MulAssign}; -#[allow(dead_code, unused_assignments, clippy::useless_vec)] -#[warn(clippy::assign_op_pattern)] fn main() { let mut a = 5; a = a + 1; @@ -39,3 +41,35 @@ fn main() { v[0] = v[0] + v[1]; let _ = || v[0] = v[0] + v[1]; } + +fn cow_add_assign() { + use std::borrow::Cow; + let mut buf = Cow::Owned(String::from("bar")); + let cows = Cow::Borrowed("foo"); + + // this can be linted + buf = buf + cows.clone(); + //~^ assign_op_pattern + + // this should not as cow Add is not commutative + buf = cows + buf; +} + +// check that we don't lint on op assign impls, because that's just the way to impl them + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Wrap(i64); + +impl Mul for Wrap { + type Output = Self; + + fn mul(self, rhs: i64) -> Self { + Wrap(self.0 * rhs) + } +} + +impl MulAssign for Wrap { + fn mul_assign(&mut self, rhs: i64) { + *self = *self * rhs + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr index 17f216ee4a07..881a333fbe4b 100644 --- a/src/tools/clippy/tests/ui/assign_ops.stderr +++ b/src/tools/clippy/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:7:5 + --> tests/ui/assign_ops.rs:9:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -8,64 +8,70 @@ LL | a = a + 1; = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:9:5 + --> tests/ui/assign_ops.rs:11:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:11:5 + --> tests/ui/assign_ops.rs:13:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:13:5 + --> tests/ui/assign_ops.rs:15:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:15:5 + --> tests/ui/assign_ops.rs:17:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:17:5 + --> tests/ui/assign_ops.rs:19:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:19:5 + --> tests/ui/assign_ops.rs:21:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:21:5 + --> tests/ui/assign_ops.rs:23:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:28:5 + --> tests/ui/assign_ops.rs:30:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:33:5 + --> tests/ui/assign_ops.rs:35:5 | LL | a = a + Wrapping(1u32); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:36:5 + --> tests/ui/assign_ops.rs:38:5 | LL | v[0] = v[0] + v[1]; | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` -error: aborting due to 11 previous errors +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:51:5 + | +LL | buf = buf + cows.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/assign_ops2.rs b/src/tools/clippy/tests/ui/assign_ops2.rs deleted file mode 100644 index 51867fa6962c..000000000000 --- a/src/tools/clippy/tests/ui/assign_ops2.rs +++ /dev/null @@ -1,77 +0,0 @@ -//@no-rustfix: overlapping suggestions -#![allow(clippy::uninlined_format_args)] - -#[allow(unused_assignments)] -#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] -fn main() { - let mut a = 5; - a += a + 1; - //~^ misrefactored_assign_op - - a += 1 + a; - //~^ misrefactored_assign_op - - a -= a - 1; - //~^ misrefactored_assign_op - - a *= a * 99; - //~^ misrefactored_assign_op - - a *= 42 * a; - //~^ misrefactored_assign_op - - a /= a / 2; - //~^ misrefactored_assign_op - - a %= a % 5; - //~^ misrefactored_assign_op - - a &= a & 1; - //~^ misrefactored_assign_op - - a *= a * a; - //~^ misrefactored_assign_op - - a = a * a * a; - a = a * 42 * a; - a = a * 2 + a; - a -= 1 - a; - a /= 5 / a; - a %= 42 % a; - a <<= 6 << a; -} - -// check that we don't lint on op assign impls, because that's just the way to impl them - -use std::ops::{Mul, MulAssign}; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Wrap(i64); - -impl Mul for Wrap { - type Output = Self; - - fn mul(self, rhs: i64) -> Self { - Wrap(self.0 * rhs) - } -} - -impl MulAssign for Wrap { - fn mul_assign(&mut self, rhs: i64) { - *self = *self * rhs - } -} - -fn cow_add_assign() { - use std::borrow::Cow; - let mut buf = Cow::Owned(String::from("bar")); - let cows = Cow::Borrowed("foo"); - - // this can be linted - buf = buf + cows.clone(); - //~^ assign_op_pattern - - // this should not as cow Add is not commutative - buf = cows + buf; - println!("{}", buf); -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/src/tools/clippy/tests/ui/auxiliary/interior_mutable_const.rs similarity index 100% rename from src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs rename to src/tools/clippy/tests/ui/auxiliary/interior_mutable_const.rs diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs index 4c61c5accd39..9b8e62867f0a 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs @@ -1,5 +1,4 @@ -#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] -#![allow(incomplete_features)] +#![feature(proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(clippy::useless_conversion, clippy::uninlined_format_args)] extern crate proc_macro; diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 1815dd58f510..5992d15935d5 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -1,5 +1,4 @@ -#![feature(repr128, proc_macro_quote, proc_macro_span)] -#![allow(incomplete_features)] +#![feature(proc_macro_quote, proc_macro_span)] #![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] #![allow(clippy::literal_string_with_formatting_args)] diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs new file mode 100644 index 000000000000..0f439f789150 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs @@ -0,0 +1,221 @@ +//@aux-build:interior_mutable_const.rs + +#![deny(clippy::borrow_interior_mutable_const)] +#![allow( + clippy::declare_interior_mutable_const, + clippy::out_of_bounds_indexing, + const_item_mutation, + unconditional_panic +)] + +use core::cell::{Cell, UnsafeCell}; +use core::ops::{Deref, Index}; + +trait ConstDefault { + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl ConstDefault for Cell { + const DEFAULT: Self = Cell::new(T::DEFAULT); +} + +fn main() { + { + const C: String = String::new(); + let _ = C; + let _ = &C; + let _ = C.len(); + let _ = &*C; + } + { + const C: UnsafeCell = UnsafeCell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + let _ = C.get(); //~ borrow_interior_mutable_const + } + { + const C: Cell = Cell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &mut C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + + let local = C; + C.swap(&local) //~ borrow_interior_mutable_const + } + { + const C: [(Cell,); 1] = [(Cell::new(0),)]; + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C[0].0; //~ borrow_interior_mutable_const + C[0].0.set(1); //~ borrow_interior_mutable_const + } + { + struct S(Cell); + impl S { + const C: Self = Self(Cell::new(0)); + } + impl Deref for S { + type Target = Cell; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + let _ = S::C; + let _ = S::C.0; + let _ = &S::C; //~ borrow_interior_mutable_const + let _ = &S::C.0; //~ borrow_interior_mutable_const + S::C.set(1); //~ borrow_interior_mutable_const + let _ = &*S::C; //~ borrow_interior_mutable_const + (*S::C).set(1); //~ borrow_interior_mutable_const + } + { + enum E { + Cell(Cell), + Other, + } + const CELL: E = E::Cell(Cell::new(0)); + const OTHER: E = E::Other; + + let _ = CELL; + let _ = &CELL; //~ borrow_interior_mutable_const + let E::Cell(_) = CELL else { + return; + }; + + let _ = OTHER; + let _ = &OTHER; + let E::Cell(ref _x) = OTHER else { + return; + }; + } + { + struct S { + cell: (Cell, u32), + other: Option, + } + impl S { + const C: Self = Self { + cell: (Cell::::DEFAULT, 0), + other: Some(T::DEFAULT), + }; + + fn f() { + let _ = Self::C; + let _ = &Self::C; //~ borrow_interior_mutable_const + let _ = Self::C.other; + let _ = &Self::C.other; + let _ = &Self::C.cell; //~ borrow_interior_mutable_const + let _ = &Self::C.cell.0; //~ borrow_interior_mutable_const + Self::C.cell.0.set(T::DEFAULT); //~ borrow_interior_mutable_const + let _ = &Self::C.cell.1; + } + } + } + { + trait T { + const VALUE: Option> = Some(Cell::new(0)); + } + impl T for u32 {} + impl T for i32 { + const VALUE: Option> = None; + } + + let _ = &u32::VALUE; //~ borrow_interior_mutable_const + let _ = &i32::VALUE; + } + { + trait Trait { + type T: ConstDefault; + const VALUE: Option> = Some(Self::T::::DEFAULT); + } + impl Trait for u32 { + type T = Cell; + } + impl Trait for i32 { + type T = Cell; + const VALUE: Option> = None; + } + + fn f() { + let _ = &>::VALUE; //~ borrow_interior_mutable_const + let _ = &>::VALUE; + } + } + { + trait Trait { + const UNFROZEN: Option> = Some(Cell::new(0)); + const FROZEN: Option> = None; + const NON_FREEZE: u32 = 0; + } + fn f() { + // None of these are guaranteed to be frozen, so don't lint. + let _ = &T::UNFROZEN; + let _ = &T::FROZEN; + let _ = &T::NON_FREEZE; + } + } + { + struct S([Option>; 2]); + impl Index for S { + type Output = Option>; + fn index(&self, idx: usize) -> &Self::Output { + &self.0[idx] + } + } + + const C: S = S([Some(Cell::new(0)), None]); + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C.0[0]; //~ borrow_interior_mutable_const + let _ = &C.0[1]; + } + { + const C: [Option>; 2] = [None, None]; + let _ = &C[0]; + let _ = &C[1]; + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; + } + } + { + const C: [Option>; 2] = [None, Some(Cell::new(0))]; + let _ = &C[0]; + let _ = &C[1]; //~ borrow_interior_mutable_const + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; //~ borrow_interior_mutable_const + } + } + { + let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const + let _ = &interior_mutable_const::WRAPPED_PRIVATE_FROZEN_VARIANT; + } + { + type Cell2 = Cell; + type MyCell = Cell2; + struct S(Option); + trait T { + type Assoc; + } + struct S2(T, T, u32); + impl T for S { + type Assoc = S2; + } + type Assoc = ::Assoc; + impl S { + const VALUE: Assoc = S2(Self(None), Self(Some(Cell::new(0))), 0); + } + let _ = &S::VALUE; //~ borrow_interior_mutable_const + let _ = &S::VALUE.0; + let _ = &S::VALUE.1; //~ borrow_interior_mutable_const + let _ = &S::VALUE.2; + } +} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr new file mode 100644 index 000000000000..e7c3f879b05b --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr @@ -0,0 +1,247 @@ +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:35:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing +note: the lint level is defined here + --> tests/ui/borrow_interior_mutable_const.rs:3:9 + | +LL | #![deny(clippy::borrow_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:37:17 + | +LL | let _ = C.get(); + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:42:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:43:17 + | +LL | let _ = &mut C; + | ^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:47:9 + | +LL | C.swap(&local) + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:52:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:53:17 + | +LL | let _ = &C[0]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:54:17 + | +LL | let _ = &C[0].0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:55:9 + | +LL | C[0].0.set(1); + | ^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:70:17 + | +LL | let _ = &S::C; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:71:17 + | +LL | let _ = &S::C.0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:72:9 + | +LL | S::C.set(1); + | ^^^^ + | + = note: there is a compiler inserted call to `Deref::deref` here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:73:18 + | +LL | let _ = &*S::C; + | ^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:74:9 + | +LL | (*S::C).set(1); + | ^^^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:85:17 + | +LL | let _ = &CELL; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:109:25 + | +LL | let _ = &Self::C; + | ^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:112:25 + | +LL | let _ = &Self::C.cell; + | ^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:113:25 + | +LL | let _ = &Self::C.cell.0; + | ^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:114:17 + | +LL | Self::C.cell.0.set(T::DEFAULT); + | ^^^^^^^^^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:128:17 + | +LL | let _ = &u32::VALUE; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:145:21 + | +LL | let _ = &>::VALUE; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:172:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:173:18 + | +LL | let _ = &C[0]; + | ^^^^ + | + = note: this index expression is a call to `Index::index` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:174:17 + | +LL | let _ = &C.0[0]; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:190:17 + | +LL | let _ = &C[1]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:194:21 + | +LL | let _ = &C[i]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:198:17 + | +LL | let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:216:17 + | +LL | let _ = &S::VALUE; + | ^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:218:17 + | +LL | let _ = &S::VALUE.1; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: aborting due to 29 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs deleted file mode 100644 index da940a4cfb50..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs +++ /dev/null @@ -1,101 +0,0 @@ -//@aux-build:helper.rs - -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions. - -extern crate helper; - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell), - Frozen, -} - -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -fn borrow_optional_cell() { - let _ = &UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &FROZEN_VARIANT; -} - -trait AssocConsts { - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - const TO_BE_FROZEN_VARIANT: OptionalCell; - - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - // This is the "suboptimal behavior" mentioned in `is_value_unfrozen` - // caused by a similar reason to unfrozen types without any default values - // get linted even if it has frozen variants'. - let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR: interior mutability - - // The lint ignores default values because an impl of this trait can set - // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`. - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR: interior mutability - } -} - -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &::TO_BE_FROZEN_VARIANT; - let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - } -} - -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option; - const TO_BE_FROZEN_VARIANT: Option; - - // there's no need to test here because it's the exactly same as `trait::AssocTypes` - fn function(); -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - const TO_BE_FROZEN_VARIANT: Option = None; - - fn function() { - let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &::TO_BE_FROZEN_VARIANT; - } -} - -enum BothOfCellAndGeneric { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl BothOfCellAndGeneric { - const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); - - fn function() { - let _ = &Self::UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::GENERIC_VARIANT; //~ ERROR: interior mutability - let _ = &Self::FROZEN_VARIANT; - } -} - -fn main() { - // constants defined in foreign crates - let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr deleted file mode 100644 index 43850384b903..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,79 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:22:14 - | -LL | let _ = &UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/enums.rs:3:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:37:18 - | -LL | let _ = &Self::TO_BE_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:41:18 - | -LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:50:18 - | -LL | let _ = &::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:52:18 - | -LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:74:18 - | -LL | let _ = &::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:91:18 - | -LL | let _ = &Self::UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:92:18 - | -LL | let _ = &Self::GENERIC_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:99:14 - | -LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 9 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs deleted file mode 100644 index fa729b62d7f5..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ /dev/null @@ -1,128 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)] -#![allow(const_item_mutation)] - -use std::borrow::Cow; -use std::cell::{Cell, UnsafeCell}; -use std::fmt::Display; -use std::sync::Once; -use std::sync::atomic::{AtomicUsize, Ordering}; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -const CELL: Cell = Cell::new(6); -const ATOMIC_TUPLE: ([AtomicUsize; 1], Option>, u8) = ([ATOMIC], None, 7); -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -const NO_ANN: &dyn Display = &70; -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -const ONCE_INIT: Once = Once::new(); - -// This is just a pointer that can be safely dereferenced, -// it's semantically the same as `&'static T`; -// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. -// For more information, please see the issue #5918. -pub struct StaticRef { - ptr: *const T, -} - -impl StaticRef { - /// Create a new `StaticRef` from a raw pointer - /// - /// ## Safety - /// - /// Callers must pass in a reference to statically allocated memory which - /// does not overlap with other values. - pub const unsafe fn new(ptr: *const T) -> StaticRef { - StaticRef { ptr } - } -} - -impl std::ops::Deref for StaticRef { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.ptr } - } -} - -// ICE regression test -mod issue12979 { - use std::cell::UnsafeCell; - - const ATOMIC_TUPLE: (Vec>, ()) = (Vec::new(), ()); - - fn main() { - let _x = &ATOMIC_TUPLE.0; - } -} - -// use a tuple to make sure referencing a field behind a pointer isn't linted. -const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; - -fn main() { - ATOMIC.store(1, Ordering::SeqCst); - //~^ borrow_interior_mutable_const - assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - //~^ borrow_interior_mutable_const - - let _once = ONCE_INIT; - let _once_ref = &ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_2 = &&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_4 = &&&&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_mut = &mut ONCE_INIT; - //~^ borrow_interior_mutable_const - let _atomic_into_inner = ATOMIC.into_inner(); - // these should be all fine. - let _twice = (ONCE_INIT, ONCE_INIT); - let _ref_twice = &(ONCE_INIT, ONCE_INIT); - let _ref_once = &(ONCE_INIT, ONCE_INIT).0; - let _array_twice = [ONCE_INIT, ONCE_INIT]; - let _ref_array_twice = &[ONCE_INIT, ONCE_INIT]; - let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0]; - - // referencing projection is still bad. - let _ = &ATOMIC_TUPLE; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0; - //~^ borrow_interior_mutable_const - let _ = &(&&&&ATOMIC_TUPLE).0; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.2; - let _ = (&&&&ATOMIC_TUPLE).0; - let _ = (&&&&ATOMIC_TUPLE).2; - let _ = ATOMIC_TUPLE.0; - let _ = ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.1.into_iter(); - let _ = ATOMIC_TUPLE.2; - let _ = &{ ATOMIC_TUPLE }; - - CELL.set(2); - //~^ borrow_interior_mutable_const - assert_eq!(CELL.get(), 6); - //~^ borrow_interior_mutable_const - - assert_eq!(INTEGER, 8); - assert!(STRING.is_empty()); - - let a = ATOMIC; - a.store(4, Ordering::SeqCst); - assert_eq!(a.load(Ordering::SeqCst), 4); - - STATIC_TUPLE.0.store(3, Ordering::SeqCst); - assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); - assert!(STATIC_TUPLE.1.is_empty()); - - assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. - - let _ = &CELL_REF.0; -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr deleted file mode 100644 index decea153f717..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr +++ /dev/null @@ -1,119 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 - | -LL | ATOMIC.store(1, Ordering::SeqCst); - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/others.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:67:16 - | -LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:71:22 - | -LL | let _once_ref = &ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:73:25 - | -LL | let _once_ref_2 = &&ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:75:27 - | -LL | let _once_ref_4 = &&&&ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:77:26 - | -LL | let _once_mut = &mut ONCE_INIT; - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:89:14 - | -LL | let _ = &ATOMIC_TUPLE; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:91:14 - | -LL | let _ = &ATOMIC_TUPLE.0; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:93:19 - | -LL | let _ = &(&&&&ATOMIC_TUPLE).0; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:95:14 - | -LL | let _ = &ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:97:13 - | -LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:103:13 - | -LL | let _ = ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:109:5 - | -LL | CELL.set(2); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:111:16 - | -LL | assert_eq!(CELL.get(), 6); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 14 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs deleted file mode 100644 index bbe5538fbe12..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![deny(clippy::declare_interior_mutable_const)] - -// Inspired by https://github.com/rust-lang/rust/pull/130543#issuecomment-2364828139 - -use std::cell::UnsafeCell; - -trait Trait { - type Assoc; -} - -type Assoc = ::Assoc; - -impl Trait for u8 { - type Assoc = UnsafeCell; -} - -impl Trait for () { - type Assoc = (); -} - -enum MaybeMutable { - Mutable(Assoc), - Immutable(Assoc<()>), -} - -const CELL: Assoc = UnsafeCell::new(0); //~ ERROR: interior mutable -const UNIT: Assoc<()> = (); -const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); //~ ERROR: interior mutable -const IMMUTABLE: MaybeMutable = MaybeMutable::Immutable(UNIT); - -fn print_ref(t: &T) { - let p: *const T = t; - println!("{p:p}") -} - -fn main() { - print_ref(&CELL); //~ ERROR: interior mutability - print_ref(&UNIT); - print_ref(&MUTABLE); //~ ERROR: interior mutability - print_ref(&IMMUTABLE); -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr deleted file mode 100644 index eabaf66560ad..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:27:1 - | -LL | const CELL: Assoc = UnsafeCell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:2:9 - | -LL | #![deny(clippy::declare_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:29:1 - | -LL | const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:38:16 - | -LL | print_ref(&CELL); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:40:16 - | -LL | print_ref(&MUTABLE); - | ^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs deleted file mode 100644 index c4878dbe57b2..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs +++ /dev/null @@ -1,219 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file replicates its `declare` counterpart. Please see it for more discussions. - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::{AtomicUsize, Ordering}; - -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - const STRING: String; - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const STRING: String = String::new(); - - fn function() { - // Lint this again since implementers can choose not to borrow it. - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -trait GenericTypes { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - } -} - -impl GenericTypes for Vec { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - let _ = &Self::TO_BE_CONCRETE; - //~^ borrow_interior_mutable_const - } -} - -// a helper type used below -pub struct Wrapper(T); - -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper; - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - } -} - -impl AssocTypes for Vec { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM; - } -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -trait AssocTypesFromGenericParam -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -impl AssocTypesFromGenericParam for Vec -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -trait SelfType: Sized { - const SELF: Self; - const WRAPPED_SELF: Option; - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option = Some(20); - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for AtomicUsize { - const SELF: Self = AtomicUsize::new(17); - const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - - fn function() { - let _ = &Self::SELF; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_SELF; - //~^ borrow_interior_mutable_const - } -} - -trait BothOfCellAndGeneric { - const DIRECT: Cell; - const INDIRECT: Cell<*const T>; - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -impl BothOfCellAndGeneric for Vec { - const DIRECT: Cell = Cell::new(T::DEFAULT); - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -struct Local(T); - -impl Local -where - T: ConstDefault + AssocTypesHelper, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::COW; - let _ = &Self::GENERIC_TYPE; - let _ = &Self::ASSOC_TYPE; - let _ = &Self::BOUNDED_ASSOC_TYPE; - //~^ borrow_interior_mutable_const - } -} - -fn main() { - u64::ATOMIC.store(5, Ordering::SeqCst); - //~^ borrow_interior_mutable_const - assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - //~^ borrow_interior_mutable_const -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr deleted file mode 100644 index cad68ca9260c..000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,143 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:15:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/traits.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:27:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:53:18 - | -LL | let _ = &Self::TO_BE_CONCRETE; - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:89:18 - | -LL | let _ = &Self::TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:91:18 - | -LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:114:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:128:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:158:18 - | -LL | let _ = &Self::SELF; - | ^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:160:18 - | -LL | let _ = &Self::WRAPPED_SELF; - | ^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:170:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:172:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:182:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:184:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:204:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:209:18 - | -LL | let _ = &Self::BOUNDED_ASSOC_TYPE; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:215:5 - | -LL | u64::ATOMIC.store(5, Ordering::SeqCst); - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:217:16 - | -LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 17 previous errors - diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index 88c2549f4dc5..525be8216500 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -1,7 +1,5 @@ -//@no-rustfix +//@no-rustfix: only some diagnostics have suggestions -#![feature(repr128)] -#![allow(incomplete_features)] #![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr index 4d03282f6676..1cb30d956679 100644 --- a/src/tools/clippy/tests/ui/cast.stderr +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -1,5 +1,5 @@ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:25:5 + --> tests/ui/cast.rs:23:5 | LL | x0 as f32; | ^^^^^^^^^ @@ -8,37 +8,37 @@ LL | x0 as f32; = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:29:5 + --> tests/ui/cast.rs:27:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:32:5 + --> tests/ui/cast.rs:30:5 | LL | x1 as f64; | ^^^^^^^^^ error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:36:5 + --> tests/ui/cast.rs:34:5 | LL | x2 as f32; | ^^^^^^^^^ error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:40:5 + --> tests/ui/cast.rs:38:5 | LL | x3 as f32; | ^^^^^^^^^ error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:43:5 + --> tests/ui/cast.rs:41:5 | LL | x3 as f64; | ^^^^^^^^^ error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:47:5 + --> tests/ui/cast.rs:45:5 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | 1f32 as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:50:5 + --> tests/ui/cast.rs:48:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | 1f32 as u32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:50:5 + --> tests/ui/cast.rs:48:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | 1f32 as u32; = help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]` error: casting `f64` to `f32` may truncate the value - --> tests/ui/cast.rs:54:5 + --> tests/ui/cast.rs:52:5 | LL | 1f64 as f32; | ^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | 1f64 as f32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:57:5 + --> tests/ui/cast.rs:55:5 | LL | 1i32 as i8; | ^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + i8::try_from(1i32); | error: casting `i32` to `u8` may truncate the value - --> tests/ui/cast.rs:60:5 + --> tests/ui/cast.rs:58:5 | LL | 1i32 as u8; | ^^^^^^^^^^ @@ -99,7 +99,7 @@ LL + u8::try_from(1i32); | error: casting `f64` to `isize` may truncate the value - --> tests/ui/cast.rs:63:5 + --> tests/ui/cast.rs:61:5 | LL | 1f64 as isize; | ^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL | 1f64 as isize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may truncate the value - --> tests/ui/cast.rs:66:5 + --> tests/ui/cast.rs:64:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ @@ -115,13 +115,13 @@ LL | 1f64 as usize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:66:5 + --> tests/ui/cast.rs:64:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ error: casting `u32` to `u16` may truncate the value - --> tests/ui/cast.rs:70:5 + --> tests/ui/cast.rs:68:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL + u16::try_from(1f32 as u32); | error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:70:5 + --> tests/ui/cast.rs:68:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ @@ -142,13 +142,13 @@ LL | 1f32 as u32 as u16; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:70:5 + --> tests/ui/cast.rs:68:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:76:22 + --> tests/ui/cast.rs:74:22 | LL | let _x: i8 = 1i32 as _; | ^^^^^^^^^ @@ -161,7 +161,7 @@ LL + let _x: i8 = 1i32.try_into(); | error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:79:9 + --> tests/ui/cast.rs:77:9 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL | 1f32 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `i32` may truncate the value - --> tests/ui/cast.rs:82:9 + --> tests/ui/cast.rs:80:9 | LL | 1f64 as i32; | ^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | 1f64 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may truncate the value - --> tests/ui/cast.rs:85:9 + --> tests/ui/cast.rs:83:9 | LL | 1f32 as u8; | ^^^^^^^^^^ @@ -185,13 +185,13 @@ LL | 1f32 as u8; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:85:9 + --> tests/ui/cast.rs:83:9 | LL | 1f32 as u8; | ^^^^^^^^^^ error: casting `u8` to `i8` may wrap around the value - --> tests/ui/cast.rs:90:5 + --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; | ^^^^^^^^^ @@ -200,31 +200,31 @@ LL | 1u8 as i8; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `u16` to `i16` may wrap around the value - --> tests/ui/cast.rs:93:5 + --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; | ^^^^^^^^^^^ error: casting `u32` to `i32` may wrap around the value - --> tests/ui/cast.rs:96:5 + --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; | ^^^^^^^^^^^ error: casting `u64` to `i64` may wrap around the value - --> tests/ui/cast.rs:99:5 + --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; | ^^^^^^^^^^^ error: casting `usize` to `isize` may wrap around the value - --> tests/ui/cast.rs:102:5 + --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; | ^^^^^^^^^^^^^^^ error: casting `usize` to `i8` may truncate the value - --> tests/ui/cast.rs:106:5 + --> tests/ui/cast.rs:104:5 | LL | 1usize as i8; | ^^^^^^^^^^^^ @@ -237,7 +237,7 @@ LL + i8::try_from(1usize); | error: casting `usize` to `i16` may truncate the value - --> tests/ui/cast.rs:110:5 + --> tests/ui/cast.rs:108:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -250,7 +250,7 @@ LL + i16::try_from(1usize); | error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:110:5 + --> tests/ui/cast.rs:108:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -259,7 +259,7 @@ LL | 1usize as i16; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:115:5 + --> tests/ui/cast.rs:113:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -272,19 +272,19 @@ LL + i32::try_from(1usize); | error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:115:5 + --> tests/ui/cast.rs:113:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:120:5 + --> tests/ui/cast.rs:118:5 | LL | 1usize as i64; | ^^^^^^^^^^^^^ error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:126:5 + --> tests/ui/cast.rs:124:5 | LL | 1u16 as isize; | ^^^^^^^^^^^^^ @@ -293,13 +293,13 @@ LL | 1u16 as isize; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:130:5 + --> tests/ui/cast.rs:128:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:134:5 + --> tests/ui/cast.rs:132:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -312,55 +312,55 @@ LL + isize::try_from(1u64); | error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:134:5 + --> tests/ui/cast.rs:132:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:140:5 + --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; | ^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:144:5 + --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; | ^^^^^^^^^^^^^^^^ error: casting `i8` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:156:5 + --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; | ^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:161:5 + --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; | ^^^^^^^^^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:163:5 + --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:171:5 + --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:187:5 + --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `i8` may truncate the value - --> tests/ui/cast.rs:239:5 + --> tests/ui/cast.rs:237:5 | LL | (-99999999999i64).min(1) as i8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -373,7 +373,7 @@ LL + i8::try_from((-99999999999i64).min(1)); | error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:253:5 + --> tests/ui/cast.rs:251:5 | LL | 999999u64.clamp(0, 256) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -386,7 +386,7 @@ LL + u8::try_from(999999u64.clamp(0, 256)); | error: casting `main::E2` to `u8` may truncate the value - --> tests/ui/cast.rs:276:21 + --> tests/ui/cast.rs:274:21 | LL | let _ = self as u8; | ^^^^^^^^^^ @@ -399,7 +399,7 @@ LL + let _ = u8::try_from(self); | error: casting `main::E2::B` to `u8` will truncate the value - --> tests/ui/cast.rs:279:21 + --> tests/ui/cast.rs:277:21 | LL | let _ = Self::B as u8; | ^^^^^^^^^^^^^ @@ -408,7 +408,7 @@ LL | let _ = Self::B as u8; = help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]` error: casting `main::E5` to `i8` may truncate the value - --> tests/ui/cast.rs:321:21 + --> tests/ui/cast.rs:319:21 | LL | let _ = self as i8; | ^^^^^^^^^^ @@ -421,13 +421,13 @@ LL + let _ = i8::try_from(self); | error: casting `main::E5::A` to `i8` will truncate the value - --> tests/ui/cast.rs:324:21 + --> tests/ui/cast.rs:322:21 | LL | let _ = Self::A as i8; | ^^^^^^^^^^^^^ error: casting `main::E6` to `i16` may truncate the value - --> tests/ui/cast.rs:342:21 + --> tests/ui/cast.rs:340:21 | LL | let _ = self as i16; | ^^^^^^^^^^^ @@ -440,7 +440,7 @@ LL + let _ = i16::try_from(self); | error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:362:21 + --> tests/ui/cast.rs:360:21 | LL | let _ = self as usize; | ^^^^^^^^^^^^^ @@ -453,7 +453,7 @@ LL + let _ = usize::try_from(self); | error: casting `main::E10` to `u16` may truncate the value - --> tests/ui/cast.rs:410:21 + --> tests/ui/cast.rs:408:21 | LL | let _ = self as u16; | ^^^^^^^^^^^ @@ -466,7 +466,7 @@ LL + let _ = u16::try_from(self); | error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:422:13 + --> tests/ui/cast.rs:420:13 | LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ @@ -479,7 +479,7 @@ LL + let c = u8::try_from(q >> 16); | error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:427:13 + --> tests/ui/cast.rs:425:13 | LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ @@ -492,85 +492,85 @@ LL + let c = u8::try_from(q / 1000); | error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:440:9 + --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:446:32 + --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:449:5 + --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:451:5 + --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:456:5 + --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:459:5 + --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:463:5 + --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:467:5 + --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:469:5 + --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:473:5 + --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:476:5 + --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:478:5 + --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:481:5 + --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `/` - --> tests/ui/cast.rs:481:6 + --> tests/ui/cast.rs:479:6 | LL | (y / y * y * -2) as u16; | ^^^^^ @@ -578,97 +578,97 @@ LL | (y / y * y * -2) as u16; = note: `#[deny(clippy::eq_op)]` on by default error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:485:5 + --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:488:5 + --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:492:5 + --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; | ^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:495:5 + --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:499:9 + --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:501:9 + --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:504:9 + --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:507:9 + --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:509:9 + --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; | ^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:512:9 + --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:515:9 + --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:517:9 + --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:520:9 + --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:523:9 + --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:526:9 + --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:534:21 + --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss | ^^^^^^^^^^^^^^^ @@ -679,7 +679,7 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:537:21 + --> tests/ui/cast.rs:535:21 | LL | let _ = u32::MAX as u8; // cast_possible_truncation | ^^^^^^^^^^^^^^ @@ -696,7 +696,7 @@ LL + let _ = u8::try_from(u32::MAX); // cast_possible_truncation | error: casting `f64` to `f32` may truncate the value - --> tests/ui/cast.rs:540:21 + --> tests/ui/cast.rs:538:21 | LL | let _ = std::f64::consts::PI as f32; // cast_possible_truncation | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -708,7 +708,7 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:551:5 + --> tests/ui/cast.rs:549:5 | LL | bar.unwrap().unwrap() as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -721,13 +721,13 @@ LL + usize::try_from(bar.unwrap().unwrap()) | error: casting `i64` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:551:5 + --> tests/ui/cast.rs:549:5 | LL | bar.unwrap().unwrap() as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:568:5 + --> tests/ui/cast.rs:566:5 | LL | (256 & 999999u64) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -740,7 +740,7 @@ LL + u8::try_from(256 & 999999u64); | error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:571:5 + --> tests/ui/cast.rs:569:5 | LL | (255 % 999999u64) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index e92401808867..e5bef2a99d59 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -1,7 +1,7 @@ //@revisions: 32bit 64bit //@[32bit]ignore-bitwidth: 64 //@[64bit]ignore-bitwidth: 32 -//@no-rustfix +//@no-rustfix: only some diagnostics have suggestions #![warn( clippy::cast_precision_loss, diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 145885702a9b..7635f848cb34 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -4,7 +4,7 @@ clippy::branches_sharing_code, clippy::unnecessary_literal_unwrap )] -//@no-rustfix +//@no-rustfix: has placeholders fn test_nested() { fn nested() { let x = Some(()); diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs index ba0d36d85fe5..785b2473c053 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,4 +1,4 @@ -//@no-rustfix: overlapping suggestions +//@no-rustfix: has placeholders #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] #![allow( clippy::if_same_then_else, diff --git a/src/tools/clippy/tests/ui/comparison_chain.rs b/src/tools/clippy/tests/ui/comparison_chain.rs index 669690a4d42c..6db75a4f364d 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.rs +++ b/src/tools/clippy/tests/ui/comparison_chain.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: has placeholders #![allow(dead_code)] #![warn(clippy::comparison_chain)] @@ -12,11 +12,10 @@ fn f(x: u8, y: u8, z: u8) { a() } - if x > y { - //~^ comparison_chain - + // Ignored: Not all cases are covered + if x < y { a() - } else if x < y { + } else if x > y { b() } @@ -123,9 +122,8 @@ fn g(x: f64, y: f64, z: f64) { } fn h(x: T, y: T, z: T) { + // Ignored: Not all cases are covered if x > y { - //~^ comparison_chain - a() } else if x < y { b() diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr index 0256573d0d90..ce580bd54a16 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.stderr +++ b/src/tools/clippy/tests/ui/comparison_chain.stderr @@ -1,12 +1,12 @@ error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:15:5 + --> tests/ui/comparison_chain.rs:29:5 | LL | / if x > y { LL | | LL | | LL | | a() -LL | | } else if x < y { -LL | | b() +... | +LL | | c() LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` | @@ -14,7 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::comparison_chain)]` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:30:5 + --> tests/ui/comparison_chain.rs:39:5 | LL | / if x > y { LL | | @@ -26,19 +26,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:40:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -... | -LL | | c() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:50:5 + --> tests/ui/comparison_chain.rs:49:5 | LL | / if x > 1 { LL | | @@ -50,19 +38,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&1) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:126:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -LL | | } else if x < y { -LL | | b() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:134:5 + --> tests/ui/comparison_chain.rs:132:5 | LL | / if x > y { LL | | @@ -74,7 +50,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:144:5 + --> tests/ui/comparison_chain.rs:142:5 | LL | / if x > y { LL | | @@ -86,7 +62,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:251:5 + --> tests/ui/comparison_chain.rs:249:5 | LL | / if x + 1 > y * 2 { LL | | @@ -97,5 +73,5 @@ LL | | "cc" LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match (x + 1).cmp(&(y * 2)) {...}` -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs index 5dffddc119aa..cf37a4c5c4bb 100644 --- a/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -1,6 +1,3 @@ -#![feature(repr128)] -#![allow(incomplete_features)] - extern crate proc_macro; use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.fixed b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed index ab9c61463e92..8a31eb9550b2 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed @@ -3,6 +3,6 @@ fn main() { if (true) { // anything一些中文 - //~^^ needless_return + //~^ needless_return } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.rs b/src/tools/clippy/tests/ui/crashes/ice-12491.rs index b774bd3788a7..013aadadce51 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.rs @@ -4,6 +4,6 @@ fn main() { if (true) { // anything一些中文 return; - //~^^ needless_return + //~^ needless_return } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.stderr b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr index 7cc418898e88..4b77299dd5e6 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr @@ -1,10 +1,8 @@ error: unneeded `return` statement - --> tests/ui/crashes/ice-12491.rs:5:24 + --> tests/ui/crashes/ice-12491.rs:6:9 | -LL | // anything一些中文 - | ____________________________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_return)]` diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed b/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed new file mode 100644 index 000000000000..e68f1c20a8e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed @@ -0,0 +1,2 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed b/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed new file mode 100644 index 000000000000..e89fa636d4b2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed @@ -0,0 +1,3 @@ +#![deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.rs b/src/tools/clippy/tests/ui/crashes/ice-12979.rs new file mode 100644 index 000000000000..a2787291d9e1 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.rs @@ -0,0 +1,3 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.stderr b/src/tools/clippy/tests/ui/crashes/ice-12979.stderr new file mode 100644 index 000000000000..5e7608161646 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.stderr @@ -0,0 +1,19 @@ +error: empty line after outer attribute + --> tests/ui/crashes/ice-12979.rs:1:1 + | +LL | / #[deny(clippy::declare_interior_mutable_const)] +LL | | + | |_^ +LL | const FOO: u8 = 0; + | --------- the attribute applies to this constant item + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` + = help: if the empty line is unintentional, remove it +help: if the attribute should apply to the crate use an inner attribute + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | + + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-6840.rs b/src/tools/clippy/tests/ui/crashes/ice-6840.rs index 94481f248998..f30d83e1eeec 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6840.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6840.rs @@ -2,6 +2,7 @@ //! This is a reproducer for the ICE 6840: https://github.com/rust-lang/rust-clippy/issues/6840. //! The ICE is caused by `TyCtxt::layout_of` and `is_normalizable` not being strict enough #![allow(dead_code)] +#![deny(clippy::zero_sized_map_values)] // For ICE 14822 use std::collections::HashMap; pub trait Rule { diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.rs b/src/tools/clippy/tests/ui/crashes/ice-9445.rs deleted file mode 100644 index 232b8e4a7959..000000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.rs +++ /dev/null @@ -1,4 +0,0 @@ -const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); -//~^ declare_interior_mutable_const - -fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr deleted file mode 100644 index 76689cd6f5c2..000000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/crashes/ice-9445.rs:1:1 - | -LL | const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs index 1a5119651b53..96b35c9a20c4 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: overlapping suggestions //@error-in-other-file: #![warn(clippy::dbg_macro)] diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs new file mode 100644 index 000000000000..c65df275038d --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs @@ -0,0 +1,200 @@ +#![deny(clippy::declare_interior_mutable_const)] +#![allow(clippy::missing_const_for_thread_local)] + +use core::cell::{Cell, RefCell, UnsafeCell}; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ptr; +use core::sync::atomic::AtomicUsize; + +fn main() {} + +const _: Cell = Cell::new(0); +const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(0); //~ declare_interior_mutable_const +const REF_CELL: RefCell = RefCell::new(0); //~ declare_interior_mutable_const +const CELL: Cell = Cell::new(0); //~ declare_interior_mutable_const + +// Constants can't contain pointers or references to type with interior mutability. +const fn make_ptr() -> *const Cell { + ptr::null() +} +const PTR: *const Cell = make_ptr(); + +const fn casted_to_cell_ptr() -> *const Cell { + const VALUE: u32 = 0; + &VALUE as *const _ as *const Cell +} +const TRANSMUTED_PTR: *const Cell = casted_to_cell_ptr(); + +const CELL_TUPLE: (bool, Cell) = (true, Cell::new(0)); //~ declare_interior_mutable_const +const CELL_ARRAY: [Cell; 2] = [Cell::new(0), Cell::new(0)]; //~ declare_interior_mutable_const + +const UNINIT_CELL: MaybeUninit> = MaybeUninit::uninit(); + +struct CellStruct { + x: u32, + cell: Cell, +} +//~v declare_interior_mutable_const +const CELL_STRUCT: CellStruct = CellStruct { + x: 0, + cell: Cell::new(0), +}; + +enum CellEnum { + Cell(Cell), +} +const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); //~ declare_interior_mutable_const + +const NONE_CELL: Option> = None; +const SOME_CELL: Option> = Some(Cell::new(0)); //~ declare_interior_mutable_const + +struct NestedCell([(Option>,); 1]); +const NONE_NESTED_CELL: NestedCell = NestedCell([(None,)]); +const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); //~ declare_interior_mutable_const + +union UnionCell { + cell: ManuallyDrop>, + x: u32, +} +//~v declare_interior_mutable_const +const UNION_CELL: UnionCell = UnionCell { + cell: ManuallyDrop::new(Cell::new(0)), +}; +// Access to either union field is valid so we have to be conservative here. +const UNION_U32: UnionCell = UnionCell { x: 0 }; //~ declare_interior_mutable_const + +struct Assoc; +impl Assoc { + const SELF: Self = Self; + const CELL: Cell = Cell::new(0); //~ declare_interior_mutable_const +} + +struct AssocCell(Cell); +impl AssocCell { + const SELF: Self = Self(Cell::new(0)); //~ declare_interior_mutable_const + const NONE_SELF: Option = None; + const SOME_SELF: Option = Some(Self(Cell::new(0))); //~ declare_interior_mutable_const +} + +trait ConstDefault { + // May or may not be `Freeze` + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl ConstDefault for Cell { + // Interior mutability is forced by the trait. + const DEFAULT: Self = Cell::new(T::DEFAULT); +} +impl ConstDefault for Option> { + // Could have been `None` + const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} + +enum GenericEnumCell { + Cell(Cell), + Other(T), +} +impl ConstDefault for GenericEnumCell { + const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} +impl GenericEnumCell { + const CELL: Self = Self::DEFAULT; //~ declare_interior_mutable_const + const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); //~ declare_interior_mutable_const + const OTHER: Self = Self::Other(T::DEFAULT); + const FROM_OTHER: Self = Self::OTHER; +} + +enum GenericNestedEnumCell { + GenericEnumCell(GenericEnumCell), + EnumCell(GenericEnumCell), + Other(T), +} +impl GenericNestedEnumCell { + const GENERIC_OTHER: Self = Self::GenericEnumCell(GenericEnumCell::::FROM_OTHER); + const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::::CELL); //~ declare_interior_mutable_const + const ENUM_OTHER: Self = Self::EnumCell(GenericEnumCell::::FROM_OTHER); + const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::::CELL); //~ declare_interior_mutable_const +} + +trait CellTrait: ConstDefault + Sized { + // Must be non-`Freeze` due to the type + const CELL: Cell; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option> = None; +} + +trait CellWithAssoc { + type T; + const DEFAULT: Self::T; + // Must be non-`Freeze` due to the type + const CELL: Cell; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option> = None; +} + +impl CellWithAssoc for () { + type T = u32; + const DEFAULT: Self::T = 0; + const CELL: Cell = Cell::new(0); + const OPTION_CELL: Option> = None; +} + +trait WithAssoc { + type T; + const VALUE: Self::T; +} + +impl WithAssoc for u32 { + type T = Cell; + // The cell comes from the impl block, not the trait. + const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithLayeredAssoc { + type T: WithAssoc; + const VALUE: ::T; +} + +impl WithLayeredAssoc for u32 { + type T = u32; + // The cell comes from the impl block, not the trait. + const VALUE: ::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssoc { + type T; + const VALUE: Self::T; +} + +impl WithGenericAssoc for u32 { + type T = Cell; + const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssocCell { + type T; + const VALUE: Self::T>; +} + +impl WithGenericAssocCell for u32 { + type T = Option; + const VALUE: Self::T> = None; +} + +impl WithGenericAssocCell for i32 { + type T = Option; + const VALUE: Self::T> = Some(Cell::new(0)); //~ declare_interior_mutable_const +} + +thread_local!(static THREAD_LOCAL_CELL: Cell = const { Cell::new(0) }); +thread_local!(static THREAD_LOCAL_CELL2: Cell = Cell::new(0)); diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr new file mode 100644 index 000000000000..9742c17486c5 --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr @@ -0,0 +1,197 @@ +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:12:7 + | +LL | const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(0); + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item +note: the lint level is defined here + --> tests/ui/declare_interior_mutable_const.rs:1:9 + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:13:7 + | +LL | const REF_CELL: RefCell = RefCell::new(0); + | ^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:14:7 + | +LL | const CELL: Cell = Cell::new(0); + | ^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:28:7 + | +LL | const CELL_TUPLE: (bool, Cell) = (true, Cell::new(0)); + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:29:7 + | +LL | const CELL_ARRAY: [Cell; 2] = [Cell::new(0), Cell::new(0)]; + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:38:7 + | +LL | const CELL_STRUCT: CellStruct = CellStruct { + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:46:7 + | +LL | const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:49:7 + | +LL | const SOME_CELL: Option> = Some(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:53:7 + | +LL | const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); + | ^^^^^^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:60:7 + | +LL | const UNION_CELL: UnionCell = UnionCell { + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:64:7 + | +LL | const UNION_U32: UnionCell = UnionCell { x: 0 }; + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:69:11 + | +LL | const CELL: Cell = Cell::new(0); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:74:11 + | +LL | const SELF: Self = Self(Cell::new(0)); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:76:11 + | +LL | const SOME_SELF: Option = Some(Self(Cell::new(0))); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:92:11 + | +LL | const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:100:11 + | +LL | const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:103:11 + | +LL | const CELL: Self = Self::DEFAULT; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:104:11 + | +LL | const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); + | ^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:116:11 + | +LL | const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::::CELL); + | ^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:118:11 + | +LL | const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::::CELL); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:123:11 + | +LL | const CELL: Cell; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:127:11 + | +LL | const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:136:11 + | +LL | const CELL: Cell; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:140:11 + | +LL | const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:160:11 + | +LL | const VALUE: Self::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:171:11 + | +LL | const VALUE: ::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:181:11 + | +LL | const VALUE: Self::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:196:11 + | +LL | const VALUE: Self::T> = Some(Cell::new(0)); + | ^^^^^ + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs deleted file mode 100644 index c87468277fb3..000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs +++ /dev/null @@ -1,135 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell), - Frozen, -} - -// a constant with enums should be linted only when the used variant is unfrozen (#3962). -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -const fn unfrozen_variant() -> OptionalCell { - OptionalCell::Unfrozen(Cell::new(false)) -} - -const fn frozen_variant() -> OptionalCell { - OptionalCell::Frozen -} - -const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); - -enum NestedInnermost { - Unfrozen(AtomicUsize), - Frozen, -} - -struct NestedInner { - inner: NestedInnermost, -} - -enum NestedOuter { - NestedInner(NestedInner), - NotNested(usize), -} - -struct NestedOutermost { - outer: NestedOuter, -} - -// a constant with enums should be linted according to its value, no matter how structs involve. -const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { - //~^ declare_interior_mutable_const - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), - }), -}; -const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost { - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Frozen, - }), -}; - -trait AssocConsts { - // When there's no default value, lint it only according to its type. - // Further details are on the corresponding code (`NonCopyConst::check_trait_item`). - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - - // Lint default values accordingly. - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - //~^ declare_interior_mutable_const - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; -} - -// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it -// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'. -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - // even if this sets an unfrozen variant, the lint ignores it. - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); -} - -// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters -// here are values; and I think substituted generics at definitions won't appear in MIR. -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option; - const TO_BE_FROZEN_VARIANT: Option; -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: Option = None; -} - -// Use raw pointers since direct generics have a false negative at the type level. -enum BothOfCellAndGeneric { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl BothOfCellAndGeneric { - const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - //~^ declare_interior_mutable_const - - // This is a false positive. The argument about this is on `is_value_unfrozen_raw` - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); - - // This is what is likely to be a false negative when one tries to fix - // the `GENERIC_VARIANT` false positive. - const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - //~^ declare_interior_mutable_const -} - -// associated types here is basically the same as the one above. -trait BothOfCellAndGenericWithAssocType { - type AssocType; - - const UNFROZEN_VARIANT: BothOfCellAndGeneric = - //~^ declare_interior_mutable_const - BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr deleted file mode 100644 index 32839d14f0ed..000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:12:1 - | -LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:24:1 - | -LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:47:1 - | -LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { -LL | | -LL | | outer: NestedOuter::NestedInner(NestedInner { -LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), -LL | | }), -LL | | }; - | |__^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:62:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:64:5 - | -LL | const TO_BE_FROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:68:5 - | -LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:95:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:108:5 - | -LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:112:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:119:5 - | -LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:127:5 - | -LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric = -LL | | -LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | |____________________________________________________________________^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:130:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs deleted file mode 100644 index 7ce04a3f2c34..000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs +++ /dev/null @@ -1,76 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::fmt::Display; -use std::ptr; -use std::sync::Once; -use std::sync::atomic::AtomicUsize; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -//~^ declare_interior_mutable_const -const CELL: Cell = Cell::new(6); -//~^ declare_interior_mutable_const -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); -//~^ declare_interior_mutable_const - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} -declare_const!(_ONCE: Once = Once::new()); - -// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. - -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -// note: a const item of Cow is used in the `postgres` package. - -const NO_ANN: &dyn Display = &70; - -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -// there should be no lints on the line above line - -mod issue_8493 { - use std::cell::Cell; - - thread_local! { - static _BAR: Cell = const { Cell::new(0) }; - } - - macro_rules! issue_8493 { - () => { - const _BAZ: Cell = Cell::new(0); - //~^ declare_interior_mutable_const - static _FOOBAR: () = { - thread_local! { - static _VAR: Cell = const { Cell::new(0) }; - } - }; - }; - } - - issue_8493!(); -} - -#[repr(C, align(8))] -struct NoAtomic(usize); -#[repr(C, align(8))] -struct WithAtomic(AtomicUsize); - -const fn with_non_null() -> *const WithAtomic { - const NO_ATOMIC: NoAtomic = NoAtomic(0); - (&NO_ATOMIC as *const NoAtomic).cast() -} -const WITH_ATOMIC: *const WithAtomic = with_non_null(); - -struct Generic(T); -impl Generic { - const RAW_POINTER: *const Cell = ptr::null(); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr deleted file mode 100644 index 09299b290416..000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:10:1 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:12:1 - | -LL | const CELL: Cell = Cell::new(6); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:14:1 - | -LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:19:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(_ONCE: Once = Once::new()); - | ----------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:47:13 - | -LL | const _BAZ: Cell = Cell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | issue_8493!(); - | ------------- in this macro invocation - | - = note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 5 previous errors - diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs deleted file mode 100644 index d3139be6859f..000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs +++ /dev/null @@ -1,162 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} - -// a constant whose type is a concrete type should be linted at the definition site. -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - //~^ declare_interior_mutable_const - const INTEGER: u64; - const STRING: String; - declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INTEGER: u64 = 10; - const STRING: String = String::new(); -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -// a constant whose type is a generic type should be linted at the implementation site. -trait GenericTypes { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; - declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); -} - -impl GenericTypes for u64 { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - //~^ declare_interior_mutable_const -} - -// a helper type used below -struct Wrapper(T); - -// a constant whose type is an associated type should be linted at the implementation site, too. -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper; - // to ensure it can handle things when a generic type remains after normalization. - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; -} - -impl AssocTypes for Vec { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -// a constant whose type is an assoc type originated from a generic param bounded at the definition -// site should be linted at there. -trait AssocTypesFromGenericParam -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - //~^ declare_interior_mutable_const -} - -impl AssocTypesFromGenericParam for u64 -where - T: AssocTypesHelper, -{ - // an associated type could remain unknown in a trait impl. - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); -} - -// a constant whose type is `Self` should be linted at the implementation site as well. -// (`Option` requires `Sized` bound.) -trait SelfType: Sized { - const SELF: Self; - // this was the one in the original issue (#5050). - const WRAPPED_SELF: Option; -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option = Some(20); -} - -impl SelfType for AtomicUsize { - // this (interior mutable `Self` const) exists in `parking_lot`. - // `const_trait_impl` will replace it in the future, hopefully. - const SELF: Self = AtomicUsize::new(17); - //~^ declare_interior_mutable_const - const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - //~^ declare_interior_mutable_const -} - -// Even though a constant contains a generic type, if it also have an interior mutable type, -// it should be linted at the definition site. -trait BothOfCellAndGeneric { - const DIRECT: Cell; - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T>; - //~^ declare_interior_mutable_const -} - -impl BothOfCellAndGeneric for u64 { - const DIRECT: Cell = Cell::new(T::DEFAULT); - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); -} - -struct Local(T); - -// a constant in an inherent impl are essentially the same as a normal const item -// except there can be a generic or associated type. -impl Local -where - T: ConstDefault + AssocTypesHelper, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - //~^ declare_interior_mutable_const - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - //~^ declare_interior_mutable_const -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr deleted file mode 100644 index b03dd7a08403..000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,88 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:16:5 - | -LL | const ATOMIC: AtomicUsize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:9:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); - | ---------------------------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:45:5 - | -LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:71:5 - | -LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:73:5 - | -LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:93:5 - | -LL | const BOUNDED: T::ToBeBounded; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:122:5 - | -LL | const SELF: Self = AtomicUsize::new(17); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:124:5 - | -LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:131:5 - | -LL | const DIRECT: Cell; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:133:5 - | -LL | const INDIRECT: Cell<*const T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:138:5 - | -LL | const DIRECT: Cell = Cell::new(T::DEFAULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:151:5 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:158:5 - | -LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 13 previous errors - diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 2787f6406fe3..6b69bdd29cea 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -2,20 +2,20 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` -#![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice` -#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` -#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` -#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` -#![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute` #![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` -#![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` -#![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect` -#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` -#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` -#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` -#![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` -#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` +#![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice` #![warn(clippy::match_on_vec_items)] //~ ERROR: lint `clippy::match_on_vec_items` +#![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute` +#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` +#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` +#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` +#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` +#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` +#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` +#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` +#![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect` +#![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index 604732405c37..07e59d33d608 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -1,8 +1,8 @@ -error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can +error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy --> tests/ui/deprecated.rs:5:9 | -LL | #![warn(clippy::should_assert_eq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::assign_ops)] + | ^^^^^^^^^^^^^^^^^^ | = note: `-D renamed-and-removed-lints` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` @@ -13,83 +13,83 @@ error: lint `clippy::extend_from_slice` has been removed: `Vec::extend_from_slic LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator - --> tests/ui/deprecated.rs:7:9 - | -LL | #![warn(clippy::range_step_by_zero)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable - --> tests/ui/deprecated.rs:8:9 - | -LL | #![warn(clippy::unstable_as_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable - --> tests/ui/deprecated.rs:9:9 - | -LL | #![warn(clippy::unstable_as_mut_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` - --> tests/ui/deprecated.rs:10:9 - | -LL | #![warn(clippy::misaligned_transmute)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy - --> tests/ui/deprecated.rs:11:9 - | -LL | #![warn(clippy::assign_ops)] - | ^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower - --> tests/ui/deprecated.rs:12:9 - | -LL | #![warn(clippy::unsafe_vector_initialization)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` - --> tests/ui/deprecated.rs:13:9 - | -LL | #![warn(clippy::unused_collect)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated - --> tests/ui/deprecated.rs:14:9 - | -LL | #![warn(clippy::replace_consts)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 - --> tests/ui/deprecated.rs:15:9 - | -LL | #![warn(clippy::regex_macro)] - | ^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config - --> tests/ui/deprecated.rs:16:9 - | -LL | #![warn(clippy::pub_enum_variant_names)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config - --> tests/ui/deprecated.rs:17:9 - | -LL | #![warn(clippy::wrong_pub_self_convention)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case - --> tests/ui/deprecated.rs:18:9 - | -LL | #![warn(clippy::option_map_or_err_ok)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: lint `clippy::match_on_vec_items` has been removed: `clippy::indexing_slicing` covers indexing and slicing on `Vec<_>` - --> tests/ui/deprecated.rs:19:9 + --> tests/ui/deprecated.rs:7:9 | LL | #![warn(clippy::match_on_vec_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` + --> tests/ui/deprecated.rs:8:9 + | +LL | #![warn(clippy::misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case + --> tests/ui/deprecated.rs:9:9 + | +LL | #![warn(clippy::option_map_or_err_ok)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config + --> tests/ui/deprecated.rs:10:9 + | +LL | #![warn(clippy::pub_enum_variant_names)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator + --> tests/ui/deprecated.rs:11:9 + | +LL | #![warn(clippy::range_step_by_zero)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 + --> tests/ui/deprecated.rs:12:9 + | +LL | #![warn(clippy::regex_macro)] + | ^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated + --> tests/ui/deprecated.rs:13:9 + | +LL | #![warn(clippy::replace_consts)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can + --> tests/ui/deprecated.rs:14:9 + | +LL | #![warn(clippy::should_assert_eq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower + --> tests/ui/deprecated.rs:15:9 + | +LL | #![warn(clippy::unsafe_vector_initialization)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable + --> tests/ui/deprecated.rs:16:9 + | +LL | #![warn(clippy::unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable + --> tests/ui/deprecated.rs:17:9 + | +LL | #![warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` + --> tests/ui/deprecated.rs:18:9 + | +LL | #![warn(clippy::unused_collect)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config + --> tests/ui/deprecated.rs:19:9 + | +LL | #![warn(clippy::wrong_pub_self_convention)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs index e9218bbb4094..73f62ac12469 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: requires manual changes #![warn(clippy::double_ended_iterator_last)] // Should not be linted because applying the lint would move the original iterator. This can only be diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs index 874f5d22075c..3ca91d6f1829 100644 --- a/src/tools/clippy/tests/ui/duplicated_attributes.rs +++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs @@ -21,7 +21,7 @@ fn foo() {} fn bar() {} // No warning: -#[rustc_on_unimplemented(on(_Self = "&str", label = "`a"), on(_Self = "alloc::string::String", label = "a"))] +#[rustc_on_unimplemented(on(Self = "&str", label = "`a"), on(Self = "alloc::string::String", label = "a"))] trait Abc {} #[proc_macro_attr::duplicated_attr()] // Should not warn! diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed index b1600862a8f6..419cf2354f89 100644 --- a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed @@ -23,4 +23,12 @@ struct MyTupleStruct(usize, String); // should not trigger lint struct MySingleTupleStruct(usize); // should not trigger lint struct MyUnitLikeStruct; // should not trigger lint +macro_rules! empty_struct { + ($s:ident) => { + struct $s {} + }; +} + +empty_struct!(FromMacro); + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs index 1f69c4be9ec7..90c415c12206 100644 --- a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs @@ -23,4 +23,12 @@ struct MyTupleStruct(usize, String); // should not trigger lint struct MySingleTupleStruct(usize); // should not trigger lint struct MyUnitLikeStruct; // should not trigger lint +macro_rules! empty_struct { + ($s:ident) => { + struct $s {} + }; +} + +empty_struct!(FromMacro); + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_unfixable.rs b/src/tools/clippy/tests/ui/entry_unfixable.rs index dbdacf950569..c4c055572086 100644 --- a/src/tools/clippy/tests/ui/entry_unfixable.rs +++ b/src/tools/clippy/tests/ui/entry_unfixable.rs @@ -1,6 +1,5 @@ -#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![allow(clippy::needless_pass_by_value, clippy::collapsible_if)] #![warn(clippy::map_entry)] -//@no-rustfix use std::collections::HashMap; use std::hash::Hash; diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr index 9f9956d351b2..0197d2ab4cf9 100644 --- a/src/tools/clippy/tests/ui/entry_unfixable.stderr +++ b/src/tools/clippy/tests/ui/entry_unfixable.stderr @@ -1,5 +1,5 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> tests/ui/entry_unfixable.rs:28:13 + --> tests/ui/entry_unfixable.rs:27:13 | LL | / if !self.values.contains_key(&name) { LL | | @@ -14,7 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> tests/ui/entry_unfixable.rs:43:5 + --> tests/ui/entry_unfixable.rs:42:5 | LL | / if hm.contains_key(&key) { LL | | @@ -26,7 +26,7 @@ LL | | } | |_____^ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> tests/ui/entry_unfixable.rs:81:13 + --> tests/ui/entry_unfixable.rs:80:13 | LL | / if self.globals.contains_key(&name) { LL | | diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed index 99d09774d16d..8a8c2e1939c8 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.fixed +++ b/src/tools/clippy/tests/ui/excessive_precision.fixed @@ -79,6 +79,9 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + // issue #6341 + let exponential: f64 = 4.886506780521244E-03; + // issue #7744 let _ = 2.225_073_858_507_201e-308_f64; //~^ excessive_precision diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs index a542fb2e7e3c..5dcf55cb9273 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.rs +++ b/src/tools/clippy/tests/ui/excessive_precision.rs @@ -79,6 +79,9 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + // issue #6341 + let exponential: f64 = 4.886506780521244E-03; + // issue #7744 let _ = 2.225_073_858_507_201_1e-308_f64; //~^ excessive_precision diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr index 934a367e1065..f5eeadf0c8cb 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.stderr +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -157,7 +157,7 @@ LL + let bad_bige32: f32 = 1.123_456_8E-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:83:13 + --> tests/ui/excessive_precision.rs:86:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + let _ = 2.225_073_858_507_201e-308_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:87:13 + --> tests/ui/excessive_precision.rs:90:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _ = 0_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:98:20 + --> tests/ui/excessive_precision.rs:101:20 | LL | const _: f64 = 3.0000000000000000e+00; | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs index 8340d99ace22..13934785d7b1 100644 --- a/src/tools/clippy/tests/ui/explicit_counter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs @@ -1,6 +1,6 @@ #![warn(clippy::explicit_counter_loop)] #![allow(clippy::uninlined_format_args, clippy::useless_vec)] -//@no-rustfix +//@no-rustfix: suggestion does not remove the `+= 1` fn main() { let mut vec = vec![1, 2, 3, 4]; let mut _index = 0; diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed index 0d1a5f80f3d5..619329a6ade0 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = &*opt_a.unwrap(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); ::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &*&"foo"; //~ explicit_deref_methods + let mut x = String::new(); + let _ = &&mut **&mut x; //~ explicit_deref_methods + let _ = &&mut ***(&mut &mut x); //~ explicit_deref_methods } diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs index 8d4a899cd260..9f2d513283c9 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.rs +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = opt_a.unwrap().deref(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); ::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &Deref::deref(&"foo"); //~ explicit_deref_methods + let mut x = String::new(); + let _ = &DerefMut::deref_mut(&mut x); //~ explicit_deref_methods + let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); //~ explicit_deref_methods } diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr index 2ca376cba00b..a81e2f60317b 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:54:19 + --> tests/ui/explicit_deref_methods.rs:55:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,70 +8,88 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:57:23 + --> tests/ui/explicit_deref_methods.rs:58:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:39 + --> tests/ui/explicit_deref_methods.rs:62:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:50 + --> tests/ui/explicit_deref_methods.rs:62:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:20 + --> tests/ui/explicit_deref_methods.rs:66:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:69:11 + --> tests/ui/explicit_deref_methods.rs:70:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:74:28 + --> tests/ui/explicit_deref_methods.rs:75:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:77:13 + --> tests/ui/explicit_deref_methods.rs:78:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:80:28 + --> tests/ui/explicit_deref_methods.rs:81:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:83:19 + --> tests/ui/explicit_deref_methods.rs:84:19 | LL | let b: &str = a.deref().deref(); | ^^^^^^^^^^^^^^^^^ help: try: `&**a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:87:13 + --> tests/ui/explicit_deref_methods.rs:88:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*opt_a.unwrap()` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:125:31 + --> tests/ui/explicit_deref_methods.rs:123:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` -error: aborting due to 12 previous errors +error: explicit `deref` method call + --> tests/ui/explicit_deref_methods.rs:141:14 + | +LL | let _ = &Deref::deref(&"foo"); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:143:14 + | +LL | let _ = &DerefMut::deref_mut(&mut x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:144:14 + | +LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed index 2b68906ae39f..c1b3c478eeb9 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed @@ -73,3 +73,16 @@ fn main() { for _ in S.into_iter::() {} } + +fn issue14630() { + macro_rules! mac { + (into_iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]) {} + //~^ explicit_into_iter_loop + + for _ in mac!(into_iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs index ca335b62d906..581e0dadcecb 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs @@ -73,3 +73,16 @@ fn main() { for _ in S.into_iter::() {} } + +fn issue14630() { + macro_rules! mac { + (into_iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]).into_iter() {} + //~^ explicit_into_iter_loop + + for _ in mac!(into_iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr index 1c3156755d4e..26fb11e00482 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr @@ -37,5 +37,11 @@ error: it is more concise to loop over containers instead of using explicit iter LL | for _ in mr.into_iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *mr` -error: aborting due to 6 previous errors +error: it is more concise to loop over containers instead of using explicit iteration methods + --> tests/ui/explicit_into_iter_loop.rs:84:14 + | +LL | for _ in dbg!([1, 2]).into_iter() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `dbg!([1, 2])` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed index cd0898dfc367..f246ec61800e 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed @@ -183,3 +183,16 @@ pub fn issue_13184() { let rvalues = &values; for _ in rvalues.iter() {} } + +fn issue14630() { + macro_rules! mac { + (iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in &dbg!([1, 2]) {} + //~^ explicit_iter_loop + + for _ in mac!(iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs index 02405280ce42..35f4fb7097d8 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.rs @@ -183,3 +183,16 @@ pub fn issue_13184() { let rvalues = &values; for _ in rvalues.iter() {} } + +fn issue14630() { + macro_rules! mac { + (iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]).iter() {} + //~^ explicit_iter_loop + + for _ in mac!(iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr index 3816bb4db98b..575dbe7813d7 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr @@ -112,5 +112,11 @@ error: it is more concise to loop over references to containers instead of using LL | for _ in r.iter() {} | ^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 18 previous errors +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> tests/ui/explicit_iter_loop.rs:194:14 + | +LL | for _ in dbg!([1, 2]).iter() {} + | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&dbg!([1, 2])` + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs index 68294292502a..5d29e0317bb2 100644 --- a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs @@ -1,6 +1,5 @@ -#![allow(clippy::question_mark, unused)] +#![allow(clippy::question_mark)] #![warn(clippy::filter_map_bool_then)] -//@no-rustfix fn issue11617() { let mut x: Vec = vec![0; 10]; diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr index 2025958136ba..2990423973e1 100644 --- a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr @@ -1,5 +1,5 @@ error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:7:48 + --> tests/ui/filter_map_bool_then_unfixable.rs:6:48 | LL | let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { | ________________________________________________^ @@ -16,7 +16,7 @@ LL | | }); = help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]` error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:23:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:22:26 | LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:27:14 + --> tests/ui/filter_map_bool_then_unfixable.rs:26:14 | LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }. = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:29:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:28:26 | LL | let _ = x.iter().filter_map(|&x| { | __________________________^ @@ -47,7 +47,7 @@ LL | | }); = help: consider using `filter` then `map` instead error: usage of `bool::then` in `filter_map` - --> tests/ui/filter_map_bool_then_unfixable.rs:47:26 + --> tests/ui/filter_map_bool_then_unfixable.rs:46:26 | LL | let _ = x.iter().filter_map(|&x| { | __________________________^ diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs index ebc3acb1b77f..7067434953d6 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs @@ -6,19 +6,25 @@ fn ifs_same_cond() { let b = false; if b { - } else if b { //~^ ifs_same_cond + } else if b { + } + + if b { + //~^ ifs_same_cond + } else if b { + } else if b { } if a == 1 { - } else if a == 1 { //~^ ifs_same_cond + } else if a == 1 { } if 2 * a == 1 { + //~^ ifs_same_cond } else if 2 * a == 2 { } else if 2 * a == 1 { - //~^ ifs_same_cond } else if a == 1 { } @@ -50,8 +56,8 @@ fn ifs_same_cond() { fn issue10272() { let a = String::from("ha"); if a.contains("ah") { - } else if a.contains("ah") { //~^ ifs_same_cond + } else if a.contains("ah") { // Trigger this lint } else if a.contains("ha") { diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr index df21e6f1b826..7acbc1a6399b 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.stderr +++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr @@ -1,52 +1,52 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:9:15 - | -LL | } else if b { - | ^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui/ifs_same_cond.rs:8:8 | LL | if b { | ^ +LL | +LL | } else if b { + | ^ + | = note: `-D clippy::ifs-same-cond` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:14:15 - | -LL | } else if a == 1 { - | ^^^^^^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui/ifs_same_cond.rs:13:8 | +LL | if b { + | ^ +LL | +LL | } else if b { + | ^ +LL | } else if b { + | ^ + +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:19:8 + | LL | if a == 1 { | ^^^^^^ +LL | +LL | } else if a == 1 { + | ^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:20:15 - | -LL | } else if 2 * a == 1 { - | ^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:18:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:24:8 | LL | if 2 * a == 1 { | ^^^^^^^^^^ +... +LL | } else if 2 * a == 1 { + | ^^^^^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:53:15 - | -LL | } else if a.contains("ah") { - | ^^^^^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:52:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:58:8 | LL | if a.contains("ah") { | ^^^^^^^^^^^^^^^^ +LL | +LL | } else if a.contains("ah") { + | ^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.rs b/src/tools/clippy/tests/ui/impl_trait_in_params.rs index 2039f6339a87..72e3e068c9c7 100644 --- a/src/tools/clippy/tests/ui/impl_trait_in_params.rs +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![warn(clippy::impl_trait_in_params)] -//@no-rustfix +//@no-rustfix: has placeholders pub trait Trait {} pub trait AnotherTrait {} diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs index 4a0968918bfb..8ff7f3b0c18d 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.rs +++ b/src/tools/clippy/tests/ui/infinite_loop.rs @@ -1,5 +1,3 @@ -//@no-rustfix - fn fn_val(i: i32) -> i32 { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr index 7ba1374d64f4..04da9776c302 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.stderr +++ b/src/tools/clippy/tests/ui/infinite_loop.stderr @@ -1,5 +1,5 @@ error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:22:11 + --> tests/ui/infinite_loop.rs:20:11 | LL | while y < 10 { | ^^^^^^ @@ -8,7 +8,7 @@ LL | while y < 10 { = note: `#[deny(clippy::while_immutable_condition)]` on by default error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:29:11 + --> tests/ui/infinite_loop.rs:27:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:38:11 + --> tests/ui/infinite_loop.rs:36:11 | LL | while !cond { | ^^^^^ @@ -24,7 +24,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:84:11 + --> tests/ui/infinite_loop.rs:82:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +32,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:91:11 + --> tests/ui/infinite_loop.rs:89:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:97:11 + --> tests/ui/infinite_loop.rs:95:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +48,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:114:11 + --> tests/ui/infinite_loop.rs:112:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +56,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:121:11 + --> tests/ui/infinite_loop.rs:119:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +64,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:189:15 + --> tests/ui/infinite_loop.rs:187:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:199:11 + --> tests/ui/infinite_loop.rs:197:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +82,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:208:11 + --> tests/ui/infinite_loop.rs:206:11 | LL | while y < 10 { | ^^^^^^ diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index eaa8d0088068..fcd1f795fff0 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: multiple suggestions add `-> !` to the same fn //@aux-build:proc_macros.rs #![allow(clippy::never_loop)] diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs index 45e34b3930ad..f0b86e5620e9 100644 --- a/src/tools/clippy/tests/ui/into_iter_without_iter.rs +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: suggestions reference out of scope lifetimes/types //@aux-build:proc_macros.rs #![warn(clippy::into_iter_without_iter)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/iter_next_loop.rs b/src/tools/clippy/tests/ui/iter_next_loop.rs index 32711c7ef623..8e62ed963b90 100644 --- a/src/tools/clippy/tests/ui/iter_next_loop.rs +++ b/src/tools/clippy/tests/ui/iter_next_loop.rs @@ -15,3 +15,16 @@ fn main() { let u = Unrelated(&[0]); for _v in u.next() {} // no error } + +fn issue14630() { + macro_rules! mac { + (next $e:expr) => { + $e.iter().next() + }; + } + + for _ in dbg!([1, 2].iter()).next() {} + //~^ iter_next_loop + + for _ in mac!(next [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/iter_next_loop.stderr b/src/tools/clippy/tests/ui/iter_next_loop.stderr index acc55031c3b2..c076e86db93b 100644 --- a/src/tools/clippy/tests/ui/iter_next_loop.stderr +++ b/src/tools/clippy/tests/ui/iter_next_loop.stderr @@ -7,5 +7,11 @@ LL | for _ in x.iter().next() {} = note: `-D clippy::iter-next-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_next_loop)]` -error: aborting due to 1 previous error +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> tests/ui/iter_next_loop.rs:26:14 + | +LL | for _ in dbg!([1, 2].iter()).next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs index b34e4ad78240..6458b1342dcd 100644 --- a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs +++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #![deny(clippy::iter_out_of_bounds)] #![allow(clippy::useless_vec)] diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr index 19ac60b9d0ac..1b3a99e1e945 100644 --- a/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr +++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr @@ -1,18 +1,18 @@ error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:12:14 + --> tests/ui/iter_out_of_bounds.rs:10:14 | LL | for _ in [1, 2, 3].iter().skip(4) { | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this operation is useless and will create an empty iterator note: the lint level is defined here - --> tests/ui/iter_out_of_bounds.rs:3:9 + --> tests/ui/iter_out_of_bounds.rs:1:9 | LL | #![deny(clippy::iter_out_of_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:17:19 + --> tests/ui/iter_out_of_bounds.rs:15:19 | LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { = note: this operation is useless and the returned iterator will simply yield the same items error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:24:14 + --> tests/ui/iter_out_of_bounds.rs:22:14 | LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:27:14 + --> tests/ui/iter_out_of_bounds.rs:25:14 | LL | for _ in [1, 2, 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | for _ in [1, 2, 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:30:14 + --> tests/ui/iter_out_of_bounds.rs:28:14 | LL | for _ in [1; 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | for _ in [1; 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:36:14 + --> tests/ui/iter_out_of_bounds.rs:34:14 | LL | for _ in vec![1, 2, 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | for _ in vec![1, 2, 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:39:14 + --> tests/ui/iter_out_of_bounds.rs:37:14 | LL | for _ in vec![1; 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | for _ in vec![1; 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:43:14 + --> tests/ui/iter_out_of_bounds.rs:41:14 | LL | for _ in x.iter().skip(4) {} | ^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | for _ in x.iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:47:14 + --> tests/ui/iter_out_of_bounds.rs:45:14 | LL | for _ in x.iter().skip(n) {} | ^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | for _ in x.iter().skip(n) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:52:14 + --> tests/ui/iter_out_of_bounds.rs:50:14 | LL | for _ in empty().skip(1) {} | ^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | for _ in empty().skip(1) {} = note: this operation is useless and will create an empty iterator error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:55:14 + --> tests/ui/iter_out_of_bounds.rs:53:14 | LL | for _ in empty().take(1) {} | ^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | for _ in empty().take(1) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:58:14 + --> tests/ui/iter_out_of_bounds.rs:56:14 | LL | for _ in std::iter::once(1).skip(2) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | for _ in std::iter::once(1).skip(2) {} = note: this operation is useless and will create an empty iterator error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:61:14 + --> tests/ui/iter_out_of_bounds.rs:59:14 | LL | for _ in std::iter::once(1).take(2) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | for _ in std::iter::once(1).take(2) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:64:14 + --> tests/ui/iter_out_of_bounds.rs:62:14 | LL | for x in [].iter().take(1) { | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_inspect.fixed b/src/tools/clippy/tests/ui/manual_inspect.fixed index ec87fe217aee..9b768dbad700 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.fixed +++ b/src/tools/clippy/tests/ui/manual_inspect.fixed @@ -107,7 +107,7 @@ fn main() { let _ = || { let _x = x; }; - return ; + return; } println!("test"); }); diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr index eb98f9f5995a..78b085fdfca3 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.stderr +++ b/src/tools/clippy/tests/ui/manual_inspect.stderr @@ -98,7 +98,7 @@ LL | if x.is_empty() { LL | let _ = || { LL ~ let _x = x; LL | }; -LL ~ return ; +LL ~ return; LL | } LL ~ println!("test"); | diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed index 294d1e1506de..090f0fd30c55 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed @@ -60,7 +60,26 @@ fn main() { let _ = size_of::() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY) } -const fn _const(s_i32: &[i32]) { - // True negative: - let _ = s_i32.len() * size_of::(); // Ok, can't use size_of_val in const +#[clippy::msrv = "1.85"] +const fn const_ok(s_i32: &[i32]) { + let _ = std::mem::size_of_val(s_i32); + //~^ manual_slice_size_calculation +} + +#[clippy::msrv = "1.84"] +const fn const_before_msrv(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); +} + +fn issue_14802() { + struct IcedSlice { + dst: [u8], + } + + impl IcedSlice { + fn get_len(&self) -> usize { + std::mem::size_of_val(&self.dst) + //~^ manual_slice_size_calculation + } + } } diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs index ae5225663139..3c19a0eb5cea 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs @@ -60,7 +60,26 @@ fn main() { let _ = size_of::() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY) } -const fn _const(s_i32: &[i32]) { - // True negative: - let _ = s_i32.len() * size_of::(); // Ok, can't use size_of_val in const +#[clippy::msrv = "1.85"] +const fn const_ok(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); + //~^ manual_slice_size_calculation +} + +#[clippy::msrv = "1.84"] +const fn const_before_msrv(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); +} + +fn issue_14802() { + struct IcedSlice { + dst: [u8], + } + + impl IcedSlice { + fn get_len(&self) -> usize { + self.dst.len() * size_of::() + //~^ manual_slice_size_calculation + } + } } diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr index f07e97a1c863..8e9b49e4bf29 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr @@ -55,5 +55,17 @@ error: manual slice size calculation LL | let _ = external!(&[1u64][..]).len() * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(external!(&[1u64][..]))` -error: aborting due to 9 previous errors +error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:65:13 + | +LL | let _ = s_i32.len() * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` + +error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:81:13 + | +LL | self.dst.len() * size_of::() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(&self.dst)` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs index d7e9c9d9900d..0970da8039a4 100644 --- a/src/tools/clippy/tests/ui/map_flatten.rs +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -1,5 +1,5 @@ #![warn(clippy::map_flatten)] -#![feature(result_flattening)] + //@no-rustfix // issue #8506, multi-line #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed index f8379ed23c5b..6d8a27d3018d 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed @@ -1,4 +1,3 @@ -#![feature(result_flattening)] #![allow( clippy::let_underscore_untyped, clippy::missing_docs_in_private_items, diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.rs b/src/tools/clippy/tests/ui/map_flatten_fixable.rs index 040a9ca85f64..845e3a79ae2b 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.rs +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.rs @@ -1,4 +1,3 @@ -#![feature(result_flattening)] #![allow( clippy::let_underscore_untyped, clippy::missing_docs_in_private_items, diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr index fe68eb7e4ab4..05d4d9a6ad85 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:17:47 + --> tests/ui/map_flatten_fixable.rs:16:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)` @@ -8,43 +8,43 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = help: to override `-D warnings` add `#[allow(clippy::map_flatten)]` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:19:47 + --> tests/ui/map_flatten_fixable.rs:18:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:21:47 + --> tests/ui/map_flatten_fixable.rs:20:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:23:47 + --> tests/ui/map_flatten_fixable.rs:22:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:27:47 + --> tests/ui/map_flatten_fixable.rs:26:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)` error: called `map(..).flatten()` on `Option` - --> tests/ui/map_flatten_fixable.rs:31:40 + --> tests/ui/map_flatten_fixable.rs:30:40 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Result` - --> tests/ui/map_flatten_fixable.rs:35:42 + --> tests/ui/map_flatten_fixable.rs:34:42 | LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:45:10 + --> tests/ui/map_flatten_fixable.rs:44:10 | LL | .map(|n| match n { | __________^ @@ -74,7 +74,7 @@ LL ~ }); | error: called `map(..).flatten()` on `Option` - --> tests/ui/map_flatten_fixable.rs:66:10 + --> tests/ui/map_flatten_fixable.rs:65:10 | LL | .map(|_| { | __________^ diff --git a/src/tools/clippy/tests/ui/match_same_arms.fixed b/src/tools/clippy/tests/ui/match_same_arms.fixed new file mode 100644 index 000000000000..31684a5759fe --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms.fixed @@ -0,0 +1,142 @@ +#![allow(clippy::manual_range_patterns)] +#![warn(clippy::match_same_arms)] + +pub enum Abc { + A, + B, + C, +} + +fn match_same_arms() { + let _ = match Abc::A { + Abc::B => 1, + _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + _ => 'b', + //~^ match_same_arms + }; + + match (1, 2, 3) { + (1, .., 3) | (.., 3) => 42, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 42 | 51 => 1, + 41 | 52 => 2, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 1 | 2 | 3 => 2, + 4 => 3, + _ => 0, + }; +} + +mod issue4244 { + #[derive(PartialEq, PartialOrd, Eq, Ord)] + pub enum CommandInfo { + BuiltIn { name: String, about: Option }, + External { name: String, path: std::path::PathBuf }, + } + + impl CommandInfo { + pub fn name(&self) -> String { + match self { + //~^ match_same_arms + CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), + } + } + } +} + +macro_rules! m { + (foo) => {}; + (bar) => {}; +} +macro_rules! foo { + () => { + 1 + }; +} +macro_rules! bar { + () => { + 1 + }; +} + +fn main() { + let x = 0; + let _ = match 0 { + 0 => { + m!(foo); + x + }, + 1 => { + m!(bar); + x + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + m!(foo); + 0 + }, + 1 => { + m!(bar); + 0 + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + let mut x = 0; + #[cfg(not_enabled)] + { + x = 5; + } + #[cfg(not(not_enabled))] + { + x = 6; + } + x + }, + 1 => { + let mut x = 0; + #[cfg(also_not_enabled)] + { + x = 5; + } + #[cfg(not(also_not_enabled))] + { + x = 6; + } + x + }, + _ => 0, + }; + + let _ = match 0 { + 0 => foo!(), + 1 => bar!(), + _ => 1, + }; + + let _ = match 0 { + 0 => cfg!(not_enabled), + 1 => cfg!(also_not_enabled), + _ => false, + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs index 55441948e91b..39bee01bac22 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.rs +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -1,4 +1,4 @@ -//@no-rustfix: overlapping suggestions +#![allow(clippy::manual_range_patterns)] #![warn(clippy::match_same_arms)] pub enum Abc { @@ -10,9 +10,17 @@ pub enum Abc { fn match_same_arms() { let _ = match Abc::A { Abc::A => 0, - //~^ match_same_arms Abc::B => 1, _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + 2 => 'b', + 3 => 'b', + _ => 'b', + //~^ match_same_arms }; match (1, 2, 3) { @@ -24,8 +32,8 @@ fn match_same_arms() { let _ = match 42 { 42 => 1, - 51 => 1, //~^ match_same_arms + 51 => 1, 41 => 2, //~^ match_same_arms 52 => 2, @@ -34,11 +42,9 @@ fn match_same_arms() { let _ = match 42 { 1 => 2, + //~^ match_same_arms 2 => 2, - //~^ match_same_arms - //~| match_same_arms 3 => 2, - //~^ match_same_arms 4 => 3, _ => 0, }; @@ -55,8 +61,8 @@ mod issue4244 { pub fn name(&self) -> String { match self { CommandInfo::BuiltIn { name, .. } => name.to_string(), - CommandInfo::External { name, .. } => name.to_string(), //~^ match_same_arms + CommandInfo::External { name, .. } => name.to_string(), } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr index 3744b83d89cc..8aa60f835766 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -1,118 +1,121 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms.rs:12:9 | -LL | / Abc::A => 0, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms.rs:15:9 - | +LL | Abc::A => 0, + | ^^^^^^^^^^^ +LL | Abc::B => 1, LL | _ => 0, - | ^^^^^^ + | ^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise remove the non-wildcard arm + | +LL - Abc::A => 0, + | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:19:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:20:9 + | +LL | 2 => 'b', + | ^^^^^^^^ +LL | 3 => 'b', + | ^^^^^^^^ +LL | _ => 'b', + | ^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arms + | +LL - 2 => 'b', +LL - 3 => 'b', +LL + _ => 'b', + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:27:9 | LL | (1, .., 3) => 42, | ^^^^^^^^^^^^^^^^ +LL | +LL | (.., 3) => 42, + | ^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (1, .., 3) | (.., 3) => 42, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:27:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:34:9 | +LL | 42 => 1, + | ^^^^^^^ +LL | LL | 51 => 1, | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => 1, -LL - 51 => 1, -LL + 51 | 42 => 1, +LL ~ +LL ~ 42 | 51 => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:29:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:37:9 | LL | 41 => 2, | ^^^^^^^ +LL | +LL | 52 => 2, + | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ 41 | 52 => 2, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:44:9 | +LL | 1 => 2, + | ^^^^^^ +LL | LL | 2 => 2, | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL - 1 => 2, -LL - 2 => 2, -LL + 2 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:40:9 - | LL | 3 => 2, | ^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ 2 => 2, -LL | -LL | -LL ~ 3 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 - | -LL | 2 => 2, - | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL ~ 2 | 3 => 2, -LL | -LL | LL ~ +LL ~ 1 | 2 | 3 => 2, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:58:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:63:17 | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | CommandInfo::External { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), -LL - CommandInfo::External { name, .. } => name.to_string(), -LL + CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), +LL ~ +LL ~ CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), | -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed index 0d93e2c728d7..cb860cef1e68 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed @@ -14,7 +14,7 @@ fn foo() -> bool { fn match_same_arms() { let _ = match 42 { - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -27,14 +27,14 @@ fn match_same_arms() { }; let _ = match 42 { - 51 | 42 => foo(), //~^ match_same_arms + 42 | 51 => foo(), _ => true, }; let _ = match Some(42) { - None | Some(_) => 24, //~^ match_same_arms + Some(_) | None => 24, }; let _ = match Some(42) { @@ -55,8 +55,8 @@ fn match_same_arms() { }; match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) => bar(a), //~^ match_same_arms + (Some(a), None) | (None, Some(a)) => bar(a), _ => (), } @@ -69,8 +69,8 @@ fn match_same_arms() { }; let _ = match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) if a == 42 => a, //~^ match_same_arms + (Some(a), None) | (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -124,8 +124,8 @@ fn match_same_arms() { // False negative #2251. match x { Ok(_tmp) => println!("ok"), - Ok(_) | Ok(3) => println!("ok"), //~^ match_same_arms + Ok(3) | Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -149,10 +149,10 @@ fn match_same_arms() { // still lint if the tokens are the same match 0 { - 1 | 0 => { + //~^^^ match_same_arms + 0 | 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -208,9 +208,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { - Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) | Foo::X(0) => 1, //~^ match_same_arms + Foo::Y(_) | Foo::Z(0) => 2, + Foo::X(0) | Foo::Z(_) => 1, _ => 0, }; @@ -230,10 +230,10 @@ fn main() { // Lint. let _ = match None { + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, - Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, - //~^ match_same_arms + Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, _ => 200, }; @@ -246,8 +246,8 @@ fn main() { }; let _ = match 0 { - 1 | 0 => cfg!(not_enable), //~^ match_same_arms + 0 | 1 => cfg!(not_enable), _ => false, }; } @@ -262,9 +262,34 @@ mod with_lifetime { impl<'a> MaybeStaticStr<'a> { fn get(&self) -> &'a str { match *self { - MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, //~^ match_same_arms + MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index b0ebc4784f33..0fd5d76e7d3e 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -23,7 +23,7 @@ fn match_same_arms() { a = -31 - a; a }, - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -37,15 +37,15 @@ fn match_same_arms() { let _ = match 42 { 42 => foo(), - 51 => foo(), //~^ match_same_arms + 51 => foo(), _ => true, }; let _ = match Some(42) { Some(_) => 24, - None => 24, //~^ match_same_arms + None => 24, }; let _ = match Some(42) { @@ -67,8 +67,8 @@ fn match_same_arms() { match (Some(42), Some(42)) { (Some(a), None) => bar(a), - (None, Some(a)) => bar(a), //~^ match_same_arms + (None, Some(a)) => bar(a), _ => (), } @@ -82,8 +82,8 @@ fn match_same_arms() { let _ = match (Some(42), Some(42)) { (Some(a), None) if a == 42 => a, - (None, Some(a)) if a == 42 => a, //~^ match_same_arms + (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -140,8 +140,8 @@ fn match_same_arms() { match x { Ok(_tmp) => println!("ok"), Ok(3) => println!("ok"), - Ok(_) => println!("ok"), //~^ match_same_arms + Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -168,10 +168,10 @@ fn match_same_arms() { 0 => { empty!(0); }, + //~^^^ match_same_arms 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -229,9 +229,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { Foo::X(0) => 1, + //~^ match_same_arms Foo::Y(_) | Foo::Z(0) => 2, Foo::Z(_) => 1, - //~^ match_same_arms _ => 0, }; @@ -252,10 +252,10 @@ fn main() { // Lint. let _ = match None { Some(Bar { x: 0, y: 5, .. }) => 1, + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, Some(Bar { y: 0, x: 5, .. }) => 1, - //~^ match_same_arms _ => 200, }; @@ -269,8 +269,8 @@ fn main() { let _ = match 0 { 0 => cfg!(not_enable), - 1 => cfg!(not_enable), //~^ match_same_arms + 1 => cfg!(not_enable), _ => false, }; } @@ -286,9 +286,36 @@ mod with_lifetime { fn get(&self) -> &'a str { match *self { MaybeStaticStr::Static(s) => s, - MaybeStaticStr::Borrowed(s) => s, //~^ match_same_arms + MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index 21a8743cc324..f3031619cce5 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,4 +1,4 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:17:9 | LL | / 42 => { @@ -6,14 +6,10 @@ LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; LL | | if true { ... | +LL | | a LL | | }, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:27:9 - | + | |_________^ +LL | LL | / _ => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; @@ -21,134 +17,169 @@ LL | | if true { ... | LL | | a LL | | }, - | |_________^ + | |_________^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:40:9 +help: otherwise remove the non-wildcard arm | +LL - 42 => { +LL - foo(); +LL - let mut a = 42 + [23].len() as i32; +LL - if true { +LL - a += 7; +LL - } +LL - a = -31 - a; +LL - a +LL - }, + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:39:9 + | +LL | 42 => foo(), + | ^^^^^^^^^^^ +LL | LL | 51 => foo(), | ^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => foo(), -LL - 51 => foo(), -LL + 51 | 42 => foo(), +LL ~ +LL ~ 42 | 51 => foo(), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:47:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:46:9 | +LL | Some(_) => 24, + | ^^^^^^^^^^^^^ +LL | LL | None => 24, | ^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Some(_) => 24, -LL - None => 24, -LL + None | Some(_) => 24, +LL ~ +LL ~ Some(_) | None => 24, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:70:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:69:9 | +LL | (Some(a), None) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) => bar(a), -LL - (None, Some(a)) => bar(a), -LL + (None, Some(a)) | (Some(a), None) => bar(a), +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) => bar(a), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:85:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:84:9 | +LL | (Some(a), None) if a == 42 => a, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) if a == 42 => a, -LL - (None, Some(a)) if a == 42 => a, -LL + (None, Some(a)) | (Some(a), None) if a == 42 => a, +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) if a == 42 => a, | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:91:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (.., Some(a)) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Some(a), ..) | (.., Some(a)) => bar(a), LL | LL ~ _ => (), | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:126:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), LL | LL ~ _ => println!("err"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:143:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:142:9 | +LL | Ok(3) => println!("ok"), + | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | Ok(_) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Ok(3) => println!("ok"), -LL - Ok(_) => println!("ok"), -LL + Ok(_) | Ok(3) => println!("ok"), +LL ~ +LL ~ Ok(3) | Ok(_) => println!("ok"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:171:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:168:9 | +LL | / 0 => { +LL | | empty!(0); +LL | | }, + | |_________^ +LL | LL | / 1 => { LL | | empty!(0); LL | | }, | |_________^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => { -LL - empty!(0); -LL - }, -LL - 1 => { -LL + 1 | 0 => { +LL ~ +LL ~ 0 | 1 => { | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:222:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ +... +LL | Foo::Z(_) => 1, + | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ Foo::X(0) | Foo::Z(_) => 1, LL | @@ -156,60 +187,106 @@ LL | Foo::X(_) | Foo::Y(_) => 2, LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:233:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:231:9 | +LL | Foo::X(0) => 1, + | ^^^^^^^^^^^^^^ +... LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Foo::Y(_) | Foo::Z(0) => 2, -LL ~ Foo::Z(_) | Foo::X(0) => 1, +LL ~ +LL | Foo::Y(_) | Foo::Z(0) => 2, +LL ~ Foo::X(0) | Foo::Z(_) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:257:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:254:9 | +LL | Some(Bar { x: 0, y: 5, .. }) => 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Some(Bar { y: 10, z: 0, .. }) => 2, +LL ~ +LL | Some(Bar { y: 10, z: 0, .. }) => 2, LL | None => 50, -LL ~ Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, +LL ~ Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:272:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:271:9 | +LL | 0 => cfg!(not_enable), + | ^^^^^^^^^^^^^^^^^^^^^ +LL | LL | 1 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => cfg!(not_enable), -LL - 1 => cfg!(not_enable), -LL + 1 | 0 => cfg!(not_enable), +LL ~ +LL ~ 0 | 1 => cfg!(not_enable), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:289:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:288:17 | +LL | MaybeStaticStr::Static(s) => s, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | MaybeStaticStr::Borrowed(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - MaybeStaticStr::Static(s) => s, -LL - MaybeStaticStr::Borrowed(s) => s, -LL + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, +LL ~ +LL ~ MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, | -error: aborting due to 14 previous errors +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:306:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[allow(clippy::match_same_arms)] + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:315:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[expect(clippy::match_same_arms)] + | + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed index 0c9398933b80..61a5bd0323a8 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed @@ -7,14 +7,22 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } @@ -28,21 +36,21 @@ mod f { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -54,8 +62,8 @@ mod g { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs index 304a9e5c28e7..66f65eb39d0a 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs @@ -7,15 +7,25 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel => repeat(), + //~^ match_same_arms + Ordering::SeqCst | _ => repeat(), + } } mod f { @@ -29,12 +39,13 @@ mod f { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { @@ -42,8 +53,8 @@ pub fn g(x: Ordering) { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -56,8 +67,8 @@ mod g { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr index aa7f8c95dce8..03252f346c6c 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,32 +1,80 @@ -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:44:9 - | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:46:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:16:9 | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:58:13 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:25:9 | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |____________^ help: try removing the arm +LL | Ordering::AcqRel => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | Ordering::SeqCst | _ => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:60:13 + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:41:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | -error: aborting due to 2 previous errors +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:55:9 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:69:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed new file mode 100644 index 000000000000..882ff6bf8944 --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a += 1; + //~^ misrefactored_assign_op + + a += 1; + //~^ misrefactored_assign_op + + a -= 1; + //~^ misrefactored_assign_op + + a *= 99; + //~^ misrefactored_assign_op + + a *= 42; + //~^ misrefactored_assign_op + + a /= 2; + //~^ misrefactored_assign_op + + a %= 5; + //~^ misrefactored_assign_op + + a &= 1; + //~^ misrefactored_assign_op + + a *= a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed new file mode 100644 index 000000000000..de3a0f1710d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a = a + a + 1; + //~^ misrefactored_assign_op + + a = a + 1 + a; + //~^ misrefactored_assign_op + + a = a - (a - 1); + //~^ misrefactored_assign_op + + a = a * a * 99; + //~^ misrefactored_assign_op + + a = a * 42 * a; + //~^ misrefactored_assign_op + + a = a / (a / 2); + //~^ misrefactored_assign_op + + a = a % (a % 5); + //~^ misrefactored_assign_op + + a = a & a & 1; + //~^ misrefactored_assign_op + + a = a * a * a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs new file mode 100644 index 000000000000..62d83d1619c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a += a + 1; + //~^ misrefactored_assign_op + + a += 1 + a; + //~^ misrefactored_assign_op + + a -= a - 1; + //~^ misrefactored_assign_op + + a *= a * 99; + //~^ misrefactored_assign_op + + a *= 42 * a; + //~^ misrefactored_assign_op + + a /= a / 2; + //~^ misrefactored_assign_op + + a %= a % 5; + //~^ misrefactored_assign_op + + a &= a & 1; + //~^ misrefactored_assign_op + + a *= a * a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/assign_ops2.stderr b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr similarity index 80% rename from src/tools/clippy/tests/ui/assign_ops2.stderr rename to src/tools/clippy/tests/ui/misrefactored_assign_op.stderr index d9ecd3f8b230..63f3a3e28f12 100644 --- a/src/tools/clippy/tests/ui/assign_ops2.stderr +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:8:5 + --> tests/ui/misrefactored_assign_op.rs:6:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -18,7 +18,7 @@ LL + a = a + a + 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:11:5 + --> tests/ui/misrefactored_assign_op.rs:9:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + a = a + 1 + a; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:14:5 + --> tests/ui/misrefactored_assign_op.rs:12:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + a = a - (a - 1); | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:17:5 + --> tests/ui/misrefactored_assign_op.rs:15:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + a = a * a * 99; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:20:5 + --> tests/ui/misrefactored_assign_op.rs:18:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + a = a * 42 * a; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:23:5 + --> tests/ui/misrefactored_assign_op.rs:21:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + a = a / (a / 2); | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:26:5 + --> tests/ui/misrefactored_assign_op.rs:24:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + a = a % (a % 5); | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:29:5 + --> tests/ui/misrefactored_assign_op.rs:27:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + a = a & a & 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:32:5 + --> tests/ui/misrefactored_assign_op.rs:30:5 | LL | a *= a * a; | ^^^^^^^^^^ @@ -153,14 +153,5 @@ LL - a *= a * a; LL + a = a * a * a; | -error: manual implementation of an assign operation - --> tests/ui/assign_ops2.rs:71:5 - | -LL | buf = buf + cows.clone(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` - | - = note: `-D clippy::assign-op-pattern` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index b2c2bbfaa361..41acf44023f6 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -301,4 +301,16 @@ pub fn issue13574() -> Option<()> { None } +fn issue14754(t: Result) -> Result { + let _ = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => return err, + }; + println!("Still here"); + let x = t; + //~^^^^ needless_match + println!("Still here"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index 1cb670edc60f..936653b961bb 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -364,4 +364,19 @@ pub fn issue13574() -> Option<()> { None } +fn issue14754(t: Result) -> Result { + let _ = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => return err, + }; + println!("Still here"); + let x = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => err, + }; + //~^^^^ needless_match + println!("Still here"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr index 719b0ef88469..5195ecdfa555 100644 --- a/src/tools/clippy/tests/ui/needless_match.stderr +++ b/src/tools/clippy/tests/ui/needless_match.stderr @@ -151,5 +151,15 @@ LL | | None LL | | } | |_________^ help: replace it with: `A` -error: aborting due to 14 previous errors +error: this match expression is unnecessary + --> tests/ui/needless_match.rs:373:13 + | +LL | let x = match t { + | _____________^ +LL | | Ok(v) => Ok::<_, &'static str>(v), +LL | | err @ Err(_) => err, +LL | | }; + | |_____^ help: replace it with: `t` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index ad625ad6d507..17d3862cd866 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -84,14 +84,14 @@ fn test_macro_call() -> i32 { } fn test_void_fun() { - //~^^ needless_return + //~^ needless_return } fn test_void_if_fun(b: bool) { if b { - //~^^ needless_return + //~^ needless_return } else { - //~^^ needless_return + //~^ needless_return } } @@ -108,7 +108,7 @@ fn test_nested_match(x: u32) { 0 => (), 1 => { let _ = 42; - //~^^ needless_return + //~^ needless_return }, _ => (), //~^ needless_return @@ -156,7 +156,7 @@ mod issue6501 { fn test_closure() { let _ = || { - //~^^ needless_return + //~^ needless_return }; let _ = || {}; //~^ needless_return @@ -220,14 +220,14 @@ async fn async_test_macro_call() -> i32 { } async fn async_test_void_fun() { - //~^^ needless_return + //~^ needless_return } async fn async_test_void_if_fun(b: bool) { if b { - //~^^ needless_return + //~^ needless_return } else { - //~^^ needless_return + //~^ needless_return } } @@ -354,7 +354,7 @@ fn issue9503(x: usize) -> isize { mod issue9416 { pub fn with_newline() { let _ = 42; - //~^^ needless_return + //~^ needless_return } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 41d7e5bdd506..1c6e7ffa1ee8 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -85,16 +85,16 @@ fn test_macro_call() -> i32 { fn test_void_fun() { return; - //~^^ needless_return + //~^ needless_return } fn test_void_if_fun(b: bool) { if b { return; - //~^^ needless_return + //~^ needless_return } else { return; - //~^^ needless_return + //~^ needless_return } } @@ -112,7 +112,7 @@ fn test_nested_match(x: u32) { 1 => { let _ = 42; return; - //~^^ needless_return + //~^ needless_return }, _ => return, //~^ needless_return @@ -161,7 +161,7 @@ mod issue6501 { fn test_closure() { let _ = || { return; - //~^^ needless_return + //~^ needless_return }; let _ = || return; //~^ needless_return @@ -226,16 +226,16 @@ async fn async_test_macro_call() -> i32 { async fn async_test_void_fun() { return; - //~^^ needless_return + //~^ needless_return } async fn async_test_void_if_fun(b: bool) { if b { return; - //~^^ needless_return + //~^ needless_return } else { return; - //~^^ needless_return + //~^ needless_return } } @@ -363,7 +363,7 @@ mod issue9416 { pub fn with_newline() { let _ = 42; return; - //~^^ needless_return + //~^ needless_return } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 80863b9b62b2..26dd265379be 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -133,12 +133,10 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:86:21 + --> tests/ui/needless_return.rs:87:5 | -LL | fn test_void_fun() { - | _____________________^ -LL | | return; - | |__________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -148,12 +146,10 @@ LL + fn test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:92:11 + --> tests/ui/needless_return.rs:93:9 | -LL | if b { - | ___________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -163,12 +159,10 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:95:13 + --> tests/ui/needless_return.rs:96:9 | -LL | } else { - | _____________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -190,12 +184,10 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:113:24 + --> tests/ui/needless_return.rs:114:13 | -LL | let _ = 42; - | ________________________^ -LL | | return; - | |__________________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -253,12 +245,10 @@ LL + bar.unwrap_or_else(|_| {}) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:162:21 + --> tests/ui/needless_return.rs:163:13 | -LL | let _ = || { - | _____________________^ -LL | | return; - | |__________________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -400,12 +390,10 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:227:33 + --> tests/ui/needless_return.rs:228:5 | -LL | async fn async_test_void_fun() { - | _________________________________^ -LL | | return; - | |__________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -415,12 +403,10 @@ LL + async fn async_test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:233:11 + --> tests/ui/needless_return.rs:234:9 | -LL | if b { - | ___________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -430,12 +416,10 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:236:13 + --> tests/ui/needless_return.rs:237:9 | -LL | } else { - | _____________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -593,12 +577,10 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:364:20 + --> tests/ui/needless_return.rs:365:9 | -LL | let _ = 42; - | ____________________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -608,10 +590,10 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:371:20 + --> tests/ui/needless_return.rs:371:21 | LL | let _ = 42; return; - | ^^^^^^^ + | ^^^^^^ | help: remove `return` | diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index 703c2a3d9847..4ab5bc9acdee 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -221,3 +221,56 @@ fn main() { Cout << 142; -Cout; } + +fn issue14592() { + struct MyStruct { + _inner: MyInner, + } + struct MyInner {} + + impl Drop for MyInner { + fn drop(&mut self) { + println!("dropping"); + } + } + + let x = MyStruct { _inner: MyInner {} }; + + let closure = || { + // Do not lint: dropping the assignment or assigning to `_` would + // change the output. + let _x = x; + }; + + println!("1"); + closure(); + println!("2"); + + struct Innocuous { + a: i32, + } + + // Do not lint: one of the fields has a side effect. + let x = MyInner {}; + let closure = || { + let _x = Innocuous { + a: { + x; + 10 + }, + }; + }; + + // Do not lint: the base has a side effect. + let x = MyInner {}; + let closure = || { + let _x = Innocuous { + ..Innocuous { + a: { + x; + 10 + }, + } + }; + }; +} diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 55e287b91596..ff81c6426027 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -7,85 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(clippy::manual_find_map)] #![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] -#![allow(clippy::needless_borrow)] -#![allow(clippy::single_char_add_str)] -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::missing_const_for_thread_local)] -#![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(invalid_null_arguments)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(dangling_pointers_from_temporaries)] +#![allow(clippy::missing_const_for_thread_local)] +#![allow(clippy::recursive_format_impl)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] -#![allow(unnecessary_transmutes)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_collection)] //~ ERROR: lint `clippy::box_vec` +#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::redundant_static_lifetimes)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cognitive_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derived_hash_with_manual_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` +#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` +#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` +#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::non_canonical_clone_impl)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::non_canonical_partial_ord_impl)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::arithmetic_side_effects)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::overly_complex_bool_expr)] //~ ERROR: lint `clippy::logic_bug` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -93,49 +115,27 @@ #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::panicking_overflow_checks)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` +#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::needless_borrow)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` +#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::recursive_format_impl)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` -#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` -#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` -#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` -#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` -#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` -#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` -#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` -#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 31dcd2cea081..b5d5d07e639a 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -7,85 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] +#![allow(clippy::manual_find_map)] #![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] -#![allow(clippy::needless_borrow)] -#![allow(clippy::single_char_add_str)] -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::missing_const_for_thread_local)] -#![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(invalid_null_arguments)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(dangling_pointers_from_temporaries)] +#![allow(clippy::missing_const_for_thread_local)] +#![allow(clippy::recursive_format_impl)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] -#![allow(unnecessary_transmutes)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::block_in_if_condition_stmt)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_if_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_vec)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::const_static_lifetime)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cyclomatic_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derive_hash_xor_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` +#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` +#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` +#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::incorrect_clone_impl_on_copy_type)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::integer_arithmetic)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::logic_bug)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -93,49 +115,27 @@ #![warn(clippy::option_map_unwrap_or_else)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::option_unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::overflow_check_conditional)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` +#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::ref_in_deref)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::to_string_in_display)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` -#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` -#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` -#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` -#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` -#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` -#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` -#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` -#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` -#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` -#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` -#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index a8d5c96acc3a..2487dfc8eba4 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -37,407 +37,407 @@ error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` -error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:73:9 - | -LL | #![warn(clippy::const_static_lifetime)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` - -error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:74:9 - | -LL | #![warn(clippy::cyclomatic_complexity)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` - -error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:75:9 - | -LL | #![warn(clippy::derive_hash_xor_eq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` - -error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:76:9 - | -LL | #![warn(clippy::disallowed_method)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` - -error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:77:9 - | -LL | #![warn(clippy::disallowed_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` - -error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:78:9 - | -LL | #![warn(clippy::eval_order_dependence)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` - -error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:79:9 - | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` - -error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:80:9 - | -LL | #![warn(clippy::filter_map)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` - -error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:81:9 - | -LL | #![warn(clippy::fn_address_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` - -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:82:9 - | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` - -error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:83:9 - | -LL | #![warn(clippy::if_let_redundant_pattern_matching)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` - -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:84:9 - | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` - -error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:85:9 - | -LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` - -error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:86:9 - | -LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` - -error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:87:9 - | -LL | #![warn(clippy::integer_arithmetic)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` - -error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:88:9 - | -LL | #![warn(clippy::logic_bug)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` - -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:89:9 - | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` - -error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:90:9 - | -LL | #![warn(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` - -error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:91:9 - | -LL | #![warn(clippy::option_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` - -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:92:9 - | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:93:9 - | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:94:9 - | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` - -error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:95:9 - | -LL | #![warn(clippy::overflow_check_conditional)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` - -error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:96:9 - | -LL | #![warn(clippy::ref_in_deref)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` - -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:97:9 - | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` - -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:98:9 - | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:99:9 - | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` - -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:100:9 - | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` - -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:101:9 - | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` - -error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:102:9 - | -LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` - -error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:103:9 - | -LL | #![warn(clippy::to_string_in_display)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` - -error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:104:9 - | -LL | #![warn(clippy::unwrap_or_else_default)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` - -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:105:9 - | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` - error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` -error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:109:9 +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` + --> tests/ui/rename.rs:76:9 | -LL | #![warn(clippy::invalid_null_ptr_usage)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` +LL | #![warn(clippy::const_static_lifetime)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` + +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` + --> tests/ui/rename.rs:77:9 + | +LL | #![warn(clippy::cyclomatic_complexity)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` + +error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` + --> tests/ui/rename.rs:78:9 + | +LL | #![warn(clippy::derive_hash_xor_eq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` + +error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` + --> tests/ui/rename.rs:79:9 + | +LL | #![warn(clippy::disallowed_method)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` + +error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` + --> tests/ui/rename.rs:80:9 + | +LL | #![warn(clippy::disallowed_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` + --> tests/ui/rename.rs:85:9 + | +LL | #![warn(clippy::eval_order_dependence)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` + +error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` + --> tests/ui/rename.rs:86:9 + | +LL | #![warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` + +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` + --> tests/ui/rename.rs:87:9 + | +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` + +error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` + --> tests/ui/rename.rs:88:9 + | +LL | #![warn(clippy::fn_address_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` + error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` + --> tests/ui/rename.rs:95:9 + | +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` + +error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` + --> tests/ui/rename.rs:96:9 + | +LL | #![warn(clippy::if_let_redundant_pattern_matching)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` + +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` + --> tests/ui/rename.rs:97:9 + | +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` + +error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` + --> tests/ui/rename.rs:98:9 + | +LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` + +error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` + --> tests/ui/rename.rs:99:9 + | +LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` + +error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` + --> tests/ui/rename.rs:100:9 + | +LL | #![warn(clippy::integer_arithmetic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` + error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` +error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` + --> tests/ui/rename.rs:103:9 + | +LL | #![warn(clippy::invalid_null_ptr_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` + error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` +error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` + --> tests/ui/rename.rs:107:9 + | +LL | #![warn(clippy::logic_bug)] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` + error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` + --> tests/ui/rename.rs:111:9 + | +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` + +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` + --> tests/ui/rename.rs:112:9 + | +LL | #![warn(clippy::option_and_then_some)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` + +error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` + --> tests/ui/rename.rs:113:9 + | +LL | #![warn(clippy::option_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` + +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` + --> tests/ui/rename.rs:114:9 + | +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` + +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` + --> tests/ui/rename.rs:115:9 + | +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` + +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` + --> tests/ui/rename.rs:116:9 + | +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` + +error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` + --> tests/ui/rename.rs:117:9 + | +LL | #![warn(clippy::overflow_check_conditional)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` + error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` + --> tests/ui/rename.rs:120:9 + | +LL | #![warn(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` + +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` + --> tests/ui/rename.rs:121:9 + | +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` + +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` + --> tests/ui/rename.rs:122:9 + | +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` + +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` + --> tests/ui/rename.rs:123:9 + | +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` + +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` + --> tests/ui/rename.rs:124:9 + | +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` + +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` + --> tests/ui/rename.rs:125:9 + | +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` + +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` + --> tests/ui/rename.rs:126:9 + | +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` + error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` -error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` +error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` + --> tests/ui/rename.rs:128:9 + | +LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` + +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` + --> tests/ui/rename.rs:129:9 + | +LL | #![warn(clippy::to_string_in_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` + +error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:130:9 + | +LL | #![warn(clippy::transmute_float_to_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` --> tests/ui/rename.rs:131:9 | +LL | #![warn(clippy::transmute_int_to_char)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:132:9 + | +LL | #![warn(clippy::transmute_int_to_float)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:133:9 + | +LL | #![warn(clippy::transmute_num_to_bytes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` + --> tests/ui/rename.rs:134:9 + | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` + --> tests/ui/rename.rs:137:9 + | +LL | #![warn(clippy::unwrap_or_else_default)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` + error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:135:9 - | -LL | #![warn(clippy::reverse_range_loop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` - -error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:136:9 - | -LL | #![warn(clippy::transmute_int_to_float)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` - -error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:137:9 - | -LL | #![warn(clippy::transmute_int_to_char)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` - -error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:138:9 - | -LL | #![warn(clippy::transmute_float_to_int)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` - -error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` --> tests/ui/rename.rs:139:9 | -LL | #![warn(clippy::transmute_num_to_bytes)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: aborting due to 73 previous errors diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs index a98b73c9e1c8..3f205b322ab0 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -31,34 +31,34 @@ fn ifs_same_cond_fn() { let obj = Struct; if function() { - } else if function() { //~^ same_functions_in_if_condition + } else if function() { } if fn_arg(a) { - } else if fn_arg(a) { //~^ same_functions_in_if_condition + } else if fn_arg(a) { } if obj.method() { - } else if obj.method() { //~^ same_functions_in_if_condition + } else if obj.method() { } if obj.method_arg(a) { - } else if obj.method_arg(a) { //~^ same_functions_in_if_condition + } else if obj.method_arg(a) { } let mut v = vec![1]; if v.pop().is_none() { - } else if v.pop().is_none() { //~^ same_functions_in_if_condition + } else if v.pop().is_none() { } if v.len() == 42 { - } else if v.len() == 42 { //~^ same_functions_in_if_condition + } else if v.len() == 42 { } if v.len() == 1 { diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr index 35dcbadce595..59f4511757d6 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -1,79 +1,62 @@ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:34:15 - | -LL | } else if function() { - | ^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:33:8 | LL | if function() { | ^^^^^^^^^^ +LL | +LL | } else if function() { + | ^^^^^^^^^^ + | note: the lint level is defined here --> tests/ui/same_functions_in_if_condition.rs:2:9 | LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:39:15 - | -LL | } else if fn_arg(a) { - | ^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:38:8 | LL | if fn_arg(a) { | ^^^^^^^^^ +LL | +LL | } else if fn_arg(a) { + | ^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:44:15 - | -LL | } else if obj.method() { - | ^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:43:8 | LL | if obj.method() { | ^^^^^^^^^^^^ +LL | +LL | } else if obj.method() { + | ^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:49:15 - | -LL | } else if obj.method_arg(a) { - | ^^^^^^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:48:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:55:15 - | -LL | } else if v.pop().is_none() { +LL | +LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - | -note: same as this + +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:54:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ +LL | +LL | } else if v.pop().is_none() { + | ^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:60:15 - | -LL | } else if v.len() == 42 { - | ^^^^^^^^^^^^^ - | -note: same as this +error: these `if` branches have the same function call --> tests/ui/same_functions_in_if_condition.rs:59:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ +LL | +LL | } else if v.len() == 42 { + | ^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs index c6c0cb347dc6..25884450b084 100644 --- a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs //@no-rustfix: overlapping suggestions -#![allow(clippy::no_effect, clippy::useless_vec, unused)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] #![warn(clippy::single_range_in_vec_init)] #![feature(generic_arg_infer)] diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.fixed b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed similarity index 100% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.fixed rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs similarity index 100% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr similarity index 80% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr index e334ca5241eb..4998b9bd2cc4 100644 --- a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr +++ b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr @@ -1,5 +1,5 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)` - --> tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs:6:19 + --> tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs:6:19 | LL | fn aa(a: Aa::new(); + s.remove(&"hello".to_owned()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 7954a4ad4ce7..f2dbd1db3c9f 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -675,3 +675,9 @@ mod issue_14242 { rc_slice_provider().to_vec().into_iter() } } + +fn issue14833() { + use std::collections::HashSet; + let mut s = HashSet::<&String>::new(); + s.remove(&"hello".to_owned()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr index ba562f5a50be..13d71271e211 100644 --- a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr @@ -1,13 +1,8 @@ error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:9:1 | -LL | / fn func1(a: bool, b: bool) -> Option { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn func1(a: bool, b: bool) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_wraps)]` @@ -16,7 +11,7 @@ help: remove `Option` from the return type... LL - fn func1(a: bool, b: bool) -> Option { LL + fn func1(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 42; LL | } @@ -30,21 +25,15 @@ LL ~ return 1337; error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:24:1 | -LL | / fn func2(a: bool, b: bool) -> Option { -LL | | -LL | | -LL | | if a && b { -... | -LL | | if a { Some(20) } else { Some(30) } -LL | | } - | |_^ +LL | fn func2(a: bool, b: bool) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func2(a: bool, b: bool) -> Option { LL + fn func2(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 10; LL | } @@ -54,19 +43,15 @@ LL ~ if a { 20 } else { 30 } error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:44:1 | -LL | / fn func5() -> Option { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_^ +LL | fn func5() -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func5() -> Option { LL + fn func5() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -75,19 +60,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Result` --> tests/ui/unnecessary_wraps.rs:56:1 | -LL | / fn func7() -> Result { -LL | | -LL | | -LL | | Ok(1) -LL | | } - | |_^ +LL | fn func7() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Result` from the return type... | LL - fn func7() -> Result { LL + fn func7() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Ok()` from returning expressions | LL - Ok(1) LL + 1 @@ -96,19 +77,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:86:5 | -LL | / fn func12() -> Option { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_____^ +LL | fn func12() -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func12() -> Option { LL + fn func12() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -117,13 +94,8 @@ LL + 1 error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:115:1 | -LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | @@ -144,13 +116,8 @@ LL ~ return ; error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:130:1 | -LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed index 8c1f948fb580..3c3ea5a736d4 100644 --- a/src/tools/clippy/tests/ui/useless_asref.fixed +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -248,6 +248,16 @@ impl Issue12357 { } } +fn issue_14828() { + pub trait T { + fn as_ref(&self) {} + } + + impl T for () {} + + ().as_ref(); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs index d9db2d4f5596..c173dd677152 100644 --- a/src/tools/clippy/tests/ui/useless_asref.rs +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -248,6 +248,16 @@ impl Issue12357 { } } +fn issue_14828() { + pub trait T { + fn as_ref(&self) {} + } + + impl T for () {} + + ().as_ref(); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_concat.fixed b/src/tools/clippy/tests/ui/useless_concat.fixed new file mode 100644 index 000000000000..360b6f6ce82e --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.fixed @@ -0,0 +1,41 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::useless_concat)] +#![allow(clippy::print_literal)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +macro_rules! my_concat { + ($fmt:literal $(, $e:expr)*) => { + println!(concat!("ERROR: ", $fmt), $($e,)*); + } +} + +fn main() { + let x = ""; //~ useless_concat + let x = "c"; //~ useless_concat + let x = "\""; //~ useless_concat + let x = "true"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "a😀\n"; //~ useless_concat + let x = "a"; //~ useless_concat + let x = "1"; //~ useless_concat + println!("b: {}", "a"); //~ useless_concat + // Should not lint. + let x = concat!("a", "b"); + let local_i32 = 1; + my_concat!("{}", local_i32); + let x = concat!(file!(), "#L", line!()); + + external! { concat!(); } + with_span! { + span + concat!(); + } +} diff --git a/src/tools/clippy/tests/ui/useless_concat.rs b/src/tools/clippy/tests/ui/useless_concat.rs new file mode 100644 index 000000000000..338d20a48ae9 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.rs @@ -0,0 +1,41 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::useless_concat)] +#![allow(clippy::print_literal)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +macro_rules! my_concat { + ($fmt:literal $(, $e:expr)*) => { + println!(concat!("ERROR: ", $fmt), $($e,)*); + } +} + +fn main() { + let x = concat!(); //~ useless_concat + let x = concat!('c'); //~ useless_concat + let x = concat!('"'); //~ useless_concat + let x = concat!(true); //~ useless_concat + let x = concat!(1f32); //~ useless_concat + let x = concat!(1.0000f32); //~ useless_concat + let x = concat!(1_f32); //~ useless_concat + let x = concat!(1_); //~ useless_concat + let x = concat!(1.0000_f32); //~ useless_concat + let x = concat!(1.0000_); //~ useless_concat + let x = concat!("a\u{1f600}\n"); //~ useless_concat + let x = concat!(r##"a"##); //~ useless_concat + let x = concat!(1); //~ useless_concat + println!("b: {}", concat!("a")); //~ useless_concat + // Should not lint. + let x = concat!("a", "b"); + let local_i32 = 1; + my_concat!("{}", local_i32); + let x = concat!(file!(), "#L", line!()); + + external! { concat!(); } + with_span! { + span + concat!(); + } +} diff --git a/src/tools/clippy/tests/ui/useless_concat.stderr b/src/tools/clippy/tests/ui/useless_concat.stderr new file mode 100644 index 000000000000..43d6d9ff5799 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.stderr @@ -0,0 +1,89 @@ +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:16:13 + | +LL | let x = concat!(); + | ^^^^^^^^^ help: replace with: `""` + | + = note: `-D clippy::useless-concat` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_concat)]` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:17:13 + | +LL | let x = concat!('c'); + | ^^^^^^^^^^^^ help: replace with: `"c"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:18:13 + | +LL | let x = concat!('"'); + | ^^^^^^^^^^^^ help: replace with: `"\""` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:19:13 + | +LL | let x = concat!(true); + | ^^^^^^^^^^^^^ help: replace with: `"true"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:20:13 + | +LL | let x = concat!(1f32); + | ^^^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:21:13 + | +LL | let x = concat!(1.0000f32); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:22:13 + | +LL | let x = concat!(1_f32); + | ^^^^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:23:13 + | +LL | let x = concat!(1_); + | ^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:24:13 + | +LL | let x = concat!(1.0000_f32); + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:25:13 + | +LL | let x = concat!(1.0000_); + | ^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:26:13 + | +LL | let x = concat!("a\u{1f600}\n"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `"a😀\n"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:27:13 + | +LL | let x = concat!(r##"a"##); + | ^^^^^^^^^^^^^^^^^ help: replace with: `"a"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:28:13 + | +LL | let x = concat!(1); + | ^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:29:23 + | +LL | println!("b: {}", concat!("a")); + | ^^^^^^^^^^^^ help: replace with: `"a"` + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 489caacf2123..ad30c94f3478 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.for_each(|_x| {}); + //~^ useless_conversion + let _ = R.map(|_x| 0); + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 4f3a3b00ea20..505afb340009 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.into_iter().for_each(|_x| {}); + //~^ useless_conversion + let _ = R.into_iter().map(|_x| 0); + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 3cde2a786e46..3bfaf1411c2c 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -377,5 +377,17 @@ LL - takes_into_iter(self.my_field.into_iter()); LL + takes_into_iter(&mut *self.my_field); | -error: aborting due to 41 previous errors +error: useless conversion to the same type: `std::ops::Range` + --> tests/ui/useless_conversion.rs:440:5 + | +LL | R.into_iter().for_each(|_x| {}); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: useless conversion to the same type: `std::ops::Range` + --> tests/ui/useless_conversion.rs:442:13 + | +LL | let _ = R.into_iter().map(|_x| 0); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: aborting due to 43 previous errors diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs index 4beeef421f30..dcbfd16843de 100644 --- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs @@ -71,6 +71,27 @@ fn test2(map: HashMap, key: &str) -> HashMap { todo!(); } +fn issue14822() { + trait Trait { + type T; + } + struct S(T::T); + + // The `delay_bug` happens when evaluating the pointer metadata of `S` which depends on + // whether `T::T` is `Sized`. Since the type alias doesn't have a trait bound of `T: Trait` + // evaluating `T::T: Sized` ultimately fails with `NoSolution`. + type A = HashMap>; + type B = HashMap>; + + enum E {} + impl Trait for E { + type T = (); + } + type C = HashMap>; + type D = HashMap>; + //~^ zero_sized_map_values +} + fn main() { let _: HashMap = HashMap::new(); //~^ zero_sized_map_values diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr index ed8536acfe8f..d29491fa05c7 100644 --- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr @@ -81,7 +81,15 @@ LL | fn test(map: HashMap, key: &str) -> HashMap { = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:75:34 + --> tests/ui/zero_sized_hashmap_values.rs:91:14 + | +LL | type D = HashMap>; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> tests/ui/zero_sized_hashmap_values.rs:96:34 | LL | let _: HashMap = HashMap::new(); | ^^^^^^^ @@ -89,7 +97,7 @@ LL | let _: HashMap = HashMap::new(); = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:75:12 + --> tests/ui/zero_sized_hashmap_values.rs:96:12 | LL | let _: HashMap = HashMap::new(); | ^^^^^^^^^^^^^^^^^^^ @@ -97,12 +105,12 @@ LL | let _: HashMap = HashMap::new(); = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:81:12 + --> tests/ui/zero_sized_hashmap_values.rs:102:12 | LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); | ^^^^^^^^^^^^^ | = help: consider using a set instead -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index eb2f9f9dd61f..389f22c6a2ca 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*", - "good-first-issue", "beta-nominated" + "good first issue", "beta-nominated" ] # Allows shortcuts like `@rustbot ready` diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index ab76d0fc01e7..e420a450d422 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -8,7 +8,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust [dependencies] anyhow = "1.0.65" -askama = "0.13.0" +askama = "0.14.0" cargo_metadata = "0.18.1" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs index b717bd53eb1a..3fae26bda475 100644 --- a/src/tools/generate-copyright/src/cargo_metadata.rs +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -46,11 +46,12 @@ pub struct PackageMetadata { /// covered it already. pub fn get_metadata_and_notices( cargo: &Path, + cargo_home_path: &Path, vendor_path: &Path, root_path: &Path, manifest_paths: &[PathBuf], ) -> Result, Error> { - let mut output = get_metadata(cargo, root_path, manifest_paths)?; + let mut output = get_metadata(cargo, cargo_home_path, root_path, manifest_paths)?; // Now for each dependency we found, go and grab any important looking files for (package, metadata) in output.iter_mut() { @@ -66,6 +67,7 @@ pub fn get_metadata_and_notices( /// assume `reuse` has covered it already. pub fn get_metadata( cargo: &Path, + cargo_home_path: &Path, root_path: &Path, manifest_paths: &[PathBuf], ) -> Result, Error> { @@ -81,8 +83,11 @@ pub fn get_metadata( .manifest_path(manifest_path) .exec()?; for package in metadata.packages { - let manifest_path = package.manifest_path.as_path(); - if manifest_path.starts_with(root_path) { + let package_manifest_path = package.manifest_path.as_path(); + + if package_manifest_path.starts_with(root_path) + && !package_manifest_path.starts_with(cargo_home_path) + { // it's an in-tree dependency and reuse covers it continue; } diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index d6ed7261b7c8..5497db1f5f3a 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -15,6 +15,7 @@ mod cargo_metadata; /// /// Run `x.py run generate-copyright` fn main() -> Result<(), Error> { + let cargo_home = env_path("CARGO_HOME")?; let dest_file = env_path("DEST")?; let libstd_dest_file = env_path("DEST_LIBSTD")?; let src_dir = env_path("SRC_DIR")?; @@ -39,11 +40,17 @@ fn main() -> Result<(), Error> { .collect::>(); // Scan Cargo dependencies - let mut collected_cargo_metadata = - cargo_metadata::get_metadata_and_notices(&cargo, &vendor_dir, &src_dir, &cargo_manifests)?; + let mut collected_cargo_metadata = cargo_metadata::get_metadata_and_notices( + &cargo, + &cargo_home, + &vendor_dir, + &src_dir, + &cargo_manifests, + )?; let library_collected_cargo_metadata = cargo_metadata::get_metadata_and_notices( &cargo, + &cargo_home, &vendor_dir, &src_dir, &library_manifests, diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml index 3578bda8276e..f1ffda75ac0f 100644 --- a/src/tools/lint-docs/Cargo.toml +++ b/src/tools/lint-docs/Cargo.toml @@ -7,6 +7,7 @@ description = "A script to extract the lint documentation for the rustc book." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +rustc-literal-escaper = "0.0.2" serde_json = "1.0.57" tempfile = "3.1.0" walkdir = "2.3.1" diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index cacce01675fe..6bb18c2bced7 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -4,6 +4,7 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; +use rustc_literal_escaper::{Mode, unescape_unicode}; use walkdir::WalkDir; mod groups; @@ -214,6 +215,16 @@ impl<'a> LintExtractor<'a> { let line = line.trim(); if let Some(text) = line.strip_prefix("/// ") { doc_lines.push(text.to_string()); + } else if let Some(text) = line.strip_prefix("#[doc = \"") { + let escaped = text.strip_suffix("\"]").unwrap(); + let mut buf = String::new(); + unescape_unicode(escaped, Mode::Str, &mut |_, c| match c { + Ok(c) => buf.push(c), + Err(err) => { + assert!(!err.is_fatal(), "failed to unescape string literal") + } + }); + doc_lines.push(buf); } else if line == "///" { doc_lines.push("".to_string()); } else if line.starts_with("// ") { diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index b692ddab4ffc..de521393cd0a 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -218,7 +218,7 @@ degree documented below): make no promises and we don't run tests for such targets. - We have unofficial support (not maintained by the Miri team itself) for some further operating systems. - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite. - - `freebsd`: maintained by @YohDeadfall. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`. + - `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - `wasi`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. @@ -580,6 +580,7 @@ Definite bugs found: * [Weak-memory-induced memory leak in Windows thread-local storage](https://github.com/rust-lang/rust/pull/124281) * [A bug in the new `RwLock::downgrade` implementation](https://rust-lang.zulipchat.com/#narrow/channel/269128-miri/topic/Miri.20error.20library.20test) (caught by Miri before it landed in the Rust repo) * [Mockall reading unintialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite) +* [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index bd4ca2860f35..c1915ae617e8 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d984a9db43148467059309bd1e5ad577085162f695d9fe2cf3543aeb25cd38" +checksum = "10edc2e4393515193bd766e2f6c050b0536a68e56f2b6d56c07ababfdc114ff0" dependencies = [ "anyhow", "rustc_version", diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index 23048914af18..5c579b2a77d6 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "6" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.19" -rustc-build-sysroot = "0.5.4" +rustc-build-sysroot = "0.5.7" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 4857f62cd3a1..a5e019a8ea97 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -90,7 +90,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { "`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, `clean`, and `setup`." ), }; - let verbose = num_arg_flag("-v"); + let verbose = num_arg_flag("-v") + num_arg_flag("--verbose"); let quiet = has_arg_flag("-q") || has_arg_flag("--quiet"); // Determine the involved architectures. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 755e02d02eca..8941af681a47 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -142,12 +142,12 @@ case $HOST_TARGET in # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 - # With reduced many-seed count to avoid spending too much time on that. - # (All OSes and ABIs are run with 64 seeds at least once though via the macOS runner.) - MANY_SEEDS=16 TEST_TARGET=i686-unknown-linux-gnu run_tests - MANY_SEEDS=16 TEST_TARGET=aarch64-unknown-linux-gnu run_tests - MANY_SEEDS=16 TEST_TARGET=x86_64-apple-darwin run_tests - MANY_SEEDS=16 TEST_TARGET=x86_64-pc-windows-gnu run_tests + MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests + MANY_SEEDS=64 TEST_TARGET=aarch64-unknown-linux-gnu run_tests + MANY_SEEDS=64 TEST_TARGET=x86_64-apple-darwin run_tests + MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-gnu run_tests + # Extra tier 1 candidate + MANY_SEEDS=64 TEST_TARGET=aarch64-pc-windows-msvc run_tests ;; aarch64-apple-darwin) # Host @@ -156,16 +156,18 @@ case $HOST_TARGET in MANY_SEEDS=64 TEST_TARGET=i686-pc-windows-gnu run_tests MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests # Extra tier 2 - TEST_TARGET=arm-unknown-linux-gnueabi run_tests - TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice + MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM + MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI + MANY_SEEDS=16 TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice # Not officially supported tier 2 - TEST_TARGET=x86_64-unknown-illumos run_tests - TEST_TARGET=x86_64-pc-solaris run_tests + MANY_SEEDS=16 TEST_TARGET=mips-unknown-linux-gnu run_tests # a 32bit big-endian target, and also a target without 64bit atomics + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests + MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests # Partially supported targets (tier 2) BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there - TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe - TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency fs libc-pipe TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std @@ -177,7 +179,7 @@ case $HOST_TARGET in # Host # Without GC_STRESS and with reduced many-seeds count as this is the slowest runner. # (The macOS runner checks windows-msvc with full many-seeds count.) - MIR_OPT=1 MANY_SEEDS=16 TEST_BENCH=1 run_tests + MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, # and a 64bit target works on a 32bit host. diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 3b7b159aeab7..86362145d475 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::BTreeMap; use std::ffi::{OsStr, OsString}; use std::fmt::Write as _; use std::fs::{self, File}; @@ -404,7 +404,28 @@ impl Command { // We want to forward the host stdin so apparently we cannot use `cmd!`. let mut cmd = process::Command::new("git"); cmd.arg("rebase").arg(&base).arg("--interactive"); - cmd.env("GIT_SEQUENCE_EDITOR", env::current_exe()?); + let current_exe = { + if cfg!(windows) { + // Apparently git-for-Windows gets confused by backslashes if we just use + // `current_exe()` here. So replace them by forward slashes if this is not a "magic" + // path starting with "\\". This is clearly a git bug but we work around it here. + // Also see . + let bin = env::current_exe()?; + match bin.into_os_string().into_string() { + Err(not_utf8) => not_utf8.into(), // :shrug: + Ok(str) => { + if str.starts_with(r"\\") { + str.into() // don't touch these magic paths, they must use backslashes + } else { + str.replace('\\', "/").into() + } + } + } + } else { + env::current_exe()? + } + }; + cmd.env("GIT_SEQUENCE_EDITOR", current_exe); cmd.env("MIRI_SCRIPT_IS_GIT_SEQUENCE_EDITOR", "1"); cmd.current_dir(sh.current_dir()); let result = cmd.status()?; @@ -489,7 +510,9 @@ impl Command { sh.read_dir(benches_dir)? .into_iter() .filter(|path| path.is_dir()) - .map(|path| path.into_os_string().into_string().unwrap()) + // Only keep the basename: that matches the usage with a manual bench list, + // and it ensure the path concatenations below work as intended. + .map(|path| path.file_name().unwrap().to_owned().into_string().unwrap()) .collect() } else { benches.into_iter().collect() @@ -530,14 +553,16 @@ impl Command { stddev: f64, } - let gather_results = || -> Result> { + let gather_results = || -> Result> { let baseline_temp_dir = results_json_dir.unwrap(); - let mut results = HashMap::new(); + let mut results = BTreeMap::new(); for bench in &benches { - let result = File::open(path!(baseline_temp_dir / format!("{bench}.bench.json")))?; - let mut result: serde_json::Value = - serde_json::from_reader(BufReader::new(result))?; - let result: BenchResult = serde_json::from_value(result["results"][0].take())?; + let result = File::open(path!(baseline_temp_dir / format!("{bench}.bench.json"))) + .context("failed to read hyperfine JSON")?; + let mut result: serde_json::Value = serde_json::from_reader(BufReader::new(result)) + .context("failed to parse hyperfine JSON")?; + let result: BenchResult = serde_json::from_value(result["results"][0].take()) + .context("failed to interpret hyperfine JSON")?; results.insert(bench as &str, result); } Ok(results) @@ -549,15 +574,15 @@ impl Command { serde_json::to_writer_pretty(BufWriter::new(baseline), &results)?; } else if let Some(baseline_file) = load_baseline { let new_results = gather_results()?; - let baseline_results: HashMap = { + let baseline_results: BTreeMap = { let f = File::open(baseline_file)?; serde_json::from_reader(BufReader::new(f))? }; println!( "Comparison with baseline (relative speed, lower is better for the new results):" ); - for (bench, new_result) in new_results.iter() { - let Some(baseline_result) = baseline_results.get(*bench) else { continue }; + for (bench, new_result) in new_results { + let Some(baseline_result) = baseline_results.get(bench) else { continue }; // Compare results (inspired by hyperfine) let ratio = new_result.mean / baseline_result.mean; diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 8b98fe3c4fc3..553d410b2bc7 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ac17c3486c6fdfbb0c3c18b99f3d8dfbff625d29 +337c11e5932275e7d450c1f2e26f289f0ddfa717 diff --git a/src/tools/miri/src/alloc_bytes.rs b/src/tools/miri/src/alloc/alloc_bytes.rs similarity index 64% rename from src/tools/miri/src/alloc_bytes.rs rename to src/tools/miri/src/alloc/alloc_bytes.rs index 69ede279aa9b..2a253952b27a 100644 --- a/src/tools/miri/src/alloc_bytes.rs +++ b/src/tools/miri/src/alloc/alloc_bytes.rs @@ -1,10 +1,23 @@ use std::alloc::Layout; use std::borrow::Cow; use std::{alloc, slice}; +#[cfg(target_os = "linux")] +use std::{cell::RefCell, rc::Rc}; use rustc_abi::{Align, Size}; use rustc_middle::mir::interpret::AllocBytes; +#[cfg(target_os = "linux")] +use crate::alloc::isolated_alloc::IsolatedAlloc; +use crate::helpers::ToU64 as _; + +#[derive(Clone, Debug)] +pub enum MiriAllocParams { + Global, + #[cfg(target_os = "linux")] + Isolated(Rc>), +} + /// Allocation bytes that explicitly handle the layout of the data they're storing. /// This is necessary to interface with native code that accesses the program store in Miri. #[derive(Debug)] @@ -16,13 +29,16 @@ pub struct MiriAllocBytes { /// * If `self.layout.size() == 0`, then `self.ptr` was allocated with the equivalent layout with size 1. /// * Otherwise, `self.ptr` points to memory allocated with `self.layout`. ptr: *mut u8, + /// Whether this instance of `MiriAllocBytes` had its allocation created by calling `alloc::alloc()` + /// (`Global`) or the discrete allocator (`Isolated`) + params: MiriAllocParams, } impl Clone for MiriAllocBytes { fn clone(&self) -> Self { let bytes: Cow<'_, [u8]> = Cow::Borrowed(self); - let align = Align::from_bytes(self.layout.align().try_into().unwrap()).unwrap(); - MiriAllocBytes::from_bytes(bytes, align) + let align = Align::from_bytes(self.layout.align().to_u64()).unwrap(); + MiriAllocBytes::from_bytes(bytes, align, self.params.clone()) } } @@ -35,8 +51,16 @@ impl Drop for MiriAllocBytes { } else { self.layout }; + // SAFETY: Invariant, `self.ptr` points to memory allocated with `self.layout`. - unsafe { alloc::dealloc(self.ptr, alloc_layout) } + unsafe { + match self.params.clone() { + MiriAllocParams::Global => alloc::dealloc(self.ptr, alloc_layout), + #[cfg(target_os = "linux")] + MiriAllocParams::Isolated(alloc) => + alloc.borrow_mut().dealloc(self.ptr, alloc_layout), + } + } } } @@ -65,7 +89,8 @@ impl MiriAllocBytes { fn alloc_with( size: u64, align: u64, - alloc_fn: impl FnOnce(Layout) -> *mut u8, + params: MiriAllocParams, + alloc_fn: impl FnOnce(Layout, &MiriAllocParams) -> *mut u8, ) -> Result { let size = usize::try_from(size).map_err(|_| ())?; let align = usize::try_from(align).map_err(|_| ())?; @@ -73,24 +98,36 @@ impl MiriAllocBytes { // When size is 0 we allocate 1 byte anyway, to ensure each allocation has a unique address. let alloc_layout = if size == 0 { Layout::from_size_align(1, align).unwrap() } else { layout }; - let ptr = alloc_fn(alloc_layout); + let ptr = alloc_fn(alloc_layout, ¶ms); if ptr.is_null() { Err(()) } else { // SAFETY: All `MiriAllocBytes` invariants are fulfilled. - Ok(Self { ptr, layout }) + Ok(Self { ptr, layout, params }) } } } impl AllocBytes for MiriAllocBytes { - fn from_bytes<'a>(slice: impl Into>, align: Align) -> Self { + type AllocParams = MiriAllocParams; + + fn from_bytes<'a>( + slice: impl Into>, + align: Align, + params: MiriAllocParams, + ) -> Self { let slice = slice.into(); let size = slice.len(); let align = align.bytes(); // SAFETY: `alloc_fn` will only be used with `size != 0`. - let alloc_fn = |layout| unsafe { alloc::alloc(layout) }; - let alloc_bytes = MiriAllocBytes::alloc_with(size.try_into().unwrap(), align, alloc_fn) + let alloc_fn = |layout, params: &MiriAllocParams| unsafe { + match params { + MiriAllocParams::Global => alloc::alloc(layout), + #[cfg(target_os = "linux")] + MiriAllocParams::Isolated(alloc) => alloc.borrow_mut().alloc(layout), + } + }; + let alloc_bytes = MiriAllocBytes::alloc_with(size.to_u64(), align, params, alloc_fn) .unwrap_or_else(|()| { panic!("Miri ran out of memory: cannot create allocation of {size} bytes") }); @@ -100,12 +137,18 @@ impl AllocBytes for MiriAllocBytes { alloc_bytes } - fn zeroed(size: Size, align: Align) -> Option { + fn zeroed(size: Size, align: Align, params: MiriAllocParams) -> Option { let size = size.bytes(); let align = align.bytes(); // SAFETY: `alloc_fn` will only be used with `size != 0`. - let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) }; - MiriAllocBytes::alloc_with(size, align, alloc_fn).ok() + let alloc_fn = |layout, params: &MiriAllocParams| unsafe { + match params { + MiriAllocParams::Global => alloc::alloc_zeroed(layout), + #[cfg(target_os = "linux")] + MiriAllocParams::Isolated(alloc) => alloc.borrow_mut().alloc_zeroed(layout), + } + }; + MiriAllocBytes::alloc_with(size, align, params, alloc_fn).ok() } fn as_mut_ptr(&mut self) -> *mut u8 { diff --git a/src/tools/miri/src/alloc/isolated_alloc.rs b/src/tools/miri/src/alloc/isolated_alloc.rs new file mode 100644 index 000000000000..7b74d1713734 --- /dev/null +++ b/src/tools/miri/src/alloc/isolated_alloc.rs @@ -0,0 +1,389 @@ +use std::alloc::{self, Layout}; + +use rustc_index::bit_set::DenseBitSet; + +/// How many bytes of memory each bit in the bitset represents. +const COMPRESSION_FACTOR: usize = 4; + +/// A dedicated allocator for interpreter memory contents, ensuring they are stored on dedicated +/// pages (not mixed with Miri's own memory). This is used in native-lib mode. +#[derive(Debug)] +pub struct IsolatedAlloc { + /// Pointers to page-aligned memory that has been claimed by the allocator. + /// Every pointer here must point to a page-sized allocation claimed via + /// the global allocator. These pointers are used for "small" allocations. + page_ptrs: Vec<*mut u8>, + /// Metadata about which bytes have been allocated on each page. The length + /// of this vector must be the same as that of `page_ptrs`, and the domain + /// size of the bitset must be exactly `page_size / COMPRESSION_FACTOR`. + /// + /// Conceptually, each bit of the bitset represents the allocation status of + /// one n-byte chunk on the corresponding element of `page_ptrs`. Thus, + /// indexing into it should be done with a value one-nth of the corresponding + /// offset on the matching `page_ptrs` element (n = `COMPRESSION_FACTOR`). + page_infos: Vec>, + /// Pointers to multiple-page-sized allocations. These must also be page-aligned, + /// with their size stored as the second element of the vector. + huge_ptrs: Vec<(*mut u8, usize)>, + /// The host (not emulated) page size. + page_size: usize, +} + +impl IsolatedAlloc { + /// Creates an empty allocator. + pub fn new() -> Self { + Self { + page_ptrs: Vec::new(), + huge_ptrs: Vec::new(), + page_infos: Vec::new(), + // SAFETY: `sysconf(_SC_PAGESIZE)` is always safe to call at runtime + // See https://www.man7.org/linux/man-pages/man3/sysconf.3.html + page_size: unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }, + } + } + + /// For simplicity, we serve small allocations in multiples of COMPRESSION_FACTOR + /// bytes with at least that alignment. + #[inline] + fn normalized_layout(layout: Layout) -> Layout { + let align = + if layout.align() < COMPRESSION_FACTOR { COMPRESSION_FACTOR } else { layout.align() }; + let size = layout.size().next_multiple_of(COMPRESSION_FACTOR); + Layout::from_size_align(size, align).unwrap() + } + + /// Returns the layout used to allocate the pages that hold small allocations. + #[inline] + fn page_layout(&self) -> Layout { + Layout::from_size_align(self.page_size, self.page_size).unwrap() + } + + /// If the allocation is greater than a page, then round to the nearest page #. + #[inline] + fn huge_normalized_layout(layout: Layout, page_size: usize) -> Layout { + // Allocate in page-sized chunks + let size = layout.size().next_multiple_of(page_size); + // And make sure the align is at least one page + let align = std::cmp::max(layout.align(), page_size); + Layout::from_size_align(size, align).unwrap() + } + + /// Determined whether a given normalized (size, align) should be sent to + /// `alloc_huge` / `dealloc_huge`. + #[inline] + fn is_huge_alloc(&self, layout: &Layout) -> bool { + layout.align() > self.page_size / 2 || layout.size() >= self.page_size / 2 + } + + /// Allocates memory as described in `Layout`. This memory should be deallocated + /// by calling `dealloc` on this same allocator. + /// + /// SAFETY: See `alloc::alloc()` + pub unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 { + // SAFETY: Upheld by caller + unsafe { self.allocate(layout, false) } + } + + /// Same as `alloc`, but zeroes out the memory. + /// + /// SAFETY: See `alloc::alloc_zeroed()` + pub unsafe fn alloc_zeroed(&mut self, layout: Layout) -> *mut u8 { + // SAFETY: Upheld by caller + unsafe { self.allocate(layout, true) } + } + + /// Abstracts over the logic of `alloc_zeroed` vs `alloc`, as determined by + /// the `zeroed` argument. + /// + /// SAFETY: See `alloc::alloc()`, with the added restriction that `page_size` + /// corresponds to the host pagesize. + unsafe fn allocate(&mut self, layout: Layout, zeroed: bool) -> *mut u8 { + let layout = IsolatedAlloc::normalized_layout(layout); + if self.is_huge_alloc(&layout) { + // SAFETY: Validity of `layout` upheld by caller; we checked that + // the size and alignment are appropriate for being a huge alloc + unsafe { self.alloc_huge(layout, zeroed) } + } else { + for (&mut page, pinfo) in std::iter::zip(&mut self.page_ptrs, &mut self.page_infos) { + // SAFETY: The value in `self.page_size` is used to allocate + // `page`, with page alignment + if let Some(ptr) = + unsafe { Self::alloc_small(self.page_size, layout, page, pinfo, zeroed) } + { + return ptr; + } + } + + // We get here only if there's no space in our existing pages + let page_size = self.page_size; + // Add another page and allocate from it; this cannot fail since the + // new page is empty and we already asserted it fits into a page + let (page, pinfo) = self.add_page(); + + // SAFETY: See comment on `alloc_from_page` above + unsafe { Self::alloc_small(page_size, layout, page, pinfo, zeroed).unwrap() } + } + } + + /// Used internally by `allocate` to abstract over some logic. + /// + /// SAFETY: `page` must be a page-aligned pointer to an allocated page, + /// where the allocation is (at least) `page_size` bytes. + unsafe fn alloc_small( + page_size: usize, + layout: Layout, + page: *mut u8, + pinfo: &mut DenseBitSet, + zeroed: bool, + ) -> Option<*mut u8> { + // Check every alignment-sized block and see if there exists a `size` + // chunk of empty space i.e. forall idx . !pinfo.contains(idx / n) + for offset in (0..page_size).step_by(layout.align()) { + let offset_pinfo = offset / COMPRESSION_FACTOR; + let size_pinfo = layout.size() / COMPRESSION_FACTOR; + // DenseBitSet::contains() panics if the index is out of bounds + if pinfo.domain_size() < offset_pinfo + size_pinfo { + break; + } + // FIXME: is there a more efficient way to check whether the entire range is unset + // in the bitset? + let range_avail = !(offset_pinfo..offset_pinfo + size_pinfo).any(|i| pinfo.contains(i)); + if range_avail { + pinfo.insert_range(offset_pinfo..offset_pinfo + size_pinfo); + // SAFETY: We checked the available bytes after `idx` in the call + // to `domain_size` above and asserted there are at least `idx + + // layout.size()` bytes available and unallocated after it. + // `page` must point to the start of the page, so adding `idx` + // is safe per the above. + unsafe { + let ptr = page.add(offset); + if zeroed { + // Only write the bytes we were specifically asked to + // zero out, even if we allocated more + ptr.write_bytes(0, layout.size()); + } + return Some(ptr); + } + } + } + None + } + + /// Expands the available memory pool by adding one page. + fn add_page(&mut self) -> (*mut u8, &mut DenseBitSet) { + // SAFETY: The system page size, which is the layout size, cannot be 0 + let page_ptr = unsafe { alloc::alloc(self.page_layout()) }; + // `page_infos` has to have one bit for each `COMPRESSION_FACTOR`-sized chunk of bytes in the page. + assert!(self.page_size % COMPRESSION_FACTOR == 0); + self.page_infos.push(DenseBitSet::new_empty(self.page_size / COMPRESSION_FACTOR)); + self.page_ptrs.push(page_ptr); + (page_ptr, self.page_infos.last_mut().unwrap()) + } + + /// Allocates in multiples of one page on the host system. + /// + /// SAFETY: Same as `alloc()`. + unsafe fn alloc_huge(&mut self, layout: Layout, zeroed: bool) -> *mut u8 { + let layout = IsolatedAlloc::huge_normalized_layout(layout, self.page_size); + // SAFETY: Upheld by caller + let ret = + unsafe { if zeroed { alloc::alloc_zeroed(layout) } else { alloc::alloc(layout) } }; + self.huge_ptrs.push((ret, layout.size())); + ret + } + + /// Deallocates a pointer from this allocator. + /// + /// SAFETY: This pointer must have been allocated by calling `alloc()` (or + /// `alloc_zeroed()`) with the same layout as the one passed on this same + /// `IsolatedAlloc`. + pub unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + let layout = IsolatedAlloc::normalized_layout(layout); + + if self.is_huge_alloc(&layout) { + // SAFETY: Partly upheld by caller, and we checked that the size + // and align, meaning this must have been allocated via `alloc_huge` + unsafe { + self.dealloc_huge(ptr, layout); + } + } else { + // SAFETY: It's not a huge allocation, therefore it is a small one. + let idx = unsafe { self.dealloc_small(ptr, layout) }; + + // This may have been the last allocation on this page. If so, free the entire page. + // FIXME: this can lead to threshold effects, we should probably add some form + // of hysteresis. + if self.page_infos[idx].is_empty() { + self.page_infos.remove(idx); + let page_ptr = self.page_ptrs.remove(idx); + // SAFETY: We checked that there are no outstanding allocations + // from us pointing to this page, and we know it was allocated + // with this layout + unsafe { + alloc::dealloc(page_ptr, self.page_layout()); + } + } + } + } + + /// Returns the index of the page that this was deallocated from + /// + /// SAFETY: the pointer must have been allocated with `alloc_small`. + unsafe fn dealloc_small(&mut self, ptr: *mut u8, layout: Layout) -> usize { + // Offset of the pointer in the current page + let offset = ptr.addr() % self.page_size; + // And then the page's base address + let page_addr = ptr.addr() - offset; + + // Find the page this allocation belongs to. + // This could be made faster if the list was sorted -- the allocator isn't fully optimized at the moment. + let pinfo = std::iter::zip(&mut self.page_ptrs, &mut self.page_infos) + .enumerate() + .find(|(_, (page, _))| page.addr() == page_addr); + let Some((idx_of_pinfo, (_, pinfo))) = pinfo else { + panic!("Freeing in an unallocated page: {ptr:?}\nHolding pages {:?}", self.page_ptrs) + }; + // Mark this range as available in the page. + let ptr_idx_pinfo = offset / COMPRESSION_FACTOR; + let size_pinfo = layout.size() / COMPRESSION_FACTOR; + for idx in ptr_idx_pinfo..ptr_idx_pinfo + size_pinfo { + pinfo.remove(idx); + } + idx_of_pinfo + } + + /// SAFETY: Same as `dealloc()` with the added requirement that `layout` + /// must ask for a size larger than the host pagesize. + unsafe fn dealloc_huge(&mut self, ptr: *mut u8, layout: Layout) { + let layout = IsolatedAlloc::huge_normalized_layout(layout, self.page_size); + // Find the pointer matching in address with the one we got + let idx = self + .huge_ptrs + .iter() + .position(|pg| ptr.addr() == pg.0.addr()) + .expect("Freeing unallocated pages"); + // And kick it from the list + self.huge_ptrs.remove(idx); + // SAFETY: Caller ensures validity of the layout + unsafe { + alloc::dealloc(ptr, layout); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Helper function to assert that all bytes from `ptr` to `ptr.add(layout.size())` + /// are zeroes. + /// + /// SAFETY: `ptr` must have been allocated with `layout`. + unsafe fn assert_zeroes(ptr: *mut u8, layout: Layout) { + // SAFETY: Caller ensures this is valid + unsafe { + for ofs in 0..layout.size() { + assert_eq!(0, ptr.add(ofs).read()); + } + } + } + + /// Check that small (sub-pagesize) allocations are properly zeroed out. + #[test] + fn small_zeroes() { + let mut alloc = IsolatedAlloc::new(); + // 256 should be less than the pagesize on *any* system + let layout = Layout::from_size_align(256, 32).unwrap(); + // SAFETY: layout size is the constant above, not 0 + let ptr = unsafe { alloc.alloc_zeroed(layout) }; + // SAFETY: `ptr` was just allocated with `layout` + unsafe { + assert_zeroes(ptr, layout); + alloc.dealloc(ptr, layout); + } + } + + /// Check that huge (> 1 page) allocations are properly zeroed out also. + #[test] + fn huge_zeroes() { + let mut alloc = IsolatedAlloc::new(); + // 16k is about as big as pages get e.g. on macos aarch64 + let layout = Layout::from_size_align(16 * 1024, 128).unwrap(); + // SAFETY: layout size is the constant above, not 0 + let ptr = unsafe { alloc.alloc_zeroed(layout) }; + // SAFETY: `ptr` was just allocated with `layout` + unsafe { + assert_zeroes(ptr, layout); + alloc.dealloc(ptr, layout); + } + } + + /// Check that repeatedly reallocating the same memory will still zero out + /// everything properly + #[test] + fn repeated_allocs() { + let mut alloc = IsolatedAlloc::new(); + // Try both sub-pagesize allocs and those larger than / equal to a page + for sz in (1..=(16 * 1024)).step_by(128) { + let layout = Layout::from_size_align(sz, 1).unwrap(); + // SAFETY: all sizes in the range above are nonzero as we start from 1 + let ptr = unsafe { alloc.alloc_zeroed(layout) }; + // SAFETY: `ptr` was just allocated with `layout`, which was used + // to bound the access size + unsafe { + assert_zeroes(ptr, layout); + ptr.write_bytes(255, sz); + alloc.dealloc(ptr, layout); + } + } + } + + /// Checks that allocations of different sizes do not overlap, then for memory + /// leaks that might have occurred. + #[test] + fn check_leaks_and_overlaps() { + let mut alloc = IsolatedAlloc::new(); + + // Some random sizes and aligns + let mut sizes = vec![32; 10]; + sizes.append(&mut vec![15; 4]); + sizes.append(&mut vec![256; 12]); + // Give it some multi-page ones too + sizes.append(&mut vec![32 * 1024; 4]); + + // Matching aligns for the sizes + let mut aligns = vec![16; 12]; + aligns.append(&mut vec![256; 2]); + aligns.append(&mut vec![64; 12]); + aligns.append(&mut vec![4096; 4]); + + // Make sure we didn't mess up in the test itself! + assert_eq!(sizes.len(), aligns.len()); + + // Aggregate the sizes and aligns into a vec of layouts, then allocate them + let layouts: Vec<_> = std::iter::zip(sizes, aligns) + .map(|(sz, al)| Layout::from_size_align(sz, al).unwrap()) + .collect(); + // SAFETY: all sizes specified in `sizes` are nonzero + let ptrs: Vec<_> = + layouts.iter().map(|layout| unsafe { alloc.alloc_zeroed(*layout) }).collect(); + + for (&ptr, &layout) in std::iter::zip(&ptrs, &layouts) { + // We requested zeroed allocations, so check that that's true + // Then write to the end of the current size, so if the allocs + // overlap (or the zeroing is wrong) then `assert_zeroes` will panic. + // Also check that the alignment we asked for was respected + assert_eq!(ptr.addr().strict_rem(layout.align()), 0); + // SAFETY: each `ptr` was allocated with its corresponding `layout`, + // which is used to bound the access size + unsafe { + assert_zeroes(ptr, layout); + ptr.write_bytes(255, layout.size()); + alloc.dealloc(ptr, layout); + } + } + + // And then verify that no memory was leaked after all that + assert!(alloc.page_ptrs.is_empty() && alloc.huge_ptrs.is_empty()); + } +} diff --git a/src/tools/miri/src/alloc/mod.rs b/src/tools/miri/src/alloc/mod.rs new file mode 100644 index 000000000000..3be885920d22 --- /dev/null +++ b/src/tools/miri/src/alloc/mod.rs @@ -0,0 +1,5 @@ +mod alloc_bytes; +#[cfg(target_os = "linux")] +pub mod isolated_alloc; + +pub use self::alloc_bytes::{MiriAllocBytes, MiriAllocParams}; diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index dd389d97cdce..12a320b96767 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -135,11 +135,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.machine.native_lib.is_some() { // In native lib mode, we use the "real" address of the bytes for this allocation. // This ensures the interpreted program and native code have the same view of memory. + let params = this.machine.get_default_alloc_params(); let base_ptr = match info.kind { AllocKind::LiveData => { if memory_kind == MiriMemoryKind::Global.into() { // For new global allocations, we always pre-allocate the memory to be able use the machine address directly. - let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align) + let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align, params) .unwrap_or_else(|| { panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes", size = info.size) }); @@ -158,8 +159,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } AllocKind::Function | AllocKind::VTable => { // Allocate some dummy memory to get a unique address for this function/vtable. - let alloc_bytes = - MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap()); + let alloc_bytes = MiriAllocBytes::from_bytes( + &[0u8; 1], + Align::from_bytes(1).unwrap(), + params, + ); let ptr = alloc_bytes.as_ptr(); // Leak the underlying memory to ensure it remains unique. std::mem::forget(alloc_bytes); @@ -168,7 +172,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { AllocKind::Dead => unreachable!(), }; // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`. - return interp_ok(base_ptr.addr().try_into().unwrap()); + return interp_ok(base_ptr.addr().to_u64()); } // We are not in native lib mode, so we control the addresses ourselves. if let Some((reuse_addr, clock)) = global_state.reuse.take_addr( @@ -429,7 +433,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { prepared_alloc_bytes.copy_from_slice(bytes); interp_ok(prepared_alloc_bytes) } else { - interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) + let params = this.machine.get_default_alloc_params(); + interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align, params)) } } diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs index 29d4f2bb7b0f..ab6aaed5e3e1 100644 --- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs +++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs @@ -4,6 +4,7 @@ use rand::Rng; use rustc_abi::{Align, Size}; use crate::concurrency::VClock; +use crate::helpers::ToUsize as _; use crate::{MemoryKind, MiriConfig, ThreadId}; const MAX_POOL_SIZE: usize = 64; @@ -46,7 +47,7 @@ impl ReusePool { } fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, ThreadId, VClock)> { - let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap(); + let pool_idx: usize = align.bytes().trailing_zeros().to_usize(); if self.pool.len() <= pool_idx { self.pool.resize(pool_idx + 1, Vec::new()); } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 469fc2649703..0121472d330f 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -281,7 +281,9 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { } let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); if codegen_fn_attrs.contains_extern_indicator() - || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED) + || codegen_fn_attrs + .flags + .contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { Some(( 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 f5a0013047aa..7b4c533cfaed 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -504,7 +504,7 @@ impl DisplayFmt { if let Some(perm) = perm { format!( "{ac}{st}", - ac = if perm.is_initialized() { self.accessed.yes } else { self.accessed.no }, + ac = if perm.is_accessed() { self.accessed.yes } else { self.accessed.no }, st = perm.permission().short_name(), ) } else { 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 f3e32e75f2f2..411ae89da906 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -3,6 +3,8 @@ use rustc_middle::mir::{Mutability, RetagKind}; use rustc_middle::ty::layout::HasTypingEnv; use rustc_middle::ty::{self, Ty}; +use self::foreign_access_skipping::IdempotentForeignAccess; +use self::tree::LocationState; use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}; use crate::concurrency::data_race::NaReadType; use crate::*; @@ -95,7 +97,7 @@ impl<'tcx> Tree { /// A tag just lost its protector. /// /// This emits a special kind of access that is only applied - /// to initialized locations, as a protection against other + /// to accessed locations, as a protection against other /// tags not having been made aware of the existence of this /// protector. pub fn release_protector( @@ -113,16 +115,19 @@ impl<'tcx> Tree { /// Policy for a new borrow. #[derive(Debug, Clone, Copy)] -struct NewPermission { - /// Which permission should the pointer start with. - initial_state: Permission, +pub struct NewPermission { + /// Permission for the frozen part of the range. + freeze_perm: Permission, + /// Whether a read access should be performed on the frozen part on a retag. + freeze_access: bool, + /// Permission for the non-frozen part of the range. + nonfreeze_perm: Permission, + /// Whether a read access should be performed on the non-frozen + /// part on a retag. + nonfreeze_access: bool, /// Whether this pointer is part of the arguments of a function call. /// `protector` is `Some(_)` for all pointers marked `noalias`. protector: Option, - /// Whether a read should be performed on a retag. This should be `false` - /// for `Cell` because this could cause data races when using thread-safe - /// data types like `Mutex`. - initial_read: bool, } impl<'tcx> NewPermission { @@ -133,27 +138,42 @@ impl<'tcx> NewPermission { kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>, ) -> Option { - let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env()); let is_protected = kind == RetagKind::FnEntry; - // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, - // interior mutability and protectors interact poorly. - // To eliminate the case of Protected Reserved IM we override interior mutability - // in the case of a protected reference: protected references are always considered - // "freeze" in their reservation phase. - let (initial_state, initial_read) = match mutability { - Mutability::Mut if ty_is_unpin => - (Permission::new_reserved(ty_is_freeze, is_protected), true), - Mutability::Not if ty_is_freeze => (Permission::new_frozen(), true), - Mutability::Not if !ty_is_freeze => (Permission::new_cell(), false), - // Raw pointers never enter this function so they are not handled. - // However raw pointers are not the only pointers that take the parent - // tag, this also happens for `!Unpin` `&mut`s, which are excluded above. - _ => return None, - }; - let protector = is_protected.then_some(ProtectorKind::StrongProtector); - Some(Self { initial_state, protector, initial_read }) + + Some(match mutability { + Mutability::Mut if ty_is_unpin => + NewPermission { + freeze_perm: Permission::new_reserved( + /* ty_is_freeze */ true, + is_protected, + ), + freeze_access: true, + nonfreeze_perm: Permission::new_reserved( + /* ty_is_freeze */ false, + is_protected, + ), + // If we have a mutable reference, then the non-frozen part will + // have state `ReservedIM` or `Reserved`, which can have an initial read access + // performed on it because you cannot have multiple mutable borrows. + nonfreeze_access: true, + protector, + }, + Mutability::Not => + NewPermission { + freeze_perm: Permission::new_frozen(), + freeze_access: true, + nonfreeze_perm: Permission::new_cell(), + // If it is a shared reference, then the non-frozen + // part will have state `Cell`, which should not have an initial access, + // as this can cause data races when using thread-safe data types like + // `Mutex`. + nonfreeze_access: false, + protector, + }, + _ => return None, + }) } /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled). @@ -168,13 +188,17 @@ impl<'tcx> NewPermission { pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| { // Regular `Unpin` box, give it `noalias` but only a weak protector // because it is valid to deallocate it within the function. - let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); - let protected = kind == RetagKind::FnEntry; - let initial_state = Permission::new_reserved(ty_is_freeze, protected); - Self { - initial_state, - protector: protected.then_some(ProtectorKind::WeakProtector), - initial_read: true, + let is_protected = kind == RetagKind::FnEntry; + let protector = is_protected.then_some(ProtectorKind::WeakProtector); + NewPermission { + freeze_perm: Permission::new_reserved(/* ty_is_freeze */ true, is_protected), + freeze_access: true, + nonfreeze_perm: Permission::new_reserved( + /* ty_is_freeze */ false, + is_protected, + ), + nonfreeze_access: true, + protector, } }) } @@ -194,8 +218,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { new_tag: BorTag, ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - // Make sure the new permission makes sense as the initial permission of a fresh tag. - assert!(new_perm.initial_state.is_initial()); // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050). this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?; @@ -206,7 +228,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let global = this.machine.borrow_tracker.as_ref().unwrap().borrow(); let ty = place.layout.ty; if global.tracked_pointer_tags.contains(&new_tag) { - let kind_str = format!("initial state {} (pointee type {ty})", new_perm.initial_state); + let ty_is_freeze = ty.is_freeze(*this.tcx, this.typing_env()); + let kind_str = + if ty_is_freeze { + format!("initial state {} (pointee type {ty})", new_perm.freeze_perm) + } else { + format!("initial state {}/{} outside/inside UnsafeCell (pointee type {ty})", new_perm.freeze_perm, new_perm.nonfreeze_perm) + }; this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag( new_tag.inner(), Some(kind_str), @@ -285,43 +313,103 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let span = this.machine.current_span(); let alloc_extra = this.get_alloc_extra(alloc_id)?; - let range = alloc_range(base_offset, ptr_size); let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut(); - // All reborrows incur a (possibly zero-sized) read access to the parent - if new_perm.initial_read { - tree_borrows.perform_access( - orig_tag, - Some((range, AccessKind::Read, diagnostics::AccessCause::Reborrow)), - this.machine.borrow_tracker.as_ref().unwrap(), - alloc_id, - this.machine.current_span(), - )?; - } + // Store initial permissions and their corresponding range. + let mut perms_map: RangeMap = RangeMap::new( + ptr_size, + LocationState::new_accessed(Permission::new_disabled(), IdempotentForeignAccess::None), // this will be overwritten + ); + // Keep track of whether the node has any part that allows for interior mutability. + // FIXME: This misses `PhantomData>` which could be considered a marker + // for requesting interior mutability. + let mut has_unsafe_cell = false; + + // When adding a new node, the SIFA of its parents needs to be updated, potentially across + // the entire memory range. For the parts that are being accessed below, the access itself + // trivially takes care of that. However, we have to do some more work to also deal with + // the parts that are not being accessed. Specifically what we do is that we + // call `update_last_accessed_after_retag` on the SIFA of the permission set for the part of + // memory outside `perm_map` -- so that part is definitely taken care of. The remaining concern + // is the part of memory that is in the range of `perms_map`, but not accessed below. + // There we have two cases: + // * If we do have an `UnsafeCell` (`has_unsafe_cell` becomes true), then the non-accessed part + // uses `nonfreeze_perm`, so the `nonfreeze_perm` initialized parts are also fine. We enforce + // the `freeze_perm` parts to be accessed, and thus everything is taken care of. + // * If there is no `UnsafeCell`, then `freeze_perm` is used everywhere (both inside and outside the initial range), + // and we update everything to have the `freeze_perm`'s SIFA, so there are no issues. (And this assert below is not + // actually needed in this case). + assert!(new_perm.freeze_access); + + let protected = new_perm.protector.is_some(); + this.visit_freeze_sensitive(place, ptr_size, |range, frozen| { + has_unsafe_cell = has_unsafe_cell || !frozen; + + // We are only ever `Frozen` inside the frozen bits. + let (perm, access) = if frozen { + (new_perm.freeze_perm, new_perm.freeze_access) + } else { + (new_perm.nonfreeze_perm, new_perm.nonfreeze_access) + }; + + // Store initial permissions. + for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) { + let sifa = perm.strongest_idempotent_foreign_access(protected); + // NOTE: Currently, `access` is false if and only if `perm` is Cell, so this `if` + // doesn't not change whether any code is UB or not. We could just always use + // `new_accessed` and everything would stay the same. But that seems conceptually + // odd, so we keep the initial "accessed" bit of the `LocationState` in sync with whether + // a read access is performed below. + if access { + *loc = LocationState::new_accessed(perm, sifa); + } else { + *loc = LocationState::new_non_accessed(perm, sifa); + } + } + + // Some reborrows incur a read access to the parent. + if access { + // Adjust range to be relative to allocation start (rather than to `place`). + let mut range_in_alloc = range; + range_in_alloc.start += base_offset; + + tree_borrows.perform_access( + orig_tag, + Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)), + this.machine.borrow_tracker.as_ref().unwrap(), + alloc_id, + this.machine.current_span(), + )?; + + // Also inform the data race model (but only if any bytes are actually affected). + if range.size.bytes() > 0 { + if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() { + data_race.read( + alloc_id, + range_in_alloc, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )? + } + } + } + interp_ok(()) + })?; + // Record the parent-child pair in the tree. tree_borrows.new_child( + base_offset, orig_tag, new_tag, - new_perm.initial_state, - range, + perms_map, + // Allow lazily writing to surrounding data if we found an `UnsafeCell`. + if has_unsafe_cell { new_perm.nonfreeze_perm } else { new_perm.freeze_perm }, + protected, span, - new_perm.protector.is_some(), )?; drop(tree_borrows); - // Also inform the data race model (but only if any bytes are actually affected). - if range.size.bytes() > 0 && new_perm.initial_read { - if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() { - data_race.read( - alloc_id, - range, - NaReadType::Retag, - Some(place.layout.ty), - &this.machine, - )?; - } - } - interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) } @@ -508,15 +596,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); - // Note: if we were to inline `new_reserved` below we would find out that - // `ty_is_freeze` is eventually unused because it appears in a `ty_is_freeze || true`. - // We are nevertheless including it here for clarity. - let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); // Retag it. With protection! That is the entire point. let new_perm = NewPermission { - initial_state: Permission::new_reserved(ty_is_freeze, /* protected */ true), + // Note: If we are creating a protected Reserved, which can + // never be ReservedIM, the value of the `ty_is_freeze` + // argument doesn't matter + // (`ty_is_freeze || true` in `new_reserved` will always be `true`). + freeze_perm: Permission::new_reserved( + /* ty_is_freeze */ true, /* protected */ true, + ), + freeze_access: true, + nonfreeze_perm: Permission::new_reserved( + /* ty_is_freeze */ false, /* protected */ true, + ), + nonfreeze_access: true, protector: Some(ProtectorKind::StrongProtector), - initial_read: true, }; this.tb_retag_place(place, new_perm) } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 087f6fc3f24b..38863ca0734a 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -94,6 +94,7 @@ impl PermissionPriv { } /// Reject `ReservedIM` that cannot exist in the presence of a protector. + #[cfg(test)] fn compatible_with_protector(&self) -> bool { // FIXME(TB-Cell): It is unclear what to do here. // `Cell` will occur with a protector but won't provide the guarantees @@ -253,10 +254,6 @@ impl Permission { pub fn is_disabled(&self) -> bool { self.inner == Disabled } - /// Check if `self` is the post-child-write state of a pointer (is `Active`). - pub fn is_active(&self) -> bool { - self.inner == Active - } /// Check if `self` is the never-allow-writes-again state of a pointer (is `Frozen`). pub fn is_frozen(&self) -> bool { self.inner == Frozen @@ -289,6 +286,11 @@ impl Permission { /// is a protector is relevant because being protected takes priority over being /// interior mutable) pub fn new_reserved(ty_is_freeze: bool, protected: bool) -> Self { + // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, + // interior mutability and protectors interact poorly. + // To eliminate the case of Protected Reserved IM we override interior mutability + // in the case of a protected reference: protected references are always considered + // "freeze" in their reservation phase. if ty_is_freeze || protected { Self::new_reserved_frz() } else { Self::new_reserved_im() } } @@ -309,6 +311,7 @@ impl Permission { } /// Reject `ReservedIM` that cannot exist in the presence of a protector. + #[cfg(test)] pub fn compatible_with_protector(&self) -> bool { self.inner.compatible_with_protector() } @@ -393,11 +396,6 @@ impl PermTransition { self.from <= self.to } - pub fn from(from: Permission, to: Permission) -> Option { - let t = Self { from: from.inner, to: to.inner }; - t.is_possible().then_some(t) - } - pub fn is_noop(self) -> bool { self.from == self.to } @@ -407,11 +405,6 @@ impl PermTransition { (starting_point.inner == self.from).then_some(Permission { inner: self.to }) } - /// Extract starting point of a transition - pub fn started(self) -> Permission { - Permission { inner: self.from } - } - /// Determines if this transition would disable the permission. pub fn produces_disabled(self) -> bool { self.to == Disabled 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 47ccaadbb9e3..48e4a19e263f 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -10,6 +10,7 @@ //! and the relative position of the access; //! - idempotency properties asserted in `perms.rs` (for optimizations) +use std::ops::Range; use std::{fmt, mem}; use rustc_abi::Size; @@ -32,18 +33,18 @@ mod tests; /// Data for a single *location*. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(super) struct LocationState { - /// A location is initialized when it is child-accessed for the first time (and the initial + /// A location is "accessed" when it is child-accessed for the first time (and the initial /// retag initializes the location for the range covered by the type), and it then stays - /// initialized forever. - /// For initialized locations, "permission" is the current permission. However, for - /// uninitialized locations, we still need to track the "future initial permission": this will + /// accessed forever. + /// For accessed locations, "permission" is the current permission. However, for + /// non-accessed locations, we still need to track the "future initial permission": this will /// start out to be `default_initial_perm`, but foreign accesses need to be taken into account. /// Crucially however, while transitions to `Disabled` would usually be UB if this location is - /// protected, that is *not* the case for uninitialized locations. Instead we just have a latent + /// protected, that is *not* the case for non-accessed locations. Instead we just have a latent /// "future initial permission" of `Disabled`, causing UB only if an access is ever actually /// performed. - /// Note that the tree root is also always initialized, as if the allocation was a write access. - initialized: bool, + /// Note that the tree root is also always accessed, as if the allocation was a write access. + accessed: bool, /// This pointer's current permission / future initial permission. permission: Permission, /// See `foreign_access_skipping.rs`. @@ -58,30 +59,30 @@ impl LocationState { /// to any foreign access yet. /// The permission is not allowed to be `Active`. /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs` - fn new_uninit(permission: Permission, sifa: IdempotentForeignAccess) -> Self { + pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self { assert!(permission.is_initial() || permission.is_disabled()); - Self { permission, initialized: false, idempotent_foreign_access: sifa } + Self { permission, accessed: false, idempotent_foreign_access: sifa } } /// Constructs a new initial state. It has not yet been subjected /// to any foreign access. However, it is already marked as having been accessed. /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs` - fn new_init(permission: Permission, sifa: IdempotentForeignAccess) -> Self { - Self { permission, initialized: true, idempotent_foreign_access: sifa } + pub fn new_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self { + Self { permission, accessed: true, idempotent_foreign_access: sifa } } - /// Check if the location has been initialized, i.e. if it has + /// Check if the location has been accessed, i.e. if it has /// ever been accessed through a child pointer. - pub fn is_initialized(&self) -> bool { - self.initialized + pub fn is_accessed(&self) -> bool { + self.accessed } /// Check if the state can exist as the initial permission of a pointer. /// - /// Do not confuse with `is_initialized`, the two are almost orthogonal - /// as apart from `Active` which is not initial and must be initialized, + /// Do not confuse with `is_accessed`, the two are almost orthogonal + /// as apart from `Active` which is not initial and must be accessed, /// any other permission can have an arbitrary combination of being - /// initial/initialized. + /// initial/accessed. /// FIXME: when the corresponding `assert` in `tree_borrows/mod.rs` finally /// passes and can be uncommented, remove this `#[allow(dead_code)]`. #[cfg_attr(not(test), allow(dead_code))] @@ -95,8 +96,8 @@ impl LocationState { /// Apply the effect of an access to one location, including /// - applying `Permission::perform_access` to the inner `Permission`, - /// - emitting protector UB if the location is initialized, - /// - updating the initialized status (child accesses produce initialized locations). + /// - emitting protector UB if the location is accessed, + /// - updating the accessed status (child accesses produce accessed locations). fn perform_access( &mut self, access_kind: AccessKind, @@ -106,14 +107,14 @@ impl LocationState { let old_perm = self.permission; let transition = Permission::perform_access(access_kind, rel_pos, old_perm, protected) .ok_or(TransitionError::ChildAccessForbidden(old_perm))?; - self.initialized |= !rel_pos.is_foreign(); + self.accessed |= !rel_pos.is_foreign(); self.permission = transition.applied(old_perm).unwrap(); - // Why do only initialized locations cause protector errors? + // Why do only accessed locations cause protector errors? // Consider two mutable references `x`, `y` into disjoint parts of // the same allocation. A priori, these may actually both be used to // access the entire allocation, as long as only reads occur. However, // a write to `y` needs to somehow record that `x` can no longer be used - // on that location at all. For these uninitialized locations (i.e., locations + // on that location at all. For these non-accessed locations (i.e., locations // that haven't been accessed with `x` yet), we track the "future initial state": // it defaults to whatever the initial state of the tag is, // but the access to `y` moves that "future initial state" of `x` to `Disabled`. @@ -121,8 +122,8 @@ impl LocationState { // So clearly protectors shouldn't fire for such "future initial state" transitions. // // See the test `two_mut_protected_same_alloc` in `tests/pass/tree_borrows/tree-borrows.rs` - // for an example of safe code that would be UB if we forgot to check `self.initialized`. - if protected && self.initialized && transition.produces_disabled() { + // for an example of safe code that would be UB if we forgot to check `self.accessed`. + if protected && self.accessed && transition.produces_disabled() { return Err(TransitionError::ProtectedDisabled(old_perm)); } Ok(transition) @@ -157,11 +158,11 @@ impl LocationState { self.idempotent_foreign_access.can_skip_foreign_access(happening_now); if self.permission.is_disabled() { // A foreign access to a `Disabled` tag will have almost no observable effect. - // It's a theorem that `Disabled` node have no protected initialized children, + // It's a theorem that `Disabled` node have no protected accessed children, // and so this foreign access will never trigger any protector. - // (Intuition: You're either protected initialized, and thus can't become Disabled - // or you're already Disabled protected, but not initialized, and then can't - // become initialized since that requires a child access, which Disabled blocks.) + // (Intuition: You're either protected accessed, and thus can't become Disabled + // or you're already Disabled protected, but not accessed, and then can't + // become accessed since that requires a child access, which Disabled blocks.) // Further, the children will never be able to read or write again, since they // have a `Disabled` parent. So this only affects diagnostics, such that the // blocking write will still be identified directly, just at a different tag. @@ -217,7 +218,7 @@ impl LocationState { impl fmt::Display for LocationState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.permission)?; - if !self.initialized { + if !self.accessed { write!(f, "?")?; } Ok(()) @@ -598,12 +599,15 @@ impl Tree { let rperms = { let mut perms = UniValMap::default(); // We manually set it to `Active` on all in-bounds positions. - // We also ensure that it is initialized, so that no `Active` but - // not yet initialized nodes exist. Essentially, we pretend there + // We also ensure that it is accessed, so that no `Active` but + // not yet accessed nodes exist. Essentially, we pretend there // was a write that initialized these to `Active`. perms.insert( root_idx, - LocationState::new_init(Permission::new_active(), IdempotentForeignAccess::None), + LocationState::new_accessed( + Permission::new_active(), + IdempotentForeignAccess::None, + ), ); RangeMap::new(size, perms) }; @@ -612,20 +616,32 @@ impl Tree { } impl<'tcx> Tree { - /// Insert a new tag in the tree - pub fn new_child( + /// Insert a new tag in the tree. + /// + /// `initial_perms` defines the initial permissions for the part of memory + /// that is already considered "initialized" immediately. The ranges in this + /// map are relative to `base_offset`. + /// `default_perm` defines the initial permission for the rest of the allocation. + /// + /// For all non-accessed locations in the RangeMap (those that haven't had an + /// implicit read), their SIFA must be weaker than or as weak as the SIFA of + /// `default_perm`. + pub(super) fn new_child( &mut self, + base_offset: Size, parent_tag: BorTag, new_tag: BorTag, - default_initial_perm: Permission, - reborrow_range: AllocRange, + initial_perms: RangeMap, + default_perm: Permission, + protected: bool, span: Span, - prot: bool, ) -> InterpResult<'tcx> { - assert!(!self.tag_mapping.contains_key(&new_tag)); let idx = self.tag_mapping.insert(new_tag); let parent_idx = self.tag_mapping.get(&parent_tag).unwrap(); - let strongest_idempotent = default_initial_perm.strongest_idempotent_foreign_access(prot); + assert!(default_perm.is_initial()); + + let default_strongest_idempotent = + default_perm.strongest_idempotent_foreign_access(protected); // Create the node self.nodes.insert( idx, @@ -633,25 +649,36 @@ impl<'tcx> Tree { tag: new_tag, parent: Some(parent_idx), children: SmallVec::default(), - default_initial_perm, - default_initial_idempotent_foreign_access: strongest_idempotent, - debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span), + default_initial_perm: default_perm, + default_initial_idempotent_foreign_access: default_strongest_idempotent, + debug_info: NodeDebugInfo::new(new_tag, default_perm, span), }, ); // Register new_tag as a child of parent_tag self.nodes.get_mut(parent_idx).unwrap().children.push(idx); - // Initialize perms - let perm = LocationState::new_init(default_initial_perm, strongest_idempotent); - for (_perms_range, perms) in self.rperms.iter_mut(reborrow_range.start, reborrow_range.size) + + for (Range { start, end }, &perm) in + initial_perms.iter(Size::from_bytes(0), initial_perms.size()) { - perms.insert(idx, perm); + assert!(perm.is_initial()); + for (_perms_range, perms) in self + .rperms + .iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start)) + { + assert!( + default_strongest_idempotent + >= perm.permission.strongest_idempotent_foreign_access(protected) + ); + perms.insert(idx, perm); + } } // Inserting the new perms might have broken the SIFA invariant (see `foreign_access_skipping.rs`). // We now weaken the recorded SIFA for our parents, until the invariant is restored. // We could weaken them all to `LocalAccess`, but it is more efficient to compute the SIFA // for the new permission statically, and use that. - self.update_last_accessed_after_retag(parent_idx, strongest_idempotent); + // See the comment in `tb_reborrow` for why it is correct to use the SIFA of `default_uninit_perm`. + self.update_last_accessed_after_retag(parent_idx, default_strongest_idempotent); interp_ok(()) } @@ -758,14 +785,14 @@ impl<'tcx> Tree { /// /// If `access_range_and_kind` is `None`, this is interpreted as the special /// access that is applied on protector release: - /// - the access will be applied only to initialized locations of the allocation, + /// - the access will be applied only to accessed locations of the allocation, /// - it will not be visible to children, /// - it will be recorded as a `FnExit` diagnostic access /// - and it will be a read except if the location is `Active`, i.e. has been written to, /// in which case it will be a write. /// /// `LocationState::perform_access` will take care of raising transition - /// errors and updating the `initialized` status of each location, + /// errors and updating the `accessed` status of each location, /// this traversal adds to that: /// - inserting into the map locations that do not exist yet, /// - trimming the traversal, @@ -858,7 +885,7 @@ impl<'tcx> Tree { } } else { // This is a special access through the entire allocation. - // It actually only affects `initialized` locations, so we need + // It actually only affects `accessed` locations, so we need // to filter on those before initiating the traversal. // // In addition this implicit access should not be visible to children, @@ -868,10 +895,10 @@ impl<'tcx> Tree { // why this is important. for (perms_range, perms) in self.rperms.iter_mut_all() { let idx = self.tag_mapping.get(&tag).unwrap(); - // Only visit initialized permissions + // Only visit accessed permissions if let Some(p) = perms.get(idx) && let Some(access_kind) = p.permission.protector_end_access() - && p.initialized + && p.accessed { let access_cause = diagnostics::AccessCause::FnExit(access_kind); TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms } @@ -1035,7 +1062,7 @@ impl Tree { impl Node { pub fn default_location_state(&self) -> LocationState { - LocationState::new_uninit( + LocationState::new_non_accessed( self.default_initial_perm, self.default_initial_idempotent_foreign_access, ) @@ -1073,15 +1100,4 @@ impl AccessRelatedness { pub fn is_foreign(self) -> bool { matches!(self, AccessRelatedness::AncestorAccess | AccessRelatedness::CousinAccess) } - - /// Given the AccessRelatedness for the parent node, compute the AccessRelatedness - /// for the child node. This function assumes that we propagate away from the initial - /// access. - pub fn for_child(self) -> Self { - use AccessRelatedness::*; - match self { - AncestorAccess | This => AncestorAccess, - StrictChildAccess | CousinAccess => CousinAccess, - } - } } 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 dbfa9807e3b5..bb3fc2d80b3f 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 @@ -9,10 +9,10 @@ use crate::borrow_tracker::tree_borrows::exhaustive::{Exhaustive, precondition}; impl Exhaustive for LocationState { fn exhaustive() -> Box> { // We keep `latest_foreign_access` at `None` as that's just a cache. - Box::new(<(Permission, bool)>::exhaustive().map(|(permission, initialized)| { + Box::new(<(Permission, bool)>::exhaustive().map(|(permission, accessed)| { Self { permission, - initialized, + accessed, idempotent_foreign_access: IdempotentForeignAccess::default(), } })) @@ -76,8 +76,8 @@ fn as_protected(b: bool) -> &'static str { if b { " (protected)" } else { "" } } -fn as_lazy_or_init(b: bool) -> &'static str { - if b { "initialized" } else { "lazy" } +fn as_lazy_or_accessed(b: bool) -> &'static str { + if b { "accessed" } else { "lazy" } } /// Test that tree compacting (as performed by the GC) is sound. @@ -106,7 +106,7 @@ fn tree_compacting_is_sound() { as_foreign_or_child(rel), kind, parent.permission(), - as_lazy_or_init(child.is_initialized()), + as_lazy_or_accessed(child.is_accessed()), child.permission(), as_protected(child_protected), np.permission(), @@ -122,7 +122,7 @@ fn tree_compacting_is_sound() { as_foreign_or_child(rel), kind, parent.permission(), - as_lazy_or_init(child.is_initialized()), + as_lazy_or_accessed(child.is_accessed()), child.permission(), as_protected(child_protected), nc.permission() @@ -435,19 +435,19 @@ mod spurious_read { Ok(Self { x, y, ..self }) } - /// Perform a read on the given pointer if its state is `initialized`. + /// Perform a read on the given pointer if its state is `accessed`. /// Must be called just after reborrowing a pointer, and just after /// removing a protector. - fn read_if_initialized(self, ptr: PtrSelector) -> Result { - let initialized = match ptr { - PtrSelector::X => self.x.state.initialized, - PtrSelector::Y => self.y.state.initialized, + fn read_if_accessed(self, ptr: PtrSelector) -> Result { + let accessed = match ptr { + PtrSelector::X => self.x.state.accessed, + PtrSelector::Y => self.y.state.accessed, PtrSelector::Other => panic!( - "the `initialized` status of `PtrSelector::Other` is unknown, do not pass it to `read_if_initialized`" + "the `accessed` status of `PtrSelector::Other` is unknown, do not pass it to `read_if_accessed`" ), }; - if initialized { + if accessed { self.perform_test_access(&TestAccess { ptr, kind: AccessKind::Read }) } else { Ok(self) @@ -457,13 +457,13 @@ mod spurious_read { /// Remove the protector of `x`, including the implicit read on function exit. fn end_protector_x(self) -> Result { let x = self.x.end_protector(); - Self { x, ..self }.read_if_initialized(PtrSelector::X) + Self { x, ..self }.read_if_accessed(PtrSelector::X) } /// Remove the protector of `y`, including the implicit read on function exit. fn end_protector_y(self) -> Result { let y = self.y.end_protector(); - Self { y, ..self }.read_if_initialized(PtrSelector::Y) + Self { y, ..self }.read_if_accessed(PtrSelector::Y) } fn retag_y(self, new_y: LocStateProt) -> Result { @@ -473,7 +473,7 @@ mod spurious_read { } // `xy_rel` changes to "mutually foreign" now: `y` can no longer be a parent of `x`. Self { y: new_y, xy_rel: RelPosXY::MutuallyForeign, ..self } - .read_if_initialized(PtrSelector::Y) + .read_if_accessed(PtrSelector::Y) } fn perform_test_event(self, evt: &TestEvent) -> Result { @@ -602,14 +602,14 @@ mod spurious_read { xy_rel: RelPosXY::MutuallyForeign, x: LocStateProt { // For the tests, the strongest idempotent foreign access does not matter, so we use `Default::default` - state: LocationState::new_init( + state: LocationState::new_accessed( Permission::new_frozen(), IdempotentForeignAccess::default(), ), prot: true, }, y: LocStateProt { - state: LocationState::new_uninit( + state: LocationState::new_non_accessed( Permission::new_reserved(/* freeze */ true, /* protected */ true), IdempotentForeignAccess::default(), ), @@ -650,8 +650,8 @@ mod spurious_read { for xy_rel in RelPosXY::exhaustive() { for (x_retag_perm, y_current_perm) in <(LocationState, LocationState)>::exhaustive() { - // We can only do spurious reads for initialized locations anyway. - precondition!(x_retag_perm.initialized); + // We can only do spurious reads for accessed locations anyway. + precondition!(x_retag_perm.accessed); // And `x` just got retagged, so it must be initial. precondition!(x_retag_perm.permission.is_initial()); // As stated earlier, `x` is always protected in the patterns we consider here. @@ -696,7 +696,7 @@ mod spurious_read { fn initial_state(&self) -> Result { let (x, y) = self.retag_permissions(); let state = LocStateProtPair { xy_rel: self.xy_rel, x, y }; - state.read_if_initialized(PtrSelector::X) + state.read_if_accessed(PtrSelector::X) } } 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 7874721c0ac3..dcd5a6cb0230 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs @@ -17,6 +17,8 @@ use std::mem; use rustc_data_structures::fx::FxHashMap; +use crate::helpers::ToUsize; + /// Intermediate key between a UniKeyMap and a UniValMap. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct UniIndex { @@ -158,7 +160,7 @@ where impl UniValMap { /// Whether this index has an associated value. pub fn contains_idx(&self, idx: UniIndex) -> bool { - self.data.get(idx.idx as usize).and_then(Option::as_ref).is_some() + self.data.get(idx.idx.to_usize()).and_then(Option::as_ref).is_some() } /// Reserve enough space to insert the value at the right index. @@ -174,29 +176,29 @@ impl UniValMap { /// Assign a value to the index. Permanently overwrites any previous value. pub fn insert(&mut self, idx: UniIndex, val: V) { - self.extend_to_length(idx.idx as usize + 1); - self.data[idx.idx as usize] = Some(val) + self.extend_to_length(idx.idx.to_usize() + 1); + self.data[idx.idx.to_usize()] = Some(val) } /// Get the value at this index, if it exists. pub fn get(&self, idx: UniIndex) -> Option<&V> { - self.data.get(idx.idx as usize).and_then(Option::as_ref) + self.data.get(idx.idx.to_usize()).and_then(Option::as_ref) } /// Get the value at this index mutably, if it exists. pub fn get_mut(&mut self, idx: UniIndex) -> Option<&mut V> { - self.data.get_mut(idx.idx as usize).and_then(Option::as_mut) + self.data.get_mut(idx.idx.to_usize()).and_then(Option::as_mut) } /// Delete any value associated with this index. /// Returns None if the value was not present, otherwise /// returns the previously stored value. pub fn remove(&mut self, idx: UniIndex) -> Option { - if idx.idx as usize >= self.data.len() { + if idx.idx.to_usize() >= self.data.len() { return None; } let mut res = None; - mem::swap(&mut res, &mut self.data[idx.idx as usize]); + mem::swap(&mut res, &mut self.data[idx.idx.to_usize()]); res } } @@ -209,8 +211,8 @@ pub struct UniEntry<'a, V> { impl<'a, V> UniValMap { /// Get a wrapper around a mutable access to the value corresponding to `idx`. pub fn entry(&'a mut self, idx: UniIndex) -> UniEntry<'a, V> { - self.extend_to_length(idx.idx as usize + 1); - UniEntry { inner: &mut self.data[idx.idx as usize] } + self.extend_to_length(idx.idx.to_usize() + 1); + UniEntry { inner: &mut self.data[idx.idx.to_usize()] } } } diff --git a/src/tools/miri/src/concurrency/cpu_affinity.rs b/src/tools/miri/src/concurrency/cpu_affinity.rs index b47b614cf5f0..9583de5a4838 100644 --- a/src/tools/miri/src/concurrency/cpu_affinity.rs +++ b/src/tools/miri/src/concurrency/cpu_affinity.rs @@ -25,7 +25,7 @@ impl CpuAffinityMask { let mut this = Self([0; Self::CPU_MASK_BYTES]); // the default affinity mask includes only the available CPUs - for i in 0..cpu_count as usize { + for i in 0..cpu_count.to_usize() { this.set(cx, i); } diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index dd33f90f153d..17d0f3f5ff67 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -9,7 +9,7 @@ mod vector_clock; pub mod weak_memory; // Import either the real genmc adapter or a dummy module. -cfg_match! { +cfg_select! { feature = "genmc" => { mod genmc; pub use self::genmc::{GenmcCtx, GenmcConfig}; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 8aa65e6cb612..ba1436b77b8c 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -218,34 +218,37 @@ impl<'tcx> Thread<'tcx> { } } - /// Return the top user-relevant frame, if there is one. + /// Return the top user-relevant frame, if there is one. `skip` indicates how many top frames + /// should be skipped. /// Note that the choice to return `None` here when there is no user-relevant frame is part of /// justifying the optimization that only pushes of user-relevant frames require updating the /// `top_user_relevant_frame` field. - fn compute_top_user_relevant_frame(&self) -> Option { + fn compute_top_user_relevant_frame(&self, skip: usize) -> Option { self.stack .iter() .enumerate() .rev() + .skip(skip) .find_map(|(idx, frame)| if frame.extra.is_user_relevant { Some(idx) } else { None }) } - /// Re-compute the top user-relevant frame from scratch. - pub fn recompute_top_user_relevant_frame(&mut self) { - self.top_user_relevant_frame = self.compute_top_user_relevant_frame(); + /// Re-compute the top user-relevant frame from scratch. `skip` indicates how many top frames + /// should be skipped. + pub fn recompute_top_user_relevant_frame(&mut self, skip: usize) { + self.top_user_relevant_frame = self.compute_top_user_relevant_frame(skip); } /// Set the top user-relevant frame to the given value. Must be equal to what /// `get_top_user_relevant_frame` would return! pub fn set_top_user_relevant_frame(&mut self, frame_idx: usize) { - debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame()); + debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame(0)); self.top_user_relevant_frame = Some(frame_idx); } /// Returns the topmost frame that is considered user-relevant, or the /// top of the stack if there is no such frame, or `None` if the stack is empty. pub fn top_user_relevant_frame(&self) -> Option { - debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame()); + debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame(0)); // This can be called upon creation of an allocation. We create allocations while setting up // parts of the Rust runtime when we do not have any stack frames yet, so we need to handle // empty stacks. @@ -894,12 +897,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if tcx.is_foreign_item(def_id) { throw_unsup_format!("foreign thread-local statics are not supported"); } + let params = this.machine.get_default_alloc_params(); let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; // We make a full copy of this allocation. let mut alloc = alloc.inner().adjust_from_tcx( &this.tcx, |bytes, align| { - interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)) + interp_ok(MiriAllocBytes::from_bytes( + std::borrow::Cow::Borrowed(bytes), + align, + params, + )) }, |ptr| this.global_root_pointer(ptr), )?; diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs index 78858fcedaec..494e7922d2b2 100644 --- a/src/tools/miri/src/concurrency/vector_clock.rs +++ b/src/tools/miri/src/concurrency/vector_clock.rs @@ -7,6 +7,7 @@ use rustc_span::{DUMMY_SP, Span, SpanData}; use smallvec::SmallVec; use super::data_race::NaReadType; +use crate::helpers::ToUsize; /// A vector clock index, this is associated with a thread id /// but in some cases one vector index may be shared with @@ -157,7 +158,7 @@ impl VClock { #[inline] pub(super) fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp { - self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap() + self.0.as_mut_slice().get_mut(index.to_u32().to_usize()).unwrap() } /// Get a mutable slice to the internal vector with minimum `min_len` @@ -420,7 +421,7 @@ impl Index for VClock { #[inline] fn index(&self, index: VectorIdx) -> &VTimestamp { - self.as_slice().get(index.to_u32() as usize).unwrap_or(&VTimestamp::ZERO) + self.as_slice().get(index.to_u32().to_usize()).unwrap_or(&VTimestamp::ZERO) } } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 10570a37e5d8..1728a9cfd6de 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -255,8 +255,7 @@ pub fn report_error<'tcx>( ], UnsupportedForeignItem(_) => { vec![ - note!("if this is a basic API commonly used on this target, please report an issue with Miri"), - note!("however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases"), + note!("this means the program tried to do something Miri does not support; it does not indicate a bug in the program"), ] } StackedBorrowsUb { help, history, .. } => { diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index a90c6ab9d40e..8fe034d25829 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -354,11 +354,10 @@ pub fn create_ecx<'tcx>( argvs.push(arg_place.to_ref(&ecx)); } // Make an array with all these pointers, in the Miri memory. - let argvs_layout = ecx.layout_of(Ty::new_array( - tcx, - Ty::new_imm_ptr(tcx, tcx.types.u8), - u64::try_from(argvs.len()).unwrap(), - ))?; + let u8_ptr_type = Ty::new_imm_ptr(tcx, tcx.types.u8); + let u8_ptr_ptr_type = Ty::new_imm_ptr(tcx, u8_ptr_type); + let argvs_layout = + ecx.layout_of(Ty::new_array(tcx, u8_ptr_type, u64::try_from(argvs.len()).unwrap()))?; let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; for (idx, arg) in argvs.into_iter().enumerate() { let place = ecx.project_field(&argvs_place, idx)?; @@ -373,10 +372,8 @@ pub fn create_ecx<'tcx>( ecx.mark_immutable(&argc_place); ecx.machine.argc = Some(argc_place.ptr()); - let argv_place = ecx.allocate( - ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?, - MiriMemoryKind::Machine.into(), - )?; + let argv_place = + ecx.allocate(ecx.layout_of(u8_ptr_ptr_type)?, MiriMemoryKind::Machine.into())?; ecx.write_pointer(argvs_place.ptr(), &argv_place)?; ecx.mark_immutable(&argv_place); ecx.machine.argv = Some(argv_place.ptr()); @@ -398,7 +395,9 @@ pub fn create_ecx<'tcx>( } ecx.mark_immutable(&cmd_place); } - ecx.mplace_to_ref(&argvs_place)? + let imm = argvs_place.to_ref(&ecx); + let layout = ecx.layout_of(u8_ptr_ptr_type)?; + ImmTy::from_immediate(imm, layout) }; // Return place (in static memory so that it does not count as leak). diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 8e7c9edfcc07..dcc74b099d62 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -135,7 +135,7 @@ pub fn iter_exported_symbols<'tcx>( let codegen_attrs = tcx.codegen_fn_attrs(def_id); codegen_attrs.contains_extern_indicator() || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED) + || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) }; if exported { @@ -470,7 +470,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { caller_fn_abi, &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::>(), /*with_caller_location*/ false, - &dest, + &dest.into(), stack_pop, ) } @@ -1412,3 +1412,26 @@ pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 { u32::try_from(len).unwrap() } } + +/// We don't support 16-bit systems, so let's have ergonomic conversion from `u32` to `usize`. +pub trait ToUsize { + fn to_usize(self) -> usize; +} + +impl ToUsize for u32 { + fn to_usize(self) -> usize { + self.try_into().unwrap() + } +} + +/// Similarly, a maximum address size of `u64` is assumed widely here, so let's have ergonomic +/// converion from `usize` to `u64`. +pub trait ToU64 { + fn to_u64(self) -> u64; +} + +impl ToU64 for usize { + fn to_u64(self) -> u64 { + self.try_into().unwrap() + } +} diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index 2eb8086f578f..a61226eeed9e 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::BinOp; +use rustc_middle::ty::AtomicOrdering; use rustc_middle::{mir, ty}; use self::helpers::check_intrinsic_arg_count; @@ -19,6 +20,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_atomic_intrinsic( &mut self, intrinsic_name: &str, + generic_args: ty::GenericArgsRef<'tcx>, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { @@ -35,6 +37,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + fn read_ord_const_generic(o: AtomicOrdering) -> AtomicReadOrd { + match o { + AtomicOrdering::SeqCst => AtomicReadOrd::SeqCst, + AtomicOrdering::Acquire => AtomicReadOrd::Acquire, + AtomicOrdering::Relaxed => AtomicReadOrd::Relaxed, + _ => panic!("invalid read ordering `{o:?}`"), + } + } + fn write_ord(ord: &str) -> AtomicWriteOrd { match ord { "seqcst" => AtomicWriteOrd::SeqCst, @@ -66,7 +77,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } match &*intrinsic_structure { - ["load", ord] => this.atomic_load(args, dest, read_ord(ord))?, + // New-style intrinsics that use const generics + ["load"] => { + let ordering = generic_args.const_at(1).to_value(); + let ordering = + ordering.valtree.unwrap_branch()[0].unwrap_leaf().to_atomic_ordering(); + this.atomic_load(args, dest, read_ord_const_generic(ordering))?; + } + + // Old-style intrinsics that have the ordering in the intrinsic name ["store", ord] => this.atomic_store(args, write_ord(ord))?, ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord))?, diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 982fbc318110..a4882a201481 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -22,7 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { @@ -45,7 +45,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let intrinsic_name = this.tcx.item_name(instance.def_id()); let intrinsic_name = intrinsic_name.as_str(); - match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest, ret)? { + // FIXME: avoid allocating memory + let dest = this.force_allocation(dest)?; + + match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, &dest, ret)? { EmulateItemResult::NotSupported => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { @@ -94,7 +97,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); if let Some(name) = intrinsic_name.strip_prefix("atomic_") { - return this.emulate_atomic_intrinsic(name, args, dest); + return this.emulate_atomic_intrinsic(name, generic_args, args, dest); } if let Some(name) = intrinsic_name.strip_prefix("simd_") { return this.emulate_simd_intrinsic(name, generic_args, args, dest); @@ -156,67 +159,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_bool(branch), dest)?; } - "floorf16" | "ceilf16" | "truncf16" | "roundf16" | "round_ties_even_f16" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f16()?; - let mode = match intrinsic_name { - "floorf16" => Round::TowardNegative, - "ceilf16" => Round::TowardPositive, - "truncf16" => Round::TowardZero, - "roundf16" => Round::NearestTiesToAway, - "round_ties_even_f16" => Round::NearestTiesToEven, - _ => bug!(), - }; - let res = f.round_to_integral(mode).value; - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "round_ties_even_f32" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - let mode = match intrinsic_name { - "floorf32" => Round::TowardNegative, - "ceilf32" => Round::TowardPositive, - "truncf32" => Round::TowardZero, - "roundf32" => Round::NearestTiesToAway, - "round_ties_even_f32" => Round::NearestTiesToEven, - _ => bug!(), - }; - let res = f.round_to_integral(mode).value; - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "round_ties_even_f64" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - let mode = match intrinsic_name { - "floorf64" => Round::TowardNegative, - "ceilf64" => Round::TowardPositive, - "truncf64" => Round::TowardZero, - "roundf64" => Round::NearestTiesToAway, - "round_ties_even_f64" => Round::NearestTiesToEven, - _ => bug!(), - }; - let res = f.round_to_integral(mode).value; - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "floorf128" | "ceilf128" | "truncf128" | "roundf128" | "round_ties_even_f128" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f128()?; - let mode = match intrinsic_name { - "floorf128" => Round::TowardNegative, - "ceilf128" => Round::TowardPositive, - "truncf128" => Round::TowardZero, - "roundf128" => Round::NearestTiesToAway, - "round_ties_even_f128" => Round::NearestTiesToEven, - _ => bug!(), - }; - let res = f.round_to_integral(mode).value; - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf32" => { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index c9250ba1b818..b17fd4fb7f93 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -634,7 +634,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let index_len = index.len(); assert_eq!(left_len, right_len); - assert_eq!(index_len as u64, dest_len); + assert_eq!(u64::try_from(index_len).unwrap(), dest_len); for i in 0..dest_len { let src_index: u64 = diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 0b7a067058b9..8802216448f6 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,5 +1,5 @@ #![feature(rustc_private)] -#![feature(cfg_match)] +#![feature(cfg_select)] #![feature(float_gamma)] #![feature(float_erf)] #![feature(map_try_insert)] @@ -11,6 +11,7 @@ #![feature(nonzero_ops)] #![feature(strict_overflow_ops)] #![feature(pointer_is_aligned_to)] +#![feature(ptr_metadata)] #![feature(unqualified_local_imports)] #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] @@ -41,14 +42,7 @@ rustc::potential_query_instability, rustc::untranslatable_diagnostic, )] -#![warn( - rust_2018_idioms, - unqualified_local_imports, - clippy::cast_possible_wrap, // unsigned -> signed - clippy::cast_sign_loss, // signed -> unsigned - clippy::cast_lossless, - clippy::cast_possible_truncation, -)] +#![warn(rust_2018_idioms, unqualified_local_imports, clippy::as_conversions)] // Needed for rustdoc from bootstrap (with `-Znormalize-docs`). #![recursion_limit = "256"] @@ -60,7 +54,7 @@ extern crate tracing; extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; -extern crate rustc_attr_parsing; +extern crate rustc_attr_data_structures; extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; @@ -76,8 +70,8 @@ extern crate rustc_target; #[allow(unused_extern_crates)] extern crate rustc_driver; +mod alloc; mod alloc_addresses; -mod alloc_bytes; mod borrow_tracker; mod clock; mod concurrency; @@ -112,8 +106,8 @@ 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::alloc::MiriAllocBytes; pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode}; -pub use crate::alloc_bytes::MiriAllocBytes; pub use crate::borrow_tracker::stacked_borrows::{ EvalContextExt as _, Item, Permission, Stack, Stacks, }; @@ -140,7 +134,7 @@ pub use crate::eval::{ AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, MiriEntryFnType, RejectOpWith, ValidationMode, create_ecx, eval_entry, }; -pub use crate::helpers::{AccessKind, EvalContextExt as _}; +pub use crate::helpers::{AccessKind, EvalContextExt as _, ToU64 as _, ToUsize as _}; pub use crate::intrinsics::EvalContextExt as _; pub use crate::machine::{ AllocExtra, DynMachineCallback, FrameExtra, MachineCallback, MemoryKind, MiriInterpCx, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index dbde415170c3..15b3653d7aef 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -13,7 +13,7 @@ use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use rustc_abi::{Align, ExternAbi, Size}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; @@ -532,6 +532,10 @@ pub struct MiriMachine<'tcx> { /// Needs to be queried by ptr_to_int, hence needs interior mutability. pub(crate) rng: RefCell, + /// The allocator used for the machine's `AllocBytes` in native-libs mode. + #[cfg(target_os = "linux")] + pub(crate) allocator: Option>>, + /// The allocation IDs to report when they are being allocated /// (helps for debugging memory leaks and use after free bugs). tracked_alloc_ids: FxHashSet, @@ -544,9 +548,6 @@ pub struct MiriMachine<'tcx> { /// Failure rate of compare_exchange_weak, between 0.0 and 1.0 pub(crate) cmpxchg_weak_failure_rate: f64, - /// Corresponds to -Zmiri-mute-stdout-stderr and doesn't write the output but acts as if it succeeded. - pub(crate) mute_stdout_stderr: bool, - /// The probability of the active thread being preempted at the end of each basic block. pub(crate) preemption_rate: f64, @@ -718,11 +719,14 @@ impl<'tcx> MiriMachine<'tcx> { local_crates, extern_statics: FxHashMap::default(), rng: RefCell::new(rng), + #[cfg(target_os = "linux")] + allocator: if config.native_lib.is_some() { + Some(Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))) + } else { None }, tracked_alloc_ids: config.tracked_alloc_ids.clone(), track_alloc_accesses: config.track_alloc_accesses, check_alignment: config.check_alignment, cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate, - mute_stdout_stderr: config.mute_stdout_stderr, preemption_rate: config.preemption_rate, report_progress: config.report_progress, basic_block_count: 0, @@ -921,11 +925,12 @@ impl VisitProvenance for MiriMachine<'_> { backtrace_style: _, local_crates: _, rng: _, + #[cfg(target_os = "linux")] + allocator: _, tracked_alloc_ids: _, track_alloc_accesses: _, check_alignment: _, cmpxchg_weak_failure_rate: _, - mute_stdout_stderr: _, preemption_rate: _, report_progress: _, basic_block_count: _, @@ -1120,7 +1125,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { instance: ty::Instance<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Provenance>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -1147,7 +1152,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { fn_val: DynSym, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Provenance>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -1160,7 +1165,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut MiriInterpCx<'tcx>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { @@ -1639,15 +1644,21 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { interp_ok(()) } - fn before_stack_pop( - ecx: &InterpCx<'tcx, Self>, - frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, - ) -> InterpResult<'tcx> { + fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { + let frame = ecx.frame(); // We want this *before* the return value copy, because the return place itself is protected - // until we do `end_call` here. + // until we do `on_stack_pop` here, and we need to un-protect it to copy the return value. if ecx.machine.borrow_tracker.is_some() { ecx.on_stack_pop(frame)?; } + if frame.extra.is_user_relevant { + // All that we store is whether or not the frame we just removed is local, so now we + // have no idea where the next topmost local frame is. So we recompute it. + // (If this ever becomes a bottleneck, we could have `push` store the previous + // user-relevant frame and restore that here.) + // We have to skip the frame that is just being popped. + ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1); + } // tracing-tree can autoamtically annotate scope changes, but it gets very confused by our // 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) @@ -1661,15 +1672,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>, unwinding: bool, ) -> InterpResult<'tcx, ReturnAction> { - if frame.extra.is_user_relevant { - // All that we store is whether or not the frame we just removed is local, so now we - // have no idea where the next topmost local frame is. So we recompute it. - // (If this ever becomes a bottleneck, we could have `push` store the previous - // user-relevant frame and restore that here.) - ecx.active_thread_mut().recompute_top_user_relevant_frame(); - } let res = { - // Move `frame`` into a sub-scope so we control when it will be dropped. + // Move `frame` into a sub-scope so we control when it will be dropped. let mut frame = frame; let timing = frame.extra.timing.take(); let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding); @@ -1781,7 +1785,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { let is_generic = instance .args .into_iter() - .any(|kind| !matches!(kind.unpack(), ty::GenericArgKind::Lifetime(_))); + .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_))); let can_be_inlined = matches!( ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold, InliningThreshold::Always @@ -1809,6 +1813,18 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ) -> Cow<'e, RangeSet> { Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range)) } + + fn get_default_alloc_params(&self) -> ::AllocParams { + use crate::alloc::MiriAllocParams; + + #[cfg(target_os = "linux")] + match &self.allocator { + Some(alloc) => MiriAllocParams::Isolated(alloc.clone()), + None => MiriAllocParams::Global, + } + #[cfg(not(target_os = "linux"))] + MiriAllocParams::Global + } } /// Trait for callbacks handling asynchronous machine operations. diff --git a/src/tools/miri/src/range_map.rs b/src/tools/miri/src/range_map.rs index 2c2484cd0bcc..29a5a8537a41 100644 --- a/src/tools/miri/src/range_map.rs +++ b/src/tools/miri/src/range_map.rs @@ -31,6 +31,11 @@ impl RangeMap { RangeMap { v } } + pub fn size(&self) -> Size { + let size = self.v.last().map(|x| x.range.end).unwrap_or(0); + Size::from_bytes(size) + } + /// Finds the index containing the given offset. fn find_offset(&self, offset: u64) -> usize { self.v @@ -71,10 +76,7 @@ impl RangeMap { }; // The first offset that is not included any more. let end = offset + len; - assert!( - end <= self.v.last().unwrap().range.end, - "iterating beyond the bounds of this RangeMap" - ); + assert!(end <= self.size().bytes(), "iterating beyond the bounds of this RangeMap"); slice .iter() .take_while(move |elem| elem.range.start < end) @@ -327,4 +329,16 @@ mod tests { let map = RangeMap::::new(Size::from_bytes(20), -1); let _ = map.iter(Size::from_bytes(11), Size::from_bytes(11)); } + + #[test] + fn empty_map_iter() { + let map = RangeMap::::new(Size::from_bytes(0), -1); + let _ = map.iter(Size::from_bytes(0), Size::from_bytes(0)); + } + + #[test] + fn empty_map_iter_mut() { + let mut map = RangeMap::::new(Size::from_bytes(0), -1); + let _ = map.iter_mut(Size::from_bytes(0), Size::from_bytes(0)); + } } diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 7e667e70a172..9f3bc06771f5 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -25,7 +25,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let frame_count = this.active_thread_stack().len(); - this.write_scalar(Scalar::from_target_usize(frame_count.try_into().unwrap(), this), dest) + this.write_scalar(Scalar::from_target_usize(frame_count.to_u64(), this), dest) } fn handle_miri_get_backtrace( @@ -70,7 +70,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } 1 => for (i, ptr) in ptrs.into_iter().enumerate() { - let offset = ptr_layout.size.checked_mul(i.try_into().unwrap(), this).unwrap(); + let offset = ptr_layout.size.checked_mul(i.to_u64(), this).unwrap(); let op_place = buf_place.offset(offset, ptr_layout, this)?; @@ -158,11 +158,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } 1 => { this.write_scalar( - Scalar::from_target_usize(name.len().try_into().unwrap(), this), + Scalar::from_target_usize(name.len().to_u64(), this), &this.project_field(dest, 0)?, )?; this.write_scalar( - Scalar::from_target_usize(filename.len().try_into().unwrap(), this), + Scalar::from_target_usize(filename.len().to_u64(), this), &this.project_field(dest, 1)?, )?; } diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index 42603e784bbd..606d1ffbea6c 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -135,7 +135,10 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { /// Reads as much as possible into the given buffer `ptr`. /// `len` indicates how many bytes we should try to read. - /// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error. + /// + /// When the read is done, `finish` will be called. Note that `read` itself may return before + /// that happens! Everything that should happen "after" the `read` needs to happen inside + /// `finish`. fn read<'tcx>( self: FileDescriptionRef, _communicate_allowed: bool, @@ -149,7 +152,10 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { /// Writes as much as possible from the given buffer `ptr`. /// `len` indicates how many bytes we should try to write. - /// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error. + /// + /// When the write is done, `finish` will be called. Note that `write` itself may return before + /// that happens! Everything that should happen "after" the `write` needs to happen inside + /// `finish`. fn write<'tcx>( self: FileDescriptionRef, _communicate_allowed: bool, @@ -196,6 +202,20 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { panic!("Not a unix file descriptor: {}", self.name()); } + + /// Implementation of fcntl(F_GETFL) for this FD. + fn get_flags<'tcx>(&self, _ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> { + throw_unsup_format!("fcntl: {} is not supported for F_GETFL", self.name()); + } + + /// Implementation of fcntl(F_SETFL) for this FD. + fn set_flags<'tcx>( + &self, + _flag: i32, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + throw_unsup_format!("fcntl: {} is not supported for F_SETFL", self.name()); + } } impl FileDescription for io::Stdin { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 52c16a0c2e2d..b08b522d279a 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -43,7 +43,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -69,8 +69,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => {} } + // FIXME: avoid allocating memory + let dest = this.force_allocation(dest)?; + // The rest either implements the logic, or falls back to `lookup_exported_symbol`. - match this.emulate_foreign_item_inner(link_name, abi, args, dest)? { + match this.emulate_foreign_item_inner(link_name, abi, args, &dest)? { EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; @@ -111,7 +114,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { sym: DynSym, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -639,7 +642,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = this.read_scalar(val)?.to_i32()?; let num = this.read_target_usize(num)?; // The docs say val is "interpreted as unsigned char". - #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)] + #[expect(clippy::as_conversions)] let val = val as u8; // C requires that this must always be a valid pointer (C18 §7.1.4). @@ -665,7 +668,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = this.read_scalar(val)?.to_i32()?; let num = this.read_target_usize(num)?; // The docs say val is "interpreted as unsigned char". - #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)] + #[expect(clippy::as_conversions)] let val = val as u8; // C requires that this must always be a valid pointer (C18 §7.1.4). @@ -676,7 +679,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { .iter() .position(|&c| c == val); if let Some(idx) = idx { - let new_ptr = ptr.wrapping_offset(Size::from_bytes(idx as u64), this); + let new_ptr = ptr.wrapping_offset(Size::from_bytes(idx), this); this.write_pointer(new_ptr, dest)?; } else { this.write_null(dest)?; diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index acf3f74a93d8..e597b527cb7a 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -1,4 +1,5 @@ use std::io; +use std::io::ErrorKind; use crate::*; @@ -13,6 +14,29 @@ pub enum IoError { } pub use self::IoError::*; +impl IoError { + pub(crate) fn into_ntstatus(self) -> i32 { + let raw = match self { + HostError(e) => + match e.kind() { + // STATUS_MEDIA_WRITE_PROTECTED + ErrorKind::ReadOnlyFilesystem => 0xC00000A2u32, + // STATUS_FILE_INVALID + ErrorKind::InvalidInput => 0xC0000098, + // STATUS_DISK_FULL + ErrorKind::QuotaExceeded => 0xC000007F, + // STATUS_ACCESS_DENIED + ErrorKind::PermissionDenied => 0xC0000022, + // For the default error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR. + _ => 0xC0000185, + }, + // For the default error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR. + _ => 0xC0000185, + }; + raw.cast_signed() + } +} + impl From for IoError { fn from(value: io::Error) -> Self { IoError::HostError(value) diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index 837e1b31cac5..1e6c93333c15 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -92,16 +92,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { let this = self.eval_context_mut(); // Try getting the function from the shared library. - // On windows `_lib_path` will be unused, hence the name starting with `_`. - let (lib, _lib_path) = this.machine.native_lib.as_ref().unwrap(); - let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { - match lib.get(link_name.as_str().as_bytes()) { - Ok(x) => x, - Err(_) => { - return None; - } - } - }; + let (lib, lib_path) = this.machine.native_lib.as_ref().unwrap(); + let func: libloading::Symbol<'_, unsafe extern "C" fn()> = + unsafe { lib.get(link_name.as_str().as_bytes()).ok()? }; + #[expect(clippy::as_conversions)] // fn-ptr to raw-ptr cast needs `as`. + let fn_ptr = *func.deref() as *mut std::ffi::c_void; // FIXME: this is a hack! // The `libloading` crate will automatically load system libraries like `libc`. @@ -114,23 +109,25 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 // using the `libc` crate where this interface is public. - let mut info = std::mem::MaybeUninit::::uninit(); + let mut info = std::mem::MaybeUninit::::zeroed(); unsafe { - if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { + if libc::dladdr(fn_ptr, info.as_mut_ptr()) != 0 { let info = info.assume_init(); #[cfg(target_os = "cygwin")] let fname_ptr = info.dli_fname.as_ptr(); #[cfg(not(target_os = "cygwin"))] let fname_ptr = info.dli_fname; + assert!(!fname_ptr.is_null()); if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap() - != _lib_path.to_str().unwrap() + != lib_path.to_str().unwrap() { return None; } } } + // Return a pointer to the function. - Some(CodePtr(*func.deref() as *mut _)) + Some(CodePtr(fn_ptr)) } } diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index b5ed5ea837b3..549d859a6e1e 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -56,7 +56,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(()) } - /// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`. + /// Handles the `catch_unwind` intrinsic. fn handle_catch_unwind( &mut self, args: &[OpTy<'tcx>], @@ -66,7 +66,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); // Signature: - // fn r#try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32 + // fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32 // Calls `try_fn` with `data` as argument. If that executes normally, returns 0. // If that unwinds, calls `catch_fn` with the first argument being `data` and // then second argument being a target-dependent `payload` (i.e. it is up to us to define @@ -120,14 +120,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We only care about `catch_panic` if we're unwinding - if we're doing a normal // return, then we don't need to do anything special. if let (true, Some(catch_unwind)) = (unwinding, extra.catch_unwind.take()) { - // We've just popped a frame that was pushed by `try`, + // We've just popped a frame that was pushed by `catch_unwind`, // and we are unwinding, so we should catch that. trace!( "unwinding: found catch_panic frame during unwinding: {:?}", this.frame().instance() ); - // We set the return value of `try` to 1, since there was a panic. + // We set the return value of `catch_unwind` to 1, since there was a panic. this.write_scalar(Scalar::from_i32(1), &catch_unwind.dest)?; // The Thread's `panic_payload` holds what was passed to `miri_start_unwind`. @@ -142,7 +142,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ExternAbi::Rust, &[catch_unwind.data, payload], None, - // Directly return to caller of `try`. + // Directly return to caller of `catch_unwind`. StackPopCleanup::Goto { ret: catch_unwind.ret, // `catch_fn` must not unwind. diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs index 30ec0aefcbf1..aa3a05ead85b 100644 --- a/src/tools/miri/src/shims/unix/android/thread.rs +++ b/src/tools/miri/src/shims/unix/android/thread.rs @@ -7,7 +7,7 @@ use crate::helpers::check_min_vararg_count; use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult}; use crate::*; -const TASK_COMM_LEN: usize = 16; +const TASK_COMM_LEN: u64 = 16; pub fn prctl<'tcx>( ecx: &mut MiriInterpCx<'tcx>, @@ -38,7 +38,7 @@ pub fn prctl<'tcx>( let [name] = check_min_vararg_count("prctl(PR_GET_NAME, ...)", varargs)?; let name = ecx.read_scalar(name)?; let thread = ecx.pthread_self()?; - let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, ecx); + let len = Scalar::from_target_usize(TASK_COMM_LEN, ecx); ecx.check_ptr_access( name.to_pointer(ecx)?, Size::from_bytes(TASK_COMM_LEN), diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 156814a26fa7..71102d9f2f33 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -141,6 +141,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f_getfd = this.eval_libc_i32("F_GETFD"); let f_dupfd = this.eval_libc_i32("F_DUPFD"); let f_dupfd_cloexec = this.eval_libc_i32("F_DUPFD_CLOEXEC"); + let f_getfl = this.eval_libc_i32("F_GETFL"); + let f_setfl = this.eval_libc_i32("F_SETFL"); // We only support getting the flags for a descriptor. match cmd { @@ -175,6 +177,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.set_last_error_and_return_i32(LibcError("EBADF")) } } + cmd if cmd == f_getfl => { + // Check if this is a valid open file descriptor. + let Some(fd) = this.machine.fds.get(fd_num) else { + return this.set_last_error_and_return_i32(LibcError("EBADF")); + }; + + fd.get_flags(this) + } + cmd if cmd == f_setfl => { + // Check if this is a valid open file descriptor. + let Some(fd) = this.machine.fds.get(fd_num) else { + return this.set_last_error_and_return_i32(LibcError("EBADF")); + }; + + let [flag] = check_min_vararg_count("fcntl(fd, F_SETFL, ...)", varargs)?; + let flag = this.read_scalar(flag)?.to_i32()?; + + fd.set_flags(flag, this) + } cmd if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") => { 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 21a386b29272..533a741fea3f 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -24,7 +24,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?; - let max_len = usize::MAX; // FreeBSD does not seem to have a limit. + let max_len = u64::MAX; // FreeBSD does not seem to have a limit. let res = match this.pthread_setname_np( this.read_scalar(thread)?, this.read_scalar(name)?, @@ -56,6 +56,70 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } + "cpuset_getaffinity" => { + // The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically. + let [level, which, id, set_size, mask] = + this.check_shim(abi, Conv::C, link_name, args)?; + + let level = this.read_scalar(level)?.to_i32()?; + let which = this.read_scalar(which)?.to_i32()?; + let id = this.read_scalar(id)?.to_i64()?; + let set_size = this.read_target_usize(set_size)?; // measured in bytes + let mask = this.read_pointer(mask)?; + + let _level_root = this.eval_libc_i32("CPU_LEVEL_ROOT"); + let _level_cpuset = this.eval_libc_i32("CPU_LEVEL_CPUSET"); + let level_which = this.eval_libc_i32("CPU_LEVEL_WHICH"); + + let _which_tid = this.eval_libc_i32("CPU_WHICH_TID"); + let which_pid = this.eval_libc_i32("CPU_WHICH_PID"); + let _which_jail = this.eval_libc_i32("CPU_WHICH_JAIL"); + let _which_cpuset = this.eval_libc_i32("CPU_WHICH_CPUSET"); + let _which_irq = this.eval_libc_i32("CPU_WHICH_IRQ"); + + // For sched_getaffinity, the current process is identified by -1. + // TODO: Use gettid? I'm (LorrensP-2158466) not that familiar with this api . + let id = match id { + -1 => this.active_thread(), + _ => + throw_unsup_format!( + "`cpuset_getaffinity` is only supported with a pid of -1 (indicating the current thread)" + ), + }; + + if this.ptr_is_null(mask)? { + this.set_last_error_and_return(LibcError("EFAULT"), dest)?; + } + // We only support CPU_LEVEL_WHICH and CPU_WHICH_PID for now. + // This is the bare minimum to make the tests pass. + else if level != level_which || which != which_pid { + throw_unsup_format!( + "`cpuset_getaffinity` is only supported with `level` set to CPU_LEVEL_WHICH and `which` set to CPU_WHICH_PID." + ); + } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&id) { + // `cpusetsize` must be large enough to contain the entire CPU mask. + // FreeBSD only uses `cpusetsize` to verify that it's sufficient for the kernel's CPU mask. + // If it's too small, the syscall returns ERANGE. + // If it's large enough, copying the kernel mask to user space is safe, regardless of the actual size. + // See https://github.com/freebsd/freebsd-src/blob/909aa6781340f8c0b4ae01c6366bf1556ee2d1be/sys/kern/kern_cpuset.c#L1985 + if set_size < u64::from(this.machine.num_cpus).div_ceil(8) { + this.set_last_error_and_return(LibcError("ERANGE"), dest)?; + } else { + let cpuset = cpuset.clone(); + let byte_count = + Ord::min(cpuset.as_slice().len(), set_size.try_into().unwrap()); + this.write_bytes_ptr( + mask, + cpuset.as_slice()[..byte_count].iter().copied(), + )?; + this.write_null(dest)?; + } + } else { + // `id` is always that of the active thread, so this is currently unreachable. + unreachable!(); + } + } + // Synchronization primitives "_umtx_op" => { let [obj, op, val, uaddr, uaddr2] = diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 1f6acff0787a..347930c52f23 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -90,7 +90,7 @@ impl UnixFileDescription for FileHandle { op: FlockOp, ) -> InterpResult<'tcx, io::Result<()>> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); - cfg_match! { + cfg_select! { all(target_family = "unix", not(target_os = "solaris")) => { use std::os::fd::AsRawFd; @@ -121,13 +121,13 @@ impl UnixFileDescription for FileHandle { use std::os::windows::io::AsRawHandle; use windows_sys::Win32::Foundation::{ - ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE, + ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, TRUE, }; use windows_sys::Win32::Storage::FileSystem::{ LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile, }; - let fh = self.file.as_raw_handle() as HANDLE; + let fh = self.file.as_raw_handle(); use FlockOp::*; let (ret, lock_nb) = match op { 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 f5da7b0170b6..51c2434d68ac 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -14,7 +14,7 @@ use crate::*; // The documentation of glibc complains that the kernel never exposes // TASK_COMM_LEN through the headers, so it's assumed to always be 16 bytes // long including a null terminator. -const TASK_COMM_LEN: usize = 16; +const TASK_COMM_LEN: u64 = 16; pub fn is_dyn_sym(name: &str) -> bool { matches!(name, "statx") @@ -96,7 +96,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // In case of glibc, the length of the output buffer must // be not shorter than TASK_COMM_LEN. let len = this.read_scalar(len)?; - let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64 { + let res = if len.to_target_usize(this)? >= TASK_COMM_LEN { match this.pthread_getname_np( this.read_scalar(thread)?, this.read_scalar(name)?, 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 5046e9650822..0281bb9f71df 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -186,7 +186,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = match this.pthread_setname_np( thread, this.read_scalar(name)?, - this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(), + this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?, /* truncate */ false, )? { ThreadNameResult::Ok => Scalar::from_u32(0), diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 3d990a1a0420..4b6615b3ea82 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -86,7 +86,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, thread: Scalar, name: Scalar, - name_max_len: usize, + name_max_len: u64, truncate: bool, ) -> InterpResult<'tcx, ThreadNameResult> { let this = self.eval_context_mut(); @@ -99,9 +99,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut name = this.read_c_str(name)?.to_owned(); // Comparing with `>=` to account for null terminator. - if name.len() >= name_max_len { + if name.len().to_u64() >= name_max_len { if truncate { - name.truncate(name_max_len.saturating_sub(1)); + name.truncate(name_max_len.saturating_sub(1).try_into().unwrap()); } else { return interp_ok(ThreadNameResult::NameTooLong); } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 135d8f6bee7e..817ddd7954df 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -20,6 +20,16 @@ use crate::*; /// be configured in the real system. const MAX_SOCKETPAIR_BUFFER_CAPACITY: usize = 212992; +#[derive(Debug, PartialEq)] +enum AnonSocketType { + // Either end of the socketpair fd. + Socketpair, + // Read end of the pipe. + PipeRead, + // Write end of the pipe. + PipeWrite, +} + /// One end of a pair of connected unnamed sockets. #[derive(Debug)] struct AnonSocket { @@ -40,7 +50,10 @@ struct AnonSocket { /// A list of thread ids blocked because the buffer was full. /// Once another thread reads some bytes, these threads will be unblocked. blocked_write_tid: RefCell>, - is_nonblock: bool, + /// Whether this fd is non-blocking or not. + is_nonblock: Cell, + // Differentiate between different AnonSocket fd types. + fd_type: AnonSocketType, } #[derive(Debug)] @@ -63,7 +76,10 @@ impl AnonSocket { impl FileDescription for AnonSocket { fn name(&self) -> &'static str { - "socketpair" + match self.fd_type { + AnonSocketType::Socketpair => "socketpair", + AnonSocketType::PipeRead | AnonSocketType::PipeWrite => "pipe", + } } fn close<'tcx>( @@ -110,6 +126,66 @@ impl FileDescription for AnonSocket { fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { self } + + fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> { + let mut flags = 0; + + // Get flag for file access mode. + // The flag for both socketpair and pipe will remain the same even when the peer + // fd is closed, so we need to look at the original type of this socket, not at whether + // the peer socket still exists. + match self.fd_type { + AnonSocketType::Socketpair => { + flags |= ecx.eval_libc_i32("O_RDWR"); + } + AnonSocketType::PipeRead => { + flags |= ecx.eval_libc_i32("O_RDONLY"); + } + AnonSocketType::PipeWrite => { + flags |= ecx.eval_libc_i32("O_WRONLY"); + } + } + + // Get flag for blocking status. + if self.is_nonblock.get() { + flags |= ecx.eval_libc_i32("O_NONBLOCK"); + } + + interp_ok(Scalar::from_i32(flags)) + } + + fn set_flags<'tcx>( + &self, + mut flag: i32, + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + // FIXME: File creation flags should be ignored. + + let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK"); + let o_rdonly = ecx.eval_libc_i32("O_RDONLY"); + let o_wronly = ecx.eval_libc_i32("O_WRONLY"); + let o_rdwr = ecx.eval_libc_i32("O_RDWR"); + + // O_NONBLOCK flag can be set / unset by user. + if flag & o_nonblock == o_nonblock { + self.is_nonblock.set(true); + flag &= !o_nonblock; + } else { + self.is_nonblock.set(false); + } + + // Ignore all file access mode flags. + flag &= !(o_rdonly | o_wronly | o_rdwr); + + // Throw error if there is any unsupported flag. + if flag != 0 { + throw_unsup_format!( + "fcntl: only O_NONBLOCK is supported for F_SETFL on socketpairs and pipes" + ) + } + + interp_ok(Scalar::from_i32(0)) + } } /// Write to AnonSocket based on the space available and return the written byte size. @@ -141,7 +217,7 @@ fn anonsocket_write<'tcx>( // Let's see if we can write. let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len()); if available_space == 0 { - if self_ref.is_nonblock { + if self_ref.is_nonblock.get() { // Non-blocking socketpair with a full buffer. return finish.call(ecx, Err(ErrorKind::WouldBlock.into())); } else { @@ -223,7 +299,7 @@ fn anonsocket_read<'tcx>( // Socketpair with no peer and empty buffer. // 0 bytes successfully read indicates end-of-file. return finish.call(ecx, Ok(0)); - } else if self_ref.is_nonblock { + } else if self_ref.is_nonblock.get() { // Non-blocking socketpair with writer and empty buffer. // https://linux.die.net/man/2/read // EAGAIN or EWOULDBLOCK can be returned for socket, @@ -407,7 +483,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { peer_lost_data: Cell::new(false), blocked_read_tid: RefCell::new(Vec::new()), blocked_write_tid: RefCell::new(Vec::new()), - is_nonblock: is_sock_nonblock, + is_nonblock: Cell::new(is_sock_nonblock), + fd_type: AnonSocketType::Socketpair, }); let fd1 = fds.new_ref(AnonSocket { readbuf: Some(RefCell::new(Buffer::new())), @@ -415,7 +492,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { peer_lost_data: Cell::new(false), blocked_read_tid: RefCell::new(Vec::new()), blocked_write_tid: RefCell::new(Vec::new()), - is_nonblock: is_sock_nonblock, + is_nonblock: Cell::new(is_sock_nonblock), + fd_type: AnonSocketType::Socketpair, }); // Make the file descriptions point to each other. @@ -475,7 +553,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { peer_lost_data: Cell::new(false), blocked_read_tid: RefCell::new(Vec::new()), blocked_write_tid: RefCell::new(Vec::new()), - is_nonblock, + is_nonblock: Cell::new(is_nonblock), + fd_type: AnonSocketType::PipeRead, }); let fd1 = fds.new_ref(AnonSocket { readbuf: None, @@ -483,7 +562,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { peer_lost_data: Cell::new(false), blocked_read_tid: RefCell::new(Vec::new()), blocked_write_tid: RefCell::new(Vec::new()), - is_nonblock, + is_nonblock: Cell::new(is_nonblock), + fd_type: AnonSocketType::PipeWrite, }); // Make the file descriptions point to each other. diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 1b2ccd99ef9f..a7c26d601e50 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -230,7 +230,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(match directories::UserDirs::new() { Some(dirs) => { let home = dirs.home_dir(); - let size_avail = if this.ptr_is_null(size.ptr())? { + let size_avail = if this.ptr_is_null(buf)? { 0 // if the buf pointer is null, we can't write to it; `size` will be updated to the required length } else { this.read_scalar(&size)?.to_u32()? @@ -238,8 +238,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Of course we cannot use `windows_check_buffer_size` here since this uses // a different method for dealing with a too-small buffer than the other functions... let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?; - // The Windows docs just say that this is written on failure. But std - // seems to rely on it always being written. + // As per , the size is always + // written, not just on failure. this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?; if success { Scalar::from_i32(1) // return TRUE diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index c80858c63639..98099e07b2ea 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -195,69 +195,52 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "NtWriteFile" => { - if !this.frame_in_std() { - throw_unsup_format!( - "`NtWriteFile` support is crude and just enough for stdout to work" - ); - } - let [ handle, - _event, - _apc_routine, - _apc_context, + event, + apc_routine, + apc_context, io_status_block, buf, n, byte_offset, - _key, + key, ] = this.check_shim(abi, sys_conv, link_name, args)?; - let handle = this.read_target_isize(handle)?; - let buf = this.read_pointer(buf)?; - let n = this.read_scalar(n)?.to_u32()?; - let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer - let io_status_block = this - .deref_pointer_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?; - - if byte_offset != 0 { - throw_unsup_format!( - "`NtWriteFile` `ByteOffset` parameter is non-null, which is unsupported" - ); - } - - let written = if handle == -11 || handle == -12 { - // stdout/stderr - use io::Write; - - let buf_cont = - this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?; - let res = if this.machine.mute_stdout_stderr { - Ok(buf_cont.len()) - } else if handle == -11 { - io::stdout().write(buf_cont) - } else { - io::stderr().write(buf_cont) - }; - // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that. - res.ok().map(|n| u32::try_from(n).unwrap()) - } else { - throw_unsup_format!( - "on Windows, writing to anything except stdout/stderr is not supported" - ) - }; - // We have to put the result into io_status_block. - if let Some(n) = written { - let io_status_information = - this.project_field_named(&io_status_block, "Information")?; - this.write_scalar( - Scalar::from_target_usize(n.into(), this), - &io_status_information, - )?; - } - // Return whether this was a success. >= 0 is success. - // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR. - this.write_scalar( - Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }), + this.NtWriteFile( + handle, + event, + apc_routine, + apc_context, + io_status_block, + buf, + n, + byte_offset, + key, + dest, + )?; + } + "NtReadFile" => { + let [ + handle, + event, + apc_routine, + apc_context, + io_status_block, + buf, + n, + byte_offset, + key, + ] = this.check_shim(abi, sys_conv, link_name, args)?; + this.NtReadFile( + handle, + event, + apc_routine, + apc_context, + io_status_block, + buf, + n, + byte_offset, + key, dest, )?; } @@ -322,6 +305,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = this.DeleteFileW(file_name)?; this.write_scalar(res, dest)?; } + "SetFilePointerEx" => { + let [file, distance_to_move, new_file_pointer, move_method] = + this.check_shim(abi, sys_conv, link_name, args)?; + let res = + this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?; + this.write_scalar(res, dest)?; + } // Allocation "HeapAlloc" => { @@ -582,6 +572,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ret = this.WaitForSingleObject(handle, timeout)?; this.write_scalar(ret, dest)?; } + "GetCurrentProcess" => { + let [] = this.check_shim(abi, sys_conv, link_name, args)?; + + this.write_scalar( + Handle::Pseudo(PseudoHandle::CurrentProcess).to_scalar(this), + dest, + )?; + } "GetCurrentThread" => { let [] = this.check_shim(abi, sys_conv, link_name, args)?; @@ -700,12 +698,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetStdHandle" => { let [which] = this.check_shim(abi, sys_conv, link_name, args)?; - let which = this.read_scalar(which)?.to_i32()?; - // We just make this the identity function, so we know later in `NtWriteFile` which - // one it is. This is very fake, but libtest needs it so we cannot make it a - // std-only shim. - // FIXME: this should return real HANDLEs when io support is added - this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?; + let res = this.GetStdHandle(which)?; + this.write_scalar(res, dest)?; + } + "DuplicateHandle" => { + let [src_proc, src_handle, target_proc, target_handle, access, inherit, options] = + this.check_shim(abi, sys_conv, link_name, args)?; + let res = this.DuplicateHandle( + src_proc, + src_handle, + target_proc, + target_handle, + access, + inherit, + options, + )?; + this.write_scalar(res, dest)?; } "CloseHandle" => { let [handle] = this.check_shim(abi, sys_conv, link_name, args)?; diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index 7561bf45219b..72e016c12e94 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -1,5 +1,6 @@ use std::fs::{Metadata, OpenOptions}; use std::io; +use std::io::SeekFrom; use std::path::PathBuf; use std::time::SystemTime; @@ -390,6 +391,267 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } + + fn NtWriteFile( + &mut self, + handle: &OpTy<'tcx>, // HANDLE + event: &OpTy<'tcx>, // HANDLE + apc_routine: &OpTy<'tcx>, // PIO_APC_ROUTINE + apc_ctx: &OpTy<'tcx>, // PVOID + io_status_block: &OpTy<'tcx>, // PIO_STATUS_BLOCK + buf: &OpTy<'tcx>, // PVOID + n: &OpTy<'tcx>, // ULONG + byte_offset: &OpTy<'tcx>, // PLARGE_INTEGER + key: &OpTy<'tcx>, // PULONG + dest: &MPlaceTy<'tcx>, // return type: NTSTATUS + ) -> InterpResult<'tcx, ()> { + let this = self.eval_context_mut(); + let handle = this.read_handle(handle, "NtWriteFile")?; + let event = this.read_handle(event, "NtWriteFile")?; + let apc_routine = this.read_pointer(apc_routine)?; + let apc_ctx = this.read_pointer(apc_ctx)?; + let buf = this.read_pointer(buf)?; + let count = this.read_scalar(n)?.to_u32()?; + let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer, but we only support null + let key = this.read_pointer(key)?; + let io_status_block = + this.deref_pointer_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?; + + if event != Handle::Null { + throw_unsup_format!( + "`NtWriteFile` `Event` parameter is non-null, which is unsupported" + ); + } + + if !this.ptr_is_null(apc_routine)? { + throw_unsup_format!( + "`NtWriteFile` `ApcRoutine` parameter is non-null, which is unsupported" + ); + } + + if !this.ptr_is_null(apc_ctx)? { + throw_unsup_format!( + "`NtWriteFile` `ApcContext` parameter is non-null, which is unsupported" + ); + } + + if byte_offset != 0 { + throw_unsup_format!( + "`NtWriteFile` `ByteOffset` parameter is non-null, which is unsupported" + ); + } + + if !this.ptr_is_null(key)? { + throw_unsup_format!("`NtWriteFile` `Key` parameter is non-null, which is unsupported"); + } + + let fd = match handle { + Handle::File(fd) => fd, + _ => this.invalid_handle("NtWriteFile")?, + }; + + let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtWriteFile")? }; + + // Windows writes the output code to IO_STATUS_BLOCK.Status, and number of bytes written + // to IO_STATUS_BLOCK.Information. + // The status block value and the returned value don't need to match - but + // for the cases implemented by miri so far, we can choose to decide that they do. + let io_status = { + let anon = this.project_field_named(&io_status_block, "Anonymous")?; + this.project_field_named(&anon, "Status")? + }; + let io_status_info = this.project_field_named(&io_status_block, "Information")?; + + let finish = { + let io_status = io_status.clone(); + let io_status_info = io_status_info.clone(); + let dest = dest.clone(); + callback!( + @capture<'tcx> { + count: u32, + io_status: MPlaceTy<'tcx>, + io_status_info: MPlaceTy<'tcx>, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + match result { + Ok(read_size) => { + assert!(read_size <= count.try_into().unwrap()); + // This must fit since `count` fits. + this.write_int(u64::try_from(read_size).unwrap(), &io_status_info)?; + this.write_int(0, &io_status)?; + this.write_int(0, &dest) + } + Err(e) => { + this.write_int(0, &io_status_info)?; + let status = e.into_ntstatus(); + this.write_int(status, &io_status)?; + this.write_int(status, &dest) + } + }} + ) + }; + + desc.write(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?; + + // Return status is written to `dest` and `io_status_block` on callback completion. + interp_ok(()) + } + + fn NtReadFile( + &mut self, + handle: &OpTy<'tcx>, // HANDLE + event: &OpTy<'tcx>, // HANDLE + apc_routine: &OpTy<'tcx>, // PIO_APC_ROUTINE + apc_ctx: &OpTy<'tcx>, // PVOID + io_status_block: &OpTy<'tcx>, // PIO_STATUS_BLOCK + buf: &OpTy<'tcx>, // PVOID + n: &OpTy<'tcx>, // ULONG + byte_offset: &OpTy<'tcx>, // PLARGE_INTEGER + key: &OpTy<'tcx>, // PULONG + dest: &MPlaceTy<'tcx>, // return type: NTSTATUS + ) -> InterpResult<'tcx, ()> { + let this = self.eval_context_mut(); + let handle = this.read_handle(handle, "NtReadFile")?; + let event = this.read_handle(event, "NtReadFile")?; + let apc_routine = this.read_pointer(apc_routine)?; + let apc_ctx = this.read_pointer(apc_ctx)?; + let buf = this.read_pointer(buf)?; + let count = this.read_scalar(n)?.to_u32()?; + let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer, but we only support null + let key = this.read_pointer(key)?; + let io_status_block = + this.deref_pointer_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?; + + if event != Handle::Null { + throw_unsup_format!("`NtReadFile` `Event` parameter is non-null, which is unsupported"); + } + + if !this.ptr_is_null(apc_routine)? { + throw_unsup_format!( + "`NtReadFile` `ApcRoutine` parameter is non-null, which is unsupported" + ); + } + + if !this.ptr_is_null(apc_ctx)? { + throw_unsup_format!( + "`NtReadFile` `ApcContext` parameter is non-null, which is unsupported" + ); + } + + if byte_offset != 0 { + throw_unsup_format!( + "`NtReadFile` `ByteOffset` parameter is non-null, which is unsupported" + ); + } + + if !this.ptr_is_null(key)? { + throw_unsup_format!("`NtReadFile` `Key` parameter is non-null, which is unsupported"); + } + + // See NtWriteFile above for commentary on this + let io_status = { + let anon = this.project_field_named(&io_status_block, "Anonymous")?; + this.project_field_named(&anon, "Status")? + }; + let io_status_info = this.project_field_named(&io_status_block, "Information")?; + + let finish = { + let io_status = io_status.clone(); + let io_status_info = io_status_info.clone(); + let dest = dest.clone(); + callback!( + @capture<'tcx> { + count: u32, + io_status: MPlaceTy<'tcx>, + io_status_info: MPlaceTy<'tcx>, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + match result { + Ok(read_size) => { + assert!(read_size <= count.try_into().unwrap()); + // This must fit since `count` fits. + this.write_int(u64::try_from(read_size).unwrap(), &io_status_info)?; + this.write_int(0, &io_status)?; + this.write_int(0, &dest) + } + Err(e) => { + this.write_int(0, &io_status_info)?; + let status = e.into_ntstatus(); + this.write_int(status, &io_status)?; + this.write_int(status, &dest) + } + }} + ) + }; + + let fd = match handle { + Handle::File(fd) => fd, + _ => this.invalid_handle("NtWriteFile")?, + }; + + let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? }; + + desc.read(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?; + + // See NtWriteFile for commentary on this + interp_ok(()) + } + + fn SetFilePointerEx( + &mut self, + file: &OpTy<'tcx>, // HANDLE + dist_to_move: &OpTy<'tcx>, // LARGE_INTEGER + new_fp: &OpTy<'tcx>, // PLARGE_INTEGER + move_method: &OpTy<'tcx>, // DWORD + ) -> InterpResult<'tcx, Scalar> { + // ^ Returns BOOL (i32 on Windows) + let this = self.eval_context_mut(); + let file = this.read_handle(file, "SetFilePointerEx")?; + let dist_to_move = this.read_scalar(dist_to_move)?.to_i64()?; + let new_fp_ptr = this.read_pointer(new_fp)?; + let move_method = this.read_scalar(move_method)?.to_u32()?; + + let fd = match file { + Handle::File(fd) => fd, + _ => this.invalid_handle("SetFilePointerEx")?, + }; + + let Some(desc) = this.machine.fds.get(fd) else { + throw_unsup_format!("`SetFilePointerEx` is only supported on file backed handles"); + }; + + let file_begin = this.eval_windows_u32("c", "FILE_BEGIN"); + let file_current = this.eval_windows_u32("c", "FILE_CURRENT"); + let file_end = this.eval_windows_u32("c", "FILE_END"); + + let seek = if move_method == file_begin { + SeekFrom::Start(dist_to_move.try_into().unwrap()) + } else if move_method == file_current { + SeekFrom::Current(dist_to_move) + } else if move_method == file_end { + SeekFrom::End(dist_to_move) + } else { + throw_unsup_format!("Invalid move method: {move_method}") + }; + + match desc.seek(this.machine.communicate(), seek)? { + Ok(n) => { + if !this.ptr_is_null(new_fp_ptr)? { + this.write_scalar( + Scalar::from_i64(n.try_into().unwrap()), + &this.deref_pointer_as(new_fp, this.machine.layouts.i64)?, + )?; + } + interp_ok(this.eval_windows("c", "TRUE")) + } + Err(e) => { + this.set_last_error(e)?; + interp_ok(this.eval_windows("c", "FALSE")) + } + } + } } /// Windows FILETIME is measured in 100-nanosecs since 1601 @@ -401,7 +663,7 @@ fn extract_windows_epoch<'tcx>( Some(time) => { let duration = ecx.system_time_since_windows_epoch(&time)?; let duration_ticks = ecx.windows_ticks_for(duration)?; - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::as_conversions)] interp_ok(Some((duration_ticks as u32, (duration_ticks >> 32) as u32))) } None => interp_ok(None), diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index eec6c62bebc7..1e30bf25ed92 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -9,6 +9,7 @@ use crate::*; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum PseudoHandle { CurrentThread, + CurrentProcess, } /// Miri representation of a Windows `HANDLE` @@ -23,16 +24,19 @@ pub enum Handle { impl PseudoHandle { const CURRENT_THREAD_VALUE: u32 = 0; + const CURRENT_PROCESS_VALUE: u32 = 1; fn value(self) -> u32 { match self { Self::CurrentThread => Self::CURRENT_THREAD_VALUE, + Self::CurrentProcess => Self::CURRENT_PROCESS_VALUE, } } fn from_value(value: u32) -> Option { match value { Self::CURRENT_THREAD_VALUE => Some(Self::CurrentThread), + Self::CURRENT_PROCESS_VALUE => Some(Self::CurrentProcess), _ => None, } } @@ -70,8 +74,7 @@ impl Handle { Self::Null => 0, Self::Pseudo(pseudo_handle) => pseudo_handle.value(), Self::Thread(thread) => thread.to_u32(), - #[expect(clippy::cast_sign_loss)] - Self::File(fd) => fd as u32, + Self::File(fd) => fd.cast_unsigned(), // INVALID_HANDLE_VALUE is -1. This fact is explicitly declared or implied in several // pages of Windows documentation. // 1: https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.safehandles.safefilehandle?view=net-9.0 @@ -124,11 +127,10 @@ impl Handle { Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null), Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)), Self::THREAD_DISCRIMINANT => Some(Self::Thread(ThreadId::new_unchecked(data))), - #[expect(clippy::cast_possible_wrap)] Self::FILE_DISCRIMINANT => { // This cast preserves all bits. assert_eq!(size_of_val(&data), size_of::()); - Some(Self::File(data as FdNum)) + Some(Self::File(data.cast_signed())) } Self::INVALID_DISCRIMINANT => Some(Self::Invalid), _ => None, @@ -156,8 +158,7 @@ impl Handle { pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar { // 64-bit handles are sign extended 32-bit handles // see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication - #[expect(clippy::cast_possible_wrap)] // we want it to wrap - let signed_handle = self.to_packed() as i32; + let signed_handle = self.to_packed().cast_signed(); Scalar::from_target_isize(signed_handle.into(), cx) } @@ -171,9 +172,8 @@ impl Handle { ) -> InterpResult<'tcx, Result> { let sign_extended_handle = handle.to_target_isize(cx)?; - #[expect(clippy::cast_sign_loss)] // we want to lose the sign let handle = if let Ok(signed_handle) = i32::try_from(sign_extended_handle) { - signed_handle as u32 + signed_handle.cast_unsigned() } else { // if a handle doesn't fit in an i32, it isn't valid. return interp_ok(Err(HandleError::InvalidHandle)); @@ -224,6 +224,100 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ))) } + fn GetStdHandle(&mut self, which: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let which = this.read_scalar(which)?.to_i32()?; + + let stdin = this.eval_windows("c", "STD_INPUT_HANDLE").to_i32()?; + let stdout = this.eval_windows("c", "STD_OUTPUT_HANDLE").to_i32()?; + let stderr = this.eval_windows("c", "STD_ERROR_HANDLE").to_i32()?; + + // These values don't mean anything on Windows, but Miri unconditionally sets them up to the + // unix in/out/err descriptors. So we take advantage of that. + // Due to the `Handle` encoding, these values will not be directly exposed to the user. + let fd_num = if which == stdin { + 0 + } else if which == stdout { + 1 + } else if which == stderr { + 2 + } else { + throw_unsup_format!("Invalid argument to `GetStdHandle`: {which}") + }; + let handle = Handle::File(fd_num); + interp_ok(handle.to_scalar(this)) + } + + fn DuplicateHandle( + &mut self, + src_proc: &OpTy<'tcx>, // HANDLE + src_handle: &OpTy<'tcx>, // HANDLE + target_proc: &OpTy<'tcx>, // HANDLE + target_handle: &OpTy<'tcx>, // LPHANDLE + desired_access: &OpTy<'tcx>, // DWORD + inherit: &OpTy<'tcx>, // BOOL + options: &OpTy<'tcx>, // DWORD + ) -> InterpResult<'tcx, Scalar> { + // ^ Returns BOOL (i32 on Windows) + let this = self.eval_context_mut(); + + let src_proc = this.read_handle(src_proc, "DuplicateHandle")?; + let src_handle = this.read_handle(src_handle, "DuplicateHandle")?; + let target_proc = this.read_handle(target_proc, "DuplicateHandle")?; + let target_handle_ptr = this.read_pointer(target_handle)?; + // Since we only support DUPLICATE_SAME_ACCESS, this value is ignored, but should be valid + let _ = this.read_scalar(desired_access)?.to_u32()?; + // We don't support the CreateProcess API, so inheritable or not means nothing. + // If we ever add CreateProcess support, this will need to be implemented. + let _ = this.read_scalar(inherit)?; + let options = this.read_scalar(options)?; + + if src_proc != Handle::Pseudo(PseudoHandle::CurrentProcess) { + throw_unsup_format!( + "`DuplicateHandle` `hSourceProcessHandle` parameter is not the current process, which is unsupported" + ); + } + + if target_proc != Handle::Pseudo(PseudoHandle::CurrentProcess) { + throw_unsup_format!( + "`DuplicateHandle` `hSourceProcessHandle` parameter is not the current process, which is unsupported" + ); + } + + if this.ptr_is_null(target_handle_ptr)? { + throw_unsup_format!( + "`DuplicateHandle` `lpTargetHandle` parameter is null, which is unsupported" + ); + } + + if options != this.eval_windows("c", "DUPLICATE_SAME_ACCESS") { + throw_unsup_format!( + "`DuplicateHandle` `dwOptions` parameter is not `DUPLICATE_SAME_ACCESS`, which is unsupported" + ); + } + + let new_handle = match src_handle { + Handle::File(old_fd_num) => { + let Some(fd) = this.machine.fds.get(old_fd_num) else { + this.invalid_handle("DuplicateHandle")? + }; + Handle::File(this.machine.fds.insert(fd)) + } + Handle::Thread(_) => { + throw_unsup_format!( + "`DuplicateHandle` called on a thread handle, which is unsupported" + ); + } + Handle::Pseudo(pseudo) => Handle::Pseudo(pseudo), + Handle::Null | Handle::Invalid => this.invalid_handle("DuplicateHandle")?, + }; + + let target_place = this.deref_pointer_as(target_handle, this.machine.layouts.usize)?; + this.write_scalar(new_handle.to_scalar(this), &target_place)?; + + interp_ok(this.eval_windows("c", "TRUE")) + } + fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); diff --git a/src/tools/miri/src/shims/x86/gfni.rs b/src/tools/miri/src/shims/x86/gfni.rs index 4774ec9f9d8f..a91c74283fd8 100644 --- a/src/tools/miri/src/shims/x86/gfni.rs +++ b/src/tools/miri/src/shims/x86/gfni.rs @@ -133,12 +133,12 @@ fn affine_transform<'tcx>( // 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. -#[expect(clippy::cast_possible_truncation)] static TABLE: [u8; 256] = { let mut array = [0; 256]; let mut i = 1; while i < 256 { + #[expect(clippy::as_conversions)] // no `try_from` in const... let mut x = i as u8; let mut y = gf2p8_mul(x, x); x = y; @@ -160,7 +160,7 @@ static TABLE: [u8; 256] = { /// 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. -#[expect(clippy::cast_possible_truncation)] +#[expect(clippy::as_conversions)] 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 diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index e57217dc6f29..ac59cc2dfeb3 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -1110,7 +1110,7 @@ fn pmulhrsw<'tcx>( // The result of this operation can overflow a signed 16-bit integer. // When `left` and `right` are -0x8000, the result is 0x8000. - #[expect(clippy::cast_possible_truncation)] + #[expect(clippy::as_conversions)] let res = res as i16; ecx.write_scalar(Scalar::from_i16(res), &dest)?; diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs index 6d2c151243ca..23c83553f3b3 100644 --- a/src/tools/miri/src/shims/x86/sha.rs +++ b/src/tools/miri/src/shims/x86/sha.rs @@ -43,7 +43,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We reverse the order because x86 is little endian but the copied implementation uses // big endian. for (i, part) in val.into_iter().rev().enumerate() { - let projected = &ecx.project_index(dest, i.try_into().unwrap())?; + let projected = &ecx.project_index(dest, i.to_u64())?; ecx.write_scalar(Scalar::from_u32(part), projected)?; } interp_ok(()) diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index 02336a722f7e..66bff328626a 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -440,7 +440,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let crc = if bit_size == 64 { // The 64-bit version will only consider the lower 32 bits, // while the upper 32 bits get discarded. - #[expect(clippy::cast_possible_truncation)] + #[expect(clippy::as_conversions)] u128::from((left.to_u64()? as u32).reverse_bits()) } else { u128::from(left.to_u32()?.reverse_bits()) diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index 653228a5e3db..fa833b51fa31 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -25,6 +25,13 @@ page_size = "0.6" tokio = { version = "1", features = ["macros", "rt-multi-thread", "time", "net", "fs", "sync", "signal", "io-util"] } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_Storage_FileSystem", "Win32_Security"] } +windows-sys = { version = "0.59", features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_Storage_FileSystem", + "Win32_Security", + "Win32_System_IO", + "Wdk_Storage_FileSystem", +] } [workspace] diff --git a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs new file mode 100644 index 000000000000..eef32136a0a4 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs @@ -0,0 +1,20 @@ +//@ignore-target: windows # Sockets/pipes are not implemented yet +//~^ ERROR: deadlock: the evaluated program deadlocked +//@compile-flags: -Zmiri-deterministic-concurrency +use std::thread; + +/// If an O_NONBLOCK flag is set while the fd is blocking, that fd will not be woken up. +fn main() { + let mut fds = [-1, -1]; + let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; + assert_eq!(res, 0); + let mut buf: [u8; 5] = [0; 5]; + let _thread1 = thread::spawn(move || { + // Add O_NONBLOCK flag while pipe is still block on read. + let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }; + assert_eq!(res, 0); + }); + // Main thread will block on read. + let _res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + //~^ ERROR: deadlock: the evaluated program deadlocked +} diff --git a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr new file mode 100644 index 000000000000..9ca5598abae6 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr @@ -0,0 +1,19 @@ +error: deadlock: the evaluated program deadlocked + | + = note: the evaluated program deadlocked + = note: (no span available) + = note: BACKTRACE on thread `unnamed-ID`: + +error: deadlock: the evaluated program deadlocked + --> tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs:LL:CC + | +LL | let _res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `main` at tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 2 previous errors + diff --git a/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs b/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs deleted file mode 100644 index 3ef194c5c717..000000000000 --- a/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ignore-target: windows # No libc IO on Windows - -fn main() -> std::io::Result<()> { - let mut bytes = [0u8; 512]; - unsafe { - libc::read(0, bytes.as_mut_ptr() as *mut libc::c_void, 512); //~ ERROR: `read` from stdin not available when isolation is enabled - } - Ok(()) -} diff --git a/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr index a92a97cef3b3..52a93ab263d3 100644 --- a/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr +++ b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr @@ -4,8 +4,7 @@ error: unsupported operation: can't call foreign function `signal` on $OS LL | libc::signal(libc::SIGPIPE, libc::SIG_IGN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function `signal` on $OS | - = help: if this is a basic API commonly used on this target, please report an issue with Miri - = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases + = help: this means the program tried to do something Miri does not support; it does not indicate a bug in the program = note: BACKTRACE: = note: inside `main` at tests/fail-dep/libc/unsupported_incomplete_function.rs:LL:CC diff --git a/src/tools/miri/tests/fail/alloc/no_global_allocator.stderr b/src/tools/miri/tests/fail/alloc/no_global_allocator.stderr index 541af64b894d..e80a36467141 100644 --- a/src/tools/miri/tests/fail/alloc/no_global_allocator.stderr +++ b/src/tools/miri/tests/fail/alloc/no_global_allocator.stderr @@ -4,8 +4,7 @@ error: unsupported operation: can't call foreign function `__rust_alloc` on $OS LL | __rust_alloc(1, 1); | ^^^^^^^^^^^^^^^^^^ can't call foreign function `__rust_alloc` on $OS | - = help: if this is a basic API commonly used on this target, please report an issue with Miri - = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases + = help: this means the program tried to do something Miri does not support; it does not indicate a bug in the program = note: BACKTRACE: = note: inside `miri_start` at tests/fail/alloc/no_global_allocator.rs:LL:CC diff --git a/src/tools/miri/tests/fail/concurrency/read_only_atomic_load_large.rs b/src/tools/miri/tests/fail/concurrency/read_only_atomic_load_large.rs index 42c3a9619d46..2c01b0132b82 100644 --- a/src/tools/miri/tests/fail/concurrency/read_only_atomic_load_large.rs +++ b/src/tools/miri/tests/fail/concurrency/read_only_atomic_load_large.rs @@ -2,6 +2,7 @@ //@compile-flags: -Zmiri-disable-stacked-borrows // Needs atomic accesses larger than the pointer size //@ignore-bitwidth: 64 +//@ignore-target: mips- use std::sync::atomic::{AtomicI64, Ordering}; diff --git a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs index 5138bcbf8f74..e7632d43126a 100644 --- a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs +++ b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs @@ -8,7 +8,7 @@ struct MakeSend(*const i32); unsafe impl Send for MakeSend {} fn main() { - race(0); + race(0); //~ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) deallocation on thread `main` } // Using an argument for the ptr to point to, since those do not get StorageDead. @@ -22,5 +22,4 @@ fn race(local: i32) { thread::yield_now(); // Deallocating the local (when `main` returns) // races with the read in the other thread. - // Make sure the error points at this function's end, not just the call site. -} //~ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) deallocation on thread `main` +} diff --git a/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr b/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr index 643426aba99e..721b75630449 100644 --- a/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr +++ b/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here --> tests/fail/data_race/stack_pop_race.rs:LL:CC | -LL | } - | ^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here +LL | race(0); + | ^^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/stack_pop_race.rs:LL:CC @@ -12,12 +12,7 @@ LL | let _val = unsafe { *ptr.0 }; = 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 `race` at tests/fail/data_race/stack_pop_race.rs:LL:CC -note: inside `main` - --> tests/fail/data_race/stack_pop_race.rs:LL:CC - | -LL | race(0); - | ^^^^^^^ + = note: inside `main` at tests/fail/data_race/stack_pop_race.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/intrinsics/copy_overlapping.rs b/src/tools/miri/tests/fail/intrinsics/copy_overlapping.rs index e6282613df72..ff16ac61d8b8 100644 --- a/src/tools/miri/tests/fail/intrinsics/copy_overlapping.rs +++ b/src/tools/miri/tests/fail/intrinsics/copy_overlapping.rs @@ -1,14 +1,11 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -#[rustc_intrinsic] -unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); +#![feature(core_intrinsics)] fn main() { let mut data = [0u8; 16]; unsafe { let a = data.as_mut_ptr(); let b = a.wrapping_offset(1) as *mut _; - copy_nonoverlapping(a, b, 2); //~ ERROR: `copy_nonoverlapping` called on overlapping ranges + // Directly call intrinsic to avoid debug assertions in the `std::ptr` version. + std::intrinsics::copy_nonoverlapping(a, b, 2); //~ ERROR: `copy_nonoverlapping` called on overlapping ranges } } diff --git a/src/tools/miri/tests/fail/intrinsics/copy_overlapping.stderr b/src/tools/miri/tests/fail/intrinsics/copy_overlapping.stderr index fef5a0a82a07..9b60a48703ba 100644 --- a/src/tools/miri/tests/fail/intrinsics/copy_overlapping.stderr +++ b/src/tools/miri/tests/fail/intrinsics/copy_overlapping.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: `copy_nonoverlapping` called on overlapping ranges --> tests/fail/intrinsics/copy_overlapping.rs:LL:CC | -LL | copy_nonoverlapping(a, b, 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `copy_nonoverlapping` called on overlapping ranges +LL | std::intrinsics::copy_nonoverlapping(a, b, 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `copy_nonoverlapping` called on overlapping ranges | = 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/intrinsics/copy_unaligned.rs b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.rs index ded9d0b669e6..311789cdc4b4 100644 --- a/src/tools/miri/tests/fail/intrinsics/copy_unaligned.rs +++ b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.rs @@ -1,14 +1,11 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -#[rustc_intrinsic] -unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); +#![feature(core_intrinsics)] fn main() { let mut data = [0u16; 8]; let ptr = (&mut data[0] as *mut u16 as *mut u8).wrapping_add(1) as *mut u16; // Even copying 0 elements to something unaligned should error unsafe { - copy_nonoverlapping(&data[5], ptr, 0); //~ ERROR: accessing memory with alignment 1, but alignment 2 is required + // Directly call intrinsic to avoid debug assertions in the `std::ptr` version. + std::intrinsics::copy_nonoverlapping(&data[5], ptr, 0); //~ ERROR: accessing memory with alignment 1, but alignment 2 is required } } diff --git a/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr index 2d0edd4e6cbe..65dbdb3bbb64 100644 --- a/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr +++ b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required --> tests/fail/intrinsics/copy_unaligned.rs:LL:CC | -LL | copy_nonoverlapping(&data[5], ptr, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required +LL | std::intrinsics::copy_nonoverlapping(&data[5], ptr, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required | = 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/panic/bad_unwind.stderr b/src/tools/miri/tests/fail/panic/bad_unwind.stderr index 6ba39e8f7e23..8c269eae62a7 100644 --- a/src/tools/miri/tests/fail/panic/bad_unwind.stderr +++ b/src/tools/miri/tests/fail/panic/bad_unwind.stderr @@ -13,8 +13,8 @@ LL | std::panic::catch_unwind(|| unwind()).unwrap_err(); = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside closure at tests/fail/panic/bad_unwind.rs:LL:CC - = note: inside `std::panicking::r#try::do_call::<{closure@tests/fail/panic/bad_unwind.rs:LL:CC}, ()>` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panicking::r#try::<(), {closure@tests/fail/panic/bad_unwind.rs:LL:CC}>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@tests/fail/panic/bad_unwind.rs:LL:CC}, ()>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::<(), {closure@tests/fail/panic/bad_unwind.rs:LL:CC}>` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panic::catch_unwind::<{closure@tests/fail/panic/bad_unwind.rs:LL:CC}, ()>` at RUSTLIB/std/src/panic.rs:LL:CC note: inside `main` --> tests/fail/panic/bad_unwind.rs:LL:CC diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr index f57487e3ffe1..b4dadeecaa8c 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: +thread 'main' panicked at tests/fail/ptr_swap_nonoverlapping.rs:LL:CC: unsafe precondition(s) violated: ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null and the specified memory ranges do not overlap This indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety. @@ -18,9 +18,6 @@ LL | ABORT() = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC - = note: inside `std::ptr::swap_nonoverlapping::precondition_check` at RUSTLIB/core/src/ub_checks.rs:LL:CC - = note: inside `std::ptr::swap_nonoverlapping::` at RUSTLIB/core/src/ub_checks.rs:LL:CC note: inside `main` --> tests/fail/ptr_swap_nonoverlapping.rs:LL:CC | diff --git a/src/tools/miri/tests/fail/shims/isolated_stdin.rs b/src/tools/miri/tests/fail/shims/isolated_stdin.rs new file mode 100644 index 000000000000..9f809039adad --- /dev/null +++ b/src/tools/miri/tests/fail/shims/isolated_stdin.rs @@ -0,0 +1,12 @@ +//@error-in-other-file: `read` from stdin not available when isolation is enabled +//@normalize-stderr-test: "src/sys/.*\.rs" -> "$$FILE" +//@normalize-stderr-test: "\nLL \| .*" -> "" +//@normalize-stderr-test: "\n... .*" -> "" +//@normalize-stderr-test: "\| +[|_^]+" -> "| ^" +//@normalize-stderr-test: "\n *= note:.*" -> "" +use std::io::{self, Read}; + +fn main() { + let mut bytes = [0u8; 512]; + io::stdin().read(&mut bytes).unwrap(); +} diff --git a/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.stderr b/src/tools/miri/tests/fail/shims/isolated_stdin.stderr similarity index 58% rename from src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.stderr rename to src/tools/miri/tests/fail/shims/isolated_stdin.stderr index bb7e8cef5dcd..1a4d7e963298 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.stderr +++ b/src/tools/miri/tests/fail/shims/isolated_stdin.stderr @@ -1,13 +1,14 @@ error: unsupported operation: `read` from stdin not available when isolation is enabled - --> tests/fail-dep/libc/fs/isolated_stdin.rs:LL:CC + --> RUSTLIB/std/$FILE:LL:CC | -LL | libc::read(0, bytes.as_mut_ptr() as *mut libc::c_void, 512); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `read` from stdin not available when isolation is enabled + | ^ `read` from stdin not available when isolation is enabled | = help: set `MIRIFLAGS=-Zmiri-disable-isolation` to disable isolation; = help: or set `MIRIFLAGS=-Zmiri-isolation-error=warn` to make Miri return an error code from isolated operations (if supported for that operation) and continue with a warning - = note: BACKTRACE: - = note: inside `main` at tests/fail-dep/libc/fs/isolated_stdin.rs:LL:CC +note: inside `main` + --> tests/fail/shims/isolated_stdin.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/tail_calls/cc-mismatch.stderr b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr index 61ddea644720..589e30d632e2 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr @@ -11,12 +11,12 @@ LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; = note: inside `std::sys::backtrace::__rust_begin_short_backtrace::` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::ops::function::impls:: for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at RUSTLIB/core/src/ops/function.rs:LL:CC - = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panicking::r#try:: i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind:: 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#0}}, 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::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, 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/fail/tail_calls/dangling-local-var.stderr b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr index 33e1e53ea060..15f73c8a9ae1 100644 --- a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr +++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr @@ -14,8 +14,8 @@ LL | let local = 0; help: ALLOC was deallocated here: --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC | -LL | become g(ptr) - | ^^^^^^^^^^^^^ +LL | f(std::ptr::null()); + | ^^^^^^^^^^^^^^^^^^^ = note: BACKTRACE (of the first span): = note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs new file mode 100644 index 000000000000..ff7978776822 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs @@ -0,0 +1,33 @@ +//! A version of `cell_inside_struct` that dumps the tree so that we can see what is happening. +//@compile-flags: -Zmiri-tree-borrows +#[path = "../../utils/mod.rs"] +#[macro_use] +mod utils; + +use std::cell::Cell; + +struct Foo { + field1: u32, + field2: Cell, +} + +pub fn main() { + let root = Foo { field1: 42, field2: Cell::new(88) }; + unsafe { + let a = &root; + + name!(a as *const Foo, "a"); + + let a: *const Foo = a as *const Foo; + let a: *mut Foo = a as *mut Foo; + + let alloc_id = alloc_id!(a); + print_state!(alloc_id); + + // Writing to `field2`, which is interior mutable, should be allowed. + (*a).field2.set(10); + + // Writing to `field1`, which is frozen, should not be allowed. + (*a).field1 = 88; //~ ERROR: /write access through .* is forbidden/ + } +} diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr new file mode 100644 index 000000000000..717f14194522 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr @@ -0,0 +1,26 @@ +────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 4.. 8 +| Act | Act | └─┬── +| Frz |?Cel | └──── +────────────────────────────────────────────────── +error: Undefined Behavior: write access through (a) at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/cell-inside-struct.rs:LL:CC + | +LL | (*a).field1 = 88; + | ^^^^^^^^^^^^^^^^ write access through (a) at ALLOC[0x0] is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag (a) has state Frozen which forbids this child write access +help: the accessed tag was created here, in the initial state Cell + --> tests/fail/tree_borrows/cell-inside-struct.rs:LL:CC + | +LL | let a = &root; + | ^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail/tree_borrows/cell-inside-struct.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/unaligned_pointers/atomic_unaligned.rs b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.rs index 29976836b0ba..37c64c819448 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.rs @@ -1,5 +1,6 @@ //@compile-flags: -Zmiri-symbolic-alignment-check -Cdebug-assertions=no #![feature(core_intrinsics)] +use std::intrinsics; fn main() { // Do a 4-aligned u64 atomic access. That should be UB on all platforms, @@ -7,7 +8,7 @@ fn main() { let z = [0u32; 2]; let zptr = &z as *const _ as *const u64; unsafe { - ::std::intrinsics::atomic_load_seqcst(zptr); + intrinsics::atomic_load::<_, { intrinsics::AtomicOrdering::SeqCst }>(zptr); //~^ERROR: accessing memory with alignment 4, but alignment 8 is required } } diff --git a/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr index a9da740be1db..e0f9d011ce4e 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr +++ b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required --> tests/fail/unaligned_pointers/atomic_unaligned.rs:LL:CC | -LL | ::std::intrinsics::atomic_load_seqcst(zptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required +LL | intrinsics::atomic_load::<_, { intrinsics::AtomicOrdering::SeqCst }>(zptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required | = help: this usually indicates that your program performed an invalid operation and caused Undefined Behavior = help: but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives diff --git a/src/tools/miri/tests/fail/unsupported_foreign_function.stderr b/src/tools/miri/tests/fail/unsupported_foreign_function.stderr index 4fe45b0868a9..bbfc5c312568 100644 --- a/src/tools/miri/tests/fail/unsupported_foreign_function.stderr +++ b/src/tools/miri/tests/fail/unsupported_foreign_function.stderr @@ -4,8 +4,7 @@ error: unsupported operation: can't call foreign function `foo` on $OS LL | foo(); | ^^^^^ can't call foreign function `foo` on $OS | - = help: if this is a basic API commonly used on this target, please report an issue with Miri - = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases + = help: this means the program tried to do something Miri does not support; it does not indicate a bug in the program = note: BACKTRACE: = note: inside `main` at tests/fail/unsupported_foreign_function.rs:LL:CC diff --git a/src/tools/miri/tests/many-seeds/reentrant-lock.rs b/src/tools/miri/tests/many-seeds/reentrant-lock.rs new file mode 100644 index 000000000000..4c2dc463f485 --- /dev/null +++ b/src/tools/miri/tests/many-seeds/reentrant-lock.rs @@ -0,0 +1,19 @@ +#![feature(reentrant_lock)] +//! This is a regression test for +//! . + +use std::cell::Cell; +use std::sync::ReentrantLock; +use std::thread; + +static LOCK: ReentrantLock> = ReentrantLock::new(Cell::new(0)); + +fn main() { + for _ in 0..20 { + thread::spawn(move || { + let val = LOCK.lock(); + val.set(val.get() + 1); + drop(val); + }); + } +} diff --git a/src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr b/src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr index bf1cfd573b8a..b663fd41457d 100644 --- a/src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr +++ b/src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr @@ -4,8 +4,7 @@ error: unsupported operation: can't call foreign function `foo` on $OS LL | foo(); | ^^^^^ can't call foreign function `foo` on $OS | - = help: if this is a basic API commonly used on this target, please report an issue with Miri - = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases + = help: this means the program tried to do something Miri does not support; it does not indicate a bug in the program = note: BACKTRACE: = note: inside `main` at tests/native-lib/fail/function_not_in_so.rs:LL:CC diff --git a/src/tools/miri/tests/native-lib/fail/private_function.stderr b/src/tools/miri/tests/native-lib/fail/private_function.stderr index 2cfc062212b6..036812400155 100644 --- a/src/tools/miri/tests/native-lib/fail/private_function.stderr +++ b/src/tools/miri/tests/native-lib/fail/private_function.stderr @@ -4,8 +4,7 @@ error: unsupported operation: can't call foreign function `not_exported` on $OS LL | not_exported(); | ^^^^^^^^^^^^^^ can't call foreign function `not_exported` on $OS | - = help: if this is a basic API commonly used on this target, please report an issue with Miri - = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases + = help: this means the program tried to do something Miri does not support; it does not indicate a bug in the program = note: BACKTRACE: = note: inside `main` at tests/native-lib/fail/private_function.rs:LL:CC diff --git a/src/tools/miri/tests/panic/transmute_fat2.rs b/src/tools/miri/tests/panic/transmute_fat2.rs index 0205433ad9fb..e695ff2d57ba 100644 --- a/src/tools/miri/tests/panic/transmute_fat2.rs +++ b/src/tools/miri/tests/panic/transmute_fat2.rs @@ -5,6 +5,8 @@ fn main() { let bad = unsafe { std::mem::transmute::(42 << 64) }; #[cfg(all(target_endian = "little", target_pointer_width = "32"))] let bad = unsafe { std::mem::transmute::(42) }; + #[cfg(all(target_endian = "big", target_pointer_width = "32"))] + let bad = unsafe { std::mem::transmute::(42 << 32) }; // This created a slice with length 0, so the following will fail the bounds check. bad[0]; } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs index cffcf4a867fe..06a8cc7f4879 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs @@ -11,6 +11,10 @@ fn main() { assert!(libc::fcntl(1, libc::F_DUPFD, 0) >= 0); } + // Although `readlink` and `stat` require disable-isolation mode + // to properly run, they are tested with isolation mode on to check the error emitted + // with `-Zmiri-isolation-error=warn-nobacktrace`. + // test `readlink` let mut buf = vec![0; "foo_link.txt".len() + 1]; unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index 05f6c870c3d7..bc755af864c5 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -15,6 +15,8 @@ fn main() { ))] // `pipe2` only exists in some specific os. test_pipe2(); + test_pipe_setfl_getfl(); + test_pipe_fcntl_threaded(); } fn test_pipe() { @@ -127,3 +129,68 @@ fn test_pipe2() { let res = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK) }; assert_eq!(res, 0); } + +/// Basic test for pipe fcntl's F_SETFL and F_GETFL flag. +fn test_pipe_setfl_getfl() { + // Initialise pipe fds. + let mut fds = [-1, -1]; + let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; + assert_eq!(res, 0); + + // Both sides should either have O_RONLY or O_WRONLY. + let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDONLY); + let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; + assert_eq!(res, libc::O_WRONLY); + + // Add the O_NONBLOCK flag with F_SETFL. + let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }; + assert_eq!(res, 0); + + // Test if the O_NONBLOCK flag is successfully added. + let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDONLY | libc::O_NONBLOCK); + + // The other side remains unchanged. + let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; + assert_eq!(res, libc::O_WRONLY); + + // Test if O_NONBLOCK flag can be unset. + let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) }; + assert_eq!(res, 0); + let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDONLY); +} + +/// Test the behaviour of F_SETFL/F_GETFL when a fd is blocking. +/// The expected execution is: +/// 1. Main thread blocks on fds[0] `read`. +/// 2. Thread 1 sets O_NONBLOCK flag on fds[0], +/// checks the value of F_GETFL, +/// then writes to fds[1] to unblock main thread's `read`. +fn test_pipe_fcntl_threaded() { + let mut fds = [-1, -1]; + let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; + assert_eq!(res, 0); + let mut buf: [u8; 5] = [0; 5]; + let thread1 = thread::spawn(move || { + // Add O_NONBLOCK flag while pipe is still blocked on read. + let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }; + assert_eq!(res, 0); + + // Check the new flag value while the main thread is still blocked on fds[0]. + let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; + assert_eq!(res, libc::O_NONBLOCK); + + // The write below will unblock the `read` in main thread: even though + // the socket is now "non-blocking", the shim needs to deal correctly + // with threads that were blocked before the socket was made non-blocking. + let data = "abcde".as_bytes().as_ptr(); + let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + assert_eq!(res, 5); + }); + // The `read` below will block. + let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + thread1.join().unwrap(); + assert_eq!(res, 5); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index 9e48410f7045..c36f6b112244 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -12,6 +12,7 @@ fn main() { test_race(); test_blocking_read(); test_blocking_write(); + test_socketpair_setfl_getfl(); } fn test_socketpair() { @@ -182,3 +183,35 @@ fn test_blocking_write() { thread1.join().unwrap(); thread2.join().unwrap(); } + +/// Basic test for socketpair fcntl's F_SETFL and F_GETFL flag. +fn test_socketpair_setfl_getfl() { + // Initialise socketpair fds. + let mut fds = [-1, -1]; + let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + + // Test if both sides have O_RDWR. + let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDWR); + let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDWR); + + // Add the O_NONBLOCK flag with F_SETFL. + let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }; + assert_eq!(res, 0); + + // Test if the O_NONBLOCK flag is successfully added. + let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDWR | libc::O_NONBLOCK); + + // The other side remains unchanged. + let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDWR); + + // Test if O_NONBLOCK flag can be unset. + let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) }; + assert_eq!(res, 0); + let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; + assert_eq!(res, libc::O_RDWR); +} diff --git a/src/tools/miri/tests/pass-dep/shims/freebsd-cpuset-affinity.rs b/src/tools/miri/tests/pass-dep/shims/freebsd-cpuset-affinity.rs new file mode 100644 index 000000000000..9a868128d271 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/freebsd-cpuset-affinity.rs @@ -0,0 +1,51 @@ +//@only-target: freebsd +//@compile-flags: -Zmiri-num-cpus=256 + +use std::mem; + +fn getaffinity() { + let mut set: libc::cpuset_t = unsafe { mem::zeroed() }; + unsafe { + if libc::cpuset_getaffinity( + libc::CPU_LEVEL_WHICH, + libc::CPU_WHICH_PID, + -1, + size_of::(), + &mut set, + ) == 0 + { + assert!(libc::CPU_COUNT(&set) == 256); + } + } +} + +fn get_small_cpu_mask() { + let mut set: libc::cpuset_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; + + // 256 CPUs so we need 32 bytes to represent this mask. + // According to Freebsd only when `cpusetsize` is smaller than this value, does it return with ERANGE + + let err = unsafe { + libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 32, &mut set) + }; + assert_eq!(err, 0, "Success Expected"); + + // 31 is not enough, so it should fail. + let err = unsafe { + libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 31, &mut set) + }; + assert_eq!(err, -1, "Expected Failure"); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ERANGE); + + // Zero should fail as well. + let err = unsafe { + libc::cpuset_getaffinity(libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, 0, &mut set) + }; + assert_eq!(err, -1, "Expected Failure"); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ERANGE); +} + +fn main() { + getaffinity(); + get_small_cpu_mask(); +} diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs index 698ca4e0b4ba..4ca19046b676 100644 --- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -2,25 +2,28 @@ //@compile-flags: -Zmiri-disable-isolation #![allow(nonstandard_style)] -use std::io::ErrorKind; +use std::io::{ErrorKind, Read, Write}; use std::os::windows::ffi::OsStrExt; +use std::os::windows::io::AsRawHandle; use std::path::Path; -use std::ptr; +use std::{fs, ptr}; #[path = "../../utils/mod.rs"] mod utils; +use windows_sys::Wdk::Storage::FileSystem::{NtReadFile, NtWriteFile}; use windows_sys::Win32::Foundation::{ CloseHandle, ERROR_ACCESS_DENIED, ERROR_ALREADY_EXISTS, ERROR_IO_DEVICE, GENERIC_READ, GENERIC_WRITE, GetLastError, RtlNtStatusToDosError, STATUS_ACCESS_DENIED, - STATUS_IO_DEVICE_ERROR, + STATUS_IO_DEVICE_ERROR, STATUS_SUCCESS, SetLastError, }; use windows_sys::Win32::Storage::FileSystem::{ BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW, - FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_FLAG_BACKUP_SEMANTICS, - FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, - GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, + FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, FILE_CURRENT, + FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, + FILE_SHARE_WRITE, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, SetFilePointerEx, }; +use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; fn main() { unsafe { @@ -31,6 +34,8 @@ fn main() { test_open_dir_reparse(); test_delete_file(); test_ntstatus_to_dos(); + test_file_read_write(); + test_file_seek(); } } @@ -199,13 +204,13 @@ unsafe fn test_open_dir_reparse() { unsafe fn test_delete_file() { let temp = utils::tmp().join("test_delete_file.txt"); let raw_path = to_wide_cstr(&temp); - let _ = std::fs::File::create(&temp).unwrap(); + let _ = fs::File::create(&temp).unwrap(); if DeleteFileW(raw_path.as_ptr()) == 0 { panic!("Failed to delete file"); } - match std::fs::File::open(temp) { + match fs::File::open(temp) { Ok(_) => panic!("File not deleted"), Err(e) => assert!(e.kind() == ErrorKind::NotFound, "File not deleted"), } @@ -217,6 +222,82 @@ unsafe fn test_ntstatus_to_dos() { assert_eq!(RtlNtStatusToDosError(STATUS_ACCESS_DENIED), ERROR_ACCESS_DENIED); } +unsafe fn test_file_read_write() { + let temp = utils::tmp().join("test_file_read_write.txt"); + let file = fs::File::create(&temp).unwrap(); + let handle = file.as_raw_handle(); + + // Testing NtWriteFile doesn't clobber the error + SetLastError(1234); + + let text = b"Example text!"; + let mut status = std::mem::zeroed::(); + let out = NtWriteFile( + handle, + ptr::null_mut(), + None, + ptr::null_mut(), + &mut status, + text.as_ptr().cast(), + text.len() as u32, + ptr::null_mut(), + ptr::null_mut(), + ); + + assert_eq!(out, status.Anonymous.Status); + assert_eq!(out, STATUS_SUCCESS); + assert_eq!(GetLastError(), 1234); + + let file = fs::File::open(&temp).unwrap(); + let handle = file.as_raw_handle(); + + // Testing NtReadFile doesn't clobber the error + SetLastError(1234); + + let mut buffer = vec![0; 13]; + let out = NtReadFile( + handle, + ptr::null_mut(), + None, + ptr::null_mut(), + &mut status, + buffer.as_mut_ptr().cast(), + buffer.len() as u32, + ptr::null_mut(), + ptr::null_mut(), + ); + + assert_eq!(out, status.Anonymous.Status); + assert_eq!(out, STATUS_SUCCESS); + assert_eq!(buffer, text); + assert_eq!(GetLastError(), 1234); +} + +unsafe fn test_file_seek() { + let temp = utils::tmp().join("test_file_seek.txt"); + let mut file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap(); + file.write_all(b"Hello, World!\n").unwrap(); + + let handle = file.as_raw_handle(); + + if SetFilePointerEx(handle, 7, ptr::null_mut(), FILE_BEGIN) == 0 { + panic!("Failed to seek"); + } + + let mut buf = vec![0; 5]; + file.read(&mut buf).unwrap(); + assert_eq!(buf, b"World"); + + let mut pos = 0; + if SetFilePointerEx(handle, -7, &mut pos, FILE_CURRENT) == 0 { + panic!("Failed to seek"); + } + buf.truncate(2); + file.read_exact(&mut buf).unwrap(); + assert_eq!(buf, b", "); + assert_eq!(pos, 5); +} + fn to_wide_cstr(path: &Path) -> Vec { let mut raw_path = path.as_os_str().encode_wide().collect::>(); raw_path.extend([0, 0]); 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 6e88b907f5dc..067753203bbd 100644 --- a/src/tools/miri/tests/pass-dep/tokio/file-io.rs +++ b/src/tools/miri/tests/pass-dep/tokio/file-io.rs @@ -20,7 +20,11 @@ async fn test_create_and_write() -> io::Result<()> { let mut file = File::create(&path).await?; // Write 10 bytes to the file. - file.write(b"some bytes").await?; + file.write_all(b"some bytes").await?; + // For tokio's file I/O, `await` does not have its usual semantics of waiting until the + // operation is completed, so we have to wait some more to make sure the write is completed. + file.flush().await?; + // Check that 10 bytes have been written. assert_eq!(file.metadata().await.unwrap().len(), 10); remove_file(&path).unwrap(); @@ -31,10 +35,10 @@ async fn test_create_and_read() -> io::Result<()> { let bytes = b"more bytes"; let path = utils::prepare_with_content("foo.txt", bytes); let mut file = OpenOptions::new().read(true).open(&path).await.unwrap(); - let mut buffer = [0u8; 10]; + let mut buffer = vec![]; // Read the whole file. - file.read(&mut buffer[..]).await?; + file.read_to_end(&mut buffer).await?; assert_eq!(&buffer, b"more bytes"); remove_file(&path).unwrap(); diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.rs b/src/tools/miri/tests/pass/alloc-access-tracking.rs index c47063bef03c..0e88951dc43f 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.rs +++ b/src/tools/miri/tests/pass/alloc-access-tracking.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -//@compile-flags: -Zmiri-track-alloc-id=21 -Zmiri-track-alloc-accesses -Cpanic=abort -//@normalize-stderr-test: "id 21" -> "id $$ALLOC" +//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort +//@normalize-stderr-test: "id 20" -> "id $$ALLOC" //@only-target: linux # alloc IDs differ between OSes (due to extern static allocations) extern "Rust" { diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs index 2b2e89e6d70f..3de34e570c7e 100644 --- a/src/tools/miri/tests/pass/atomic.rs +++ b/src/tools/miri/tests/pass/atomic.rs @@ -7,15 +7,17 @@ #![allow(static_mut_refs)] use std::sync::atomic::Ordering::*; -use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, compiler_fence, fence}; +use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicPtr, AtomicUsize, compiler_fence, fence}; fn main() { atomic_bool(); atomic_all_ops(); - atomic_u64(); atomic_fences(); atomic_ptr(); weak_sometimes_fails(); + + #[cfg(target_has_atomic = "64")] + atomic_u64(); } fn atomic_bool() { @@ -36,25 +38,10 @@ fn atomic_bool() { } } -// There isn't a trait to use to make this generic, so just use a macro -macro_rules! compare_exchange_weak_loop { - ($atom:expr, $from:expr, $to:expr, $succ_order:expr, $fail_order:expr) => { - loop { - match $atom.compare_exchange_weak($from, $to, $succ_order, $fail_order) { - Ok(n) => { - assert_eq!(n, $from); - break; - } - Err(n) => assert_eq!(n, $from), - } - } - }; -} - /// Make sure we can handle all the intrinsics fn atomic_all_ops() { static ATOMIC: AtomicIsize = AtomicIsize::new(0); - static ATOMIC_UNSIGNED: AtomicU64 = AtomicU64::new(0); + static ATOMIC_UNSIGNED: AtomicUsize = AtomicUsize::new(0); let load_orders = [Relaxed, Acquire, SeqCst]; let stored_orders = [Relaxed, Release, SeqCst]; @@ -94,9 +81,26 @@ fn atomic_all_ops() { } } +#[cfg(target_has_atomic = "64")] fn atomic_u64() { + use std::sync::atomic::AtomicU64; static ATOMIC: AtomicU64 = AtomicU64::new(0); + // There isn't a trait to use to make this generic, so just use a macro + macro_rules! compare_exchange_weak_loop { + ($atom:expr, $from:expr, $to:expr, $succ_order:expr, $fail_order:expr) => { + loop { + match $atom.compare_exchange_weak($from, $to, $succ_order, $fail_order) { + Ok(n) => { + assert_eq!(n, $from); + break; + } + Err(n) => assert_eq!(n, $from), + } + } + }; + } + ATOMIC.store(1, SeqCst); assert_eq!(ATOMIC.compare_exchange(0, 0x100, AcqRel, Acquire), Err(1)); assert_eq!(ATOMIC.compare_exchange(0, 1, Release, Relaxed), Err(1)); 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 1ee5298f17d6..8e167dbadef6 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr @@ -7,12 +7,12 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (>::call_onc RUSTLIB/std/src/sys/backtrace.rs:LL:CC (std::sys::backtrace::__rust_begin_short_backtrace) RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start::{closure#0}) 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/panicking.rs:LL:CC (std::panicking::catch_unwind::do_call) +RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::catch_unwind) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#0}) -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/panicking.rs:LL:CC (std::panicking::catch_unwind::do_call) +RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::catch_unwind) RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind) RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal) RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start) 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 26cdee18e3c2..588bb85f35a4 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr @@ -8,17 +8,17 @@ at RUSTLIB/std/src/rt.rs:LL:CC 4: std::ops::function::impls::call_once at RUSTLIB/core/src/ops/function.rs:LL:CC - 5: std::panicking::r#try::do_call + 5: std::panicking::catch_unwind::do_call at RUSTLIB/std/src/panicking.rs:LL:CC - 6: std::panicking::r#try + 6: std::panicking::catch_unwind 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#0} at RUSTLIB/std/src/rt.rs:LL:CC - 9: std::panicking::r#try::do_call + 9: std::panicking::catch_unwind::do_call at RUSTLIB/std/src/panicking.rs:LL:CC - 10: std::panicking::r#try + 10: std::panicking::catch_unwind at RUSTLIB/std/src/panicking.rs:LL:CC 11: std::panic::catch_unwind at RUSTLIB/std/src/panic.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 d89ae3837b98..9c952755957b 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr @@ -16,17 +16,17 @@ at RUSTLIB/std/src/rt.rs:LL:CC 8: std::ops::function::impls::call_once at RUSTLIB/core/src/ops/function.rs:LL:CC - 9: std::panicking::r#try::do_call + 9: std::panicking::catch_unwind::do_call at RUSTLIB/std/src/panicking.rs:LL:CC - 10: std::panicking::r#try + 10: std::panicking::catch_unwind 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#0} at RUSTLIB/std/src/rt.rs:LL:CC - 13: std::panicking::r#try::do_call + 13: std::panicking::catch_unwind::do_call at RUSTLIB/std/src/panicking.rs:LL:CC - 14: std::panicking::r#try + 14: std::panicking::catch_unwind at RUSTLIB/std/src/panicking.rs:LL:CC 15: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index c76e7f2eebd2..6a625e597df1 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -1,6 +1,7 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(allocator_api)] +use std::cell::Cell; use std::ptr; // Test various aliasing-model-related things. @@ -22,6 +23,7 @@ fn main() { not_unpin_not_protected(); write_does_not_invalidate_all_aliases(); box_into_raw_allows_interior_mutable_alias(); + cell_inside_struct() } // Make sure that reading from an `&mut` does, like reborrowing to `&`, @@ -259,7 +261,7 @@ fn write_does_not_invalidate_all_aliases() { fn box_into_raw_allows_interior_mutable_alias() { unsafe { - let b = Box::new(std::cell::Cell::new(42)); + let b = Box::new(Cell::new(42)); let raw = Box::into_raw(b); let c = &*raw; let d = raw.cast::(); // bypassing `Cell` -- only okay in Miri tests @@ -269,3 +271,19 @@ fn box_into_raw_allows_interior_mutable_alias() { drop(Box::from_raw(raw)); } } + +fn cell_inside_struct() { + struct Foo { + field1: u32, + field2: Cell, + } + + let mut root = Foo { field1: 42, field2: Cell::new(88) }; + let a = &mut root; + + // Writing to `field2`, which is interior mutable, should be allowed. + (*a).field2.set(10); + + // Writing to `field1`, which is reserved, should also be allowed. + (*a).field1 = 88; +} diff --git a/src/tools/miri/tests/pass/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs index 1213f81a6f12..1d65e69bf726 100644 --- a/src/tools/miri/tests/pass/btreemap.rs +++ b/src/tools/miri/tests/pass/btreemap.rs @@ -50,7 +50,7 @@ pub fn main() { test_all_refs(&mut 13, b.values_mut()); // Test forgetting the extractor. - let mut d = b.extract_if(|_, i| *i < 30); + let mut d = b.extract_if(.., |_, i| *i < 30); d.next().unwrap(); mem::forget(d); } diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index d0a7f245ee0f..87df43ca7e57 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -17,20 +17,20 @@ mod utils; fn main() { test_path_conversion(); + test_file(); test_file_create_new(); + test_metadata(); + test_seek(); + test_errors(); + test_from_raw_os_error(); + test_file_clone(); // Windows file handling is very incomplete. if cfg!(not(windows)) { - test_file(); - test_seek(); - test_file_clone(); - test_metadata(); test_file_set_len(); test_file_sync(); - test_errors(); test_rename(); test_directory(); test_canonicalize(); - test_from_raw_os_error(); #[cfg(unix)] test_pread_pwrite(); } diff --git a/src/tools/miri/tests/pass/shims/io.rs b/src/tools/miri/tests/pass/shims/io.rs index 420ef95a0cbf..a96b98c02274 100644 --- a/src/tools/miri/tests/pass/shims/io.rs +++ b/src/tools/miri/tests/pass/shims/io.rs @@ -1,8 +1,10 @@ -use std::io::{self, IsTerminal}; +use std::io::{self, IsTerminal, Write}; fn main() { - // We can't really assume that this is truly a terminal, and anyway on Windows Miri will always - // return `false` here, but we can check that the call succeeds. + io::stdout().write_all(b"stdout\n").unwrap(); + io::stderr().write_all(b"stderr\n").unwrap(); + + // We can't assume that this is truly a terminal, but we can check that the call succeeds. io::stdout().is_terminal(); // Ensure we can format `io::Error` created from OS errors diff --git a/src/tools/miri/tests/pass/shims/io.stderr b/src/tools/miri/tests/pass/shims/io.stderr new file mode 100644 index 000000000000..af6415db3c72 --- /dev/null +++ b/src/tools/miri/tests/pass/shims/io.stderr @@ -0,0 +1 @@ +stderr diff --git a/src/tools/miri/tests/pass/shims/io.stdout b/src/tools/miri/tests/pass/shims/io.stdout new file mode 100644 index 000000000000..faa3a15c184b --- /dev/null +++ b/src/tools/miri/tests/pass/shims/io.stdout @@ -0,0 +1 @@ +stdout diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.stderr index 75a30c9a0837..e09aed2cf5d0 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.stderr @@ -3,8 +3,8 @@ Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 | Act | └─┬── | ReIM| └─┬── -| Cel | ├──── -| Cel | └──── +|?Cel | ├──── +|?Cel | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs new file mode 100644 index 000000000000..abe08f2cd226 --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Zmiri-tree-borrows + +use std::cell::Cell; + +fn foo(x: &Cell) { + unsafe { + let ptr = x as *const Cell as *mut Cell as *mut i32; + ptr.offset(1).write(0); + } +} + +fn main() { + let arr = [Cell::new(1), Cell::new(1)]; + foo(&arr[0]); + + let pair = (Cell::new(1), 1); + // TODO: Ideally, this would result in UB since the second element + // in `pair` is Frozen. We would need some way to express a + // "shared reference with permission to access surrounding + // interior mutable data". + foo(&pair.0); +} diff --git a/src/tools/opt-dist/src/bolt.rs b/src/tools/opt-dist/src/bolt.rs index 0f1fda38115c..a06e59fcc412 100644 --- a/src/tools/opt-dist/src/bolt.rs +++ b/src/tools/opt-dist/src/bolt.rs @@ -80,7 +80,7 @@ pub fn bolt_optimize( // Move jump tables to a separate section .arg("-jump-tables=move") // Fold functions with identical code - .arg("-icf=1") + .arg("-icf=all") // The following flag saves about 50 MiB of libLLVM.so size. // However, it succeeds very non-deterministically. To avoid frequent artifact size swings, // it is kept disabled for now. diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 47159a43140f..36a7d6a7cba4 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -36,7 +36,7 @@ fn init_compiler_benchmarks( profiles.join(",").as_str(), "--scenarios", scenarios.join(",").as_str(), - "--include", + "--exact-match", crates.join(",").as_str(), ]) .env("RUST_LOG", "collector=debug") diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..466672e3d481 --- /dev/null +++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Questions regarding rust-analyzer + url: https://github.com/rust-lang/rust-analyzer/discussions + about: Please ask and answer questions here instead of opening an issue diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md index 23c43443c84f..2b44bdc748f2 100644 --- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md +++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md @@ -12,5 +12,3 @@ Troubleshooting guide: https://rust-analyzer.github.io/book/troubleshooting.html Please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3. --> - -This is a serious regression in nightly and it's important to fix it before the next release. diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/question.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index a90ade882bd9..000000000000 --- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Support Question -about: A question regarding functionality of rust-analyzer. -title: '' -labels: 'C-support' -assignees: '' - ---- diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml index a4146d602185..dc2f432bbc75 100644 --- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml +++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml @@ -18,9 +18,9 @@ jobs: steps: - name: Install Rust toolchain run: | - rustup update --no-self-update stable - rustup default stable - rustup component add --toolchain stable rust-src + rustup update --no-self-update beta + rustup default beta + rustup component add --toolchain beta rust-src - name: Checkout repository uses: actions/checkout@v4 @@ -61,9 +61,9 @@ jobs: steps: - name: Install Rust toolchain run: | - rustup update --no-self-update stable - rustup default stable - rustup component add --toolchain stable rust-src + rustup update --no-self-update beta + rustup default beta + rustup component add --toolchain beta rust-src - name: Checkout repository uses: actions/checkout@v4 diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8d6c8284e44e..01de430925dc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -17,15 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "allocator-api2" version = "0.2.21" @@ -61,9 +52,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -80,6 +71,7 @@ version = "0.0.0" dependencies = [ "cfg", "dashmap", + "indexmap", "intern", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "query-group-macro", @@ -123,12 +115,9 @@ dependencies = [ [[package]] name = "boxcar" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6740c6e2fc6360fa57c35214c7493826aee95993926092606f27c983b40837be" -dependencies = [ - "loom", -] +checksum = "66bb12751a83493ef4b8da1120451a262554e216a247f14b48cb5e8fe7ed8bdf" [[package]] name = "camino" @@ -316,9 +305,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "ctrlc" -version = "3.4.5" +version = "3.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" dependencies = [ "nix", "windows-sys 0.59.0", @@ -472,9 +461,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -510,19 +499,6 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" -[[package]] -name = "generator" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" -dependencies = [ - "cfg-if", - "libc", - "log", - "rustversion", - "windows 0.58.0", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -1010,9 +986,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1123,12 +1099,12 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] @@ -1212,19 +1188,6 @@ version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lsp-server" version = "0.7.8" @@ -1264,15 +1227,6 @@ dependencies = [ "url", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "mbe" version = "0.0.0" @@ -1358,9 +1312,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.9.0", "cfg-if", @@ -1399,16 +1353,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "nu-ansi-term" version = "0.50.1" @@ -1470,12 +1414,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" version = "0.12.3" @@ -1640,14 +1578,14 @@ dependencies = [ [[package]] name = "process-wrap" -version = "8.2.0" +version = "8.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d35f4dc9988d1326b065b4def5e950c3ed727aa03e3151b86cc9e2aec6b03f54" +checksum = "a3ef4f2f0422f23a82ec9f628ea2acd12871c81a9362b02c43c1aa86acfc3ba1" dependencies = [ "indexmap", "nix", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -1749,9 +1687,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.110.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912228bd8ed3beff1f6f9e5e2d4b37c0827ba3e2070060bf3858a311d0e29e30" +checksum = "c33b8fa229789975647ca5426be432c7c327ebde89ab15889928185dbcee3230" dependencies = [ "bitflags 2.9.0", "ra-ap-rustc_hashes", @@ -1761,18 +1699,18 @@ dependencies = [ [[package]] name = "ra-ap-rustc_hashes" -version = "0.110.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba520764daf057a9d963fa769f4762eaf87ac5d4900ae76195eeead64cd35afd" +checksum = "0d68a3e389927002f552938a90b04787f6435f55b46fc5691360470d1cb2e99d" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.110.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76b5f9ee55f2d0e5a65bea23f6d738893349ce8d3d17a6720933e647ab04978" +checksum = "32502273df2838d0ca13f1c67e2a48feef940e591f9771869f07e2db2acede53" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1780,9 +1718,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.110.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd972eb1face2fcaa0d94c01d97862fb955b5561d4f5932003bce8a6cadd8c6" +checksum = "8a32f081864ae34c7ae6634edfa7a95ab9260ba85015e8b1d347580eda79d14f" dependencies = [ "proc-macro2", "quote", @@ -1791,9 +1729,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.110.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a9876456fb2521097deef33ddeac1c18260c8eafb68054d986f8b9d6ce9fa" +checksum = "ed34c51974718c5bd90d876d1364d9725159fc8030c2382b9cb837034152ed68" dependencies = [ "memchr", "unicode-properties", @@ -1802,9 +1740,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.110.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e85de58dfcc60a5f9d5ec0157a657e3f84abd8f22c8a0c4d707cfb42c9011f4" +checksum = "ff0440e5d27facbf4ff13ea651e48c2f6e360b3dbfc56251b41d60719b965fb8" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper", @@ -1812,9 +1750,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.110.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceadf9db550db67deff7eff2e2765109b860c9d7e5bdfca144863020289c823d" +checksum = "a6056efa57aba3aa0cc69a0bf1a8281624c23ad25b05748d11ebcd4668037bfc" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1863,50 +1801,6 @@ dependencies = [ "thiserror 2.0.12", ] -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "rowan" version = "0.15.15" @@ -2025,12 +1919,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "rustversion" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" - [[package]] name = "ryu" version = "1.0.20" @@ -2039,9 +1927,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f80d5cf3c3fcab2cef898012f242a670477a1baa609267376af9cb4409026c5" +checksum = "c8fff508e3d6ef42a32607f7538e17171a877a12015e32036f46e99d00c95781" dependencies = [ "boxcar", "crossbeam-queue", @@ -2062,15 +1950,15 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05303d72606fbf2b9c9523cda2039bb8ecb00304027a3cd7e52b02a65c7d9185" +checksum = "8ea72b3c06f2ce6350fe3a0eeb7aaaf842d1d8352b706973c19c4f02e298a87c" [[package]] name = "salsa-macros" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2f0e2a30c65cb3cd63440c491dde68d9af7e1be2b77832ac7057141107db50" +checksum = "0ce92025bc160b27814a207cb78d680973af17f863c7f4fc56cf3a535e22f378" dependencies = [ "heck", "proc-macro2", @@ -2228,6 +2116,7 @@ version = "0.0.0" dependencies = [ "backtrace", "crossbeam-channel", + "crossbeam-utils", "itertools 0.14.0", "jod-thread", "libc", @@ -2554,15 +2443,9 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ - "matchers", - "nu-ansi-term 0.46.0", - "once_cell", - "regex", "sharded-slab", - "smallvec", "thread_local", "time", - "tracing", "tracing-core", "tracing-log", ] @@ -2573,7 +2456,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f459ca79f1b0d5f71c54ddfde6debfc59c8b6eeb46808ae492077f739dc7b49c" dependencies = [ - "nu-ansi-term 0.50.1", + "nu-ansi-term", "tracing-core", "tracing-log", "tracing-subscriber", @@ -2707,22 +2590,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.9" @@ -2732,74 +2599,56 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows" -version = "0.58.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", ] [[package]] -name = "windows" -version = "0.59.0" +name = "windows-collections" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", + "windows-core", ] [[package]] name = "windows-core" -version = "0.58.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-core" -version = "0.59.0" +name = "windows-future" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-implement 0.59.0", - "windows-interface 0.59.0", - "windows-result 0.3.1", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core", + "windows-link", ] [[package]] name = "windows-implement" -version = "0.58.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-implement" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", @@ -2808,20 +2657,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", @@ -2830,43 +2668,34 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] -name = "windows-result" +name = "windows-numerics" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-targets 0.52.6", + "windows-core", + "windows-link", ] [[package]] name = "windows-result" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ "windows-link", ] @@ -3230,17 +3059,14 @@ dependencies = [ [[package]] name = "zip" -version = "2.4.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" dependencies = [ "arbitrary", "crc32fast", - "crossbeam-utils", - "displaydoc", "flate2", "indexmap", "memchr", - "thiserror 2.0.12", "time", ] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index c4c2fdf34bae..8c5071898466 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -85,11 +85,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.110", default-features = false } -ra-ap-rustc_parse_format = { version = "0.110", default-features = false } -ra-ap-rustc_index = { version = "0.110", default-features = false } -ra-ap-rustc_abi = { version = "0.110", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.110", default-features = false } +ra-ap-rustc_lexer = { version = "0.113", default-features = false } +ra-ap-rustc_parse_format = { version = "0.113", default-features = false } +ra-ap-rustc_index = { version = "0.113", default-features = false } +ra-ap-rustc_abi = { version = "0.113", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.113", default-features = false } # local crates that aren't published to crates.io. These should not have versions. @@ -132,8 +132,8 @@ pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } rayon = "1.10.0" rowan = "=0.15.15" -salsa = { version = "0.21.1", default-features = false, features = ["rayon","salsa_unstable"] } -salsa-macros = "0.21.1" +salsa = { version = "0.22.0", default-features = false, features = ["rayon","salsa_unstable"] } +salsa-macros = "0.22.0" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index e2e3253773fe..3b423a86f97a 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -21,6 +21,7 @@ rustc-hash.workspace = true triomphe.workspace = true semver.workspace = true tracing.workspace = true +indexmap.workspace = true # local deps cfg.workspace = true 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 9660e6e87cca..745238167bc1 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -14,7 +14,7 @@ use dashmap::DashMap; use dashmap::mapref::entry::Entry; use intern::Symbol; use la_arena::{Arena, Idx, RawIdx}; -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet, FxHasher}; use salsa::{Durability, Setter}; use span::Edition; use triomphe::Arc; @@ -24,6 +24,8 @@ use crate::{CrateWorkspaceData, EditionedFileId, RootQueryDb}; pub type ProcMacroPaths = FxHashMap>; +type FxIndexSet = indexmap::IndexSet; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SourceRootId(pub u32); @@ -393,21 +395,21 @@ impl BuiltDependency { pub type CratesIdMap = FxHashMap; #[salsa_macros::input] -#[derive(Debug)] +#[derive(Debug, PartialOrd, Ord)] pub struct Crate { - #[return_ref] + #[returns(ref)] pub data: BuiltCrateData, /// Crate data that is not needed for analysis. /// /// This is split into a separate field to increase incrementality. - #[return_ref] + #[returns(ref)] pub extra_data: ExtraCrateData, // This is in `Arc` because it is shared for all crates in a workspace. - #[return_ref] + #[returns(ref)] pub workspace_data: Arc, - #[return_ref] + #[returns(ref)] pub cfg_options: CfgOptions, - #[return_ref] + #[returns(ref)] pub env: Env, } @@ -474,7 +476,9 @@ impl CrateGraphBuilder { } pub fn set_in_db(self, db: &mut dyn RootQueryDb) -> CratesIdMap { - let mut all_crates = Vec::with_capacity(self.arena.len()); + // For some reason in some repositories we have duplicate crates, so we use a set and not `Vec`. + // We use an `IndexSet` because the list needs to be topologically sorted. + let mut all_crates = FxIndexSet::with_capacity_and_hasher(self.arena.len(), FxBuildHasher); let mut visited = FxHashMap::default(); let mut visited_root_files = FxHashSet::default(); @@ -494,9 +498,11 @@ impl CrateGraphBuilder { ); } - if **old_all_crates != *all_crates { + if old_all_crates.len() != all_crates.len() + || old_all_crates.iter().any(|&krate| !all_crates.contains(&krate)) + { db.set_all_crates_with_durability( - Arc::new(all_crates.into_boxed_slice()), + Arc::new(Vec::from_iter(all_crates).into_boxed_slice()), Durability::MEDIUM, ); } @@ -509,7 +515,7 @@ impl CrateGraphBuilder { crates_map: &CratesMap, visited: &mut FxHashMap, visited_root_files: &mut FxHashSet, - all_crates: &mut Vec, + all_crates: &mut FxIndexSet, source: CrateBuilderId, ) -> Crate { if let Some(&crate_id) = visited.get(&source) { @@ -597,7 +603,7 @@ impl CrateGraphBuilder { input } }; - all_crates.push(crate_input); + all_crates.insert(crate_input); visited.insert(source, crate_input); crate_input } diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a67fbf75c02f..4d4e6cae0373 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -32,6 +32,7 @@ pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet} macro_rules! impl_intern_key { ($id:ident, $loc:ident) => { #[salsa_macros::interned(no_lifetime)] + #[derive(PartialOrd, Ord)] pub struct $id { pub loc: $loc, } @@ -165,6 +166,7 @@ impl Files { } #[salsa_macros::interned(no_lifetime, debug, constructor=from_span)] +#[derive(PartialOrd, Ord)] pub struct EditionedFileId { pub editioned_file_id: span::EditionedFileId, } @@ -356,7 +358,7 @@ fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse Option<&[SyntaxError]> { - #[salsa_macros::tracked(return_ref)] + #[salsa_macros::tracked(returns(ref))] fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option> { let errors = db.parse(file_id).errors(); match &*errors { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 2cbdbe16f9bb..4a9a3b12cfab 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -24,8 +24,8 @@ use crate::{ item_tree::{AttrOwner, ItemTree}, lang_item::{self, LangItem}, nameres::{ - DefMap, LocalDefMap, assoc::{ImplItems, TraitItems}, + crate_def_map, diagnostics::DefDiagnostics, }, signatures::{ @@ -111,16 +111,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { #[salsa::invoke(ItemTree::block_item_tree_query)] fn block_item_tree(&self, block_id: BlockId) -> Arc; - #[salsa::invoke(DefMap::crate_local_def_map_query)] - fn crate_local_def_map(&self, krate: Crate) -> (Arc, Arc); - - #[salsa::invoke(DefMap::crate_def_map_query)] - fn crate_def_map(&self, krate: Crate) -> Arc; - - /// Computes the block-level `DefMap`. - #[salsa::invoke(DefMap::block_def_map_query)] - fn block_def_map(&self, block: BlockId) -> Arc; - /// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution. #[salsa::invoke(macro_def)] fn macro_def(&self, m: MacroId) -> MacroDefId; @@ -363,7 +353,7 @@ fn include_macro_invoc( db: &dyn DefDatabase, krate: Crate, ) -> Arc<[(MacroCallId, EditionedFileId)]> { - db.crate_def_map(krate) + crate_def_map(db, krate) .modules .values() .flat_map(|m| m.scope.iter_macro_invoc()) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index e3775c4931ae..f617c3225ae1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -19,7 +19,6 @@ use rustc_hash::FxHashMap; use smallvec::SmallVec; use span::{Edition, SyntaxContext}; use syntax::{AstPtr, SyntaxNodePtr, ast}; -use triomphe::Arc; use tt::TextRange; use crate::{ @@ -30,7 +29,7 @@ use crate::{ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, PatId, RecordFieldPat, Statement, }, - nameres::DefMap, + nameres::{DefMap, block_def_map}, type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId}, }; @@ -225,8 +224,8 @@ impl ExpressionStore { pub fn blocks<'a>( &'a self, db: &'a dyn DefDatabase, - ) -> impl Iterator)> + 'a { - self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block))) + ) -> impl Iterator + 'a { + self.block_scopes.iter().map(move |&block| (block, block_def_map(db, block))) } pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { @@ -299,17 +298,16 @@ impl ExpressionStore { 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::InOut { expr, .. } + | AsmOperand::Const(expr) + | AsmOperand::Label(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(_) => (), + AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), }), Expr::If { condition, then_branch, else_branch } => { f(*condition); @@ -436,17 +434,16 @@ impl ExpressionStore { 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::InOut { expr, .. } + | AsmOperand::Const(expr) + | AsmOperand::Label(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(_) => (), + AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), }), Expr::If { condition, then_branch, else_branch } => { f(*condition); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 50505d54ba2f..29871f5e04db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -56,7 +56,7 @@ use crate::{ item_scope::BuiltinShadowMode, item_tree::FieldsShape, lang_item::LangItem, - nameres::{DefMap, LocalDefMap, MacroSubNs}, + nameres::{DefMap, LocalDefMap, MacroSubNs, block_def_map}, type_ref::{ ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, Mutability, PathId, Rawness, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId, UseArgRef, @@ -436,8 +436,8 @@ pub struct ExprCollector<'db> { db: &'db dyn DefDatabase, cfg_options: &'db CfgOptions, expander: Expander, - def_map: Arc, - local_def_map: Arc, + def_map: &'db DefMap, + local_def_map: &'db LocalDefMap, module: ModuleId, pub store: ExpressionStoreBuilder, pub(crate) source_map: ExpressionStoreSourceMap, @@ -544,7 +544,7 @@ impl ExprCollector<'_> { current_file_id: HirFileId, ) -> ExprCollector<'_> { let (def_map, local_def_map) = module.local_def_map(db); - let expander = Expander::new(db, current_file_id, &def_map); + let expander = Expander::new(db, current_file_id, def_map); ExprCollector { db, cfg_options: module.krate().cfg_options(db), @@ -1947,7 +1947,7 @@ impl ExprCollector<'_> { let resolver = |path: &_| { self.def_map .resolve_path( - &self.local_def_map, + self.local_def_map, self.db, module, path, @@ -2163,12 +2163,12 @@ impl ExprCollector<'_> { }; let (module, def_map) = - match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) { + match block_id.map(|block_id| (block_def_map(self.db, block_id), block_id)) { Some((def_map, block_id)) => { self.store.block_scopes.push(block_id); (def_map.module_id(DefMap::ROOT), def_map) } - None => (self.module, self.def_map.clone()), + None => (self.module, self.def_map), }; let prev_def_map = mem::replace(&mut self.def_map, def_map); let prev_local_module = mem::replace(&mut self.module, module); @@ -2247,7 +2247,7 @@ impl ExprCollector<'_> { // This could also be a single-segment path pattern. To // decide that, we need to try resolving the name. let (resolved, _) = self.def_map.resolve_path( - &self.local_def_map, + self.local_def_map, self.db, self.module.local_id, &name.clone().into(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs index 629d1f2ada71..be006c98a582 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs @@ -232,6 +232,14 @@ pub(super) fn lower_path( .with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..))); } + if let Some(last_segment_args @ Some(GenericArgs { has_self_type: true, .. })) = + generic_args.last_mut() + { + // Well-formed code cannot have `` without an associated item after, + // and this causes panics in hir-ty lowering. + *last_segment_args = None; + } + let mod_path = Interned::new(ModPath::from_segments(kind, segments)); if type_anchor.is_none() && generic_args.is_empty() { return Some(Path::BarePath(mod_path)); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs index 337cb103bde2..8fd81c7b3dff 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs @@ -4,7 +4,6 @@ use syntax::ast::{self, make}; use test_fixture::WithFixture; use crate::{ - db::DefDatabase, expr_store::{ ExpressionStore, lower::{ @@ -14,13 +13,15 @@ use crate::{ path::Path, pretty, }, + nameres::crate_def_map, test_db::TestDB, }; fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option) { let (db, file_id) = TestDB::with_single_file(""); let krate = db.fetch_test_crate(); - let mut ctx = ExprCollector::new(&db, db.crate_def_map(krate).root_module_id(), file_id.into()); + let mut ctx = + ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into()); let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator); let store = ctx.store.finish(); (db, store, lowered_path) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index 431ea9eb1d46..a46711c67e87 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -324,11 +324,13 @@ mod tests { use test_fixture::WithFixture; use test_utils::{assert_eq_text, extract_offset}; - use crate::{FunctionId, ModuleDefId, db::DefDatabase, test_db::TestDB}; + use crate::{ + FunctionId, ModuleDefId, db::DefDatabase, nameres::crate_def_map, test_db::TestDB, + }; fn find_function(db: &TestDB, file_id: FileId) -> FunctionId { let krate = db.test_crate(); - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(db, krate); let module = crate_def_map.modules_for_file(db, file_id).next().unwrap(); let (_, def) = crate_def_map[module].scope.entries().next().unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index d6645dc1d1d3..29e249b07a72 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -1,9 +1,10 @@ mod block; -use crate::{DefWithBodyId, ModuleDefId, hir::MatchArm, test_db::TestDB}; +use crate::{DefWithBodyId, ModuleDefId, hir::MatchArm, nameres::crate_def_map, test_db::TestDB}; use expect_test::{Expect, expect}; use la_arena::RawIdx; use test_fixture::WithFixture; +use triomphe::Arc; use super::super::*; @@ -11,7 +12,7 @@ fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc, let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let mut fn_def = None; 'outer: for (_, module) in def_map.modules() { for decl in module.scope.declarations() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs index da3b65d4203d..5f7b510bba4b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs @@ -189,8 +189,8 @@ fn f() { } "#, expect![[r#" - BlockId(3801) in BlockRelativeModuleId { block: Some(BlockId(3800)), local_id: Idx::(1) } - BlockId(3800) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } + BlockId(3c01) in BlockRelativeModuleId { block: Some(BlockId(3c00)), local_id: Idx::(1) } + BlockId(3c00) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } crate scope "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs index 80561d647083..efb558a77581 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs @@ -1,6 +1,7 @@ use crate::{ GenericDefId, ModuleDefId, expr_store::pretty::{print_function, print_struct}, + nameres::crate_def_map, test_db::TestDB, }; use expect_test::{Expect, expect}; @@ -12,7 +13,7 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let mut defs = vec![]; for (_, module) in def_map.modules() { for decl in module.scope.declarations() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 9d62d9ce6526..bb75621c7e07 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -52,7 +52,7 @@ pub fn find_path( ignore_local_imports, is_std_item: item_module.krate().data(db).origin.is_lang(), from, - from_def_map: &from.def_map(db), + from_def_map: from.def_map(db), fuel: Cell::new(FIND_PATH_FUEL), }, item, @@ -691,7 +691,7 @@ mod tests { let (def_map, local_def_map) = module.local_def_map(&db); let resolved = def_map .resolve_path( - &local_def_map, + local_def_map, &db, module.local_id, &mod_path, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index f27a4062a63b..271484da7b9a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -297,7 +297,8 @@ pub(crate) fn parse( unfinished_literal.clear(); } - let span = parser.arg_places.get(placeholder_index).and_then(|s| to_span(s.clone())); + let span = + parser.arg_places.get(placeholder_index).and_then(|s| to_span(s.clone())); placeholder_index += 1; let position_span = to_span(position_span); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index db571f045d74..a6138fb6821d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -16,7 +16,7 @@ use crate::{ AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, - nameres::DefMap, + nameres::{DefMap, crate_def_map}, visibility::Visibility, }; @@ -129,7 +129,7 @@ impl ImportMap { fn collect_import_map(db: &dyn DefDatabase, krate: Crate) -> ImportMapIndex { let _p = tracing::info_span!("collect_import_map").entered(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(db, krate); let mut map = FxIndexMap::default(); // We look only into modules that are public(ly reexported), starting with the crate root. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 51a833b5f150..4ad44775ea14 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -10,6 +10,7 @@ use triomphe::Arc; use crate::{ AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, db::DefDatabase, expr_store::path::Path, + nameres::crate_def_map, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -84,13 +85,13 @@ impl LangItemTarget { } /// Salsa query. This will look for lang items in a specific crate. -#[salsa_macros::tracked(return_ref)] +#[salsa_macros::tracked(returns(ref))] pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option> { let _p = tracing::info_span!("crate_lang_items_query").entered(); let mut lang_items = LangItems::default(); - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(db, krate); for (_, module_data) in crate_def_map.modules() { for impl_def in module_data.scope.impls() { @@ -209,7 +210,7 @@ pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option let mut traits = Vec::new(); - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(db, krate); for (_, module_data) in crate_def_map.modules() { for def in module_data.scope.declarations() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 28011bda7c54..b41ff026bcaa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -92,7 +92,7 @@ use crate::{ Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules, Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant, }, - nameres::LocalDefMap, + nameres::{LocalDefMap, block_def_map, crate_def_map, crate_local_def_map}, signatures::VariantFields, }; @@ -324,12 +324,13 @@ pub struct CrateRootModuleId { } impl CrateRootModuleId { - pub fn def_map(&self, db: &dyn DefDatabase) -> Arc { - db.crate_def_map(self.krate) + pub fn def_map(self, db: &dyn DefDatabase) -> &DefMap { + crate_def_map(db, self.krate) } - pub(crate) fn local_def_map(&self, db: &dyn DefDatabase) -> (Arc, Arc) { - db.crate_local_def_map(self.krate) + pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (&DefMap, &LocalDefMap) { + let def_map = crate_local_def_map(db, self.krate); + (def_map.def_map(db), def_map.local(db)) } pub fn krate(self) -> Crate { @@ -390,26 +391,29 @@ pub struct ModuleId { } impl ModuleId { - pub fn def_map(self, db: &dyn DefDatabase) -> Arc { + pub fn def_map(self, db: &dyn DefDatabase) -> &DefMap { match self.block { - Some(block) => db.block_def_map(block), - None => db.crate_def_map(self.krate), + Some(block) => block_def_map(db, block), + None => crate_def_map(db, self.krate), } } - pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (Arc, Arc) { + pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (&DefMap, &LocalDefMap) { match self.block { - Some(block) => (db.block_def_map(block), self.only_local_def_map(db)), - None => db.crate_local_def_map(self.krate), + Some(block) => (block_def_map(db, block), self.only_local_def_map(db)), + None => { + let def_map = crate_local_def_map(db, self.krate); + (def_map.def_map(db), def_map.local(db)) + } } } - pub(crate) fn only_local_def_map(self, db: &dyn DefDatabase) -> Arc { - db.crate_local_def_map(self.krate).1 + pub(crate) fn only_local_def_map(self, db: &dyn DefDatabase) -> &LocalDefMap { + crate_local_def_map(db, self.krate).local(db) } - pub fn crate_def_map(self, db: &dyn DefDatabase) -> Arc { - db.crate_def_map(self.krate) + pub fn crate_def_map(self, db: &dyn DefDatabase) -> &DefMap { + crate_def_map(db, self.krate) } pub fn krate(self) -> Crate { @@ -701,6 +705,16 @@ pub enum AssocItemId { // casting them, and somehow making the constructors private, which would be annoying. impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId); +impl From for ModuleDefId { + fn from(item: AssocItemId) -> Self { + match item { + AssocItemId::FunctionId(f) => f.into(), + AssocItemId::ConstId(c) => c.into(), + AssocItemId::TypeAliasId(t) => t.into(), + } + } +} + #[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)] pub enum GenericDefId { AdtId(AdtId), 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 e21d1415aa29..293868df613a 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 @@ -454,13 +454,13 @@ fn test_concat_expand() { #[rustc_builtin_macro] macro_rules! concat {} -fn main() { concat!("fo", "o", 0, r#""bar""#, "\n", false, '"', '\0'); } +fn main() { concat!("fo", "o", 0, r#""bar""#, "\n", false, '"', -4, - 4, '\0'); } "##, expect![[r##" #[rustc_builtin_macro] macro_rules! concat {} -fn main() { "foo0\"bar\"\nfalse\"\u{0}"; } +fn main() { "foo0\"bar\"\nfalse\"-4-4\u{0}"; } "##]], ); } @@ -509,24 +509,6 @@ fn main() { "s"; } ); } -#[test] -fn test_concat_idents_expand() { - check( - r##" -#[rustc_builtin_macro] -macro_rules! concat_idents {} - -fn main() { concat_idents!(foo, bar); } -"##, - expect![[r##" -#[rustc_builtin_macro] -macro_rules! concat_idents {} - -fn main() { foobar; } -"##]], - ); -} - #[test] fn test_quote_string() { check( 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 800c96ebdae0..dc4334ee0816 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 @@ -39,7 +39,7 @@ use test_fixture::WithFixture; use crate::{ AdtId, Lookup, ModuleDefId, db::DefDatabase, - nameres::{DefMap, ModuleSource}, + nameres::{DefMap, ModuleSource, crate_def_map}, src::HasSource, test_db::TestDB, tt::TopSubtree, @@ -49,7 +49,7 @@ use crate::{ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let errors = def_map .modules() .flat_map(|module| module.1.scope.all_macro_calls()) @@ -113,7 +113,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let (body, sm) = db.body_with_source_map(body); if let Some(it) = - body.blocks(db).find_map(|block| resolve(db, &block.1, ast_id, ast_ptr)) + body.blocks(db).find_map(|block| resolve(db, block.1, ast_id, ast_ptr)) { return Some(it); } @@ -127,7 +127,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros); let krate = db.fetch_test_crate(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let local_id = DefMap::ROOT; let source = def_map[local_id].definition_source(&db); let source_file = match source.value { @@ -142,7 +142,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let ast_id = db.ast_id_map(source.file_id).ast_id(¯o_call_node); let ast_id = InFile::new(source.file_id, ast_id); let ptr = InFile::new(source.file_id, AstPtr::new(¯o_call_node)); - let macro_call_id = resolve(&db, &def_map, ast_id, ptr) + let macro_call_id = resolve(&db, def_map, ast_id, ptr) .unwrap_or_else(|| panic!("unable to find semantic macro call {macro_call_node}")); let expansion_result = db.parse_macro_expansion(macro_call_id); expansions.push((macro_call_node.clone(), expansion_result)); @@ -380,8 +380,4 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { panic!("got invalid macro input: {:?}", parse.errors()); } } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index fc66d8e28d8c..f337f83156a9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -112,6 +112,18 @@ pub struct LocalDefMap { extern_prelude: FxIndexMap)>, } +impl std::hash::Hash for LocalDefMap { + fn hash(&self, state: &mut H) { + let LocalDefMap { extern_prelude } = self; + extern_prelude.len().hash(state); + for (name, (crate_root, extern_crate)) in extern_prelude { + name.hash(state); + crate_root.hash(state); + extern_crate.hash(state); + } + } +} + impl LocalDefMap { pub(crate) const EMPTY: &Self = &Self { extern_prelude: FxIndexMap::with_hasher(rustc_hash::FxBuildHasher) }; @@ -250,7 +262,7 @@ struct BlockRelativeModuleId { } impl BlockRelativeModuleId { - fn def_map(self, db: &dyn DefDatabase, krate: Crate) -> Arc { + fn def_map(self, db: &dyn DefDatabase, krate: Crate) -> &DefMap { self.into_module(krate).def_map(db) } @@ -358,6 +370,87 @@ pub struct ModuleData { pub scope: ItemScope, } +#[inline] +pub fn crate_def_map(db: &dyn DefDatabase, crate_id: Crate) -> &DefMap { + crate_local_def_map(db, crate_id).def_map(db) +} + +#[allow(unused_lifetimes)] +mod __ { + use super::*; + #[salsa_macros::tracked] + pub(crate) struct DefMapPair<'db> { + #[tracked] + #[returns(ref)] + pub(crate) def_map: DefMap, + #[returns(ref)] + pub(crate) local: LocalDefMap, + } +} +pub(crate) use __::DefMapPair; + +#[salsa_macros::tracked(returns(ref))] +pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> { + let krate = crate_id.data(db); + let _p = tracing::info_span!( + "crate_def_map_query", + name=?crate_id + .extra_data(db) + .display_name + .as_ref() + .map(|it| it.crate_name().to_smolstr()) + .unwrap_or_default() + ) + .entered(); + + let module_data = ModuleData::new( + ModuleOrigin::CrateRoot { definition: krate.root_file_id(db) }, + Visibility::Public, + ); + + let def_map = + DefMap::empty(crate_id, Arc::new(DefMapCrateData::new(krate.edition)), module_data, None); + let (def_map, local_def_map) = collector::collect_defs( + db, + def_map, + TreeId::new(krate.root_file_id(db).into(), None), + None, + ); + + DefMapPair::new(db, def_map, local_def_map) +} + +#[salsa_macros::tracked(returns(ref))] +pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap { + let BlockLoc { ast_id, module } = block_id.lookup(db); + + let visibility = Visibility::Module( + ModuleId { krate: module.krate, local_id: DefMap::ROOT, block: module.block }, + VisibilityExplicitness::Implicit, + ); + let module_data = + ModuleData::new(ModuleOrigin::BlockExpr { block: ast_id, id: block_id }, visibility); + + let local_def_map = crate_local_def_map(db, module.krate); + let def_map = DefMap::empty( + module.krate, + local_def_map.def_map(db).data.clone(), + module_data, + Some(BlockInfo { + block: block_id, + parent: BlockRelativeModuleId { block: module.block, local_id: module.local_id }, + }), + ); + + let (def_map, _) = collector::collect_defs( + db, + def_map, + TreeId::new(ast_id.file_id, Some(block_id)), + Some(local_def_map.local(db)), + ); + def_map +} + impl DefMap { /// The module id of a crate or block root. pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0)); @@ -366,77 +459,6 @@ impl DefMap { self.data.edition } - pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: Crate) -> Arc { - db.crate_local_def_map(crate_id).0 - } - - pub(crate) fn crate_local_def_map_query( - db: &dyn DefDatabase, - crate_id: Crate, - ) -> (Arc, Arc) { - let krate = crate_id.data(db); - let _p = tracing::info_span!( - "crate_def_map_query", - name=?crate_id - .extra_data(db) - .display_name - .as_ref() - .map(|it| it.crate_name().to_smolstr()) - .unwrap_or_default() - ) - .entered(); - - let module_data = ModuleData::new( - ModuleOrigin::CrateRoot { definition: krate.root_file_id(db) }, - Visibility::Public, - ); - - let def_map = DefMap::empty( - crate_id, - Arc::new(DefMapCrateData::new(krate.edition)), - module_data, - None, - ); - let (def_map, local_def_map) = collector::collect_defs( - db, - def_map, - TreeId::new(krate.root_file_id(db).into(), None), - None, - ); - - (Arc::new(def_map), Arc::new(local_def_map)) - } - - pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc { - let BlockLoc { ast_id, module } = block_id.lookup(db); - - let visibility = Visibility::Module( - ModuleId { krate: module.krate, local_id: Self::ROOT, block: module.block }, - VisibilityExplicitness::Implicit, - ); - let module_data = - ModuleData::new(ModuleOrigin::BlockExpr { block: ast_id, id: block_id }, visibility); - - let (crate_map, crate_local_map) = db.crate_local_def_map(module.krate); - let def_map = DefMap::empty( - module.krate, - crate_map.data.clone(), - module_data, - Some(BlockInfo { - block: block_id, - parent: BlockRelativeModuleId { block: module.block, local_id: module.local_id }, - }), - ); - - let (def_map, _) = collector::collect_defs( - db, - def_map, - TreeId::new(ast_id.file_id, Some(block_id)), - Some(crate_local_map), - ); - Arc::new(def_map) - } - fn empty( krate: Crate, crate_data: Arc, @@ -595,7 +617,7 @@ impl DefMap { go(&mut buf, db, current_map, "block scope", Self::ROOT); buf.push('\n'); arc = block.parent.def_map(db, self.krate); - current_map = &arc; + current_map = arc; } go(&mut buf, db, current_map, "crate", Self::ROOT); return buf; @@ -628,7 +650,7 @@ impl DefMap { while let Some(block) = current_map.block { format_to!(buf, "{:?} in {:?}\n", block.block, block.parent); arc = block.parent.def_map(db, self.krate); - current_map = &arc; + current_map = arc; } format_to!(buf, "crate scope\n"); @@ -708,7 +730,7 @@ impl DefMap { let mut block = self.block; while let Some(block_info) = block { let parent = block_info.parent.def_map(db, self.krate); - if let Some(it) = f(&parent, block_info.parent.local_id) { + if let Some(it) = f(parent, block_info.parent.local_id) { return Some(it); } block = parent.block; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 448b908936a2..86225d33b4e1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -66,7 +66,16 @@ impl TraitItems { }) } - pub fn attribute_calls(&self) -> impl Iterator, MacroCallId)> + '_ { + pub fn assoc_item_by_name(&self, name: &Name) -> Option { + self.items.iter().find_map(|&(ref item_name, item)| match item { + AssocItemId::FunctionId(_) if item_name == name => Some(item), + AssocItemId::TypeAliasId(_) if item_name == name => Some(item), + AssocItemId::ConstId(_) if item_name == name => Some(item), + _ => None, + }) + } + + pub fn macro_calls(&self) -> impl Iterator, MacroCallId)> + '_ { self.macro_calls.iter().flat_map(|it| it.iter()).copied() } } @@ -100,7 +109,7 @@ impl ImplItems { (Arc::new(ImplItems { items, macro_calls }), DefDiagnostics::new(diagnostics)) } - pub fn attribute_calls(&self) -> impl Iterator, MacroCallId)> + '_ { + pub fn macro_calls(&self) -> impl Iterator, MacroCallId)> + '_ { self.macro_calls.iter().flat_map(|it| it.iter()).copied() } } @@ -108,8 +117,8 @@ impl ImplItems { struct AssocItemCollector<'a> { db: &'a dyn DefDatabase, module_id: ModuleId, - def_map: Arc, - local_def_map: Arc, + def_map: &'a DefMap, + local_def_map: &'a LocalDefMap, diagnostics: Vec, container: ItemContainerId, @@ -174,7 +183,7 @@ impl<'a> AssocItemCollector<'a> { let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id }; match self.def_map.resolve_attr_macro( - &self.local_def_map, + self.local_def_map, self.db, self.module_id.local_id, ast_id_with_path, @@ -246,7 +255,7 @@ impl<'a> AssocItemCollector<'a> { let resolver = |path: &_| { self.def_map .resolve_path( - &self.local_def_map, + self.local_def_map, self.db, self.module_id.local_id, path, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 8df0f092cd0b..350c97c39825 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -26,7 +26,7 @@ use syntax::ast; use triomphe::Arc; use crate::{ - AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, + AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, @@ -43,9 +43,10 @@ use crate::{ nameres::{ BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, ModuleData, ModuleOrigin, ResolveMode, attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id}, + crate_def_map, diagnostics::DefDiagnostic, mod_resolution::ModDir, - path_resolution::ReachedFixedPoint, + path_resolution::{ReachedFixedPoint, ResolvePathResult}, proc_macro::{ProcMacroDef, ProcMacroKind, parse_macro_name_and_helper_attrs}, sub_namespace_match, }, @@ -61,7 +62,7 @@ pub(super) fn collect_defs( db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId, - crate_local_def_map: Option>, + crate_local_def_map: Option<&LocalDefMap>, ) -> (DefMap, LocalDefMap) { let krate = &def_map.krate.data(db); let cfg_options = def_map.krate.cfg_options(db); @@ -216,7 +217,7 @@ struct DefCollector<'a> { def_map: DefMap, local_def_map: LocalDefMap, /// Set only in case of blocks. - crate_local_def_map: Option>, + crate_local_def_map: Option<&'a LocalDefMap>, // The dependencies of the current crate, including optional deps like `test`. deps: FxHashMap, glob_imports: FxHashMap>, @@ -533,7 +534,7 @@ impl DefCollector<'_> { ); let (per_ns, _) = self.def_map.resolve_path( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, DefMap::ROOT, &path, @@ -556,7 +557,7 @@ impl DefCollector<'_> { } fn local_def_map(&mut self) -> &LocalDefMap { - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map) + self.crate_local_def_map.unwrap_or(&self.local_def_map) } /// Adds a definition of procedural macro `name` to the root module. @@ -688,7 +689,7 @@ impl DefCollector<'_> { let vis = self .def_map .resolve_visibility( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, module_id, vis, @@ -731,7 +732,7 @@ impl DefCollector<'_> { names: Option>, extern_crate: Option, ) { - let def_map = self.db.crate_def_map(krate); + let def_map = crate_def_map(self.db, krate); // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` // macros. let root_scope = &def_map[DefMap::ROOT].scope; @@ -811,32 +812,35 @@ impl DefCollector<'_> { let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db, Edition::LATEST)) .entered(); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); - let res = self.def_map.resolve_path_fp_with_macro( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), - self.db, - ResolveMode::Import, - module_id, - &import.path, - BuiltinShadowMode::Module, - None, // An import may resolve to any kind of macro. - ); + let ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info } = + self.def_map.resolve_path_fp_with_macro( + self.crate_local_def_map.unwrap_or(&self.local_def_map), + self.db, + ResolveMode::Import, + module_id, + &import.path, + BuiltinShadowMode::Module, + None, // An import may resolve to any kind of macro. + ); - let def = res.resolved_def; - if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { + if reached_fixedpoint == ReachedFixedPoint::No + || resolved_def.is_none() + || segment_index.is_some() + { return PartialResolvedImport::Unresolved; } - if res.prefix_info.differing_crate { + if prefix_info.differing_crate { return PartialResolvedImport::Resolved( - def.filter_visibility(|v| matches!(v, Visibility::Public)), + resolved_def.filter_visibility(|v| matches!(v, Visibility::Public)), ); } // Check whether all namespaces are resolved. - if def.is_full() { - PartialResolvedImport::Resolved(def) + if resolved_def.is_full() { + PartialResolvedImport::Resolved(resolved_def) } else { - PartialResolvedImport::Indeterminate(def) + PartialResolvedImport::Indeterminate(resolved_def) } } @@ -849,7 +853,7 @@ impl DefCollector<'_> { let vis = self .def_map .resolve_visibility( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, module_id, &directive.import.visibility, @@ -986,6 +990,43 @@ impl DefCollector<'_> { Some(ImportOrExternCrate::Glob(glob)), ); } + Some(ModuleDefId::TraitId(it)) => { + // FIXME: Implement this correctly + // We can't actually call `trait_items`, the reason being that if macro calls + // occur, they will call back into the def map which we might be computing right + // now resulting in a cycle. + // To properly implement this, trait item collection needs to be done in def map + // collection... + let resolutions = if true { + vec![] + } else { + self.db + .trait_items(it) + .items + .iter() + .map(|&(ref name, variant)| { + let res = match variant { + AssocItemId::FunctionId(it) => { + PerNs::values(it.into(), vis, None) + } + AssocItemId::ConstId(it) => { + PerNs::values(it.into(), vis, None) + } + AssocItemId::TypeAliasId(it) => { + PerNs::types(it.into(), vis, None) + } + }; + (Some(name.clone()), res) + }) + .collect::>() + }; + self.update( + module_id, + &resolutions, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); + } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); } @@ -1240,7 +1281,7 @@ impl DefCollector<'_> { }; let resolver = |path: &_| { let resolved_res = self.def_map.resolve_path_fp_with_macro( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, ResolveMode::Other, directive.module_id, @@ -1307,7 +1348,7 @@ impl DefCollector<'_> { ); // Record its helper attributes. if def_id.krate != self.def_map.krate { - let def_map = self.db.crate_def_map(def_id.krate); + let def_map = crate_def_map(self.db, def_id.krate); if let Some(helpers) = def_map.data.exported_derives.get(&def_id) { self.def_map .derive_helpers_in_scope @@ -1553,7 +1594,7 @@ impl DefCollector<'_> { self.def_map.krate, |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, ResolveMode::Other, directive.module_id, @@ -1702,11 +1743,8 @@ impl ModCollector<'_, '_> { let module = self.def_collector.def_map.module_id(module_id); let def_map = &mut self.def_collector.def_map; - let local_def_map = self - .def_collector - .crate_local_def_map - .as_deref() - .unwrap_or(&self.def_collector.local_def_map); + let local_def_map = + self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map); match item { ModItem::Mod(m) => self.collect_module(m, &attrs), @@ -2133,10 +2171,7 @@ impl ModCollector<'_, '_> { let def_map = &mut self.def_collector.def_map; let vis = def_map .resolve_visibility( - self.def_collector - .crate_local_def_map - .as_deref() - .unwrap_or(&self.def_collector.local_def_map), + self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map), self.def_collector.db, self.module_id, visibility, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index a49155d878ca..74ce33a6419e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -17,14 +17,17 @@ use hir_expand::{ name::Name, }; use span::Edition; -use triomphe::Arc; +use stdx::TupleExt; use crate::{ AdtId, LocalModuleId, ModuleDefId, db::DefDatabase, item_scope::{BUILTIN_SCOPE, ImportOrExternCrate}, item_tree::FieldsShape, - nameres::{BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, sub_namespace_match}, + nameres::{ + BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, crate_def_map, + sub_namespace_match, + }, per_ns::PerNs, visibility::{RawVisibility, Visibility}, }; @@ -44,6 +47,7 @@ pub(super) enum ReachedFixedPoint { #[derive(Debug, Clone)] pub(super) struct ResolvePathResult { pub(super) resolved_def: PerNs, + /// The index of the last resolved segment, or `None` if the full path has been resolved. pub(super) segment_index: Option, pub(super) reached_fixedpoint: ReachedFixedPoint, pub(super) prefix_info: ResolvePathResultPrefixInfo, @@ -173,7 +177,6 @@ impl DefMap { return result; } - let mut arc; let mut current_map = self; let mut merge = |new: ResolvePathResult| { @@ -195,8 +198,7 @@ impl DefMap { Some(block) if original_module == Self::ROOT => { // Block modules "inherit" names from its parent module. original_module = block.parent.local_id; - arc = block.parent.def_map(db, current_map.krate); - current_map = &arc; + current_map = block.parent.def_map(db, current_map.krate); } // Proper (non-block) modules, including those in block `DefMap`s, don't. _ => { @@ -204,8 +206,7 @@ impl DefMap { // A module inside a block. Do not resolve items declared in upper blocks, but we do need to get // the prelude items (which are not inserted into blocks because they can be overridden there). original_module = Self::ROOT; - arc = db.crate_def_map(self.krate); - current_map = &arc; + current_map = crate_def_map(db, self.krate); let new = current_map.resolve_path_fp_in_all_preludes( local_def_map, @@ -253,7 +254,7 @@ impl DefMap { cov_mark::hit!(macro_dollar_crate_self); PerNs::types(self.crate_root().into(), Visibility::Public, None) } else { - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(db, krate); let module = def_map.module_id(Self::ROOT); cov_mark::hit!(macro_dollar_crate_other); PerNs::types(module.into(), Visibility::Public, None) @@ -312,7 +313,7 @@ impl DefMap { // Adjust `local_id` to `self`, i.e. the nearest non-block module. if def_map.module_id(local_id).is_block_module() { (ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id); - def_map = &ext; + def_map = ext; } // Go up the module tree but skip block modules as `super` always refers to the @@ -325,7 +326,7 @@ impl DefMap { if def_map.module_id(local_id).is_block_module() { (ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id); - def_map = &ext; + def_map = ext; } } else { stdx::always!(def_map.block.is_none()); @@ -364,7 +365,15 @@ impl DefMap { }, }; - self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module) + self.resolve_remaining_segments( + db, + mode, + segments, + curr_per_ns, + path, + shadow, + original_module, + ) } /// Resolves a path only in the preludes, without accounting for item scopes. @@ -413,7 +422,15 @@ impl DefMap { } }; - self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module) + self.resolve_remaining_segments( + db, + mode, + segments, + curr_per_ns, + path, + shadow, + original_module, + ) } /// 2018-style absolute path -- only extern prelude @@ -441,10 +458,11 @@ impl DefMap { fn resolve_remaining_segments<'a>( &self, + db: &dyn DefDatabase, + mode: ResolveMode, mut segments: impl Iterator, mut curr_per_ns: PerNs, path: &ModPath, - db: &dyn DefDatabase, shadow: BuiltinShadowMode, original_module: LocalModuleId, ) -> ResolvePathResult { @@ -465,6 +483,7 @@ impl DefMap { curr_per_ns = match curr.def { ModuleDefId::ModuleId(module) => { if module.krate != self.krate { + // FIXME: Inefficient let path = ModPath::from_segments( PathKind::SELF, path.segments()[i..].iter().cloned(), @@ -478,7 +497,7 @@ impl DefMap { let resolution = defp_map.resolve_path_fp_with_macro( LocalDefMap::EMPTY, db, - ResolveMode::Other, + mode, module.local_id, &path, shadow, @@ -553,6 +572,44 @@ impl DefMap { ), }; } + def @ ModuleDefId::TraitId(t) if mode == ResolveMode::Import => { + // FIXME: Implement this correctly + // We can't actually call `trait_items`, the reason being that if macro calls + // occur, they will call back into the def map which we might be computing right + // now resulting in a cycle. + // To properly implement this, trait item collection needs to be done in def map + // collection... + let item = + if true { None } else { db.trait_items(t).assoc_item_by_name(segment) }; + return match item { + Some(item) => ResolvePathResult::new( + match item { + crate::AssocItemId::FunctionId(function_id) => PerNs::values( + function_id.into(), + curr.vis, + curr.import.and_then(|it| it.import_or_glob()), + ), + crate::AssocItemId::ConstId(const_id) => PerNs::values( + const_id.into(), + curr.vis, + curr.import.and_then(|it| it.import_or_glob()), + ), + crate::AssocItemId::TypeAliasId(type_alias_id) => { + PerNs::types(type_alias_id.into(), curr.vis, curr.import) + } + }, + ReachedFixedPoint::Yes, + segments.next().map(TupleExt::head), + ResolvePathResultPrefixInfo::default(), + ), + None => ResolvePathResult::new( + PerNs::types(def, curr.vis, curr.import), + ReachedFixedPoint::Yes, + Some(i), + ResolvePathResultPrefixInfo::default(), + ), + }; + } s => { // could be an inherent method call in UFCS form // (`Struct::method`), or some other kind of associated item @@ -715,7 +772,7 @@ impl DefMap { } else { // Extend lifetime keep = prelude.def_map(db); - &keep + keep }; def_map[prelude.local_id].scope.get(name) } else { @@ -725,25 +782,23 @@ impl DefMap { } /// Given a block module, returns its nearest non-block module and the `DefMap` it belongs to. -fn adjust_to_nearest_non_block_module( - db: &dyn DefDatabase, - def_map: &DefMap, +fn adjust_to_nearest_non_block_module<'db>( + db: &'db dyn DefDatabase, + def_map: &'db DefMap, mut local_id: LocalModuleId, -) -> (Arc, LocalModuleId) { +) -> (&'db DefMap, LocalModuleId) { // INVARIANT: `local_id` in `def_map` must be a block module. stdx::always!(def_map.module_id(local_id).is_block_module()); - let mut ext; // This needs to be a local variable due to our mighty lifetime. let mut def_map = def_map; loop { let BlockInfo { parent, .. } = def_map.block.expect("block module without parent module"); - ext = parent.def_map(db, def_map.krate); - def_map = &ext; + def_map = parent.def_map(db, def_map.krate); local_id = parent.local_id; if !parent.is_block_module() { - return (ext, local_id); + return (def_map, local_id); } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 3fd095a9a98a..4a7974c4fa15 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -7,20 +7,25 @@ mod primitives; use base_db::RootQueryDb; use expect_test::{Expect, expect}; use test_fixture::WithFixture; -use triomphe::Arc; -use crate::{db::DefDatabase, nameres::DefMap, test_db::TestDB}; +use crate::{ + nameres::{DefMap, crate_def_map}, + test_db::TestDB, +}; -fn compute_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Arc { +fn compute_crate_def_map( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + cb: impl FnOnce(&DefMap), +) { let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - db.crate_def_map(krate) + cb(crate_def_map(&db, krate)); } fn render_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - db.crate_def_map(krate).dump(&db) + crate_def_map(&db, krate).dump(&db) } fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 179a9c8fec21..948e8bed66de 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -7,24 +7,37 @@ use span::Edition; use test_fixture::WithFixture; use triomphe::Arc; -use crate::{AdtId, ModuleDefId, db::DefDatabase, nameres::tests::TestDB}; +use crate::{ + AdtId, ModuleDefId, + db::DefDatabase, + nameres::{crate_def_map, tests::TestDB}, +}; -fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) { +fn check_def_map_is_not_recomputed( + #[rust_analyzer::rust_fixture] ra_fixture_initial: &str, + #[rust_analyzer::rust_fixture] ra_fixture_change: &str, +) { let (mut db, pos) = TestDB::with_position(ra_fixture_initial); let krate = db.fetch_test_crate(); { let events = db.log_executed(|| { - db.crate_def_map(krate); + crate_def_map(&db, krate); }); - assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}") + assert!( + format!("{events:?}").contains("crate_local_def_map"), + "no crate def map computed:\n{events:#?}", + ) } db.set_file_text(pos.file_id.file_id(&db), ra_fixture_change); { let events = db.log_executed(|| { - db.crate_def_map(krate); + crate_def_map(&db, krate); }); - assert!(!format!("{events:?}").contains("crate_def_map"), "{events:#?}") + assert!( + !format!("{events:?}").contains("crate_local_def_map"), + "crate def map invalidated:\n{events:#?}", + ) } } @@ -44,7 +57,7 @@ pub const BAZ: u32 = 0; ); for &krate in db.all_crates().iter() { - db.crate_def_map(krate); + crate_def_map(&db, krate); } let all_crates_before = db.all_crates(); @@ -94,11 +107,11 @@ pub const BAZ: u32 = 0; let events = db.log_executed(|| { for &krate in db.all_crates().iter() { - db.crate_def_map(krate); + crate_def_map(&db, krate); } }); let invalidated_def_maps = - events.iter().filter(|event| event.contains("crate_def_map")).count(); + events.iter().filter(|event| event.contains("crate_local_def_map")).count(); assert_eq!(invalidated_def_maps, 1, "{events:#?}") } @@ -330,7 +343,7 @@ m!(Z); let krate = db.test_crate(); { let events = db.log_executed(|| { - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(&db, krate); let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); assert_eq!(module_data.scope.resolutions().count(), 4); }); @@ -352,7 +365,7 @@ m!(Z); { let events = db.log_executed(|| { - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(&db, krate); let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); assert_eq!(module_data.scope.resolutions().count(), 4); }); @@ -403,7 +416,7 @@ pub type Ty = (); { let events = db.log_executed(|| { - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(&db, krate); let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); assert_eq!(module_data.scope.resolutions().count(), 8); assert_eq!(module_data.scope.impls().count(), 1); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index 5f8a01523d82..3cba88ec2f17 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -736,7 +736,7 @@ pub struct bar; #[test] fn macro_dollar_crate_is_correct_in_derive_meta() { - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- minicore: derive, clone //- /main.rs crate:main deps:lib @@ -753,13 +753,13 @@ macro_rules! foo { pub use core::clone::Clone; "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] fn expand_derive() { - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- /main.rs crate:main deps:core use core::Copy; @@ -775,8 +775,8 @@ pub macro Copy {} #[rustc_builtin_macro] pub macro Clone {} "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2); } #[test] @@ -803,7 +803,7 @@ pub trait Clone {} fn builtin_derive_with_unresolved_attributes_fall_back() { // Tests that we still resolve derives after ignoring an unresolved attribute. cov_mark::check!(unresolved_attribute_fallback); - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- /main.rs crate:main deps:core use core::{Clone, derive}; @@ -818,8 +818,8 @@ pub macro derive($item:item) {} #[rustc_builtin_macro] pub macro Clone {} "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] @@ -1096,7 +1096,7 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { "#, ); let krate = *db.all_crates().last().expect("no crate graph present"); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); assert_eq!(def_map.data.exported_derives.len(), 1); match def_map.data.exported_derives.values().next() { @@ -1446,7 +1446,7 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } "#, ); let krate = *db.all_crates().last().expect("no crate graph present"); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let root_module = &def_map[DefMap::ROOT].scope; assert!( @@ -1544,7 +1544,7 @@ macro_rules! mk_foo { #[test] fn macro_sub_namespace() { - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- minicore: derive, clone macro_rules! Clone { () => {} } @@ -1553,8 +1553,8 @@ macro_rules! derive { () => {} } #[derive(Clone)] struct S; "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs index 071b55c83d8d..9c97e42f4fd3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -839,6 +839,7 @@ mod foo; #[path = "./foo.rs"] mod foo; "#, + |_| (), ); compute_crate_def_map( @@ -852,6 +853,7 @@ mod bar; #[path = "./foo.rs"] mod foo; "#, + |_| (), ); } @@ -894,3 +896,149 @@ struct AlsoShouldNotAppear; "#]], ) } + +#[test] +fn invalid_imports() { + check( + r#" +//- /main.rs +mod module; + +use self::module::S::new; +use self::module::unresolved; +use self::module::C::const_based; +use self::module::Enum::Variant::NoAssoc; + +//- /module.rs +pub struct S; +impl S { + pub fn new() {} +} +pub const C: () = (); +pub enum Enum { + Variant, +} + "#, + expect![[r#" + crate + NoAssoc: _ + const_based: _ + module: t + new: _ + unresolved: _ + + crate::module + C: v + Enum: t + S: t v + "#]], + ); +} + +#[test] +fn trait_item_imports_same_crate() { + check( + r#" +//- /main.rs +mod module; + +use self::module::Trait::{AssocType, ASSOC_CONST, MACRO_CONST, method}; + +//- /module.rs +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + ASSOC_CONST: _ + AssocType: _ + MACRO_CONST: _ + method: _ + module: t + + crate::module + Trait: t + "#]], + ); + check( + r#" +//- /main.rs +mod module; + +use self::module::Trait::*; + +//- /module.rs +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + module: t + + crate::module + Trait: t + "#]], + ); +} + +#[test] +fn trait_item_imports_differing_crate() { + check( + r#" +//- /main.rs deps:lib crate:main +use lib::Trait::{AssocType, ASSOC_CONST, MACRO_CONST, method}; + +//- /lib.rs crate:lib +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + ASSOC_CONST: _ + AssocType: _ + MACRO_CONST: _ + method: _ + "#]], + ); + check( + r#" +//- /main.rs deps:lib crate:main +use lib::Trait::*; + +//- /lib.rs crate:lib +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 8a8d17018c1b..16988ddf04b2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -34,30 +34,30 @@ use crate::{ item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, ItemScope}, item_tree::ImportAlias, lang_item::LangItemTarget, - nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo}, + nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map}, per_ns::PerNs, type_ref::LifetimeRef, visibility::{RawVisibility, Visibility}, }; #[derive(Debug, Clone)] -pub struct Resolver { +pub struct Resolver<'db> { /// The stack of scopes, where the inner-most scope is the last item. /// /// When using, you generally want to process the scopes in reverse order, /// there's `scopes` *method* for that. - scopes: Vec, - module_scope: ModuleItemMap, + scopes: Vec>, + module_scope: ModuleItemMap<'db>, } #[derive(Clone)] -struct ModuleItemMap { - def_map: Arc, - local_def_map: Arc, +struct ModuleItemMap<'db> { + def_map: &'db DefMap, + local_def_map: &'db LocalDefMap, module_id: LocalModuleId, } -impl fmt::Debug for ModuleItemMap { +impl fmt::Debug for ModuleItemMap<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ModuleItemMap").field("module_id", &self.module_id).finish() } @@ -80,9 +80,9 @@ impl fmt::Debug for ExprScope { } #[derive(Debug, Clone)] -enum Scope { +enum Scope<'db> { /// All the items and imported names of a module - BlockScope(ModuleItemMap), + BlockScope(ModuleItemMap<'db>), /// Brings the generic parameters of an item into scope as well as the `Self` type alias / /// generic for ADTs and impls. GenericParams { def: GenericDefId, params: Arc }, @@ -133,7 +133,7 @@ pub enum LifetimeNs { LifetimeParam(LifetimeParamId), } -impl Resolver { +impl<'db> Resolver<'db> { /// Resolve known trait from std, like `std::futures::Future` pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?; @@ -580,7 +580,7 @@ impl Resolver { for scope in self.scopes() { scope.process_names(&mut res, db); } - let ModuleItemMap { ref def_map, module_id, ref local_def_map } = self.module_scope; + let ModuleItemMap { def_map, module_id, local_def_map } = self.module_scope; // FIXME: should we provide `self` here? // f( // Name::self_param(), @@ -842,14 +842,14 @@ impl Resolver { #[must_use] pub fn update_to_inner_scope( &mut self, - db: &dyn DefDatabase, + db: &'db dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId, ) -> UpdateGuard { #[inline(always)] - fn append_expr_scope( - db: &dyn DefDatabase, - resolver: &mut Resolver, + fn append_expr_scope<'db>( + db: &'db dyn DefDatabase, + resolver: &mut Resolver<'db>, owner: DefWithBodyId, expr_scopes: &Arc, scope_id: ScopeId, @@ -863,7 +863,7 @@ impl Resolver { scope_id, })); if let Some(block) = expr_scopes.block(scope_id) { - let def_map = db.block_def_map(block); + let def_map = block_def_map(db, block); let local_def_map = block.lookup(db).module.only_local_def_map(db); resolver.scopes.push(Scope::BlockScope(ModuleItemMap { def_map, @@ -945,8 +945,8 @@ fn hygiene_info( pub struct UpdateGuard(usize); -impl Resolver { - fn scopes(&self) -> impl Iterator { +impl<'db> Resolver<'db> { + fn scopes(&self) -> impl Iterator> { self.scopes.iter().rev() } @@ -970,12 +970,12 @@ impl Resolver { fn item_scope_(&self) -> (&DefMap, &LocalDefMap, LocalModuleId) { self.scopes() .find_map(|scope| match scope { - Scope::BlockScope(m) => Some((&*m.def_map, &*m.local_def_map, m.module_id)), + Scope::BlockScope(m) => Some((m.def_map, m.local_def_map, m.module_id)), _ => None, }) .unwrap_or(( - &self.module_scope.def_map, - &self.module_scope.local_def_map, + self.module_scope.def_map, + self.module_scope.local_def_map, self.module_scope.module_id, )) } @@ -992,8 +992,8 @@ pub enum ScopeDef { Label(LabelId), } -impl Scope { - fn process_names(&self, acc: &mut ScopeNames, db: &dyn DefDatabase) { +impl<'db> Scope<'db> { + fn process_names(&self, acc: &mut ScopeNames, db: &'db dyn DefDatabase) { match self { Scope::BlockScope(m) => { m.def_map[m.module_id].scope.entries().for_each(|(name, def)| { @@ -1047,7 +1047,11 @@ impl Scope { } } -pub fn resolver_for_expr(db: &dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId) -> Resolver { +pub fn resolver_for_expr( + db: &dyn DefDatabase, + owner: DefWithBodyId, + expr_id: ExprId, +) -> Resolver<'_> { let r = owner.resolver(db); let scopes = db.expr_scopes(owner); let scope_id = scopes.scope_for(expr_id); @@ -1058,25 +1062,25 @@ pub fn resolver_for_scope( db: &dyn DefDatabase, owner: DefWithBodyId, scope_id: Option, -) -> Resolver { +) -> Resolver<'_> { let r = owner.resolver(db); let scopes = db.expr_scopes(owner); resolver_for_scope_(db, scopes, scope_id, r, owner) } -fn resolver_for_scope_( - db: &dyn DefDatabase, +fn resolver_for_scope_<'db>( + db: &'db dyn DefDatabase, scopes: Arc, scope_id: Option, - mut r: Resolver, + mut r: Resolver<'db>, owner: DefWithBodyId, -) -> Resolver { +) -> Resolver<'db> { let scope_chain = scopes.scope_chain(scope_id).collect::>(); r.scopes.reserve(scope_chain.len()); for scope in scope_chain.into_iter().rev() { if let Some(block) = scopes.block(scope) { - let def_map = db.block_def_map(block); + let def_map = block_def_map(db, block); let local_def_map = block.lookup(db).module.only_local_def_map(db); r = r.push_block_scope(def_map, local_def_map); // FIXME: This adds as many module scopes as there are blocks, but resolving in each @@ -1092,18 +1096,26 @@ fn resolver_for_scope_( r } -impl Resolver { - fn push_scope(mut self, scope: Scope) -> Resolver { +impl<'db> Resolver<'db> { + fn push_scope(mut self, scope: Scope<'db>) -> Resolver<'db> { self.scopes.push(scope); self } - fn push_generic_params_scope(self, db: &dyn DefDatabase, def: GenericDefId) -> Resolver { + fn push_generic_params_scope( + self, + db: &'db dyn DefDatabase, + def: GenericDefId, + ) -> Resolver<'db> { let params = db.generic_params(def); self.push_scope(Scope::GenericParams { def, params }) } - fn push_block_scope(self, def_map: Arc, local_def_map: Arc) -> Resolver { + fn push_block_scope( + self, + def_map: &'db DefMap, + local_def_map: &'db LocalDefMap, + ) -> Resolver<'db> { self.push_scope(Scope::BlockScope(ModuleItemMap { def_map, local_def_map, @@ -1116,19 +1128,19 @@ impl Resolver { owner: DefWithBodyId, expr_scopes: Arc, scope_id: ScopeId, - ) -> Resolver { + ) -> Resolver<'db> { self.push_scope(Scope::ExprScope(ExprScope { owner, expr_scopes, scope_id })) } } -impl ModuleItemMap { +impl<'db> ModuleItemMap<'db> { fn resolve_path_in_value_ns( &self, - db: &dyn DefDatabase, + db: &'db dyn DefDatabase, path: &ModPath, ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally( - &self.local_def_map, + self.local_def_map, db, self.module_id, path, @@ -1167,7 +1179,7 @@ impl ModuleItemMap { ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> { let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally( - &self.local_def_map, + self.local_def_map, db, self.module_id, path, @@ -1263,11 +1275,11 @@ impl ScopeNames { pub trait HasResolver: Copy { /// Builds a resolver for type references inside this def. - fn resolver(self, db: &dyn DefDatabase) -> Resolver; + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_>; } impl HasResolver for ModuleId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let (mut def_map, local_def_map) = self.local_def_map(db); let mut module_id = self.local_id; @@ -1289,21 +1301,17 @@ impl HasResolver for ModuleId { } let mut resolver = Resolver { scopes: Vec::with_capacity(modules.len()), - module_scope: ModuleItemMap { - def_map, - local_def_map: local_def_map.clone(), - module_id, - }, + module_scope: ModuleItemMap { def_map, local_def_map, module_id }, }; for def_map in modules.into_iter().rev() { - resolver = resolver.push_block_scope(def_map, local_def_map.clone()); + resolver = resolver.push_block_scope(def_map, local_def_map); } resolver } } impl HasResolver for CrateRootModuleId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let (def_map, local_def_map) = self.local_def_map(db); Resolver { scopes: vec![], @@ -1313,75 +1321,75 @@ impl HasResolver for CrateRootModuleId { } impl HasResolver for TraitId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl HasResolver for TraitAliasId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl + Copy> HasResolver for T { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let def = self.into(); def.module(db).resolver(db).push_generic_params_scope(db, def.into()) } } impl HasResolver for FunctionId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl HasResolver for ConstId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for StaticId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for TypeAliasId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl HasResolver for ImplId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into()) } } impl HasResolver for ExternBlockId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { // Same as parent's lookup_resolver(db, self) } } impl HasResolver for ExternCrateId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for UseId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for DefWithBodyId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { DefWithBodyId::ConstId(c) => c.resolver(db), DefWithBodyId::FunctionId(f) => f.resolver(db), @@ -1392,7 +1400,7 @@ impl HasResolver for DefWithBodyId { } impl HasResolver for ItemContainerId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { ItemContainerId::ModuleId(it) => it.resolver(db), ItemContainerId::TraitId(it) => it.resolver(db), @@ -1403,7 +1411,7 @@ impl HasResolver for ItemContainerId { } impl HasResolver for GenericDefId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { GenericDefId::FunctionId(inner) => inner.resolver(db), GenericDefId::AdtId(adt) => adt.resolver(db), @@ -1418,13 +1426,13 @@ impl HasResolver for GenericDefId { } impl HasResolver for EnumVariantId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { self.lookup(db).parent.resolver(db) } } impl HasResolver for VariantId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { VariantId::EnumVariantId(it) => it.resolver(db), VariantId::StructId(it) => it.resolver(db), @@ -1434,7 +1442,7 @@ impl HasResolver for VariantId { } impl HasResolver for MacroId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { MacroId::Macro2Id(it) => it.resolver(db), MacroId::MacroRulesId(it) => it.resolver(db), @@ -1444,29 +1452,29 @@ impl HasResolver for MacroId { } impl HasResolver for Macro2Id { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for ProcMacroId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for MacroRulesId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } -fn lookup_resolver<'db>( - db: &(dyn DefDatabase + 'db), +fn lookup_resolver( + db: &dyn DefDatabase, lookup: impl Lookup< Database = dyn DefDatabase, Data = impl ItemTreeLoc, >, -) -> Resolver { +) -> Resolver<'_> { lookup.lookup(db).container().resolver(db) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index 470975482951..e30a5b65a1f7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -15,7 +15,7 @@ use triomphe::Arc; use crate::{ LocalModuleId, Lookup, ModuleDefId, ModuleId, db::DefDatabase, - nameres::{DefMap, ModuleSource}, + nameres::{DefMap, ModuleSource, block_def_map, crate_def_map}, src::HasSource, }; @@ -30,9 +30,18 @@ pub(crate) struct TestDB { impl Default for TestDB { fn default() -> Self { + let events = >>>>::default(); let mut this = Self { - storage: Default::default(), - events: Default::default(), + storage: salsa::Storage::new(Some(Box::new({ + let events = events.clone(); + move |event| { + let mut events = events.lock().unwrap(); + if let Some(events) = &mut *events { + events.push(event); + } + } + }))), + events, files: Default::default(), crates_map: Default::default(), }; @@ -45,15 +54,7 @@ impl Default for TestDB { } #[salsa_macros::db] -impl salsa::Database for TestDB { - fn salsa_event(&self, event: &dyn std::ops::Fn() -> salsa::Event) { - let mut events = self.events.lock().unwrap(); - if let Some(events) = &mut *events { - let event = event(); - events.push(event); - } - } -} +impl salsa::Database for TestDB {} impl fmt::Debug for TestDB { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -133,7 +134,7 @@ impl TestDB { pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { for &krate in self.relevant_crates(file_id).iter() { - let crate_def_map = self.crate_def_map(krate); + let crate_def_map = crate_def_map(self, krate); for (local_id, data) in crate_def_map.modules() { if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) { return crate_def_map.module_id(local_id); @@ -146,16 +147,16 @@ impl TestDB { pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { let file_module = self.module_for_file(position.file_id.file_id(self)); let mut def_map = file_module.def_map(self); - let module = self.mod_at_position(&def_map, position); + let module = self.mod_at_position(def_map, position); - def_map = match self.block_at_position(&def_map, position) { + def_map = match self.block_at_position(def_map, position) { Some(it) => it, None => return def_map.module_id(module), }; loop { - let new_map = self.block_at_position(&def_map, position); + let new_map = self.block_at_position(def_map, position); match new_map { - Some(new_block) if !Arc::ptr_eq(&new_block, &def_map) => { + Some(new_block) if !std::ptr::eq(&new_block, &def_map) => { def_map = new_block; } _ => { @@ -206,7 +207,7 @@ impl TestDB { res } - fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option> { + fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<&DefMap> { // Find the smallest (innermost) function in `def_map` containing the cursor. let mut size = None; let mut fn_def = None; @@ -263,7 +264,7 @@ impl TestDB { let mut containing_blocks = scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope)); - if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) { + if let Some(block) = containing_blocks.next().map(|block| block_def_map(self, block)) { return Some(block); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index b42c8d383d4a..3c67ee9fe5b3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -28,7 +28,7 @@ pub enum Visibility { impl Visibility { pub fn resolve( db: &dyn DefDatabase, - resolver: &crate::resolver::Resolver, + resolver: &crate::resolver::Resolver<'_>, raw_vis: &RawVisibility, ) -> Self { // we fall back to public visibility (i.e. fail open) if the path can't be resolved @@ -50,7 +50,7 @@ impl Visibility { return false; } let def_map = from_module.def_map(db); - Self::is_visible_from_def_map_(db, &def_map, to_module, from_module.local_id) + Self::is_visible_from_def_map_(db, def_map, to_module, from_module.local_id) } pub(crate) fn is_visible_from_def_map( @@ -116,7 +116,7 @@ impl Visibility { match def_map.parent() { Some(module) => { parent_arc = module.def_map(db); - def_map = &*parent_arc; + def_map = parent_arc; from_module = module.local_id; } // Reached the root module, nothing left to check. @@ -257,7 +257,7 @@ pub(crate) fn type_alias_visibility_query(db: &dyn DefDatabase, def: TypeAliasId } #[inline] -fn trait_vis(db: &dyn DefDatabase, resolver: &Resolver, trait_id: TraitId) -> Visibility { +fn trait_vis(db: &dyn DefDatabase, resolver: &Resolver<'_>, trait_id: TraitId) -> Visibility { let ItemLoc { id: tree_id, .. } = trait_id.lookup(db); let item_tree = tree_id.item_tree(db); let tr_def = &item_tree[tree_id.value]; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index bb17eb062760..94c97713f065 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -1,4 +1,5 @@ //! A higher level attributes based on TokenTree, with also some shortcuts. +use std::iter; use std::{borrow::Cow, fmt, ops}; use base_db::Crate; @@ -122,16 +123,15 @@ impl RawAttrs { (None, entries @ Some(_)) => Self { entries }, (Some(entries), None) => Self { entries: Some(entries.clone()) }, (Some(a), Some(b)) => { - let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32; + let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1); let items = a .slice .iter() .cloned() .chain(b.slice.iter().map(|it| { let mut it = it.clone(); - it.id.id = (it.id.ast_index() as u32 + last_ast_index) - | ((it.id.cfg_attr_index().unwrap_or(0) as u32) - << AttrId::AST_INDEX_BITS); + let id = it.id.ast_index() + last_ast_index; + it.id = AttrId::new(id, it.id.is_inner_attr()); it })) .collect::>(); @@ -175,25 +175,20 @@ pub struct AttrId { // FIXME: This only handles a single level of cfg_attr nesting // that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again impl AttrId { - const CFG_ATTR_BITS: usize = 7; - const AST_INDEX_MASK: usize = 0x00FF_FFFF; - const AST_INDEX_BITS: usize = Self::AST_INDEX_MASK.count_ones() as usize; - const CFG_ATTR_SET_BITS: u32 = 1 << 31; + const INNER_ATTR_SET_BIT: u32 = 1 << 31; + + pub fn new(id: usize, is_inner: bool) -> Self { + assert!(id <= !Self::INNER_ATTR_SET_BIT as usize); + let id = id as u32; + Self { id: if is_inner { id | Self::INNER_ATTR_SET_BIT } else { id } } + } pub fn ast_index(&self) -> usize { - self.id as usize & Self::AST_INDEX_MASK + (self.id & !Self::INNER_ATTR_SET_BIT) as usize } - pub fn cfg_attr_index(&self) -> Option { - if self.id & Self::CFG_ATTR_SET_BITS == 0 { - None - } else { - Some(self.id as usize >> Self::AST_INDEX_BITS) - } - } - - pub fn with_cfg_attr(self, idx: usize) -> AttrId { - AttrId { id: self.id | ((idx as u32) << Self::AST_INDEX_BITS) | Self::CFG_ATTR_SET_BITS } + pub fn is_inner_attr(&self) -> bool { + self.id & Self::INNER_ATTR_SET_BIT != 0 } } @@ -333,10 +328,7 @@ impl Attr { None => return smallvec![self.clone()], }; let index = self.id; - let attrs = parts - .enumerate() - .take(1 << AttrId::CFG_ATTR_BITS) - .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); + let attrs = parts.filter_map(|attr| Attr::from_tt(db, attr, index)); let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg); let cfg = CfgExpr::parse(&cfg); @@ -467,13 +459,18 @@ fn unescape(s: &str) -> Option> { pub fn collect_attrs( owner: &dyn ast::HasAttrs, ) -> impl Iterator)> { - let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten(); - let outer_attrs = - ast::AttrDocCommentIter::from_syntax_node(owner.syntax()).filter(|el| match el { + let inner_attrs = + inner_attributes(owner.syntax()).into_iter().flatten().zip(iter::repeat(true)); + let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax()) + .filter(|el| match el { Either::Left(attr) => attr.kind().is_outer(), Either::Right(comment) => comment.is_outer(), - }); - outer_attrs.chain(inner_attrs).enumerate().map(|(id, attr)| (AttrId { id: id as u32 }, attr)) + }) + .zip(iter::repeat(false)); + outer_attrs + .chain(inner_attrs) + .enumerate() + .map(|(id, (attr, is_inner))| (AttrId::new(id, is_inner), attr)) } fn inner_attributes( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 68283b916d74..d135584a0809 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -1,5 +1,6 @@ //! Builtin derives. +use either::Either; use intern::sym; use itertools::{Itertools, izip}; use parser::SyntaxKind; @@ -1179,10 +1180,10 @@ fn coerce_pointee_expand( }; new_predicates.push( make::where_pred( - make::ty_path(make::path_from_segments( + Either::Right(make::ty_path(make::path_from_segments( [make::path_segment(new_bounds_target)], false, - )), + ))), new_bounds, ) .clone_for_update(), @@ -1245,7 +1246,9 @@ fn coerce_pointee_expand( substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) }) }); - new_predicates.push(make::where_pred(pred_target, new_bounds).clone_for_update()); + new_predicates.push( + make::where_pred(Either::Right(pred_target), new_bounds).clone_for_update(), + ); } } @@ -1260,10 +1263,10 @@ fn coerce_pointee_expand( // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. where_clause.add_predicate( make::where_pred( - make::ty_path(make::path_from_segments( + Either::Right(make::ty_path(make::path_from_segments( [make::path_segment(make::name_ref(&pointee_param_name.text()))], false, - )), + ))), [make::type_bound(make::ty_path(make::path_from_segments( [ make::path_segment(make::name_ref("core")), 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 621e174cac99..3180b8dae10e 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 @@ -140,7 +140,6 @@ register_builtin! { EagerExpander: (compile_error, CompileError) => compile_error_expand, (concat, Concat) => concat_expand, - (concat_idents, ConcatIdents) => concat_idents_expand, (concat_bytes, ConcatBytes) => concat_bytes_expand, (include, Include) => include_expand, (include_bytes, IncludeBytes) => include_bytes_expand, @@ -452,7 +451,10 @@ fn concat_expand( Some(_) => (), None => span = Some(s), }; - for (i, mut t) in tt.iter().enumerate() { + + let mut i = 0; + let mut iter = tt.iter(); + while let Some(mut t) = iter.next() { // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses // to ensure the right parsing order, so skip the parentheses here. Ideally we'd // implement rustc's model. cc https://github.com/rust-lang/rust-analyzer/pull/10623 @@ -504,10 +506,40 @@ fn concat_expand( record_span(id.span); } TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + // handle negative numbers + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 0 && punct.char == '-' => { + let t = match iter.next() { + Some(t) => t, + None => { + err.get_or_insert(ExpandError::other( + call_site, + "unexpected end of input after '-'", + )); + break; + } + }; + + match t { + TtElement::Leaf(tt::Leaf::Literal(it)) + if matches!(it.kind, tt::LitKind::Integer | tt::LitKind::Float) => + { + format_to!(text, "-{}", it.symbol.as_str()); + record_span(punct.span.cover(it.span)); + } + _ => { + err.get_or_insert(ExpandError::other( + call_site, + "expected integer or floating pointer number after '-'", + )); + break; + } + } + } _ => { err.get_or_insert(ExpandError::other(call_site, "unexpected token")); } } + i += 1; } let span = span.unwrap_or_else(|| tt.top_subtree().delimiter.open); ExpandResult { value: quote!(span =>#text), err } @@ -627,30 +659,6 @@ fn concat_bytes_expand_subtree( Ok(()) } -fn concat_idents_expand( - _db: &dyn ExpandDatabase, - _arg_id: MacroCallId, - tt: &tt::TopSubtree, - span: Span, -) -> ExpandResult { - let mut err = None; - let mut ident = String::new(); - for (i, t) in tt.iter().enumerate() { - match t { - TtElement::Leaf(tt::Leaf::Ident(id)) => { - ident.push_str(id.sym.as_str()); - } - TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), - _ => { - err.get_or_insert(ExpandError::other(span, "unexpected token")); - } - } - } - // FIXME merge spans - let ident = tt::Ident { sym: Symbol::intern(&ident), span, is_raw: tt::IdentIsRaw::No }; - ExpandResult { value: quote!(span =>#ident), err } -} - fn relative_file( db: &dyn ExpandDatabase, call_id: MacroCallId, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index 8a1a33d7e3b4..1cd975b980df 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -19,18 +19,8 @@ pub enum ProcMacroKind { Attr, } -pub trait AsAny: Any { - fn as_any(&self) -> &dyn Any; -} - -impl AsAny for T { - fn as_any(&self) -> &dyn Any { - self - } -} - /// A proc-macro expander implementation. -pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + AsAny { +pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any { /// Run the expander with the given input subtree, optional attribute input subtree (for /// [`ProcMacroKind::Attr`]), environment variables, and span information. fn expand( @@ -44,7 +34,9 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + AsAny { current_dir: String, ) -> Result; - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool; + fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { + other.type_id() == self.type_id() + } } impl PartialEq for dyn ProcMacroExpander { 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 cd799c03ddf7..22b96b55cbb9 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 @@ -259,7 +259,7 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { } fn well_known_trait_id( &self, - well_known_trait: rust_ir::WellKnownTrait, + well_known_trait: WellKnownTrait, ) -> Option> { let lang_attr = lang_item_from_well_known_trait(well_known_trait); let trait_ = lang_attr.resolve_trait(self.db, self.krate)?; 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 d1a1e135ffff..f903b06d65e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -91,7 +91,7 @@ impl From for ConstEvalError { pub(crate) fn path_to_const<'g>( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, mode: ParamLoweringMode, args: impl FnOnce() -> &'g Generics, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 57106412765a..9eb7ffe1c719 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -25,7 +25,7 @@ use triomphe::Arc; use typed_arena::Arena; use crate::{ - Adjust, InferenceResult, Interner, Ty, TyExt, TyKind, + Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind, db::HirDatabase, diagnostics::match_check::{ self, @@ -74,8 +74,9 @@ impl BodyValidationDiagnostic { let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered(); let infer = db.infer(owner); let body = db.body(owner); + let env = db.trait_environment_for_body(owner); let mut validator = - ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints }; + ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints, env }; validator.validate_body(db); validator.diagnostics } @@ -85,6 +86,7 @@ struct ExprValidator { owner: DefWithBodyId, body: Arc, infer: Arc, + env: Arc, diagnostics: Vec, validate_lints: bool, } @@ -190,7 +192,7 @@ impl ExprValidator { return; } - let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db); + let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone()); let pattern_arena = Arena::new(); let mut m_arms = Vec::with_capacity(arms.len()); @@ -317,11 +319,14 @@ impl ExprValidator { return; }; let pattern_arena = Arena::new(); - let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db); + let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone()); for stmt in &**statements { let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else { continue; }; + if self.infer.type_mismatch_for_pat(pat).is_some() { + continue; + } let Some(initializer) = initializer else { continue }; let ty = &self.infer[initializer]; if ty.contains_unknown() { @@ -480,7 +485,7 @@ struct FilterMapNextChecker { } impl FilterMapNextChecker { - fn new(resolver: &hir_def::resolver::Resolver, db: &dyn HirDatabase) -> Self { + fn new(resolver: &hir_def::resolver::Resolver<'_>, db: &dyn HirDatabase) -> Self { // Find and store the FunctionIds for Iterator::filter_map and Iterator::next let (next_function_id, filter_map_function_id) = match LangItem::IteratorNext .resolve_function(db, resolver.krate()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 068fc22f2cac..dd82a0f45ca4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -12,9 +12,10 @@ use rustc_pattern_analysis::{ }; use smallvec::{SmallVec, smallvec}; use stdx::never; +use triomphe::Arc; use crate::{ - AdtId, Interner, Scalar, Ty, TyExt, TyKind, + AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind, db::HirDatabase, infer::normalize, inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from}, @@ -69,13 +70,19 @@ pub(crate) struct MatchCheckCtx<'db> { body: DefWithBodyId, pub(crate) db: &'db dyn HirDatabase, exhaustive_patterns: bool, + env: Arc, } impl<'db> MatchCheckCtx<'db> { - pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self { - let def_map = db.crate_def_map(module.krate()); + pub(crate) fn new( + module: ModuleId, + body: DefWithBodyId, + db: &'db dyn HirDatabase, + env: Arc, + ) -> Self { + let def_map = module.crate_def_map(db); let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); - Self { module, body, db, exhaustive_patterns } + Self { module, body, db, exhaustive_patterns, env } } pub(crate) fn compute_match_usefulness( @@ -100,7 +107,7 @@ impl<'db> MatchCheckCtx<'db> { } fn is_uninhabited(&self, ty: &Ty) -> bool { - is_ty_uninhabited_from(self.db, ty, self.module) + is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone()) } /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`. @@ -459,8 +466,13 @@ impl PatCx for MatchCheckCtx<'_> { } else { let mut variants = IndexVec::with_capacity(enum_data.variants.len()); for &(variant, _) in enum_data.variants.iter() { - let is_uninhabited = - is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module); + let is_uninhabited = is_enum_variant_uninhabited_from( + cx.db, + variant, + subst, + cx.module, + self.env.clone(), + ); let visibility = if is_uninhabited { VariantVisibility::Empty } else { 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 73b99db72684..20cf3c781151 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 @@ -7,7 +7,7 @@ use either::Either; use hir_def::{ AdtId, DefWithBodyId, FieldId, FunctionId, VariantId, expr_store::{Body, path::Path}, - hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp}, + hir::{AsmOperand, Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp}, resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs}, signatures::StaticFlags, type_ref::Rawness, @@ -131,28 +131,28 @@ pub fn unsafe_operations( visitor.walk_expr(current); } -struct UnsafeVisitor<'a> { - db: &'a dyn HirDatabase, - infer: &'a InferenceResult, - body: &'a Body, - resolver: Resolver, +struct UnsafeVisitor<'db> { + db: &'db dyn HirDatabase, + infer: &'db InferenceResult, + body: &'db Body, + resolver: Resolver<'db>, def: DefWithBodyId, inside_unsafe_block: InsideUnsafeBlock, inside_assignment: bool, inside_union_destructure: bool, - callback: &'a mut dyn FnMut(UnsafeDiagnostic), + callback: &'db mut dyn FnMut(UnsafeDiagnostic), def_target_features: TargetFeatures, // FIXME: This needs to be the edition of the span of each call. edition: Edition, } -impl<'a> UnsafeVisitor<'a> { +impl<'db> UnsafeVisitor<'db> { fn new( - db: &'a dyn HirDatabase, - infer: &'a InferenceResult, - body: &'a Body, + db: &'db dyn HirDatabase, + infer: &'db InferenceResult, + body: &'db Body, def: DefWithBodyId, - unsafe_expr_cb: &'a mut dyn FnMut(UnsafeDiagnostic), + unsafe_expr_cb: &'db mut dyn FnMut(UnsafeDiagnostic), ) -> Self { let resolver = def.resolver(db); let def_target_features = match def { @@ -199,6 +199,17 @@ impl<'a> UnsafeVisitor<'a> { } } + fn with_inside_unsafe_block( + &mut self, + inside_unsafe_block: InsideUnsafeBlock, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + let old = mem::replace(&mut self.inside_unsafe_block, inside_unsafe_block); + let result = f(self); + self.inside_unsafe_block = old; + result + } + fn walk_pats_top(&mut self, pats: impl Iterator, parent_expr: ExprId) { let guard = self.resolver.update_to_inner_scope(self.db, self.def, parent_expr); pats.for_each(|pat| self.walk_pat(pat)); @@ -303,7 +314,29 @@ impl<'a> UnsafeVisitor<'a> { self.walk_pats_top(std::iter::once(target), current); self.inside_assignment = old_inside_assignment; } - Expr::InlineAsm(_) => self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm), + Expr::InlineAsm(asm) => { + self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm); + asm.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } + | AsmOperand::Const(expr) => self.walk_expr(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.walk_expr(*in_expr); + if let Some(out_expr) = out_expr { + self.walk_expr(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), + AsmOperand::Label(expr) => { + // Inline asm labels are considered safe even when inside unsafe blocks. + self.with_inside_unsafe_block(InsideUnsafeBlock::No, |this| { + this.walk_expr(*expr) + }); + } + }); + return; + } // rustc allows union assignment to propagate through field accesses and casts. Expr::Cast { .. } => self.inside_assignment = inside_assignment, Expr::Field { .. } => { @@ -317,17 +350,16 @@ impl<'a> UnsafeVisitor<'a> { } } Expr::Unsafe { statements, .. } => { - let old_inside_unsafe_block = - mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes); - self.walk_pats_top( - statements.iter().filter_map(|statement| match statement { - &Statement::Let { pat, .. } => Some(pat), - _ => None, - }), - current, - ); - self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child)); - self.inside_unsafe_block = old_inside_unsafe_block; + self.with_inside_unsafe_block(InsideUnsafeBlock::Yes, |this| { + this.walk_pats_top( + statements.iter().filter_map(|statement| match statement { + &Statement::Let { pat, .. } => Some(pat), + _ => None, + }), + current, + ); + this.body.walk_child_exprs_without_pats(current, |child| this.walk_expr(child)); + }); return; } Expr::Block { statements, .. } | Expr::Async { statements, .. } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index f0989d9de91f..f210dd8799f9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1463,6 +1463,8 @@ impl HirDisplay for Ty { } if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() { write!(f, " -> ")?; + // FIXME: We display `AsyncFn` as `-> impl Future`, but this is hard to fix because + // we don't have a trait environment here, required to normalize `::Output`. sig.ret().hir_fmt(f)?; } } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 106b996b13ef..ed8d8dc26240 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -9,8 +9,8 @@ use chalk_ir::{ }; use chalk_solve::rust_ir::InlineBound; use hir_def::{ - AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, - lang_item::LangItem, signatures::TraitFlags, + AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, + TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; use rustc_hash::FxHashSet; use smallvec::SmallVec; @@ -343,7 +343,7 @@ where }) } AssocItemId::TypeAliasId(it) => { - let def_map = db.crate_def_map(trait_.krate(db)); + let def_map = CrateRootModuleId::from(trait_.krate(db)).def_map(db); if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { ControlFlow::Continue(()) } else { 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 f0ec31db8bb9..e698fb201cb8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -594,16 +594,16 @@ impl Index for InferenceResult { /// The inference context contains all information needed during type inference. #[derive(Clone, Debug)] -pub(crate) struct InferenceContext<'a> { - pub(crate) db: &'a dyn HirDatabase, +pub(crate) struct InferenceContext<'db> { + pub(crate) db: &'db dyn HirDatabase, pub(crate) owner: DefWithBodyId, - pub(crate) body: &'a Body, + pub(crate) body: &'db Body, /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. - pub(crate) resolver: Resolver, + pub(crate) resolver: Resolver<'db>, generic_def: GenericDefId, generics: OnceCell, - table: unify::InferenceTable<'a>, + table: unify::InferenceTable<'db>, /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet, pub(crate) result: InferenceResult, @@ -695,12 +695,12 @@ enum ImplTraitReplacingMode { TypeAlias, } -impl<'a> InferenceContext<'a> { +impl<'db> InferenceContext<'db> { fn new( - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, owner: DefWithBodyId, - body: &'a Body, - resolver: Resolver, + body: &'db Body, + resolver: Resolver<'db>, ) -> Self { let trait_env = db.trait_environment_for_body(owner); InferenceContext { 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 800897c6fc3a..bd57ca891620 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 @@ -38,7 +38,7 @@ use crate::{ infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, make_binders, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, - to_chalk_trait_id, + to_assoc_type_id, to_chalk_trait_id, traits::FnTrait, utils::{self, elaborate_clause_supertraits}, }; @@ -245,7 +245,7 @@ impl InferenceContext<'_> { } fn deduce_closure_kind_from_predicate_clauses( - &self, + &mut self, expected_ty: &Ty, clauses: impl DoubleEndedIterator, closure_kind: ClosureKind, @@ -378,7 +378,7 @@ impl InferenceContext<'_> { } fn deduce_sig_from_projection( - &self, + &mut self, closure_kind: ClosureKind, projection_ty: &ProjectionTy, projected_ty: &Ty, @@ -392,13 +392,16 @@ impl InferenceContext<'_> { // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits, // for closures and async closures, respectively. - match closure_kind { - ClosureKind::Closure | ClosureKind::Async - if self.fn_trait_kind_from_trait_id(trait_).is_some() => - { - self.extract_sig_from_projection(projection_ty, projected_ty) - } - _ => None, + let fn_trait_kind = self.fn_trait_kind_from_trait_id(trait_)?; + if !matches!(closure_kind, ClosureKind::Closure | ClosureKind::Async) { + return None; + } + if fn_trait_kind.is_async() { + // If the expected trait is `AsyncFn(...) -> X`, we don't know what the return type is, + // but we do know it must implement `Future`. + self.extract_async_fn_sig_from_projection(projection_ty, projected_ty) + } else { + self.extract_sig_from_projection(projection_ty, projected_ty) } } @@ -424,6 +427,39 @@ impl InferenceContext<'_> { ))) } + fn extract_async_fn_sig_from_projection( + &mut self, + projection_ty: &ProjectionTy, + projected_ty: &Ty, + ) -> Option> { + let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner); + + let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else { + return None; + }; + + let ret_param_future_output = projected_ty; + let ret_param_future = self.table.new_type_var(); + let future_output = + LangItem::FutureOutput.resolve_type_alias(self.db, self.resolver.krate())?; + let future_projection = crate::AliasTy::Projection(crate::ProjectionTy { + associated_ty_id: to_assoc_type_id(future_output), + substitution: Substitution::from1(Interner, ret_param_future.clone()), + }); + self.table.register_obligation( + crate::AliasEq { alias: future_projection, ty: ret_param_future_output.clone() } + .cast(Interner), + ); + + Some(FnSubst(Substitution::from_iter( + Interner, + input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new( + Interner, + chalk_ir::GenericArgData::Ty(ret_param_future), + ))), + ))) + } + fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option { FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index e3c4f5562d5c..003364d4336b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -61,7 +61,7 @@ impl<'a> InferenceTyLoweringContext<'a> { #[inline] pub(super) fn new( db: &'a dyn HirDatabase, - resolver: &'a Resolver, + resolver: &'a Resolver<'_>, store: &'a ExpressionStore, diagnostics: &'a Diagnostics, source: InferenceTyDiagnosticSource, 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 8084b394d044..87b7f3406ff7 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 @@ -959,8 +959,8 @@ impl InferenceContext<'_> { } Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), Expr::InlineAsm(asm) => { - let mut check_expr_asm_operand = |expr, is_input: bool| { - let ty = self.infer_expr_no_expect(expr, ExprIsRead::Yes); + let check_expr_asm_operand = |this: &mut Self, expr, is_input: bool| { + let ty = this.infer_expr_no_expect(expr, ExprIsRead::Yes); // 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 @@ -970,18 +970,18 @@ impl InferenceContext<'_> { // 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); + let ty = this.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(), + CallableSig::from_def(this.db, *def, parameters).to_fn_ptr(), ) .intern(Interner); - _ = self.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes); + _ = this.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes); } TyKind::Ref(mutbl, _, base_ty) => { let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); - _ = self.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes); + _ = this.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes); } _ => {} } @@ -990,22 +990,28 @@ impl InferenceContext<'_> { 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::In { expr, .. } => check_expr_asm_operand(self, expr, true), AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => { - check_expr_asm_operand(expr, false) + check_expr_asm_operand(self, expr, false) } AsmOperand::Out { expr: None, .. } => (), AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - check_expr_asm_operand(in_expr, true); + check_expr_asm_operand(self, in_expr, true); if let Some(out_expr) = out_expr { - check_expr_asm_operand(out_expr, false); + check_expr_asm_operand(self, out_expr, false); } } - // FIXME - AsmOperand::Label(_) => (), - // FIXME - AsmOperand::Const(_) => (), - // FIXME + AsmOperand::Label(expr) => { + self.infer_expr( + expr, + &Expectation::HasType(self.result.standard_types.unit.clone()), + ExprIsRead::No, + ); + } + AsmOperand::Const(expr) => { + self.infer_expr(expr, &Expectation::None, ExprIsRead::No); + } + // FIXME: `sym` should report for things that are not functions or statics. AsmOperand::Sym(_) => (), }); if diverge { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index e0c3279d3fb0..e81a5e3c3114 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -7,17 +7,24 @@ use chalk_ir::{ }; use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility}; use rustc_hash::FxHashSet; +use triomphe::Arc; use crate::{ - Binders, Interner, Substitution, Ty, TyKind, consteval::try_const_usize, db::HirDatabase, + AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind, + consteval::try_const_usize, db::HirDatabase, }; // FIXME: Turn this into a query, it can be quite slow /// Checks whether a type is visibly uninhabited from a particular module. -pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool { +pub(crate) fn is_ty_uninhabited_from( + db: &dyn HirDatabase, + ty: &Ty, + target_mod: ModuleId, + env: Arc, +) -> bool { let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered(); let mut uninhabited_from = - UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; + UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env }; let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST); inhabitedness == BREAK_VISIBLY_UNINHABITED } @@ -29,11 +36,12 @@ pub(crate) fn is_enum_variant_uninhabited_from( variant: EnumVariantId, subst: &Substitution, target_mod: ModuleId, + env: Arc, ) -> bool { let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered(); let mut uninhabited_from = - UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; + UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env }; let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst); inhabitedness == BREAK_VISIBLY_UNINHABITED } @@ -44,6 +52,7 @@ struct UninhabitedFrom<'a> { // guard for preventing stack overflow in non trivial non terminating types max_depth: usize, db: &'a dyn HirDatabase, + env: Arc, } const CONTINUE_OPAQUELY_INHABITED: ControlFlow = Continue(()); @@ -78,6 +87,12 @@ impl TypeVisitor for UninhabitedFrom<'_> { Some(0) | None => CONTINUE_OPAQUELY_INHABITED, Some(1..) => item_ty.super_visit_with(self, outer_binder), }, + TyKind::Alias(AliasTy::Projection(projection)) => { + // FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle + // `TyKind::AssociatedType`, but perhaps in the future it will. + let normalized = self.db.normalize_projection(projection.clone(), self.env.clone()); + self.visit_ty(&normalized, outer_binder) + } _ => CONTINUE_OPAQUELY_INHABITED, }; self.recursive_ty.remove(ty); 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 9def39d5f979..ea8e7cc2be90 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -42,7 +42,6 @@ use hir_def::{ use hir_expand::name::Name; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashSet; -use rustc_pattern_analysis::Captures; use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; @@ -151,10 +150,10 @@ impl LifetimeElisionKind { } #[derive(Debug)] -pub struct TyLoweringContext<'a> { - pub db: &'a dyn HirDatabase, - resolver: &'a Resolver, - store: &'a ExpressionStore, +pub struct TyLoweringContext<'db> { + pub db: &'db dyn HirDatabase, + resolver: &'db Resolver<'db>, + store: &'db ExpressionStore, def: GenericDefId, generics: OnceCell, in_binders: DebruijnIndex, @@ -170,11 +169,11 @@ pub struct TyLoweringContext<'a> { lifetime_elision: LifetimeElisionKind, } -impl<'a> TyLoweringContext<'a> { +impl<'db> TyLoweringContext<'db> { pub fn new( - db: &'a dyn HirDatabase, - resolver: &'a Resolver, - store: &'a ExpressionStore, + db: &'db dyn HirDatabase, + resolver: &'db Resolver<'db>, + store: &'db ExpressionStore, def: GenericDefId, lifetime_elision: LifetimeElisionKind, ) -> Self { @@ -1176,13 +1175,13 @@ where /// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. /// Exception is Self of a trait def. -fn implicitly_sized_clauses<'a, 'subst: 'a>( - db: &dyn HirDatabase, +fn implicitly_sized_clauses<'db, 'a, 'subst: 'a>( + db: &'db dyn HirDatabase, def: GenericDefId, explicitly_unsized_tys: &'a FxHashSet, substitution: &'subst Substitution, - resolver: &Resolver, -) -> Option + Captures<'a> + Captures<'subst>> { + resolver: &Resolver<'db>, +) -> Option> { let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()).map(to_chalk_trait_id)?; let trait_self_idx = trait_self_param_idx(db, def); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 8e549ca0cbd5..3b295d41e6e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -10,7 +10,7 @@ use chalk_ir::{UniverseIndex, WithKind, cast::Cast}; use hir_def::{ AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleId, TraitId, - nameres::{DefMap, assoc::ImplItems}, + nameres::{DefMap, assoc::ImplItems, block_def_map, crate_def_map}, signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags}, }; use hir_expand::name::Name; @@ -152,7 +152,7 @@ impl TraitImpls { let _p = tracing::info_span!("trait_impls_in_crate_query", ?krate).entered(); let mut impls = FxHashMap::default(); - Self::collect_def_map(db, &mut impls, &db.crate_def_map(krate)); + Self::collect_def_map(db, &mut impls, crate_def_map(db, krate)); Arc::new(Self::finish(impls)) } @@ -164,7 +164,7 @@ impl TraitImpls { let _p = tracing::info_span!("trait_impls_in_block_query").entered(); let mut impls = FxHashMap::default(); - Self::collect_def_map(db, &mut impls, &db.block_def_map(block)); + Self::collect_def_map(db, &mut impls, block_def_map(db, block)); if impls.is_empty() { None } else { Some(Arc::new(Self::finish(impls))) } } @@ -214,7 +214,7 @@ impl TraitImpls { for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db) { - Self::collect_def_map(db, map, &block_def_map); + Self::collect_def_map(db, map, block_def_map); } } } @@ -280,8 +280,8 @@ impl InherentImpls { let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered(); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; - let crate_def_map = db.crate_def_map(krate); - impls.collect_def_map(db, &crate_def_map); + let crate_def_map = crate_def_map(db, krate); + impls.collect_def_map(db, crate_def_map); impls.shrink_to_fit(); Arc::new(impls) @@ -294,8 +294,8 @@ impl InherentImpls { let _p = tracing::info_span!("inherent_impls_in_block_query").entered(); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; - let block_def_map = db.block_def_map(block); - impls.collect_def_map(db, &block_def_map); + let block_def_map = block_def_map(db, block); + impls.collect_def_map(db, block_def_map); impls.shrink_to_fit(); if impls.map.is_empty() && impls.invalid_impls.is_empty() { @@ -337,7 +337,7 @@ impl InherentImpls { for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db) { - self.collect_def_map(db, &block_def_map); + self.collect_def_map(db, block_def_map); } } } @@ -1399,7 +1399,7 @@ fn iterate_inherent_methods( )?; } - block = db.block_def_map(block_id).parent().and_then(|module| module.containing_block()); + block = block_def_map(db, block_id).parent().and_then(|module| module.containing_block()); } for krate in def_crates { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 26ef95d264be..90c52ee96f1f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -5,6 +5,7 @@ use std::cmp::{self, Ordering}; use chalk_ir::TyKind; use hir_def::{ + CrateRootModuleId, builtin_type::{BuiltinInt, BuiltinUint}, resolver::HasResolver, }; @@ -153,7 +154,7 @@ impl Evaluator<'_> { ) -> Result> { // `PanicFmt` is redirected to `ConstPanicFmt` if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) { - let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db); + let resolver = CrateRootModuleId::from(self.crate_id).resolver(self.db); let Some(const_panic_fmt) = LangItem::ConstPanicFmt.resolve_function(self.db, resolver.krate()) @@ -1120,7 +1121,7 @@ impl Evaluator<'_> { // We don't call any drop glue yet, so there is nothing here Ok(()) } - "transmute" => { + "transmute" | "transmute_unchecked" => { let [arg] = args else { return Err(MirEvalError::InternalError( "transmute arg is not provided".into(), 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 7b48b15d9ea9..e6caf2d8d97a 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 @@ -25,7 +25,7 @@ use syntax::TextRange; use triomphe::Arc; use crate::{ - Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, + Adjust, Adjustment, AutoBorrow, CallableDefId, TraitEnvironment, TyBuilder, TyExt, consteval::ConstEvalError, db::{HirDatabase, InternedClosure, InternedClosureId}, display::{DisplayTarget, HirDisplay, hir_display_with_store}, @@ -68,17 +68,18 @@ struct DropScope { locals: Vec, } -struct MirLowerCtx<'a> { +struct MirLowerCtx<'db> { result: MirBody, owner: DefWithBodyId, current_loop_blocks: Option, labeled_loop_blocks: FxHashMap, discr_temp: Option, - db: &'a dyn HirDatabase, - body: &'a Body, - infer: &'a InferenceResult, - resolver: Resolver, + db: &'db dyn HirDatabase, + body: &'db Body, + infer: &'db InferenceResult, + resolver: Resolver<'db>, drop_scopes: Vec, + env: Arc, } // FIXME: Make this smaller, its stored in database queries @@ -288,6 +289,7 @@ impl<'ctx> MirLowerCtx<'ctx> { closures: vec![], }; let resolver = owner.resolver(db); + let env = db.trait_environment_for_body(owner); MirLowerCtx { result: mir, @@ -300,6 +302,7 @@ impl<'ctx> MirLowerCtx<'ctx> { labeled_loop_blocks: Default::default(), discr_temp: None, drop_scopes: vec![DropScope::default()], + env, } } @@ -944,10 +947,7 @@ impl<'ctx> MirLowerCtx<'ctx> { 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), - ); + let mut table = InferenceTable::new(self.db, self.env.clone()); cast_kind(&mut table, &source_ty, &target_ty)? }; @@ -1412,11 +1412,8 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { - let size = || { - self.db - .layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner)) - .map(|it| it.size.bytes_usize()) - }; + let size = + || self.db.layout_of_ty(ty.clone(), self.env.clone()).map(|it| it.size.bytes_usize()); const USIZE_SIZE: usize = size_of::(); let bytes: Box<[_]> = match l { hir_def::hir::Literal::String(b) => { @@ -1723,7 +1720,12 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn is_uninhabited(&self, expr_id: ExprId) -> bool { - is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db)) + is_ty_uninhabited_from( + self.db, + &self.infer[expr_id], + self.owner.module(self.db), + self.env.clone(), + ) } /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index bcd8aa6c4e95..d049c678e2db 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -7,7 +7,7 @@ use base_db::{ SourceRoot, SourceRootId, SourceRootInput, }; -use hir_def::{ModuleId, db::DefDatabase}; +use hir_def::{ModuleId, db::DefDatabase, nameres::crate_def_map}; use hir_expand::EditionedFileId; use rustc_hash::FxHashMap; use salsa::{AsDynDatabase, Durability}; @@ -27,9 +27,18 @@ pub(crate) struct TestDB { impl Default for TestDB { fn default() -> Self { + let events = >>>>::default(); let mut this = Self { - storage: Default::default(), - events: Default::default(), + storage: salsa::Storage::new(Some(Box::new({ + let events = events.clone(); + move |event| { + let mut events = events.lock().unwrap(); + if let Some(events) = &mut *events { + events.push(event); + } + } + }))), + events, files: Default::default(), crates_map: Default::default(), }; @@ -103,14 +112,7 @@ impl SourceDatabase for TestDB { } #[salsa_macros::db] -impl salsa::Database for TestDB { - fn salsa_event(&self, event: &dyn std::ops::Fn() -> salsa::Event) { - let mut events = self.events.lock().unwrap(); - if let Some(events) = &mut *events { - events.push(event()); - } - } -} +impl salsa::Database for TestDB {} impl panic::RefUnwindSafe for TestDB {} @@ -118,7 +120,7 @@ impl TestDB { pub(crate) fn module_for_file_opt(&self, file_id: impl Into) -> Option { let file_id = file_id.into(); for &krate in self.relevant_crates(file_id).iter() { - let crate_def_map = self.crate_def_map(krate); + let crate_def_map = crate_def_map(self, krate); for (local_id, data) in crate_def_map.modules() { if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) { return Some(crate_def_map.module_id(local_id)); @@ -137,7 +139,7 @@ impl TestDB { ) -> FxHashMap> { let mut files = Vec::new(); for &krate in self.all_crates().iter() { - let crate_def_map = self.crate_def_map(krate); + let crate_def_map = crate_def_map(self, krate); for (module_id, _) in crate_def_map.modules() { let file_id = crate_def_map[module_id].origin.file_id(); files.extend(file_id) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index cc37f65c26c2..2b75bd6f1604 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -132,7 +132,7 @@ fn check_impl( None => continue, }; let def_map = module.def_map(&db); - visit_module(&db, &def_map, module.local_id, &mut |it| { + visit_module(&db, def_map, module.local_id, &mut |it| { let def = match it { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), @@ -391,7 +391,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| { + visit_module(&db, def_map, module.local_id, &mut |it| { let def = match it { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), @@ -504,7 +504,7 @@ pub(crate) fn visit_module( fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(ModuleDefId)) { for (_, def_map) in body.blocks(db) { for (mod_id, _) in def_map.modules() { - visit_module(db, &def_map, mod_id, cb); + visit_module(db, def_map, mod_id, cb); } } } @@ -570,7 +570,7 @@ fn salsa_bug() { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { db.infer(match def { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), @@ -609,7 +609,7 @@ fn salsa_bug() { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { db.infer(match def { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 73f1ae56457d..88d21be81ea6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -20,7 +20,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec let def_map = module.def_map(&db); let mut defs = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); + visit_module(&db, def_map, module.local_id, &mut |it| defs.push(it)); let mut captures_info = Vec::new(); for def in defs { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 0542be0ba896..48474d2d26de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -19,7 +19,7 @@ fn foo() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } @@ -41,7 +41,7 @@ fn foo() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } @@ -70,7 +70,7 @@ fn baz() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } @@ -97,7 +97,7 @@ fn baz() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } 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 446f0b21a2a1..ea7a113cae3f 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 @@ -1505,6 +1505,10 @@ fn main() { !119..120 'o': i32 293..294 'o': i32 308..317 'thread_id': usize + !314..320 'OffPtr': usize + !333..338 'OffFn': usize + !354..355 '0': i32 + !371..382 'MEM_RELEASE': 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 eeebe38f1826..cf51671afb2b 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 @@ -3902,3 +3902,66 @@ fn main() { "#]], ); } + +#[test] +fn regression_19734() { + check_infer( + r#" +trait Foo { + type Gat<'o>; +} + +trait Bar { + fn baz() -> >; +} + +fn foo() { + T::baz(); +} + "#, + expect![[r#" + 110..127 '{ ...z(); }': () + 116..122 'T::baz': fn baz() -> <{unknown} as Foo>::Gat<'?> + 116..124 'T::baz()': Foo::Gat<'?, {unknown}> + "#]], + ); +} + +#[test] +fn asm_const_label() { + check_infer( + r#" +//- minicore: asm +const fn bar() -> i32 { 123 } +fn baz(s: &str) {} + +fn foo() { + unsafe { + core::arch::asm!( + "mov eax, {}", + "jmp {}", + const bar(), + label { + baz("hello"); + }, + ); + } +} + "#, + expect![[r#" + 22..29 '{ 123 }': i32 + 24..27 '123': i32 + 37..38 's': &'? str + 46..48 '{}': () + !0..68 'builti...");},)': () + !40..43 'bar': fn bar() -> i32 + !40..45 'bar()': i32 + !51..66 '{baz("hello");}': () + !52..55 'baz': fn baz(&'? str) + !52..64 'baz("hello")': () + !56..63 '"hello"': &'static str + 59..257 '{ ... } }': () + 65..255 'unsafe... }': () + "#]], + ); +} 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 14137605c9f2..e5d1fbe9defe 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 @@ -4884,3 +4884,49 @@ async fn baz i32>(c: T) { "#]], ); } + +#[test] +fn import_trait_items() { + check_infer( + r#" +//- minicore: default +use core::default::Default::default; +fn main() { + let a: i32 = default(); +} + "#, + expect![[r#" + 47..78 '{ ...t(); }': () + 57..58 'a': i32 + 66..73 'default': {unknown} + 66..75 'default()': i32 + "#]], + ); +} + +#[test] +fn async_fn_return_type() { + check_infer( + r#" +//- minicore: async_fn +fn foo R, R>(_: F) -> R { + loop {} +} + +fn main() { + foo(async move || ()); +} + "#, + expect![[r#" + 29..30 '_': F + 40..55 '{ loop {} }': R + 46..53 'loop {}': ! + 51..53 '{}': () + 67..97 '{ ...()); }': () + 73..76 'foo': fn foo impl Future, ()>(impl AsyncFn() -> impl Future) + 73..94 'foo(as...|| ())': () + 77..93 'async ... || ()': impl AsyncFn() -> impl Future + 91..93 '()': () + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index f9f8776cff7c..7414b4fc6070 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -291,4 +291,9 @@ impl FnTrait { pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option { self.lang_item().resolve_trait(db, krate) } + + #[inline] + pub(crate) fn is_async(self) -> bool { + matches!(self, FnTrait::AsyncFn | FnTrait::AsyncFnMut | FnTrait::AsyncFnOnce) + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 6e1cd9a310f1..d6b43aeed4d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -984,7 +984,7 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); let mut defs: Vec = Vec::new(); let module = db.module_for_file_opt(file_id.file_id(&db)).unwrap(); let def_map = module.def_map(&db); - crate::tests::visit_module(&db, &def_map, module.local_id, &mut |it| { + crate::tests::visit_module(&db, def_map, module.local_id, &mut |it| { defs.push(match it { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::AdtId(it) => it.into(), diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index b1c478d1bf40..b1cf30b98f5b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -105,11 +105,12 @@ impl HasAttrs for crate::Crate { /// Resolves the item `link` points to in the scope of `def`. pub fn resolve_doc_path_on( db: &dyn HirDatabase, - def: impl HasAttrs, + def: impl HasAttrs + Copy, link: &str, ns: Option, + is_inner_doc: bool, ) -> Option { - resolve_doc_path_on_(db, link, def.attr_id(), ns) + resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner_doc) } fn resolve_doc_path_on_( @@ -117,9 +118,18 @@ fn resolve_doc_path_on_( link: &str, attr_id: AttrDefId, ns: Option, + is_inner_doc: bool, ) -> Option { let resolver = match attr_id { - AttrDefId::ModuleId(it) => it.resolver(db), + AttrDefId::ModuleId(it) => { + if is_inner_doc { + it.resolver(db) + } else if let Some(parent) = Module::from(it).parent(db) { + parent.id.resolver(db) + } else { + it.resolver(db) + } + } AttrDefId::FieldId(it) => it.parent.resolver(db), AttrDefId::AdtId(it) => it.resolver(db), AttrDefId::FunctionId(it) => it.resolver(db), @@ -160,7 +170,7 @@ fn resolve_doc_path_on_( fn resolve_assoc_or_field( db: &dyn HirDatabase, - resolver: Resolver, + resolver: Resolver<'_>, path: ModPath, name: Name, ns: Option, @@ -248,7 +258,7 @@ fn resolve_assoc_item( fn resolve_impl_trait_item( db: &dyn HirDatabase, - resolver: Resolver, + resolver: Resolver<'_>, ty: &Type, name: &Name, ns: Option, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 3f1d5bb01f2a..e8218cf8611f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -97,7 +97,8 @@ pub use crate::{ diagnostics::*, has_source::HasSource, semantics::{ - PathResolution, Semantics, SemanticsImpl, SemanticsScope, TypeInfo, VisibleTraits, + PathResolution, PathResolutionPerNs, Semantics, SemanticsImpl, SemanticsScope, TypeInfo, + VisibleTraits, }, }; @@ -119,7 +120,7 @@ pub use { find_path::PrefixKind, import_map, lang_item::LangItem, - nameres::{DefMap, ModuleSource}, + nameres::{DefMap, ModuleSource, crate_def_map}, per_ns::Namespace, type_ref::{Mutability, TypeRef}, visibility::Visibility, @@ -227,7 +228,7 @@ impl Crate { } pub fn modules(self, db: &dyn HirDatabase) -> Vec { - let def_map = db.crate_def_map(self.id); + let def_map = crate_def_map(db, self.id); def_map.modules().map(|(id, _)| def_map.module_id(id).into()).collect() } @@ -528,7 +529,7 @@ impl Module { /// might be missing `krate`. This can happen if a module's file is not included /// in the module tree of any target in `Cargo.toml`. pub fn crate_root(self, db: &dyn HirDatabase) -> Module { - let def_map = db.crate_def_map(self.id.krate()); + let def_map = crate_def_map(db, self.id.krate()); Module { id: def_map.crate_root().into() } } @@ -2468,7 +2469,7 @@ impl Function { { return None; } - let def_map = db.crate_def_map(HasModule::krate(&self.id, db)); + let def_map = crate_def_map(db, HasModule::krate(&self.id, db)); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } @@ -4015,8 +4016,7 @@ impl BuiltinAttr { if let builtin @ Some(_) = Self::builtin(name) { return builtin; } - let idx = db - .crate_def_map(krate.id) + let idx = crate_def_map(db, krate.id) .registered_attrs() .iter() .position(|it| it.as_str() == name)? as u32; @@ -4031,7 +4031,7 @@ impl BuiltinAttr { pub fn name(&self, db: &dyn HirDatabase) -> Name { match self.krate { Some(krate) => Name::new_symbol_root( - db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(), + crate_def_map(db, krate).registered_attrs()[self.idx as usize].clone(), ), None => Name::new_symbol_root(Symbol::intern( hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name, @@ -4059,14 +4059,14 @@ impl ToolModule { pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option { let krate = krate.id; let idx = - db.crate_def_map(krate).registered_tools().iter().position(|it| it.as_str() == name)? + crate_def_map(db, krate).registered_tools().iter().position(|it| it.as_str() == name)? as u32; Some(ToolModule { krate, idx }) } pub fn name(&self, db: &dyn HirDatabase) -> Name { Name::new_symbol_root( - db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone(), + crate_def_map(db, self.krate).registered_tools()[self.idx as usize].clone(), ) } @@ -4488,7 +4488,7 @@ impl Impl { MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { let module_id = self.id.lookup(db).container; ( - db.crate_def_map(module_id.krate())[module_id.local_id] + crate_def_map(db, module_id.krate())[module_id.local_id] .scope .derive_macro_invoc(ast_id, derive_attr_index)?, derive_index, @@ -4530,7 +4530,7 @@ pub struct TraitRef { impl TraitRef { pub(crate) fn new_with_resolver( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, trait_ref: hir_ty::TraitRef, ) -> TraitRef { let env = resolver @@ -4752,13 +4752,13 @@ pub struct Type { } impl Type { - pub(crate) fn new_with_resolver(db: &dyn HirDatabase, resolver: &Resolver, ty: Ty) -> Type { + pub(crate) fn new_with_resolver(db: &dyn HirDatabase, resolver: &Resolver<'_>, ty: Ty) -> Type { Type::new_with_resolver_inner(db, resolver, ty) } pub(crate) fn new_with_resolver_inner( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, ty: Ty, ) -> Type { let environment = resolver @@ -5972,6 +5972,59 @@ impl Layout { } } + pub fn tail_padding(&self, field_size: &mut impl FnMut(usize) -> Option) -> Option { + match self.0.fields { + layout::FieldsShape::Primitive => None, + layout::FieldsShape::Union(_) => None, + layout::FieldsShape::Array { stride, count } => count.checked_sub(1).and_then(|tail| { + let tail_field_size = field_size(tail as usize)?; + let offset = stride.bytes() * tail; + self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size) + }), + layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + let tail = memory_index.last_index()?; + let tail_field_size = field_size(tail.0.into_raw().into_u32() as usize)?; + let offset = offsets.get(tail)?.bytes(); + self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size) + } + } + } + + pub fn largest_padding( + &self, + field_size: &mut impl FnMut(usize) -> Option, + ) -> Option { + match self.0.fields { + layout::FieldsShape::Primitive => None, + layout::FieldsShape::Union(_) => None, + layout::FieldsShape::Array { stride: _, count: 0 } => None, + layout::FieldsShape::Array { stride, .. } => { + let size = field_size(0)?; + stride.bytes().checked_sub(size) + } + layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + let mut reverse_index = vec![None; memory_index.len()]; + for (src, (mem, offset)) in memory_index.iter().zip(offsets.iter()).enumerate() { + reverse_index[*mem as usize] = Some((src, offset.bytes())); + } + if reverse_index.iter().any(|it| it.is_none()) { + stdx::never!(); + return None; + } + reverse_index + .into_iter() + .flatten() + .chain(std::iter::once((0, self.0.size.bytes()))) + .tuple_windows() + .filter_map(|((i, start), (_, end))| { + let size = field_size(i)?; + end.checked_sub(start)?.checked_sub(size) + }) + .max() + } + } + } + pub fn enum_tag_size(&self) -> Option { let tag_size = if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants { @@ -6400,7 +6453,7 @@ pub fn resolve_absolute_path<'a, I: Iterator + Clone + 'a>( }) .filter_map(|&krate| { let segments = segments.clone(); - let mut def_map = db.crate_def_map(krate); + let mut def_map = crate_def_map(db, krate); let mut module = &def_map[DefMap::ROOT]; let mut segments = segments.with_position().peekable(); while let Some((_, segment)) = segments.next_if(|&(position, _)| { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 4d092c1f0bb0..aea22545ed5d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -15,7 +15,7 @@ use hir_def::{ DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId, expr_store::{Body, ExprOrPatSource, path::Path}, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, - nameres::ModuleOrigin, + nameres::{ModuleOrigin, crate_def_map}, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, }; @@ -103,6 +103,26 @@ impl PathResolution { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct PathResolutionPerNs { + pub type_ns: Option, + pub value_ns: Option, + pub macro_ns: Option, +} + +impl PathResolutionPerNs { + pub fn new( + type_ns: Option, + value_ns: Option, + macro_ns: Option, + ) -> Self { + PathResolutionPerNs { type_ns, value_ns, macro_ns } + } + pub fn any(&self) -> Option { + self.type_ns.or(self.value_ns).or(self.macro_ns) + } +} + #[derive(Debug)] pub struct TypeInfo { /// The original type of the expression or pattern. @@ -127,7 +147,7 @@ impl TypeInfo { } /// Primary API to get semantic information, like types, from syntax trees. -pub struct Semantics<'db, DB> { +pub struct Semantics<'db, DB: ?Sized> { pub db: &'db DB, imp: SemanticsImpl<'db>, } @@ -341,7 +361,7 @@ impl<'db> SemanticsImpl<'db> { match file_id { HirFileId::FileId(file_id) => { let module = self.file_to_module_defs(file_id.file_id(self.db)).next()?; - let def_map = self.db.crate_def_map(module.krate().id); + let def_map = crate_def_map(self.db, module.krate().id); match def_map[module.id.local_id].origin { ModuleOrigin::CrateRoot { .. } => None, ModuleOrigin::File { declaration, declaration_tree_id, .. } => { @@ -387,14 +407,10 @@ impl<'db> SemanticsImpl<'db> { res } - pub fn expand_macro_call(&self, macro_call: &ast::MacroCall) -> Option { - let sa = self.analyze_no_infer(macro_call.syntax())?; - - let macro_call = InFile::new(sa.file_id, macro_call); - let file_id = sa.expand(self.db, macro_call)?; - + pub fn expand_macro_call(&self, macro_call: &ast::MacroCall) -> Option> { + let file_id = self.to_def(macro_call)?; let node = self.parse_or_expand(file_id.into()); - Some(node) + Some(InFile::new(file_id.into(), node)) } pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option { @@ -414,10 +430,7 @@ impl<'db> SemanticsImpl<'db> { &self, macro_call: &ast::MacroCall, ) -> Option> { - let sa = self.analyze_no_infer(macro_call.syntax())?; - - let macro_call = InFile::new(sa.file_id, macro_call); - let file_id = sa.expand(self.db, macro_call)?; + let file_id = self.to_def(macro_call)?; let macro_call = self.db.lookup_intern_macro_call(file_id); let skip = matches!( @@ -448,10 +461,10 @@ impl<'db> SemanticsImpl<'db> { } /// If `item` has an attribute macro attached to it, expands it. - pub fn expand_attr_macro(&self, item: &ast::Item) -> Option> { + pub fn expand_attr_macro(&self, item: &ast::Item) -> Option>> { let src = self.wrap_node_infile(item.clone()); let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src.as_ref()))?; - Some(self.expand(macro_call_id)) + Some(self.expand(macro_call_id).map(|it| InFile::new(macro_call_id.into(), it))) } pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option { @@ -554,9 +567,7 @@ impl<'db> SemanticsImpl<'db> { speculative_args: &ast::TokenTree, token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { - let analyzer = self.analyze_no_infer(actual_macro_call.syntax())?; - let macro_call = InFile::new(analyzer.file_id, actual_macro_call); - let macro_file = analyzer.expansion(macro_call)?; + let macro_file = self.to_def(actual_macro_call)?; hir_expand::db::expand_speculative( self.db, macro_file, @@ -758,6 +769,31 @@ impl<'db> SemanticsImpl<'db> { }) } + /// Descends the token into the include expansion, if its file is an included file. + pub fn descend_token_into_include_expansion( + &self, + tok: InRealFile, + ) -> InFile { + let Some(include) = + self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, tok.file_id) + else { + return tok.into(); + }; + let span = self.db.real_span_map(tok.file_id).span_for_range(tok.value.text_range()); + let Some(InMacroFile { file_id, value: mut mapped_tokens }) = self.with_ctx(|ctx| { + Some( + ctx.cache + .get_or_insert_expansion(ctx.db, include) + .map_range_down(span)? + .map(SmallVec::<[_; 2]>::from_iter), + ) + }) else { + return tok.into(); + }; + // We should only get one result at most + mapped_tokens.pop().map_or_else(|| tok.into(), |(tok, _)| InFile::new(file_id.into(), tok)) + } + /// Maps a node down by mapping its first and last token down. pub fn descend_node_into_attributes(&self, node: N) -> SmallVec<[N; 1]> { // This might not be the correct way to do this, but it works for now @@ -826,49 +862,35 @@ impl<'db> SemanticsImpl<'db> { res } - // FIXME: This isn't quite right wrt to inner attributes - /// Does a syntactic traversal to check whether this token might be inside a macro call - pub fn might_be_inside_macro_call(&self, token: &SyntaxToken) -> bool { - token.parent_ancestors().any(|ancestor| { + pub fn is_inside_macro_call(&self, token: InFile<&SyntaxToken>) -> bool { + // FIXME: Maybe `ancestors_with_macros()` is more suitable here? Currently + // this is only used on real (not macro) files so this is not a problem. + token.value.parent_ancestors().any(|ancestor| { if ast::MacroCall::can_cast(ancestor.kind()) { return true; } - // Check if it is an item (only items can have macro attributes) that has a non-builtin attribute. - let Some(item) = ast::Item::cast(ancestor) else { return false }; - item.attrs().any(|attr| { - let Some(meta) = attr.meta() else { return false }; - let Some(path) = meta.path() else { return false }; - if let Some(attr_name) = path.as_single_name_ref() { - let attr_name = attr_name.text(); - let attr_name = Symbol::intern(attr_name.as_str()); - if attr_name == sym::derive { - return true; - } - // We ignore `#[test]` and friends in the def map, so we cannot expand them. - // FIXME: We match by text. This is both hacky and incorrect (people can, and do, create - // other macros named `test`). We cannot fix that unfortunately because we use this method - // for speculative expansion in completion, which we cannot analyze. Fortunately, most macros - // named `test` are test-like, meaning their expansion is not terribly important for IDE. - if attr_name == sym::test - || attr_name == sym::bench - || attr_name == sym::test_case - || find_builtin_attr_idx(&attr_name).is_some() - { - return false; - } + + let Some(item) = ast::Item::cast(ancestor) else { + return false; + }; + // Optimization to skip the semantic check. + if item.attrs().all(|attr| { + attr.simple_name() + .is_some_and(|attr| find_builtin_attr_idx(&Symbol::intern(&attr)).is_some()) + }) { + return false; + } + self.with_ctx(|ctx| { + if ctx.item_to_macro_call(token.with_value(&item)).is_some() { + return true; } - let mut segments = path.segments(); - let mut next_segment_text = || segments.next().and_then(|it| it.name_ref()); - // `#[core::prelude::rust_2024::test]` or `#[std::prelude::rust_2024::test]`. - if next_segment_text().is_some_and(|it| matches!(&*it.text(), "core" | "std")) - && next_segment_text().is_some_and(|it| it.text() == "prelude") - && next_segment_text().is_some() - && next_segment_text() - .is_some_and(|it| matches!(&*it.text(), "test" | "bench" | "test_case")) - { - return false; - } - true + let adt = match item { + ast::Item::Struct(it) => it.into(), + ast::Item::Enum(it) => it.into(), + ast::Item::Union(it) => it.into(), + _ => return false, + }; + ctx.has_derives(token.with_value(&adt)) }) }) } @@ -1091,16 +1113,7 @@ impl<'db> SemanticsImpl<'db> { let file_id = match m_cache.get(&mcall) { Some(&it) => it, None => { - let it = token - .parent() - .and_then(|parent| { - self.analyze_impl( - InFile::new(expansion, &parent), - None, - false, - ) - })? - .expand(self.db, mcall.as_ref())?; + let it = ast::MacroCall::to_def(self, mcall.as_ref())?; m_cache.insert(mcall, it); it } @@ -1540,14 +1553,9 @@ impl<'db> SemanticsImpl<'db> { } pub fn resolve_macro_call2(&self, macro_call: InFile<&ast::MacroCall>) -> Option { - self.with_ctx(|ctx| { - ctx.macro_call_to_macro_call(macro_call) - .and_then(|call| macro_call_to_macro_id(ctx, call)) - .map(Into::into) - }) - .or_else(|| { - self.analyze(macro_call.value.syntax())?.resolve_macro_call(self.db, macro_call) - }) + self.to_def2(macro_call) + .and_then(|call| self.with_ctx(|ctx| macro_call_to_macro_id(ctx, call))) + .map(Into::into) } pub fn is_proc_macro_call(&self, macro_call: InFile<&ast::MacroCall>) -> bool { @@ -1556,14 +1564,8 @@ impl<'db> SemanticsImpl<'db> { } pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option { - let sa = self.analyze(macro_call.syntax())?; - self.db - .parse_macro_expansion( - sa.expand(self.db, self.wrap_node_infile(macro_call.clone()).as_ref())?, - ) - .value - .1 - .matched_arm + let file_id = self.to_def(macro_call)?; + self.db.parse_macro_expansion(file_id).value.1.matched_arm } pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet { @@ -1606,6 +1608,10 @@ impl<'db> SemanticsImpl<'db> { self.resolve_path_with_subst(path).map(|(it, _)| it) } + pub fn resolve_path_per_ns(&self, path: &ast::Path) -> Option { + self.analyze(path.syntax())?.resolve_hir_path_per_ns(self.db, path) + } + pub fn resolve_path_with_subst( &self, path: &ast::Path, @@ -1664,6 +1670,10 @@ impl<'db> SemanticsImpl<'db> { T::to_def(self, src) } + pub fn to_def2(&self, src: InFile<&T>) -> Option { + T::to_def(self, src) + } + fn file_to_module_defs(&self, file: FileId) -> impl Iterator { self.with_ctx(|ctx| ctx.file_to_def(file).to_owned()).into_iter().map(Module::from) } @@ -1711,13 +1721,13 @@ impl<'db> SemanticsImpl<'db> { } /// Returns none if the file of the node is not part of a crate. - fn analyze(&self, node: &SyntaxNode) -> Option { + fn analyze(&self, node: &SyntaxNode) -> Option> { let node = self.find_file(node); self.analyze_impl(node, None, true) } /// Returns none if the file of the node is not part of a crate. - fn analyze_no_infer(&self, node: &SyntaxNode) -> Option { + fn analyze_no_infer(&self, node: &SyntaxNode) -> Option> { let node = self.find_file(node); self.analyze_impl(node, None, false) } @@ -1726,7 +1736,7 @@ impl<'db> SemanticsImpl<'db> { &self, node: &SyntaxNode, offset: TextSize, - ) -> Option { + ) -> Option> { let node = self.find_file(node); self.analyze_impl(node, Some(offset), false) } @@ -1737,7 +1747,7 @@ impl<'db> SemanticsImpl<'db> { offset: Option, // replace this, just make the inference result a `LazyCell` infer_body: bool, - ) -> Option { + ) -> Option> { let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered(); let container = self.with_ctx(|ctx| ctx.find_container(node))?; @@ -1984,13 +1994,13 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode { /// Note that if you are wondering "what does this specific existing name mean?", /// you'd better use the `resolve_` family of methods. #[derive(Debug)] -pub struct SemanticsScope<'a> { - pub db: &'a dyn HirDatabase, +pub struct SemanticsScope<'db> { + pub db: &'db dyn HirDatabase, file_id: HirFileId, - resolver: Resolver, + resolver: Resolver<'db>, } -impl SemanticsScope<'_> { +impl<'db> SemanticsScope<'db> { pub fn module(&self) -> Module { Module { id: self.resolver.module() } } @@ -2006,7 +2016,7 @@ impl SemanticsScope<'_> { }) } - pub(crate) fn resolver(&self) -> &Resolver { + pub(crate) fn resolver(&self) -> &Resolver<'db> { &self.resolver } @@ -2133,7 +2143,7 @@ impl ops::Deref for VisibleTraits { struct RenameConflictsVisitor<'a> { db: &'a dyn HirDatabase, owner: DefWithBodyId, - resolver: Resolver, + resolver: Resolver<'a>, body: &'a Body, to_be_renamed: BindingId, new_name: Symbol, diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index 9393d08ad3f9..6accf9b2e9c2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -36,9 +36,14 @@ impl ChildBySource for TraitId { fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) { let data = db.trait_items(*self); - data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each( + data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each( |(ast_id, call_id)| { - res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db), call_id); + let ptr = ast_id.to_ptr(db); + if let Some(ptr) = ptr.cast::() { + res[keys::MACRO_CALL].insert(ptr, call_id); + } else { + res[keys::ATTR_MACRO_CALL].insert(ptr, call_id); + } }, ); data.items.iter().for_each(|&(_, item)| { @@ -50,10 +55,14 @@ impl ChildBySource for TraitId { impl ChildBySource for ImplId { fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) { let data = db.impl_items(*self); - // FIXME: Macro calls - data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each( + data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each( |(ast_id, call_id)| { - res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db), call_id); + let ptr = ast_id.to_ptr(db); + if let Some(ptr) = ptr.cast::() { + res[keys::MACRO_CALL].insert(ptr, call_id); + } else { + res[keys::ATTR_MACRO_CALL].insert(ptr, call_id); + } }, ); data.items.iter().for_each(|&(_, item)| { 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 587c51d8cc99..7f6c9af47400 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 @@ -96,6 +96,7 @@ use hir_def::{ keys::{self, Key}, }, hir::{BindingId, Expr, LabelId}, + nameres::{block_def_map, crate_def_map}, }; use hir_expand::{ EditionedFileId, ExpansionInfo, HirFileId, InMacroFile, MacroCallId, attrs::AttrId, @@ -180,7 +181,7 @@ impl SourceToDefCtx<'_, '_> { for &crate_id in self.db.relevant_crates(file).iter() { // Note: `mod` declarations in block modules cannot be supported here - let crate_def_map = self.db.crate_def_map(crate_id); + let crate_def_map = crate_def_map(self.db, crate_id); let n_mods = mods.len(); let modules = |file| { crate_def_map @@ -226,7 +227,7 @@ impl SourceToDefCtx<'_, '_> { let parent_module = match parent_declaration { Some(Either::Right(parent_block)) => self .block_to_def(parent_block.as_ref()) - .map(|block| self.db.block_def_map(block).root_module_id()), + .map(|block| block_def_map(self.db, block).root_module_id()), Some(Either::Left(parent_declaration)) => { self.module_to_def(parent_declaration.as_ref()) } @@ -398,19 +399,6 @@ impl SourceToDefCtx<'_, '_> { Some((container, label?)) } - pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option { - let map = self.dyn_map(src)?; - map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied() - } - - pub(super) fn macro_call_to_macro_call( - &mut self, - src: InFile<&ast::MacroCall>, - ) -> Option { - let map = self.dyn_map(src)?; - map[keys::MACRO_CALL].get(&AstPtr::new(src.value)).copied() - } - /// (AttrId, derive attribute call id, derive call ids) pub(super) fn attr_to_derive_macro_call( &mut self, @@ -448,6 +436,17 @@ impl SourceToDefCtx<'_, '_> { .or_insert_with(|| container.child_by_source(db, file_id)) } + pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option { + self.to_def(src, keys::ATTR_MACRO_CALL) + } + + pub(super) fn macro_call_to_macro_call( + &mut self, + src: InFile<&ast::MacroCall>, + ) -> Option { + self.to_def(src, keys::MACRO_CALL) + } + pub(super) fn type_param_to_def( &mut self, src: InFile<&ast::TypeParam>, 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 c1a75ce7e574..d22812d3c692 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -10,7 +10,9 @@ use std::iter::{self, once}; use crate::{ Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait, - TraitAlias, TupleField, Type, TypeAlias, Variant, db::HirDatabase, semantics::PathResolution, + TraitAlias, TupleField, Type, TypeAlias, Variant, + db::HirDatabase, + semantics::{PathResolution, PathResolutionPerNs}, }; use either::Either; use hir_def::{ @@ -29,7 +31,7 @@ use hir_def::{ type_ref::{Mutability, TypeRefId}, }; use hir_expand::{ - HirFileId, InFile, MacroCallId, + HirFileId, InFile, mod_path::{ModPath, PathKind, path}, name::{AsName, Name}, }; @@ -57,9 +59,9 @@ use triomphe::Arc; /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of /// original source files. It should not be used inside the HIR itself. #[derive(Debug)] -pub(crate) struct SourceAnalyzer { +pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, - pub(crate) resolver: Resolver, + pub(crate) resolver: Resolver<'db>, pub(crate) body_or_sig: Option, } @@ -85,32 +87,32 @@ pub(crate) enum BodyOrSig { }, } -impl SourceAnalyzer { +impl<'db> SourceAnalyzer<'db> { pub(crate) fn new_for_body( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: DefWithBodyId, node: InFile<&SyntaxNode>, offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { Self::new_for_body_(db, def, node, offset, Some(db.infer(def))) } pub(crate) fn new_for_body_no_infer( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: DefWithBodyId, node: InFile<&SyntaxNode>, offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { Self::new_for_body_(db, def, node, offset, None) } pub(crate) fn new_for_body_( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: DefWithBodyId, node @ InFile { file_id, .. }: InFile<&SyntaxNode>, offset: Option, infer: Option>, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { let (body, source_map) = db.body_with_source_map(def); let scopes = db.expr_scopes(def); let scope = match offset { @@ -134,11 +136,11 @@ impl SourceAnalyzer { } pub(crate) fn new_generic_def( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: GenericDefId, InFile { file_id, .. }: InFile<&SyntaxNode>, _offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { let (_params, store, source_map) = db.generic_params_and_store_and_source_map(def); let resolver = def.resolver(db); SourceAnalyzer { @@ -149,11 +151,11 @@ impl SourceAnalyzer { } pub(crate) fn new_variant_body( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: VariantId, InFile { file_id, .. }: InFile<&SyntaxNode>, _offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { let (fields, source_map) = db.variant_fields_with_source_map(def); let resolver = def.resolver(db); SourceAnalyzer { @@ -168,9 +170,9 @@ impl SourceAnalyzer { } pub(crate) fn new_for_resolver( - resolver: Resolver, + resolver: Resolver<'db>, node: InFile<&SyntaxNode>, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id } } @@ -216,11 +218,7 @@ impl SourceAnalyzer { }) } - pub(crate) fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option { - self.store_sm()?.expansion(node) - } - - fn trait_environment(&self, db: &dyn HirDatabase) -> Arc { + fn trait_environment(&self, db: &'db dyn HirDatabase) -> Arc { self.body_().map(|(def, ..)| def).map_or_else( || TraitEnvironment::empty(self.resolver.krate()), |def| db.trait_environment_for_body(def), @@ -259,7 +257,7 @@ impl SourceAnalyzer { infer.expr_adjustments.get(&expr_id).map(|v| &**v) } - pub(crate) fn type_of_type(&self, db: &dyn HirDatabase, ty: &ast::Type) -> Option { + pub(crate) fn type_of_type(&self, db: &'db dyn HirDatabase, ty: &ast::Type) -> Option { let type_ref = self.type_id(ty)?; let ty = TyLoweringContext::new( db, @@ -277,7 +275,7 @@ impl SourceAnalyzer { pub(crate) fn type_of_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, expr: &ast::Expr, ) -> Option<(Type, Option)> { let expr_id = self.expr_id(expr.clone())?; @@ -293,7 +291,7 @@ impl SourceAnalyzer { pub(crate) fn type_of_pat( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option<(Type, Option)> { let expr_or_pat_id = self.pat_id(pat)?; @@ -316,7 +314,7 @@ impl SourceAnalyzer { pub(crate) fn type_of_binding_in_pat( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { let binding_id = self.binding_id_of_pat(pat)?; @@ -328,7 +326,7 @@ impl SourceAnalyzer { pub(crate) fn type_of_self( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, _param: &ast::SelfParam, ) -> Option { let binding = self.body()?.self_param?; @@ -338,7 +336,7 @@ impl SourceAnalyzer { pub(crate) fn binding_mode_of_pat( &self, - _db: &dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { let id = self.pat_id(&pat.clone().into())?; @@ -353,7 +351,7 @@ impl SourceAnalyzer { } pub(crate) fn pattern_adjustments( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option> { let pat_id = self.pat_id(pat)?; @@ -370,7 +368,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_method_call_as_callable( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::MethodCallExpr, ) -> Option { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; @@ -384,7 +382,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_method_call( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::MethodCallExpr, ) -> Option { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; @@ -395,7 +393,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_method_call_fallback( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::MethodCallExpr, ) -> Option<(Either, Option)> { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; @@ -419,7 +417,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_expr_as_callable( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::Expr, ) -> Option { let (orig, adjusted) = self.type_of_expr(db, &call.clone())?; @@ -441,7 +439,7 @@ impl SourceAnalyzer { &self, field_expr: ExprId, infer: &InferenceResult, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, ) -> Option { let body = self.store()?; if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { @@ -457,7 +455,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_field_fallback( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, field: &ast::FieldExpr, ) -> Option<(Either, Function>, Option)> { let (def, ..) = self.body_()?; @@ -490,7 +488,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_range_pat( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, range_pat: &ast::RangePat, ) -> Option { let path: ModPath = match (range_pat.op_kind()?, range_pat.start(), range_pat.end()) { @@ -509,7 +507,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_range_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, range_expr: &ast::RangeExpr, ) -> Option { let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) { @@ -529,7 +527,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_await_to_poll( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, await_expr: &ast::AwaitExpr, ) -> Option { let mut ty = self.ty_of_expr(await_expr.expr()?)?.clone(); @@ -566,7 +564,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_prefix_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, prefix_expr: &ast::PrefixExpr, ) -> Option { let (op_trait, op_fn) = match prefix_expr.op_kind()? { @@ -608,7 +606,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_index_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, index_expr: &ast::IndexExpr, ) -> Option { let base_ty = self.ty_of_expr(index_expr.base()?)?; @@ -640,7 +638,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_bin_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, binop_expr: &ast::BinExpr, ) -> Option { let op = binop_expr.op_kind()?; @@ -661,7 +659,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_try_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, try_expr: &ast::TryExpr, ) -> Option { let ty = self.ty_of_expr(try_expr.expr()?)?; @@ -680,7 +678,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_record_field( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, field: &ast::RecordExprField, ) -> Option<(Field, Option, Type, GenericSubstitution)> { let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; @@ -724,7 +722,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_record_pat_field( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, field: &ast::RecordPatField, ) -> Option<(Field, Type, GenericSubstitution)> { let field_name = field.field_name()?.as_name(); @@ -743,25 +741,9 @@ impl SourceAnalyzer { )) } - pub(crate) fn resolve_macro_call( - &self, - db: &dyn HirDatabase, - macro_call: InFile<&ast::MacroCall>, - ) -> Option { - let bs = self.store_sm()?; - bs.expansion(macro_call).and_then(|it| { - // FIXME: Block def maps - let def = it.lookup(db).def; - db.crate_def_map(def.krate) - .macro_def_to_macro_id - .get(&def.kind.erased_ast_id()) - .map(|it| (*it).into()) - }) - } - pub(crate) fn resolve_bind_pat_to_const( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { let expr_or_pat_id = self.pat_id(&pat.clone().into())?; @@ -795,7 +777,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_offset_of_field( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, name_ref: &ast::NameRef, ) -> Option<(Either, GenericSubstitution)> { let offset_of_expr = ast::OffsetOfExpr::cast(name_ref.syntax().parent()?)?; @@ -867,7 +849,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_path( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, path: &ast::Path, ) -> Option<(PathResolution, Option)> { let parent = path.syntax().parent(); @@ -1159,7 +1141,9 @@ impl SourceAnalyzer { prefer_value_ns, name_hygiene(db, InFile::new(self.file_id, path.syntax())), Some(&store), - )?; + false, + ) + .any()?; let subst = (|| { let parent = parent()?; let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) { @@ -1209,9 +1193,29 @@ impl SourceAnalyzer { } } - pub(crate) fn record_literal_missing_fields( + pub(crate) fn resolve_hir_path_per_ns( &self, db: &dyn HirDatabase, + path: &ast::Path, + ) -> Option { + let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id); + let hir_path = + collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?; + let store = collector.store.finish(); + Some(resolve_hir_path_( + db, + &self.resolver, + &hir_path, + false, + name_hygiene(db, InFile::new(self.file_id, path.syntax())), + Some(&store), + true, + )) + } + + pub(crate) fn record_literal_missing_fields( + &self, + db: &'db dyn HirDatabase, literal: &ast::RecordExpr, ) -> Option> { let body = self.store()?; @@ -1234,7 +1238,7 @@ impl SourceAnalyzer { pub(crate) fn record_pattern_missing_fields( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pattern: &ast::RecordPat, ) -> Option> { let body = self.store()?; @@ -1251,7 +1255,7 @@ impl SourceAnalyzer { fn missing_fields( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, substs: &Substitution, variant: VariantId, missing_fields: Vec, @@ -1268,18 +1272,6 @@ impl SourceAnalyzer { .collect() } - pub(crate) fn expand( - &self, - db: &dyn HirDatabase, - macro_call: InFile<&ast::MacroCall>, - ) -> Option { - self.store_sm().and_then(|bs| bs.expansion(macro_call)).or_else(|| { - self.resolver.item_scope().macro_invoc( - macro_call.with_value(db.ast_id_map(macro_call.file_id).ast_id(macro_call.value)), - ) - }) - } - pub(crate) fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option { let infer = self.infer()?; let expr_id = self.expr_id(record_lit.into())?; @@ -1288,7 +1280,7 @@ impl SourceAnalyzer { pub(crate) fn is_unsafe_macro_call_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, macro_expr: InFile<&ast::MacroExpr>, ) -> bool { if let Some((def, body, sm, Some(infer))) = self.body_() { @@ -1313,7 +1305,7 @@ impl SourceAnalyzer { pub(crate) fn resolve_offset_in_format_args( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, format_args: InFile<&ast::FormatArgsExpr>, offset: TextSize, ) -> Option<(TextRange, Option)> { @@ -1384,7 +1376,7 @@ impl SourceAnalyzer { fn resolve_impl_method_or_trait_def( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, func: FunctionId, substs: Substitution, ) -> FunctionId { @@ -1393,7 +1385,7 @@ impl SourceAnalyzer { fn resolve_impl_method_or_trait_def_with_subst( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, func: FunctionId, substs: Substitution, ) -> (FunctionId, Substitution) { @@ -1407,7 +1399,7 @@ impl SourceAnalyzer { fn resolve_impl_const_or_trait_def_with_subst( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, const_id: ConstId, subs: Substitution, ) -> (ConstId, Substitution) { @@ -1421,7 +1413,7 @@ impl SourceAnalyzer { fn lang_trait_fn( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, lang_trait: LangItem, method_name: &Name, ) -> Option<(TraitId, FunctionId)> { @@ -1527,18 +1519,18 @@ fn adjust( #[inline] pub(crate) fn resolve_hir_path( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, hygiene: HygieneId, store: Option<&ExpressionStore>, ) -> Option { - resolve_hir_path_(db, resolver, path, false, hygiene, store) + resolve_hir_path_(db, resolver, path, false, hygiene, store, false).any() } #[inline] pub(crate) fn resolve_hir_path_as_attr_macro( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, ) -> Option { resolver @@ -1549,12 +1541,13 @@ pub(crate) fn resolve_hir_path_as_attr_macro( fn resolve_hir_path_( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, prefer_value_ns: bool, hygiene: HygieneId, store: Option<&ExpressionStore>, -) -> Option { + resolve_per_ns: bool, +) -> PathResolutionPerNs { let types = || { let (ty, unresolved) = match path.type_anchor() { Some(type_ref) => resolver.generic_def().and_then(|def| { @@ -1635,14 +1628,36 @@ fn resolve_hir_path_( .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into()))) }; - if prefer_value_ns { values().or_else(types) } else { types().or_else(values) } - .or_else(items) - .or_else(macros) + if resolve_per_ns { + PathResolutionPerNs { + type_ns: types().or_else(items), + value_ns: values(), + macro_ns: macros(), + } + } else { + let res = if prefer_value_ns { + values() + .map(|value_ns| PathResolutionPerNs::new(None, Some(value_ns), None)) + .unwrap_or_else(|| PathResolutionPerNs::new(types(), None, None)) + } else { + types() + .map(|type_ns| PathResolutionPerNs::new(Some(type_ns), None, None)) + .unwrap_or_else(|| PathResolutionPerNs::new(None, values(), None)) + }; + + if res.any().is_some() { + res + } else if let Some(type_ns) = items() { + PathResolutionPerNs::new(Some(type_ns), None, None) + } else { + PathResolutionPerNs::new(None, None, macros()) + } + } } fn resolve_hir_value_path( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, body_owner: Option, path: &Path, hygiene: HygieneId, @@ -1680,7 +1695,7 @@ fn resolve_hir_value_path( /// then we know that `foo` in `my::foo::Bar` refers to the module, not the function. fn resolve_hir_path_qualifier( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, store: &ExpressionStore, ) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs new file mode 100644 index 000000000000..efadde9e3648 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -0,0 +1,281 @@ +use std::iter; + +use ide_db::{ + assists::{AssistId, ExprFillDefaultMode}, + ty_filter::TryEnum, +}; +use syntax::{ + AstNode, T, + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + make, + syntax_factory::SyntaxFactory, + }, +}; + +use crate::assist_context::{AssistContext, Assists}; + +// Assist: desugar_try_expr_match +// +// Replaces a `try` expression with a `match` expression. +// +// ``` +// # //- minicore: try, option +// fn handle() { +// let pat = Some(true)$0?; +// } +// ``` +// -> +// ``` +// fn handle() { +// let pat = match Some(true) { +// Some(it) => it, +// None => return None, +// }; +// } +// ``` + +// Assist: desugar_try_expr_let_else +// +// Replaces a `try` expression with a `let else` statement. +// +// ``` +// # //- minicore: try, option +// fn handle() { +// let pat = Some(true)$0?; +// } +// ``` +// -> +// ``` +// fn handle() { +// let Some(pat) = Some(true) else { +// return None; +// }; +// } +// ``` +pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let question_tok = ctx.find_token_syntax_at_offset(T![?])?; + let try_expr = question_tok.parent().and_then(ast::TryExpr::cast)?; + + let expr = try_expr.expr()?; + let expr_type_info = ctx.sema.type_of_expr(&expr)?; + + let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?; + + let target = try_expr.syntax().text_range(); + acc.add( + AssistId::refactor_rewrite("desugar_try_expr_match"), + "Replace try expression with match", + target, + |edit| { + let sad_pat = match try_enum { + TryEnum::Option => make::path_pat(make::ext::ident_path("None")), + TryEnum::Result => make::tuple_struct_pat( + make::ext::ident_path("Err"), + iter::once(make::path_pat(make::ext::ident_path("err"))), + ) + .into(), + }; + let sad_expr = match try_enum { + TryEnum::Option => { + make::expr_return(Some(make::expr_path(make::ext::ident_path("None")))) + } + TryEnum::Result => make::expr_return(Some( + make::expr_call( + make::expr_path(make::ext::ident_path("Err")), + make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))), + ) + .into(), + )), + }; + + let happy_arm = make::match_arm( + try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()), + None, + make::expr_path(make::ext::ident_path("it")), + ); + let sad_arm = make::match_arm(sad_pat, None, sad_expr); + + let match_arm_list = make::match_arm_list([happy_arm, sad_arm]); + + let expr_match = make::expr_match(expr.clone(), match_arm_list) + .indent(IndentLevel::from_node(try_expr.syntax())); + + edit.replace_ast::(try_expr.clone().into(), expr_match.into()); + }, + ); + + if let Some(let_stmt) = try_expr.syntax().parent().and_then(ast::LetStmt::cast) { + if let_stmt.let_else().is_none() { + let pat = let_stmt.pat()?; + acc.add( + AssistId::refactor_rewrite("desugar_try_expr_let_else"), + "Replace try expression with let else", + target, + |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(let_stmt.syntax()); + + let indent_level = IndentLevel::from_node(let_stmt.syntax()); + let new_let_stmt = make.let_else_stmt( + try_enum.happy_pattern(pat), + let_stmt.ty(), + expr, + make.block_expr( + iter::once( + make.expr_stmt( + make.expr_return(Some(match try_enum { + TryEnum::Option => make.expr_path(make.ident_path("None")), + TryEnum::Result => make + .expr_call( + make.expr_path(make.ident_path("Err")), + make.arg_list(iter::once( + match ctx.config.expr_fill_default { + ExprFillDefaultMode::Todo => make + .expr_macro( + make.ident_path("todo"), + make.token_tree( + syntax::SyntaxKind::L_PAREN, + [], + ), + ) + .into(), + ExprFillDefaultMode::Underscore => { + make.expr_underscore().into() + } + ExprFillDefaultMode::Default => make + .expr_macro( + make.ident_path("todo"), + make.token_tree( + syntax::SyntaxKind::L_PAREN, + [], + ), + ) + .into(), + }, + )), + ) + .into(), + })) + .indent(indent_level + 1) + .into(), + ) + .into(), + ), + None, + ) + .indent(indent_level), + ); + editor.replace(let_stmt.syntax(), new_let_stmt.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, + ); + } + } + Some(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; + + #[test] + fn test_desugar_try_expr_not_applicable() { + check_assist_not_applicable( + desugar_try_expr, + r#" + fn test() { + let pat: u32 = 25$0; + } + "#, + ); + } + + #[test] + fn test_desugar_try_expr_option() { + check_assist( + desugar_try_expr, + r#" +//- minicore: try, option +fn test() { + let pat = Some(true)$0?; +} + "#, + r#" +fn test() { + let pat = match Some(true) { + Some(it) => it, + None => return None, + }; +} + "#, + ); + } + + #[test] + fn test_desugar_try_expr_result() { + check_assist( + desugar_try_expr, + r#" +//- minicore: try, from, result +fn test() { + let pat = Ok(true)$0?; +} + "#, + r#" +fn test() { + let pat = match Ok(true) { + Ok(it) => it, + Err(err) => return Err(err), + }; +} + "#, + ); + } + + #[test] + fn test_desugar_try_expr_option_let_else() { + check_assist_by_label( + desugar_try_expr, + r#" +//- minicore: try, option +fn test() { + let pat = Some(true)$0?; +} + "#, + r#" +fn test() { + let Some(pat) = Some(true) else { + return None; + }; +} + "#, + "Replace try expression with let else", + ); + } + + #[test] + fn test_desugar_try_expr_result_let_else() { + check_assist_by_label( + desugar_try_expr, + r#" +//- minicore: try, from, result +fn test() { + let pat = Ok(true)$0?; +} + "#, + r#" +fn test() { + let Ok(pat) = Ok(true) else { + return Err(todo!()); + }; +} + "#, + "Replace try expression with let else", + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index 2ac960ed7e18..bab2ccf3f337 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -1,7 +1,7 @@ use ide_db::famous_defs::FamousDefs; use syntax::{ AstNode, - ast::{self, make}, + ast::{self, edit_in_place::Indent, make}, ted, }; @@ -46,6 +46,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let impl_def = ctx.find_node_at_offset::()?.clone_for_update(); + let indent = impl_def.indent_level(); let trait_ = impl_def.trait_()?; if let ast::Type::PathType(trait_path) = trait_ { @@ -97,8 +98,8 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> })?; let assoc_list = make::assoc_item_list().clone_for_update(); - assoc_list.add_item(syntax::ast::AssocItem::Fn(fn_)); ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax()); + impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_)); let target = impl_def.syntax().text_range(); acc.add( @@ -106,7 +107,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> "Generate `IndexMut` impl from this `Index` trait", target, |edit| { - edit.insert(target.start(), format!("$0{impl_def}\n\n")); + edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}")); }, ) } @@ -189,6 +190,93 @@ impl core::ops::Index for [T; 3] where T: Copy { ); } + #[test] + fn test_generate_mut_trait_impl_non_zero_indent() { + check_assist( + generate_mut_trait_impl, + r#" +//- minicore: index +mod foo { + pub enum Axis { X = 0, Y = 1, Z = 2 } + + impl core::ops::Index$0 for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } + } +} +"#, + r#" +mod foo { + pub enum Axis { X = 0, Y = 1, Z = 2 } + + $0impl core::ops::IndexMut for [T; 3] where T: Copy { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + let var_name = &self[index as usize]; + var_name + } + } + + impl core::ops::Index for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } + } +} +"#, + ); + + check_assist( + generate_mut_trait_impl, + r#" +//- minicore: index +mod foo { + mod bar { + pub enum Axis { X = 0, Y = 1, Z = 2 } + + impl core::ops::Index$0 for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } + } + } +} +"#, + r#" +mod foo { + mod bar { + pub enum Axis { X = 0, Y = 1, Z = 2 } + + $0impl core::ops::IndexMut for [T; 3] where T: Copy { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + let var_name = &self[index as usize]; + var_name + } + } + + impl core::ops::Index for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } + } + } +} +"#, + ); + } + #[test] fn test_generate_mut_trait_impl_not_applicable() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index f963f48d62ab..4837f92f9345 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -129,17 +129,23 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option // Get the mutable version of the impl to modify let impl_def = if let Some(impl_def) = impl_def { + fn_.indent(impl_def.indent_level()); builder.make_mut(impl_def) } else { // Generate a new impl to add the method to let impl_def = generate_impl(&ast::Adt::Struct(strukt.clone())); + let indent_level = strukt.indent_level(); + fn_.indent(indent_level); // Insert it after the adt let strukt = builder.make_mut(strukt.clone()); ted::insert_all_raw( ted::Position::after(strukt.syntax()), - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], + vec![ + make::tokens::whitespace(&format!("\n\n{indent_level}")).into(), + impl_def.syntax().clone().into(), + ], ); impl_def @@ -425,6 +431,135 @@ impl Foo { ); } + #[test] + fn non_zero_indent() { + check_assist( + generate_new, + r#" +mod foo { + struct $0Foo {} +} +"#, + r#" +mod foo { + struct Foo {} + + impl Foo { + fn $0new() -> Self { + Self { } + } + } +} +"#, + ); + check_assist( + generate_new, + r#" +mod foo { + mod bar { + struct $0Foo {} + } +} +"#, + r#" +mod foo { + mod bar { + struct Foo {} + + impl Foo { + fn $0new() -> Self { + Self { } + } + } + } +} +"#, + ); + check_assist( + generate_new, + r#" +mod foo { + struct $0Foo {} + + impl Foo { + fn some() {} + } +} +"#, + r#" +mod foo { + struct Foo {} + + impl Foo { + fn $0new() -> Self { + Self { } + } + + fn some() {} + } +} +"#, + ); + check_assist( + generate_new, + r#" +mod foo { + mod bar { + struct $0Foo {} + + impl Foo { + fn some() {} + } + } +} +"#, + r#" +mod foo { + mod bar { + struct Foo {} + + impl Foo { + fn $0new() -> Self { + Self { } + } + + fn some() {} + } + } +} +"#, + ); + check_assist( + generate_new, + r#" +mod foo { + mod bar { +struct $0Foo {} + + impl Foo { + fn some() {} + } + } +} +"#, + r#" +mod foo { + mod bar { +struct Foo {} + + impl Foo { + fn $0new() -> Self { + Self { } + } + + fn some() {} + } + } +} +"#, + ); + } + #[test] fn check_visibility_of_new_fn_based_on_struct() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index 7e8735bd7a24..a9df6f6fc366 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -1,3 +1,4 @@ +use either::Either; use syntax::{ ast::{ self, AstNode, HasName, HasTypeBounds, @@ -30,10 +31,11 @@ pub(crate) fn move_bounds_to_where_clause( ) -> Option<()> { let type_param_list = ctx.find_node_at_offset::()?; - let mut type_params = type_param_list.type_or_const_params(); + let mut type_params = type_param_list.generic_params(); if type_params.all(|p| match p { - ast::TypeOrConstParam::Type(t) => t.type_bound_list().is_none(), - ast::TypeOrConstParam::Const(_) => true, + ast::GenericParam::TypeParam(t) => t.type_bound_list().is_none(), + ast::GenericParam::LifetimeParam(l) => l.type_bound_list().is_none(), + ast::GenericParam::ConstParam(_) => true, }) { return None; } @@ -53,20 +55,23 @@ pub(crate) fn move_bounds_to_where_clause( match parent { ast::Fn(it) => it.get_or_create_where_clause(), ast::Trait(it) => it.get_or_create_where_clause(), + ast::TraitAlias(it) => it.get_or_create_where_clause(), ast::Impl(it) => it.get_or_create_where_clause(), ast::Enum(it) => it.get_or_create_where_clause(), ast::Struct(it) => it.get_or_create_where_clause(), + ast::TypeAlias(it) => it.get_or_create_where_clause(), _ => return, } }; - for toc_param in type_param_list.type_or_const_params() { - let type_param = match toc_param { - ast::TypeOrConstParam::Type(x) => x, - ast::TypeOrConstParam::Const(_) => continue, + for generic_param in type_param_list.generic_params() { + let param: &dyn HasTypeBounds = match &generic_param { + ast::GenericParam::TypeParam(t) => t, + ast::GenericParam::LifetimeParam(l) => l, + ast::GenericParam::ConstParam(_) => continue, }; - if let Some(tbl) = type_param.type_bound_list() { - if let Some(predicate) = build_predicate(type_param) { + if let Some(tbl) = param.type_bound_list() { + if let Some(predicate) = build_predicate(generic_param) { where_clause.add_predicate(predicate) } tbl.remove() @@ -76,9 +81,23 @@ pub(crate) fn move_bounds_to_where_clause( ) } -fn build_predicate(param: ast::TypeParam) -> Option { - let path = make::ext::ident_path(¶m.name()?.syntax().to_string()); - let predicate = make::where_pred(make::ty_path(path), param.type_bound_list()?.bounds()); +fn build_predicate(param: ast::GenericParam) -> Option { + let target = match ¶m { + ast::GenericParam::TypeParam(t) => { + Either::Right(make::ty_path(make::ext::ident_path(&t.name()?.to_string()))) + } + ast::GenericParam::LifetimeParam(l) => Either::Left(l.lifetime()?), + ast::GenericParam::ConstParam(_) => return None, + }; + let predicate = make::where_pred( + target, + match param { + ast::GenericParam::TypeParam(t) => t.type_bound_list()?, + ast::GenericParam::LifetimeParam(l) => l.type_bound_list()?, + ast::GenericParam::ConstParam(_) => return None, + } + .bounds(), + ); Some(predicate.clone_for_update()) } @@ -123,4 +142,13 @@ mod tests { r#"struct Pair(T, T) where T: u32;"#, ); } + + #[test] + fn move_bounds_to_where_clause_trait() { + check_assist( + move_bounds_to_where_clause, + r#"trait T<'a: 'static, $0T: u32> {}"#, + r#"trait T<'a, T> where 'a: 'static, T: u32 {}"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index 1baf814ca682..16debc4d7285 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -1,6 +1,9 @@ use std::collections::hash_map::Entry; -use hir::{FileRange, InFile, InRealFile, Module, ModuleSource}; +use hir::{ + FileRange, InFile, InRealFile, Module, ModuleDef, ModuleSource, PathResolution, + PathResolutionPerNs, +}; use ide_db::text_edit::TextRange; use ide_db::{ FxHashMap, RootDatabase, @@ -77,22 +80,17 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) }; // Get the actual definition associated with this use item. - let res = match ctx.sema.resolve_path(&path) { - Some(x) => x, - None => { + let res = match ctx.sema.resolve_path_per_ns(&path) { + Some(x) if x.any().is_some() => x, + Some(_) | None => { return None; } }; - let def = match res { - hir::PathResolution::Def(d) => Definition::from(d), - _ => return None, - }; - if u.star_token().is_some() { // Check if any of the children of this module are used - let def_mod = match def { - Definition::Module(module) => module, + let def_mod = match res.type_ns { + Some(PathResolution::Def(ModuleDef::Module(module))) => module, _ => return None, }; @@ -105,21 +103,13 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) }) .any(|d| used_once_in_scope(ctx, d, u.rename(), scope)) { - return Some(u); + Some(u) + } else { + None } - } else if let Definition::Trait(ref t) = def { - // If the trait or any item is used. - if !std::iter::once((def, u.rename())) - .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None))) - .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope)) - { - return Some(u); - } - } else if !used_once_in_scope(ctx, def, u.rename(), scope) { - return Some(u); + } else { + is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u) } - - None }) .peekable(); @@ -141,6 +131,52 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) } } +fn is_path_per_ns_unused_in_scope( + ctx: &AssistContext<'_>, + u: &ast::UseTree, + scope: &mut Vec, + path: &PathResolutionPerNs, +) -> bool { + if let Some(PathResolution::Def(ModuleDef::Trait(ref t))) = path.type_ns { + if is_trait_unused_in_scope(ctx, u, scope, t) { + let path = [path.value_ns, path.macro_ns]; + is_path_unused_in_scope(ctx, u, scope, &path) + } else { + false + } + } else { + let path = [path.type_ns, path.value_ns, path.macro_ns]; + is_path_unused_in_scope(ctx, u, scope, &path) + } +} + +fn is_path_unused_in_scope( + ctx: &AssistContext<'_>, + u: &ast::UseTree, + scope: &mut Vec, + path: &[Option], +) -> bool { + !path + .iter() + .filter_map(|path| *path) + .filter_map(|res| match res { + PathResolution::Def(d) => Some(Definition::from(d)), + _ => None, + }) + .any(|def| used_once_in_scope(ctx, def, u.rename(), scope)) +} + +fn is_trait_unused_in_scope( + ctx: &AssistContext<'_>, + u: &ast::UseTree, + scope: &mut Vec, + t: &hir::Trait, +) -> bool { + !std::iter::once((Definition::Trait(*t), u.rename())) + .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None))) + .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope)) +} + fn used_once_in_scope( ctx: &AssistContext<'_>, def: Definition, @@ -1009,6 +1045,112 @@ fn test(_: Bar) { let a = (); a.quxx(); } +"#, + ); + } + + #[test] + fn test_unused_macro() { + check_assist( + remove_unused_imports, + r#" +//- /foo.rs crate:foo +#[macro_export] +macro_rules! m { () => {} } + +//- /main.rs crate:main deps:foo +use foo::m;$0 +fn main() {} +"#, + r#" +fn main() {} +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- /foo.rs crate:foo +#[macro_export] +macro_rules! m { () => {} } + +//- /main.rs crate:main deps:foo +use foo::m;$0 +fn main() { + m!(); +} +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- /foo.rs crate:foo +#[macro_export] +macro_rules! m { () => {} } + +//- /bar.rs crate:bar deps:foo +pub use foo::m; +fn m() {} + + +//- /main.rs crate:main deps:bar +use bar::m;$0 +fn main() { + m!(); +} +"#, + ); + } + + #[test] + fn test_conflict_derive_macro() { + check_assist_not_applicable( + remove_unused_imports, + r#" +//- proc_macros: derive_identity +//- minicore: derive +//- /bar.rs crate:bar +pub use proc_macros::DeriveIdentity; +pub trait DeriveIdentity {} + +//- /main.rs crate:main deps:bar +$0use bar::DeriveIdentity;$0 +#[derive(DeriveIdentity)] +struct S; +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- proc_macros: derive_identity +//- minicore: derive +//- /bar.rs crate:bar +pub use proc_macros::DeriveIdentity; +pub fn DeriveIdentity() {} + +//- /main.rs crate:main deps:bar +$0use bar::DeriveIdentity;$0 +#[derive(DeriveIdentity)] +struct S; +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- proc_macros: derive_identity +//- minicore: derive +//- /bar.rs crate:bar +pub use proc_macros::DeriveIdentity; +pub fn DeriveIdentity() {} + +//- /main.rs crate:main deps:bar +$0use bar::DeriveIdentity;$0 +fn main() { + DeriveIdentity(); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs deleted file mode 100644 index c6e864fcfdba..000000000000 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::iter; - -use ide_db::{assists::AssistId, ty_filter::TryEnum}; -use syntax::{ - AstNode, T, - ast::{ - self, - edit::{AstNodeEdit, IndentLevel}, - make, - }, -}; - -use crate::assist_context::{AssistContext, Assists}; - -// Assist: replace_try_expr_with_match -// -// Replaces a `try` expression with a `match` expression. -// -// ``` -// # //- minicore: try, option -// fn handle() { -// let pat = Some(true)$0?; -// } -// ``` -// -> -// ``` -// fn handle() { -// let pat = match Some(true) { -// Some(it) => it, -// None => return None, -// }; -// } -// ``` -pub(crate) fn replace_try_expr_with_match( - acc: &mut Assists, - ctx: &AssistContext<'_>, -) -> Option<()> { - let qm_kw = ctx.find_token_syntax_at_offset(T![?])?; - let qm_kw_parent = qm_kw.parent().and_then(ast::TryExpr::cast)?; - - let expr = qm_kw_parent.expr()?; - let expr_type_info = ctx.sema.type_of_expr(&expr)?; - - let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?; - - let target = qm_kw_parent.syntax().text_range(); - acc.add( - AssistId::refactor_rewrite("replace_try_expr_with_match"), - "Replace try expression with match", - target, - |edit| { - let sad_pat = match try_enum { - TryEnum::Option => make::path_pat(make::ext::ident_path("None")), - TryEnum::Result => make::tuple_struct_pat( - make::ext::ident_path("Err"), - iter::once(make::path_pat(make::ext::ident_path("err"))), - ) - .into(), - }; - let sad_expr = match try_enum { - TryEnum::Option => { - make::expr_return(Some(make::expr_path(make::ext::ident_path("None")))) - } - TryEnum::Result => make::expr_return(Some( - make::expr_call( - make::expr_path(make::ext::ident_path("Err")), - make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))), - ) - .into(), - )), - }; - - let happy_arm = make::match_arm( - try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()), - None, - make::expr_path(make::ext::ident_path("it")), - ); - let sad_arm = make::match_arm(sad_pat, None, sad_expr); - - let match_arm_list = make::match_arm_list([happy_arm, sad_arm]); - - let expr_match = make::expr_match(expr, match_arm_list) - .indent(IndentLevel::from_node(qm_kw_parent.syntax())); - edit.replace_ast::(qm_kw_parent.into(), expr_match.into()); - }, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::tests::{check_assist, check_assist_not_applicable}; - - #[test] - fn test_replace_try_expr_with_match_not_applicable() { - check_assist_not_applicable( - replace_try_expr_with_match, - r#" - fn test() { - let pat: u32 = 25$0; - } - "#, - ); - } - - #[test] - fn test_replace_try_expr_with_match_option() { - check_assist( - replace_try_expr_with_match, - r#" -//- minicore: try, option -fn test() { - let pat = Some(true)$0?; -} - "#, - r#" -fn test() { - let pat = match Some(true) { - Some(it) => it, - None => return None, - }; -} - "#, - ); - } - - #[test] - fn test_replace_try_expr_with_match_result() { - check_assist( - replace_try_expr_with_match, - r#" -//- minicore: try, from, result -fn test() { - let pat = Ok(true)$0?; -} - "#, - r#" -fn test() { - let pat = match Ok(true) { - Ok(it) => it, - Err(err) => return Err(err), - }; -} - "#, - ); - } -} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs new file mode 100644 index 000000000000..7b5adc1858b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs @@ -0,0 +1,156 @@ +use ide_db::assists::AssistId; +use syntax::{ + AstNode, + ast::{self, GenericArg, HasGenericArgs}, +}; + +use crate::{AssistContext, Assists}; + +// Assist: unwrap_type_to_generic_arg +// +// This assist unwraps a type into its generic type argument. +// +// ``` +// fn foo() -> $0Option { +// todo!() +// } +// ``` +// -> +// ``` +// fn foo() -> i32 { +// todo!() +// } +// ``` +pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let path_type = ctx.find_node_at_offset::()?; + let path = path_type.path()?; + let segment = path.segment()?; + let args_list = segment.generic_arg_list()?; + + let mut generic_arg = None; + + for arg in args_list.generic_args() { + match arg { + GenericArg::ConstArg(_) | GenericArg::LifetimeArg(_) => (), + GenericArg::TypeArg(arg) if generic_arg.is_none() => { + generic_arg = Some(arg); + } + _ => return None, + } + } + + let generic_arg = generic_arg?; + + acc.add( + AssistId::refactor_extract("unwrap_type_to_generic_arg"), + format!("Unwrap type to type argument {generic_arg}"), + path_type.syntax().text_range(), + |builder| { + let mut editor = builder.make_editor(path_type.syntax()); + editor.replace(path_type.syntax(), generic_arg.syntax()); + + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn test_unwrap_type_to_generic_arg() { + check_assist( + unwrap_type_to_generic_arg, + r#" +//- minicore: option +fn foo() -> $0Option { + todo!() +} +"#, + r#" +fn foo() -> i32 { + todo!() +} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_not_applicable_for_non_generic_arg_list() { + check_assist_not_applicable( + unwrap_type_to_generic_arg, + r#" +fn foo() -> $0i32 {} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_not_applicable_for_multiple_generic_args() { + check_assist_not_applicable( + unwrap_type_to_generic_arg, + r#" +//- minicore: result +fn foo() -> $0Result { + todo!() +} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_with_lifetime_and_const() { + check_assist( + unwrap_type_to_generic_arg, + r#" +enum Foo<'a, T, const N: usize> { + Bar(T), + Baz(&'a [T; N]), +} + +fn test<'a>() -> $0Foo<'a, i32, 3> { + todo!() +} +"#, + r#" +enum Foo<'a, T, const N: usize> { + Bar(T), + Baz(&'a [T; N]), +} + +fn test<'a>() -> i32 { + todo!() +} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_in_let_stmt() { + check_assist( + unwrap_type_to_generic_arg, + r#" +enum Foo { + Bar(T), + Baz, +} + +fn test() { + let foo: $0Foo = todo!(); +} +"#, + r#" +enum Foo { + Bar(T), + Baz, +} + +fn test() { + let foo: i32 = todo!(); +} +"#, + ); + } +} 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 627ed37b04e5..c2604432032d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -139,6 +139,7 @@ mod handlers { mod destructure_struct_binding; mod destructure_tuple_binding; mod desugar_doc_comment; + mod desugar_try_expr; mod expand_glob_import; mod expand_rest_pattern; mod extract_expressions_from_format_string; @@ -214,7 +215,6 @@ mod handlers { mod replace_named_generic_with_impl; mod replace_qualified_name_with_use; mod replace_string_with_char; - mod replace_try_expr_with_match; mod replace_turbofish_with_explicit_type; mod sort_items; mod split_import; @@ -229,6 +229,7 @@ mod handlers { mod unwrap_block; mod unwrap_return_type; mod unwrap_tuple; + mod unwrap_type_to_generic_arg; mod wrap_return_type; mod wrap_unwrap_cfg_attr; @@ -272,6 +273,7 @@ mod handlers { destructure_struct_binding::destructure_struct_binding, destructure_tuple_binding::destructure_tuple_binding, desugar_doc_comment::desugar_doc_comment, + desugar_try_expr::desugar_try_expr, expand_glob_import::expand_glob_import, expand_glob_import::expand_glob_reexport, expand_rest_pattern::expand_rest_pattern, @@ -353,7 +355,6 @@ mod handlers { replace_method_eager_lazy::replace_with_lazy_method, replace_named_generic_with_impl::replace_named_generic_with_impl, replace_qualified_name_with_use::replace_qualified_name_with_use, - replace_try_expr_with_match::replace_try_expr_with_match, replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type, sort_items::sort_items, split_import::split_import, @@ -369,6 +370,7 @@ mod handlers { unwrap_block::unwrap_block, unwrap_return_type::unwrap_return_type, unwrap_tuple::unwrap_tuple, + unwrap_type_to_generic_arg::unwrap_type_to_generic_arg, wrap_return_type::wrap_return_type, wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr, 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 01ab0be34b28..72f7195cbd77 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 @@ -929,6 +929,47 @@ comment"] ) } +#[test] +fn doctest_desugar_try_expr_let_else() { + check_doc_test( + "desugar_try_expr_let_else", + r#####" +//- minicore: try, option +fn handle() { + let pat = Some(true)$0?; +} +"#####, + r#####" +fn handle() { + let Some(pat) = Some(true) else { + return None; + }; +} +"#####, + ) +} + +#[test] +fn doctest_desugar_try_expr_match() { + check_doc_test( + "desugar_try_expr_match", + r#####" +//- minicore: try, option +fn handle() { + let pat = Some(true)$0?; +} +"#####, + r#####" +fn handle() { + let pat = match Some(true) { + Some(it) => it, + None => return None, + }; +} +"#####, + ) +} + #[test] fn doctest_expand_glob_import() { check_doc_test( @@ -3096,27 +3137,6 @@ fn main() { ) } -#[test] -fn doctest_replace_try_expr_with_match() { - check_doc_test( - "replace_try_expr_with_match", - r#####" -//- minicore: try, option -fn handle() { - let pat = Some(true)$0?; -} -"#####, - r#####" -fn handle() { - let pat = match Some(true) { - Some(it) => it, - None => return None, - }; -} -"#####, - ) -} - #[test] fn doctest_replace_turbofish_with_explicit_type() { check_doc_test( @@ -3481,6 +3501,23 @@ fn main() { ) } +#[test] +fn doctest_unwrap_type_to_generic_arg() { + check_doc_test( + "unwrap_type_to_generic_arg", + r#####" +fn foo() -> $0Option { + todo!() +} +"#####, + r#####" +fn foo() -> i32 { + todo!() +} +"#####, + ) +} + #[test] fn doctest_wrap_return_type_in_option() { check_doc_test( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index cd18b3dcfdc2..92cbf411c1e3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -13,6 +13,7 @@ use crate::{ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO", "Path to the cargo binary performing the build"), ("CARGO_MANIFEST_DIR", "The directory containing the manifest of your package"), + ("CARGO_MANIFEST_PATH", "The path to the manifest of your package"), ("CARGO_PKG_VERSION", "The full version of your package"), ("CARGO_PKG_VERSION_MAJOR", "The major version of your package"), ("CARGO_PKG_VERSION_MINOR", "The minor version of your package"), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 54be7d2fbc33..3cdf2112835d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -311,6 +311,8 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { let mut prefix = String::new(); + let mut found_ref_or_deref = false; + while let Some(parent_deref_element) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast) { @@ -318,27 +320,26 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { break; } + found_ref_or_deref = true; resulting_element = ast::Expr::from(parent_deref_element); prefix.insert(0, '*'); } - if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { - if let Some(expr) = first_ref_expr.expr() { - resulting_element = expr; - } + while let Some(parent_ref_element) = + resulting_element.syntax().parent().and_then(ast::RefExpr::cast) + { + found_ref_or_deref = true; + let exclusive = parent_ref_element.mut_token().is_some(); + resulting_element = ast::Expr::from(parent_ref_element); - while let Some(parent_ref_element) = - resulting_element.syntax().parent().and_then(ast::RefExpr::cast) - { - let exclusive = parent_ref_element.mut_token().is_some(); - resulting_element = ast::Expr::from(parent_ref_element); + prefix.insert_str(0, if exclusive { "&mut " } else { "&" }); + } - prefix.insert_str(0, if exclusive { "&mut " } else { "&" }); - } - } else { - // If we do not find any ref expressions, restore + if !found_ref_or_deref { + // If we do not find any ref/deref expressions, restore // all the progress of tree climbing + prefix.clear(); resulting_element = initial_element.clone(); } 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 3baf1f3de610..5287627790ae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -8,8 +8,8 @@ use std::{iter, ops::ControlFlow}; use base_db::RootQueryDb as _; use hir::{ - DisplayTarget, HasAttrs, Local, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, - Semantics, SemanticsScope, Symbol, Type, TypeInfo, + DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution, + ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, }; use ide_db::{ FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs, @@ -751,7 +751,7 @@ impl<'a> CompletionContext<'a> { original_offset, } = expand_and_analyze( &sema, - original_file.syntax().clone(), + InFile::new(editioned_file_id.into(), original_file.syntax().clone()), file_with_fake_ident.syntax().clone(), offset, &original_token, 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 391e2379dcd5..7a2230b3e361 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 @@ -1,7 +1,7 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant}; +use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant}; use ide_db::{RootDatabase, active_parameter::ActiveParameter}; use itertools::Either; use syntax::{ @@ -50,7 +50,7 @@ pub(super) struct AnalysisResult { pub(super) fn expand_and_analyze( sema: &Semantics<'_, RootDatabase>, - original_file: SyntaxNode, + original_file: InFile, speculative_file: SyntaxNode, offset: TextSize, original_token: &SyntaxToken, @@ -72,7 +72,7 @@ pub(super) fn expand_and_analyze( relative_offset, ) .unwrap_or(ExpansionResult { - original_file, + original_file: original_file.value, speculative_file, original_offset: offset, speculative_offset: fake_ident_token.text_range().start(), @@ -125,7 +125,7 @@ fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Opt /// the best we can do. fn expand_maybe_stop( sema: &Semantics<'_, RootDatabase>, - original_file: SyntaxNode, + original_file: InFile, speculative_file: SyntaxNode, original_offset: TextSize, fake_ident_token: SyntaxToken, @@ -142,17 +142,16 @@ fn expand_maybe_stop( return result; } - // This needs to come after the recursive call, because our "inside macro" detection is subtly wrong - // with regard to attribute macros named `test` that are not std's test. So hopefully we will expand - // them successfully above and be able to analyze. - // Left biased since there may already be an identifier token there, and we appended to it. - if !sema.might_be_inside_macro_call(&fake_ident_token) - && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) - .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) + // We can't check whether the fake expansion is inside macro call, because that requires semantic info. + // But hopefully checking just the real one should be enough. + if token_at_offset_ignore_whitespace(&original_file.value, original_offset + relative_offset) + .is_some_and(|original_token| { + !sema.is_inside_macro_call(original_file.with_value(&original_token)) + }) { // Recursion base case. Some(ExpansionResult { - original_file, + original_file: original_file.value, speculative_file, original_offset, speculative_offset: fake_ident_token.text_range().start(), @@ -166,7 +165,7 @@ fn expand_maybe_stop( fn expand( sema: &Semantics<'_, RootDatabase>, - original_file: SyntaxNode, + original_file: InFile, speculative_file: SyntaxNode, original_offset: TextSize, fake_ident_token: SyntaxToken, @@ -176,7 +175,7 @@ fn expand( let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); - let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset) + let original_node = token_at_offset_ignore_whitespace(&original_file.value, original_offset) .and_then(|token| token.parent_ancestors().find_map(ast::Item::cast)); let ancestor_items = iter::successors( Option::zip( @@ -249,7 +248,7 @@ fn expand( } // No attributes have been expanded, so look for macro_call! token trees or derive token trees - let orig_tt = ancestors_at_offset(&original_file, original_offset) + let orig_tt = ancestors_at_offset(&original_file.value, original_offset) .map_while(Either::::cast) .last()?; let spec_tt = ancestors_at_offset(&speculative_file, fake_ident_token.text_range().start()) @@ -292,7 +291,7 @@ fn expand( fake_mapped_tokens.into_iter().min_by_key(|(_, rank)| *rank) { return Some(ExpansionResult { - original_file, + original_file: original_file.value, speculative_file, original_offset, speculative_offset: fake_ident_token.text_range().start(), @@ -349,7 +348,7 @@ fn expand( } let result = expand_maybe_stop( sema, - actual_expansion.clone(), + InFile::new(file.into(), actual_expansion.clone()), fake_expansion.clone(), new_offset, fake_mapped_token, @@ -883,9 +882,10 @@ fn classify_name_ref( }, ast::MethodCallExpr(method) => { let receiver = find_opt_node_in_file(original_file, method.receiver()); + let has_parens = has_parens(&method); let kind = NameRefKind::DotAccess(DotAccess { receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), - kind: DotAccessKind::Method { has_parens: method.arg_list().is_some_and(|it| it.l_paren_token().is_some()) }, + kind: DotAccessKind::Method { has_parens }, receiver, ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()) } }); @@ -1372,7 +1372,7 @@ fn classify_name_ref( } } - path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::cast(it).is_some_and(|it| has_parens(&it))); make_path_kind_expr(it.into()) }, @@ -1401,7 +1401,7 @@ fn classify_name_ref( match parent { ast::PathType(it) => make_path_kind_type(it.into()), ast::PathExpr(it) => { - path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::cast(it).is_some_and(|it| has_parens(&it))); make_path_kind_expr(it.into()) }, @@ -1559,6 +1559,30 @@ fn classify_name_ref( Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx)) } +/// When writing in the middle of some code the following situation commonly occurs (`|` denotes the cursor): +/// ```ignore +/// value.method| +/// (1, 2, 3) +/// ``` +/// Here, we want to complete the method parentheses & arguments (if the corresponding settings are on), +/// but the thing is parsed as a method call with parentheses. Therefore we use heuristics: if the parentheses +/// are on the next line, consider them non-existent. +fn has_parens(node: &dyn HasArgList) -> bool { + let Some(arg_list) = node.arg_list() else { return false }; + if arg_list.l_paren_token().is_none() { + return false; + } + let prev_siblings = iter::successors(arg_list.syntax().prev_sibling_or_token(), |it| { + it.prev_sibling_or_token() + }); + prev_siblings + .take_while(|syntax| syntax.kind().is_trivia()) + .filter_map(|syntax| { + syntax.into_token().filter(|token| token.kind() == SyntaxKind::WHITESPACE) + }) + .all(|whitespace| !whitespace.text().contains('\n')) +} + fn pattern_context_for( sema: &Semantics<'_, RootDatabase>, original_file: &SyntaxNode, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index d5137949d42f..b46e4c32061b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2111,6 +2111,56 @@ fn foo() { ); } +#[test] +fn cfg_attr_attr_macro() { + check( + r#" +//- proc_macros: identity +#[cfg_attr(test, proc_macros::identity)] +fn foo() { + $0 +} + "#, + expect![[r#" + fn foo() fn() + md proc_macros + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + #[test] fn escaped_label() { check( @@ -2126,3 +2176,70 @@ fn main() { "#]], ); } + +#[test] +fn call_parens_with_newline() { + check_edit( + "foo", + r#" +fn foo(v: i32) {} + +fn bar() { + foo$0 + () +} + "#, + r#" +fn foo(v: i32) {} + +fn bar() { + foo(${1:v});$0 + () +} + "#, + ); + check_edit( + "foo", + r#" +struct Foo; +impl Foo { + fn foo(&self, v: i32) {} +} + +fn bar() { + Foo.foo$0 + () +} + "#, + r#" +struct Foo; +impl Foo { + fn foo(&self, v: i32) {} +} + +fn bar() { + Foo.foo(${1:v});$0 + () +} + "#, + ); +} + +#[test] +fn dbg_too_many_asterisks() { + check_edit( + "dbg", + r#" +fn main() { + let x = &42; + let y = *x.$0; +} + "#, + r#" +fn main() { + let x = &42; + let y = dbg!(*x); +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs index 55689034fb47..ed87b339fedf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs @@ -4,7 +4,7 @@ //! in [crate::completions::mod_]. use expect_test::expect; -use crate::tests::{check_edit, check_with_base_items}; +use crate::tests::{check, check_edit, check_with_base_items}; #[test] fn target_type_or_trait_in_impl_block() { @@ -308,3 +308,39 @@ fn bar() { "#]], ); } + +#[test] +fn expression_in_item_macro() { + check( + r#" +fn foo() -> u8 { 0 } + +macro_rules! foo { + ($expr:expr) => { + const BAR: u8 = $expr; + }; +} + +foo!(f$0); + "#, + expect![[r#" + ct BAR u8 + fn foo() fn() -> u8 + ma foo!(…) macro_rules! foo + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} 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 bf4f541ff54c..d5db1c481b69 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -6,7 +6,7 @@ // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). use crate::RootDatabase; -use crate::documentation::{Documentation, HasDocs}; +use crate::documentation::{DocsRangeMap, Documentation, HasDocs}; use crate::famous_defs::FamousDefs; use arrayvec::ArrayVec; use either::Either; @@ -21,7 +21,7 @@ use hir::{ use span::Edition; use stdx::{format_to, impl_from}; use syntax::{ - SyntaxKind, SyntaxNode, SyntaxToken, + SyntaxKind, SyntaxNode, SyntaxToken, TextSize, ast::{self, AstNode}, match_ast, }; @@ -210,29 +210,40 @@ impl Definition { famous_defs: Option<&FamousDefs<'_, '_>>, display_target: DisplayTarget, ) -> Option { + self.docs_with_rangemap(db, famous_defs, display_target).map(|(docs, _)| docs) + } + + pub fn docs_with_rangemap( + &self, + db: &RootDatabase, + famous_defs: Option<&FamousDefs<'_, '_>>, + display_target: DisplayTarget, + ) -> Option<(Documentation, Option)> { let docs = match self { - Definition::Macro(it) => it.docs(db), - Definition::Field(it) => it.docs(db), - Definition::Module(it) => it.docs(db), - Definition::Crate(it) => it.docs(db), - Definition::Function(it) => it.docs(db), - Definition::Adt(it) => it.docs(db), - Definition::Variant(it) => it.docs(db), - Definition::Const(it) => it.docs(db), - Definition::Static(it) => it.docs(db), - Definition::Trait(it) => it.docs(db), - Definition::TraitAlias(it) => it.docs(db), + Definition::Macro(it) => it.docs_with_rangemap(db), + Definition::Field(it) => it.docs_with_rangemap(db), + Definition::Module(it) => it.docs_with_rangemap(db), + Definition::Crate(it) => it.docs_with_rangemap(db), + Definition::Function(it) => it.docs_with_rangemap(db), + Definition::Adt(it) => it.docs_with_rangemap(db), + Definition::Variant(it) => it.docs_with_rangemap(db), + Definition::Const(it) => it.docs_with_rangemap(db), + Definition::Static(it) => it.docs_with_rangemap(db), + Definition::Trait(it) => it.docs_with_rangemap(db), + Definition::TraitAlias(it) => it.docs_with_rangemap(db), Definition::TypeAlias(it) => { - it.docs(db).or_else(|| { + it.docs_with_rangemap(db).or_else(|| { // docs are missing, try to fall back to the docs of the aliased item. let adt = it.ty(db).as_adt()?; - let docs = adt.docs(db)?; - let docs = format!( - "*This is the documentation for* `{}`\n\n{}", - adt.display(db, display_target), - docs.as_str() + let (docs, range_map) = adt.docs_with_rangemap(db)?; + let header_docs = format!( + "*This is the documentation for* `{}`\n\n", + adt.display(db, display_target) ); - Some(Documentation::new(docs)) + let offset = TextSize::new(header_docs.len() as u32); + let range_map = range_map.shift_docstring_line_range(offset); + let docs = header_docs + docs.as_str(); + Some((Documentation::new(docs), range_map)) }) } Definition::BuiltinType(it) => { @@ -241,17 +252,17 @@ impl Definition { let primitive_mod = format!("prim_{}", it.name().display(fd.0.db, display_target.edition)); let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?; - doc_owner.docs(fd.0.db) + doc_owner.docs_with_rangemap(fd.0.db) }) } Definition::BuiltinLifetime(StaticLifetime) => None, Definition::Local(_) => None, Definition::SelfType(impl_def) => { - impl_def.self_ty(db).as_adt().map(|adt| adt.docs(db))? + impl_def.self_ty(db).as_adt().map(|adt| adt.docs_with_rangemap(db))? } Definition::GenericParam(_) => None, Definition::Label(_) => None, - Definition::ExternCrateDecl(it) => it.docs(db), + Definition::ExternCrateDecl(it) => it.docs_with_rangemap(db), Definition::BuiltinAttr(it) => { let name = it.name(db); @@ -276,7 +287,8 @@ impl Definition { name_value_str ); } - Some(Documentation::new(docs.replace('*', "\\*"))) + + return Some((Documentation::new(docs.replace('*', "\\*")), None)); } Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, @@ -291,8 +303,9 @@ impl Definition { let trait_ = assoc.implemented_trait(db)?; let name = Some(assoc.name(db)?); let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?; - item.docs(db) + item.docs_with_rangemap(db) }) + .map(|(docs, range_map)| (docs, Some(range_map))) } pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index ef2c83992c04..30c355f8b3f9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -34,11 +34,13 @@ impl From for String { pub trait HasDocs: HasAttrs { fn docs(self, db: &dyn HirDatabase) -> Option; + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)>; fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, ns: Option, + is_inner_doc: bool, ) -> Option; } /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. @@ -53,7 +55,7 @@ pub struct DocsRangeMap { impl DocsRangeMap { /// Maps a [`TextRange`] relative to the documentation string back to its AST range - pub fn map(&self, range: TextRange) -> Option> { + pub fn map(&self, range: TextRange) -> Option<(InFile, AttrId)> { let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; let (line_docs_range, idx, original_line_src_range) = self.mapping[found]; if !line_docs_range.contains_range(range) { @@ -71,7 +73,7 @@ impl DocsRangeMap { text_range.end() + original_line_src_range.start() + relative_range.start(), string.syntax().text_range().len().min(range.len()), ); - Some(InFile { file_id, value: range }) + Some((InFile { file_id, value: range }, idx)) } Either::Right(comment) => { let text_range = comment.syntax().text_range(); @@ -82,10 +84,22 @@ impl DocsRangeMap { + relative_range.start(), text_range.len().min(range.len()), ); - Some(InFile { file_id, value: range }) + Some((InFile { file_id, value: range }, idx)) } } } + + pub fn shift_docstring_line_range(self, offset: TextSize) -> DocsRangeMap { + let mapping = self + .mapping + .into_iter() + .map(|(buf_offset, id, base_offset)| { + let buf_offset = buf_offset.checked_add(offset).unwrap(); + (buf_offset, id, base_offset) + }) + .collect_vec(); + DocsRangeMap { source_map: self.source_map, mapping } + } } pub fn docs_with_rangemap( @@ -161,13 +175,20 @@ macro_rules! impl_has_docs { fn docs(self, db: &dyn HirDatabase) -> Option { docs_from_attrs(&self.attrs(db)).map(Documentation) } + fn docs_with_rangemap( + self, + db: &dyn HirDatabase, + ) -> Option<(Documentation, DocsRangeMap)> { + docs_with_rangemap(db, &self.attrs(db)) + } fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, - ns: Option + ns: Option, + is_inner_doc: bool, ) -> Option { - resolve_doc_path_on(db, self, link, ns) + resolve_doc_path_on(db, self, link, ns, is_inner_doc) } } )*}; @@ -184,13 +205,21 @@ macro_rules! impl_has_docs_enum { fn docs(self, db: &dyn HirDatabase) -> Option { hir::$enum::$variant(self).docs(db) } + + fn docs_with_rangemap( + self, + db: &dyn HirDatabase, + ) -> Option<(Documentation, DocsRangeMap)> { + hir::$enum::$variant(self).docs_with_rangemap(db) + } fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, - ns: Option + ns: Option, + is_inner_doc: bool, ) -> Option { - hir::$enum::$variant(self).resolve_doc_path(db, link, ns) + hir::$enum::$variant(self).resolve_doc_path(db, link, ns, is_inner_doc) } } )*}; @@ -207,16 +236,25 @@ impl HasDocs for hir::AssocItem { } } + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> { + match self { + hir::AssocItem::Function(it) => it.docs_with_rangemap(db), + hir::AssocItem::Const(it) => it.docs_with_rangemap(db), + hir::AssocItem::TypeAlias(it) => it.docs_with_rangemap(db), + } + } + fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, ns: Option, + is_inner_doc: bool, ) -> Option { match self { - hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), - hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), - hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns), + hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), } } } @@ -238,13 +276,36 @@ impl HasDocs for hir::ExternCrateDecl { } .map(Documentation::new) } + + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> { + let crate_docs = docs_with_rangemap(db, &self.resolved_crate(db)?.root_module().attrs(db)); + let decl_docs = docs_with_rangemap(db, &self.attrs(db)); + match (decl_docs, crate_docs) { + (None, None) => None, + (Some(decl_docs), None) => Some(decl_docs), + (None, Some(crate_docs)) => Some(crate_docs), + ( + Some((Documentation(mut decl_docs), mut decl_range_map)), + Some((Documentation(crate_docs), crate_range_map)), + ) => { + decl_docs.push('\n'); + decl_docs.push('\n'); + let offset = TextSize::new(decl_docs.len() as u32); + decl_docs += &crate_docs; + let crate_range_map = crate_range_map.shift_docstring_line_range(offset); + decl_range_map.mapping.extend(crate_range_map.mapping); + Some((Documentation(decl_docs), decl_range_map)) + } + } + } fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, ns: Option, + is_inner_doc: bool, ) -> Option { - resolve_doc_path_on(db, self, link, ns) + resolve_doc_path_on(db, self, link, ns, is_inner_doc) } } 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 63cc7cde2808..c94be7e164e2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -92,9 +92,7 @@ pub struct RootDatabase { impl std::panic::RefUnwindSafe for RootDatabase {} #[salsa_macros::db] -impl salsa::Database for RootDatabase { - fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {} -} +impl salsa::Database for RootDatabase {} impl Drop for RootDatabase { fn drop(&mut self) { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index cbe31405ab78..5356614dce52 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -2,12 +2,10 @@ //! sometimes is counter productive when, for example, the first goto definition //! request takes longer to compute. This module implements prepopulation of //! various caches, it's not really advanced at the moment. -mod topologic_sort; - -use std::time::Duration; +use std::panic::AssertUnwindSafe; use hir::{Symbol, db::DefDatabase}; -use itertools::Itertools; +use rustc_hash::FxHashMap; use salsa::{Cancelled, Database}; use crate::{ @@ -35,59 +33,114 @@ pub fn parallel_prime_caches( ) { let _p = tracing::info_span!("parallel_prime_caches").entered(); - let mut crates_to_prime = { - // FIXME: We already have the crate list topologically sorted (but without the things - // `TopologicalSortIter` gives us). Maybe there is a way to avoid using it and rip it out - // of the codebase? - let mut builder = topologic_sort::TopologicalSortIter::builder(); - - for &crate_id in db.all_crates().iter() { - builder.add(crate_id, crate_id.data(db).dependencies.iter().map(|d| d.crate_id)); - } - - builder.build() - }; - enum ParallelPrimeCacheWorkerProgress { - BeginCrate { crate_id: Crate, crate_name: Symbol }, - EndCrate { crate_id: Crate }, + BeginCrateDefMap { crate_id: Crate, crate_name: Symbol }, + EndCrateDefMap { crate_id: Crate }, + EndCrateImportMap, + EndModuleSymbols, Cancelled(Cancelled), } - // We split off def map computation from other work, - // as the def map is the relevant one. Once the defmaps are computed - // the project is ready to go, the other indices are just nice to have for some IDE features. - #[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone)] - enum PrimingPhase { - DefMap, - ImportMap, - CrateSymbols, - } + // The setup here is a bit complicated. We try to make best use of compute resources. + // The idea is that if we have a def map available to compute, we should do that first. + // This is because def map is a dependency of both import map and symbols. So if we have + // e.g. a def map and a symbols, if we compute the def map we can, after it completes, + // compute the def maps of dependencies, the existing symbols and the symbols of the + // new crate, all in parallel. But if we compute the symbols, after that we will only + // have the def map to compute, and the rest of the CPU cores will rest, which is not + // good. + // However, it's better to compute symbols/import map than to compute a def map that + // isn't ready yet, because one of its dependencies hasn't yet completed its def map. + // Such def map will just block on the dependency, which is just wasted time. So better + // to compute the symbols/import map of an already computed def map in that time. - let (work_sender, progress_receiver) = { - let (progress_sender, progress_receiver) = crossbeam_channel::unbounded(); - let (work_sender, work_receiver) = crossbeam_channel::unbounded(); - let prime_caches_worker = move |db: RootDatabase| { - while let Ok((crate_id, crate_name, kind)) = work_receiver.recv() { - progress_sender - .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?; - - let cancelled = Cancelled::catch(|| match kind { - PrimingPhase::DefMap => _ = db.crate_def_map(crate_id), - PrimingPhase::ImportMap => _ = db.import_map(crate_id), - PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()), - }); - - match cancelled { - Ok(()) => progress_sender - .send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?, - Err(cancelled) => progress_sender - .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, - } + let (reverse_deps, mut to_be_done_deps) = { + let all_crates = db.all_crates(); + let to_be_done_deps = all_crates + .iter() + .map(|&krate| (krate, krate.data(db).dependencies.len() as u32)) + .collect::>(); + let mut reverse_deps = + all_crates.iter().map(|&krate| (krate, Vec::new())).collect::>(); + for &krate in &*all_crates { + for dep in &krate.data(db).dependencies { + reverse_deps.get_mut(&dep.crate_id).unwrap().push(krate); } + } + (reverse_deps, to_be_done_deps) + }; - Ok::<_, crossbeam_channel::SendError<_>>(()) - }; + let (def_map_work_sender, import_map_work_sender, symbols_work_sender, progress_receiver) = { + let (progress_sender, progress_receiver) = crossbeam_channel::unbounded(); + let (def_map_work_sender, def_map_work_receiver) = crossbeam_channel::unbounded(); + let (import_map_work_sender, import_map_work_receiver) = crossbeam_channel::unbounded(); + let (symbols_work_sender, symbols_work_receiver) = crossbeam_channel::unbounded(); + let prime_caches_worker = + move |db: RootDatabase| { + let handle_def_map = |crate_id, crate_name| { + progress_sender.send(ParallelPrimeCacheWorkerProgress::BeginCrateDefMap { + crate_id, + crate_name, + })?; + + let cancelled = Cancelled::catch(|| _ = hir::crate_def_map(&db, crate_id)); + + match cancelled { + Ok(()) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::EndCrateDefMap { crate_id })?, + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + let handle_import_map = |crate_id| { + let cancelled = Cancelled::catch(|| _ = db.import_map(crate_id)); + + match cancelled { + Ok(()) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::EndCrateImportMap)?, + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + let handle_symbols = |module| { + let cancelled = + Cancelled::catch(AssertUnwindSafe(|| _ = db.module_symbols(module))); + + match cancelled { + Ok(()) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::EndModuleSymbols)?, + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + + loop { + db.unwind_if_revision_cancelled(); + + // Biased because we want to prefer def maps. + crossbeam_channel::select_biased! { + recv(def_map_work_receiver) -> work => { + let Ok((crate_id, crate_name)) = work else { break }; + handle_def_map(crate_id, crate_name)?; + } + recv(import_map_work_receiver) -> work => { + let Ok(crate_id) = work else { break }; + handle_import_map(crate_id)?; + } + recv(symbols_work_receiver) -> work => { + let Ok(module) = work else { break }; + handle_symbols(module)?; + } + } + } + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; for id in 0..num_worker_threads { stdx::thread::Builder::new( @@ -103,138 +156,121 @@ pub fn parallel_prime_caches( .expect("failed to spawn thread"); } - (work_sender, progress_receiver) + (def_map_work_sender, import_map_work_sender, symbols_work_sender, progress_receiver) }; - let crates_total = crates_to_prime.pending(); - let mut crates_done = 0; + let crate_def_maps_total = db.all_crates().len(); + let mut crate_def_maps_done = 0; + let (mut crate_import_maps_total, mut crate_import_maps_done) = (0usize, 0usize); + let (mut module_symbols_total, mut module_symbols_done) = (0usize, 0usize); // an index map is used to preserve ordering so we can sort the progress report in order of // "longest crate to index" first let mut crates_currently_indexing = FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default()); - let mut additional_phases = vec![]; - - while crates_done < crates_total { - db.unwind_if_revision_cancelled(); - - for krate in &mut crates_to_prime { - let name = krate.extra_data(db).display_name.as_deref().cloned().unwrap_or_else(|| { - Symbol::integer(salsa::plumbing::AsId::as_id(&krate).as_u32() as usize) - }); - let origin = &krate.data(db).origin; - if origin.is_lang() { - additional_phases.push((krate, name.clone(), PrimingPhase::ImportMap)); - } else if origin.is_local() { - // Compute the symbol search index. - // This primes the cache for `ide_db::symbol_index::world_symbols()`. - // - // We do this for workspace crates only (members of local_roots), because doing it - // for all dependencies could be *very* unnecessarily slow in a large project. - // - // FIXME: We should do it unconditionally if the configuration is set to default to - // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we - // would need to pipe that configuration information down here. - additional_phases.push((krate, name.clone(), PrimingPhase::CrateSymbols)); - } - - work_sender.send((krate, name, PrimingPhase::DefMap)).ok(); + for (&krate, &to_be_done_deps) in &to_be_done_deps { + if to_be_done_deps != 0 { + continue; } - // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision - // is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or - // if this thread exits, and closes the work channel. - let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) { - Ok(p) => p, - Err(crossbeam_channel::RecvTimeoutError::Timeout) => { - continue; - } - Err(crossbeam_channel::RecvTimeoutError::Disconnected) => { - // all our workers have exited, mark us as finished and exit - cb(ParallelPrimeCachesProgress { - crates_currently_indexing: vec![], - crates_done, - crates_total: crates_done, - work_type: "Indexing", - }); - return; - } - }; - match worker_progress { - ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => { - crates_currently_indexing.insert(crate_id, crate_name); - } - ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => { - crates_currently_indexing.swap_remove(&crate_id); - crates_to_prime.mark_done(crate_id); - crates_done += 1; - } - ParallelPrimeCacheWorkerProgress::Cancelled(cancelled) => { - // Cancelled::throw should probably be public - std::panic::resume_unwind(Box::new(cancelled)); - } - }; + let name = crate_name(db, krate); + def_map_work_sender.send((krate, name)).ok(); + } + + while crate_def_maps_done < crate_def_maps_total + || crate_import_maps_done < crate_import_maps_total + || module_symbols_done < module_symbols_total + { + db.unwind_if_revision_cancelled(); let progress = ParallelPrimeCachesProgress { crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), - crates_done, - crates_total, + crates_done: crate_def_maps_done, + crates_total: crate_def_maps_total, work_type: "Indexing", }; cb(progress); - } - let mut crates_done = 0; - let crates_total = additional_phases.len(); - for w in additional_phases.into_iter().sorted_by_key(|&(_, _, phase)| phase) { - work_sender.send(w).ok(); - } - - while crates_done < crates_total { - db.unwind_if_revision_cancelled(); - - // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision - // is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or - // if this thread exits, and closes the work channel. - let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) { + // Biased to prefer progress updates (and because it's faster). + let progress = match progress_receiver.recv() { Ok(p) => p, - Err(crossbeam_channel::RecvTimeoutError::Timeout) => { - continue; - } - Err(crossbeam_channel::RecvTimeoutError::Disconnected) => { + Err(crossbeam_channel::RecvError) => { // all our workers have exited, mark us as finished and exit cb(ParallelPrimeCachesProgress { crates_currently_indexing: vec![], - crates_done, - crates_total: crates_done, - work_type: "Populating symbols", + crates_done: crate_def_maps_done, + crates_total: crate_def_maps_done, + work_type: "Done", }); return; } }; - match worker_progress { - ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => { + + match progress { + ParallelPrimeCacheWorkerProgress::BeginCrateDefMap { crate_id, crate_name } => { crates_currently_indexing.insert(crate_id, crate_name); } - ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => { + ParallelPrimeCacheWorkerProgress::EndCrateDefMap { crate_id } => { crates_currently_indexing.swap_remove(&crate_id); - crates_done += 1; + crate_def_maps_done += 1; + + // Fire ready dependencies. + for &dep in &reverse_deps[&crate_id] { + let to_be_done = to_be_done_deps.get_mut(&dep).unwrap(); + *to_be_done -= 1; + if *to_be_done == 0 { + let dep_name = crate_name(db, dep); + def_map_work_sender.send((dep, dep_name)).ok(); + } + } + + if crate_def_maps_done == crate_def_maps_total { + cb(ParallelPrimeCachesProgress { + crates_currently_indexing: vec![], + crates_done: crate_def_maps_done, + crates_total: crate_def_maps_done, + work_type: "Collecting Symbols", + }); + } + + let origin = &crate_id.data(db).origin; + if origin.is_lang() { + crate_import_maps_total += 1; + import_map_work_sender.send(crate_id).ok(); + } else if origin.is_local() { + // Compute the symbol search index. + // This primes the cache for `ide_db::symbol_index::world_symbols()`. + // + // We do this for workspace crates only (members of local_roots), because doing it + // for all dependencies could be *very* unnecessarily slow in a large project. + // + // FIXME: We should do it unconditionally if the configuration is set to default to + // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we + // would need to pipe that configuration information down here. + let modules = hir::Crate::from(crate_id).modules(db); + module_symbols_total += modules.len(); + for module in modules { + symbols_work_sender.send(module).ok(); + } + } } + ParallelPrimeCacheWorkerProgress::EndCrateImportMap => crate_import_maps_done += 1, + ParallelPrimeCacheWorkerProgress::EndModuleSymbols => module_symbols_done += 1, ParallelPrimeCacheWorkerProgress::Cancelled(cancelled) => { // Cancelled::throw should probably be public std::panic::resume_unwind(Box::new(cancelled)); } - }; - - let progress = ParallelPrimeCachesProgress { - crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), - crates_done, - crates_total, - work_type: "Populating symbols", - }; - - cb(progress); + } } } + +fn crate_name(db: &RootDatabase, krate: Crate) -> Symbol { + krate + .extra_data(db) + .display_name + .as_deref() + .cloned() + .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).as_u32() as usize)) +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches/topologic_sort.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches/topologic_sort.rs deleted file mode 100644 index c8a038631036..000000000000 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches/topologic_sort.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! helper data structure to schedule work for parallel prime caches. -use std::{collections::VecDeque, hash::Hash}; - -use crate::FxHashMap; - -pub(crate) struct TopologicSortIterBuilder { - nodes: FxHashMap>, -} - -// this implementation has different bounds on T than would be implied by #[derive(Default)] -impl Default for TopologicSortIterBuilder -where - T: Copy + Eq + PartialEq + Hash, -{ - fn default() -> Self { - Self { nodes: Default::default() } - } -} - -impl TopologicSortIterBuilder -where - T: Copy + Eq + PartialEq + Hash, -{ - fn get_or_create_entry(&mut self, item: T) -> &mut Entry { - self.nodes.entry(item).or_default() - } - - pub(crate) fn add(&mut self, item: T, predecessors: impl IntoIterator) { - let mut num_predecessors = 0; - - for predecessor in predecessors.into_iter() { - self.get_or_create_entry(predecessor).successors.push(item); - num_predecessors += 1; - } - - let entry = self.get_or_create_entry(item); - entry.num_predecessors += num_predecessors; - } - - pub(crate) fn build(self) -> TopologicalSortIter { - let ready = self - .nodes - .iter() - .filter_map( - |(item, entry)| if entry.num_predecessors == 0 { Some(*item) } else { None }, - ) - .collect(); - - TopologicalSortIter { nodes: self.nodes, ready } - } -} - -pub(crate) struct TopologicalSortIter { - ready: VecDeque, - nodes: FxHashMap>, -} - -impl TopologicalSortIter -where - T: Copy + Eq + PartialEq + Hash, -{ - pub(crate) fn builder() -> TopologicSortIterBuilder { - TopologicSortIterBuilder::default() - } - - pub(crate) fn pending(&self) -> usize { - self.nodes.len() - } - - pub(crate) fn mark_done(&mut self, item: T) { - let entry = self.nodes.remove(&item).expect("invariant: unknown item marked as done"); - - for successor in entry.successors { - let succ_entry = self - .nodes - .get_mut(&successor) - .expect("invariant: unknown successor referenced by entry"); - - succ_entry.num_predecessors -= 1; - if succ_entry.num_predecessors == 0 { - self.ready.push_back(successor); - } - } - } -} - -impl Iterator for TopologicalSortIter { - type Item = T; - - fn next(&mut self) -> Option { - self.ready.pop_front() - } -} - -struct Entry { - successors: Vec, - num_predecessors: usize, -} - -impl Default for Entry { - fn default() -> Self { - Self { successors: Default::default(), num_predecessors: 0 } - } -} 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 b8119e1aab36..fa2a46a0f7c2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -390,11 +390,6 @@ pub fn source_edit_from_references( let mut edited_ranges = Vec::new(); for &FileReference { range, ref name, .. } in references { let name_range = name.text_range(); - if name_range.len() != range.len() { - // This usage comes from a different token kind that was downmapped to a NameLike in a macro - // Renaming this will most likely break things syntax-wise - continue; - } let has_emitted_edit = match name { // if the ranges differ then the node is inside a macro call, we can't really attempt // to make special rewrites like shorthand syntax and such, so just rename the node in 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 30be5bc21b49..d4ab75929279 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -524,6 +524,7 @@ impl<'a> FindUsages<'a> { fn find_nodes<'b>( sema: &'b Semantics<'_, RootDatabase>, name: &str, + file_id: EditionedFileId, node: &syntax::SyntaxNode, offset: TextSize, ) -> impl Iterator + 'b { @@ -534,7 +535,7 @@ impl<'a> FindUsages<'a> { }) .into_iter() .flat_map(move |token| { - if sema.might_be_inside_macro_call(&token) { + if sema.is_inside_macro_call(InFile::new(file_id.into(), &token)) { sema.descend_into_macros_exact(token) } else { <_>::from([token]) @@ -654,11 +655,14 @@ impl<'a> FindUsages<'a> { let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); for offset in FindUsages::match_indices(&file_text, &finder, search_range) { - let usages = - FindUsages::find_nodes(sema, ¤t_to_process, &tree, offset) - .filter(|it| { - matches!(it.kind(), SyntaxKind::NAME | SyntaxKind::NAME_REF) - }); + let usages = FindUsages::find_nodes( + sema, + ¤t_to_process, + file_id, + &tree, + offset, + ) + .filter(|it| matches!(it.kind(), SyntaxKind::NAME | SyntaxKind::NAME_REF)); for usage in usages { if let Some(alias) = usage.parent().and_then(|it| { let path = ast::PathSegment::cast(it)?.parent_path(); @@ -813,7 +817,7 @@ impl<'a> FindUsages<'a> { let tree = LazyCell::new(move || this.sema.parse(file_id).syntax().clone()); for offset in FindUsages::match_indices(&file_text, finder, search_range) { - let usages = FindUsages::find_nodes(this.sema, name, &tree, offset) + let usages = FindUsages::find_nodes(this.sema, name, file_id, &tree, offset) .filter_map(ast::NameRef::cast); for usage in usages { let found_usage = usage @@ -970,8 +974,8 @@ impl<'a> FindUsages<'a> { return; } - for name in - Self::find_nodes(sema, name, &tree, offset).filter_map(ast::NameLike::cast) + for name in Self::find_nodes(sema, name, file_id, &tree, offset) + .filter_map(ast::NameLike::cast) { if match name { ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink), @@ -985,8 +989,8 @@ impl<'a> FindUsages<'a> { // Search for occurrences of the `Self` referring to our type if let Some((self_ty, finder)) = &include_self_kw_refs { for offset in Self::match_indices(&text, finder, search_range) { - for name_ref in - Self::find_nodes(sema, "Self", &tree, offset).filter_map(ast::NameRef::cast) + for name_ref in Self::find_nodes(sema, "Self", file_id, &tree, offset) + .filter_map(ast::NameRef::cast) { if self.found_self_ty_name_ref(self_ty, &name_ref, sink) { return; @@ -1010,7 +1014,7 @@ impl<'a> FindUsages<'a> { let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); for offset in Self::match_indices(&text, finder, search_range) { - for name_ref in Self::find_nodes(sema, "super", &tree, offset) + for name_ref in Self::find_nodes(sema, "super", file_id, &tree, offset) .filter_map(ast::NameRef::cast) { if self.found_name_ref(&name_ref, sink) { @@ -1020,7 +1024,7 @@ impl<'a> FindUsages<'a> { } if let Some(finder) = &is_crate_root { for offset in Self::match_indices(&text, finder, search_range) { - for name_ref in Self::find_nodes(sema, "crate", &tree, offset) + for name_ref in Self::find_nodes(sema, "crate", file_id, &tree, offset) .filter_map(ast::NameRef::cast) { if self.found_name_ref(&name_ref, sink) { @@ -1064,8 +1068,8 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new("self"); for offset in Self::match_indices(&text, finder, search_range) { - for name_ref in - Self::find_nodes(sema, "self", &tree, offset).filter_map(ast::NameRef::cast) + for name_ref in Self::find_nodes(sema, "self", file_id, &tree, offset) + .filter_map(ast::NameRef::cast) { if self.found_self_module_name_ref(&name_ref, sink) { return; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs index 92ca7a74184f..2a7b0098edfd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs @@ -14,6 +14,7 @@ pub(crate) fn await_outside_of_async( format!("`await` is used inside {}, which is not an `async` context", d.location), display_range, ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs index 9ed85f9f208e..ae42a88c313c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs @@ -12,6 +12,7 @@ pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_>, d: &hir::BadRtn) -> Diagnost "return type notation not allowed in this position yet", d.rtn.map(Into::into), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index c25b0a7bf7d5..cbcaab6c7477 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -19,6 +19,7 @@ pub(crate) fn break_outside_of_loop( message, d.expr.map(|it| it.into()), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs index 438dd2fdcb6c..b284d9b35103 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs @@ -15,7 +15,6 @@ pub(crate) fn elided_lifetimes_in_path( "implicit elided lifetime not allowed here", d.generics_or_segment.map(Into::into), ) - .experimental() } else { Diagnostic::new_with_syntax_node_ptr( ctx, @@ -23,7 +22,6 @@ pub(crate) fn elided_lifetimes_in_path( "hidden lifetime parameters in types are deprecated", d.generics_or_segment.map(Into::into), ) - .experimental() } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs index a6da0fd9c5e3..7d2ac373dc08 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs @@ -15,7 +15,6 @@ pub(crate) fn expected_function( format!("expected function, found {}", d.found.display(ctx.sema.db, ctx.display_target)), d.call.map(|it| it.into()), ) - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index b617c0949839..9ae6f013c70d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -21,6 +21,7 @@ pub(crate) fn generic_args_prohibited( describe_reason(d.reason), d.args.map(Into::into), ) + .stable() .with_fixes(fixes(ctx, d)) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs index 47e1c84fecd0..8611ef653b02 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -33,6 +33,7 @@ pub(crate) fn inactive_code( message, ctx.sema.diagnostics_display_range(d.node), ) + .stable() .with_unused(true); Some(res) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs index 0b9a2ec9db3d..a0c364b00108 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs @@ -19,6 +19,7 @@ pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentI "cannot define inherent `impl` for foreign type".to_owned(), display_range, ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 289a07657325..38f10c778d69 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -29,6 +29,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas ), InFile::new(d.file, d.ident.into()), ) + .stable() .with_fixes(fixes(ctx, d)) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs index 17c7f75880c9..06f357594200 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs @@ -28,7 +28,6 @@ pub(crate) fn incorrect_generics_len( message, d.generics_or_segment.map(Into::into), ) - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs index 84496df2d7cf..b71586d6be0b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs @@ -28,6 +28,7 @@ pub(crate) fn incorrect_generics_order( message, d.provided_arg.map(Into::into), ) + .stable() } #[cfg(test)] 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 d72b21099ce3..7a6e98fe1b54 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 @@ -100,7 +100,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) - // "cannot cast to a pointer of an unknown kind".to_owned(), // ), }; - Diagnostic::new(code, message, display_range) + Diagnostic::new(code, message, display_range).stable() } // Diagnostic: cast-to-unsized @@ -113,6 +113,7 @@ pub(crate) fn cast_to_unsized(ctx: &DiagnosticsContext<'_>, d: &hir::CastToUnsiz format_ty!(ctx, "cast to unsized type: `{}`", d.cast_ty), display_range, ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs index ab0f5139f107..8b708f229d00 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs @@ -15,6 +15,7 @@ pub(crate) fn invalid_derive_target( "`derive` may only be applied to `struct`s, `enum`s and `union`s", display_range, ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index a2648a1995d7..546512a6cf92 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -19,6 +19,7 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> d.message.clone(), display_range, ) + .stable() } // Diagnostic: macro-def-error @@ -33,6 +34,7 @@ pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefErr d.message.clone(), display_range, ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs index 0e47fff6f93b..701b30b9b593 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs @@ -14,6 +14,7 @@ pub(crate) fn malformed_derive( "malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`", display_range, ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 63fd9b4e3f06..25c1e633ba3b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -26,6 +26,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count( message, invalid_args_range(ctx, d.expr_or_pat, d.expected, d.found), ) + .stable() } // Diagnostic: mismatched-arg-count @@ -42,6 +43,7 @@ pub(crate) fn mismatched_arg_count( message, invalid_args_range(ctx, d.call_expr, d.expected, d.found), ) + .stable() } fn invalid_args_range( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index a354d123f5ab..2b76efb1965b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -47,6 +47,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField ); Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr) + .stable() .with_fixes(fixes(ctx, d)) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index 8cdbb6384ff5..76b30745a04d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -13,7 +13,6 @@ pub(crate) fn missing_lifetime( "missing lifetime specifier", d.generics_or_segment.map(Into::into), ) - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index d3d3c3aa38dc..1fc96b78eda2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -13,6 +13,7 @@ pub(crate) fn missing_match_arms( format!("missing match arm: {}", d.uncovered_patterns), d.scrutinee_expr.map(Into::into), ) + .stable() } #[cfg(test)] 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 3c36b455ca9d..6bd5417b25d7 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 @@ -23,6 +23,7 @@ pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsaf format!("{operation} is unsafe and requires an unsafe function or block"), d.node.map(|it| it.into()), ) + .stable() .with_fixes(fixes(ctx, d)) } @@ -889,6 +890,70 @@ unsafe fn f() {} fn main() { const { f(); }; // ^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} + "#, + ); + } + + #[test] + fn asm_label() { + check_diagnostics( + r#" +//- minicore: asm +fn foo() { + unsafe { + core::arch::asm!( + "jmp {}", + label { + let p = 0xDEADBEAF as *mut u8; + *p = 3; + // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block + }, + ); + } +} + "#, + ); + } + + #[test] + fn regression_19823() { + check_diagnostics( + r#" +pub trait FooTrait { + unsafe fn method1(); + unsafe fn method2(); +} + +unsafe fn some_unsafe_fn() {} + +macro_rules! impl_foo { + () => { + unsafe fn method1() { + some_unsafe_fn(); + } + unsafe fn method2() { + some_unsafe_fn(); + } + }; +} + +pub struct S1; +#[allow(unsafe_op_in_unsafe_fn)] +impl FooTrait for S1 { + unsafe fn method1() { + some_unsafe_fn(); + } + + unsafe fn method2() { + some_unsafe_fn(); + } +} + +pub struct S2; +#[allow(unsafe_op_in_unsafe_fn)] +impl FooTrait for S2 { + impl_foo!(); } "#, ); 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 780271361d72..01cf5e8fa522 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 @@ -11,7 +11,7 @@ pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOf format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.display_target)), d.span, ) - .experimental() // spans are broken, and I'm not sure how precise we can detect copy types + // spans are broken, and I'm not sure how precise we can detect copy types } #[cfg(test)] 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 5d25f2c6a90f..8831efa31172 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 @@ -55,6 +55,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option ), span, ) + .stable() .with_fixes(fixes), ) } @@ -94,7 +95,7 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op "variable does not need to be mutable", ast, ) - .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. + // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. .with_fixes(fixes), ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index fa3347aa12e6..84fb467a5ce1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -22,6 +22,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) "field is private", node, ) + .stable() } else { Diagnostic::new_with_syntax_node_ptr( ctx, @@ -32,6 +33,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) "no such field", node, ) + .stable() .with_fixes(fixes(ctx, d)) } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index ff1eeb0516a9..f20b6dea1227 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -14,6 +14,7 @@ pub(crate) fn non_exhaustive_let( format!("non-exhaustive pattern: {}", d.uncovered_patterns), d.pat.map(Into::into), ) + .stable() } #[cfg(test)] @@ -105,4 +106,29 @@ fn test(x: Result) { "#, ); } + + #[test] + fn empty_patterns_normalize() { + check_diagnostics( + r#" +enum Infallible {} + +trait Foo { + type Assoc; +} +enum Enum { + A, + B(T::Assoc), +} + +impl Foo for () { + type Assoc = Infallible; +} + +fn foo(v: Enum<()>) { + let Enum::A = v; +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs index ccf517234183..68f2b1965702 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs @@ -14,6 +14,7 @@ pub(crate) fn parenthesized_generic_args_without_fn_trait( "parenthesized type parameters may only be used with a `Fn` trait", d.args.map(Into::into), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index fe32c590492d..6d33ae0cf9be 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -28,6 +28,7 @@ pub(crate) fn private_assoc_item( ), d.expr_or_pat.map(Into::into), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs index 237a9b87871c..5b4273a5a627 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs @@ -15,6 +15,7 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) ), d.expr.map(|it| it.into()), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 6b7864500261..dec7be8b7427 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -31,6 +31,7 @@ pub(crate) fn remove_trailing_return( "replace return ; with ", display_range, ) + .stable() .with_fixes(fixes(ctx, d)), ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index 8d717b9093b9..7dc5b5b45e5f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -36,7 +36,6 @@ pub(crate) fn remove_unnecessary_else( "remove unnecessary else block", display_range, ) - .experimental() .with_fixes(fixes(ctx, d)), ) } 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 6b335c52de75..37ce5f583f93 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 @@ -21,6 +21,7 @@ pub(crate) fn replace_filter_map_next_with_find_map( "replace filter_map(..).next() with find_map(..)", InFile::new(d.file, d.next_expr.into()), ) + .stable() .with_fixes(fixes(ctx, d)) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs index 19ee1caa3e6a..dd142db8590f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs @@ -33,6 +33,7 @@ pub(crate) fn trait_impl_incorrect_safety( }, ), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 2d7d78f5d7bd..fa7ba90a756b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -29,6 +29,7 @@ pub(crate) fn trait_impl_missing_assoc_item( &|impl_| impl_.trait_().map(|t| t.syntax().text_range()), ), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs index 35dc9b0fac8a..96911d4781b8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs @@ -16,8 +16,6 @@ pub(crate) fn trait_impl_orphan( .to_owned(), InFile::new(d.file_id, d.impl_.into()), ) - // Not yet checked for false positives - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index d5c4bcf768ad..4327b12dce70 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -61,6 +61,7 @@ pub(crate) fn trait_impl_redundant_assoc_item( format!("{redundant_item_name} is not a member of trait `{trait_name}`"), ide_db::FileRange { file_id: file_id.file_id(ctx.sema.db), range }, ) + .stable() .with_fixes(quickfix_for_redundant_assoc_item( ctx, d, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 500c5de791dc..076df1ab0f82 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -53,8 +53,8 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) display_range, ) .with_fixes(fixes(ctx, d)); - if diag.fixes.is_none() { - diag.experimental = true; + if diag.fixes.is_some() { + diag.experimental = false; } diag } @@ -1243,4 +1243,18 @@ fn foo(v: &Enum) { "#, ); } + + #[test] + fn regression_19844() { + check_diagnostics( + r#" +fn main() { + struct S {} + enum E { V() } + let E::V() = &S {}; + // ^^^^^^ error: expected S, found E +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index a933f1b42611..1915a88dd002 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -37,6 +37,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di }; Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range) + .stable() .with_fixes(fixes) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs index d16bfb800240..f81d34377da4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -12,6 +12,7 @@ pub(crate) fn undeclared_label( format!("use of undeclared label `{}`", name.display(ctx.sema.db, ctx.edition)), d.node.map(|it| it.into()), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs index 06f176f86f4e..5627393f3181 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs @@ -13,4 +13,5 @@ pub(crate) fn unimplemented_builtin_macro( "unimplemented built-in macro".to_owned(), d.node, ) + .stable() } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 47fa30593626..af9126c89331 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -2,7 +2,8 @@ use std::iter; -use hir::{DefMap, InFile, ModuleSource, db::DefDatabase}; +use hir::crate_def_map; +use hir::{DefMap, InFile, ModuleSource}; use ide_db::base_db::RootQueryDb; use ide_db::text_edit::TextEdit; use ide_db::{ @@ -101,7 +102,8 @@ fn fixes( // check crate roots, i.e. main.rs, lib.rs, ... let relevant_crates = db.relevant_crates(file_id); 'crates: for &krate in &*relevant_crates { - let crate_def_map = ctx.sema.db.crate_def_map(krate); + // FIXME: This shouldnt need to access the crate def map directly + let crate_def_map = crate_def_map(ctx.sema.db, krate); let root_module = &crate_def_map[DefMap::ROOT]; let Some(root_file_id) = root_module.origin.file_id() else { continue }; @@ -156,7 +158,7 @@ fn fixes( stack.pop(); let relevant_crates = db.relevant_crates(parent_id); 'crates: for &krate in relevant_crates.iter() { - let crate_def_map = ctx.sema.db.crate_def_map(krate); + let crate_def_map = crate_def_map(ctx.sema.db, krate); let Some((_, module)) = crate_def_map.modules().find(|(_, module)| { module.origin.file_id().map(|file_id| file_id.file_id(ctx.sema.db)) == Some(parent_id) && !module.origin.is_inline() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs index bdff2417ca11..0c9e0d6ce440 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs @@ -12,6 +12,7 @@ pub(crate) fn unreachable_label( format!("use of unreachable label `{}`", name.display(ctx.sema.db, ctx.edition)), d.node.map(|it| it.into()), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs index 614057ab52bf..4ae528bf9f28 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs @@ -13,7 +13,6 @@ pub(crate) fn unresolved_assoc_item( "no such associated item", d.expr_or_pat.map(Into::into), ) - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs index 4cd73d46d5f1..7c3eacf7e3ad 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs @@ -13,6 +13,7 @@ pub(crate) fn unresolved_extern_crate( "unresolved extern crate", d.decl.map(|it| it.into()), ) + .stable() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index a4f4813cf5b0..0649c97f8205 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -52,7 +52,6 @@ pub(crate) fn unresolved_field( }), ) .with_fixes(fixes(ctx, d)) - .experimental() } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option> { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs index 4f64dabeb52f..801023dabd96 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs @@ -13,7 +13,6 @@ pub(crate) fn unresolved_ident( range.range = in_node_range + range.range.start(); } Diagnostic::new(DiagnosticCode::RustcHardError("E0425"), "no such value in this scope", range) - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs index 67c7e76a3bc1..0da535d11b4f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs @@ -18,7 +18,6 @@ pub(crate) fn unresolved_import( // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) // - `core::arch` (we don't handle `#[path = "../"]` correctly) // - proc macros and/or proc macro generated code - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 0d1c97750627..a87b8c42ac1d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -16,7 +16,6 @@ pub(crate) fn unresolved_macro_call( format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db, ctx.edition)), display_range, ) - .experimental() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 7f07009dc561..00c2a8c4c468 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -47,7 +47,6 @@ pub(crate) fn unresolved_method( }), ) .with_fixes(fixes(ctx, d)) - .experimental() } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option> { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 599cabe3e4f2..1a409d7e76a2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -28,6 +28,7 @@ pub(crate) fn unresolved_module( }, d.decl.map(|it| it.into()), ) + .stable() .with_fixes(fixes(ctx, d)) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index 77b1075ea532..e6bbff05f7e8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -50,8 +50,7 @@ pub(crate) fn unused_variables( ast.file_id.is_macro(), ctx.edition, ) - })) - .experimental(), + })), ) } 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 607721d611d7..72bd66d1c8bb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -83,12 +83,11 @@ mod handlers { #[cfg(test)] mod tests; -use std::{collections::hash_map, iter, sync::LazyLock}; +use std::{iter, sync::LazyLock}; use either::Either; use hir::{ - Crate, DisplayTarget, HirFileId, InFile, Semantics, db::ExpandDatabase, - diagnostics::AnyDiagnostic, + Crate, DisplayTarget, InFile, Semantics, db::ExpandDatabase, diagnostics::AnyDiagnostic, }; use ide_db::{ EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, @@ -182,7 +181,7 @@ impl Diagnostic { DiagnosticCode::Ra(_, s) => s, }, unused: false, - experimental: false, + experimental: true, fixes: None, main_node: None, } @@ -198,8 +197,8 @@ impl Diagnostic { .with_main_node(node) } - fn experimental(mut self) -> Diagnostic { - self.experimental = true; + fn stable(mut self) -> Diagnostic { + self.experimental = false; self } @@ -424,14 +423,11 @@ pub fn semantic_diagnostics( AnyDiagnostic::MacroExpansionParseError(d) => { // FIXME: Point to the correct error span here, not just the macro-call name res.extend(d.errors.iter().take(16).map(|err| { - { Diagnostic::new( DiagnosticCode::SyntaxError, format!("Syntax Error in Expansion: {err}"), ctx.resolve_precise_location(&d.node.clone(), d.precise_location), ) - } - .experimental() })); continue; }, @@ -485,12 +481,8 @@ pub fn semantic_diagnostics( Some(it) => it, None => continue, }, - AnyDiagnostic::GenericArgsProhibited(d) => { - handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d) - } - AnyDiagnostic::ParenthesizedGenericArgsWithoutFnTrait(d) => { - handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d) - } + AnyDiagnostic::GenericArgsProhibited(d) => handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d), + AnyDiagnostic::ParenthesizedGenericArgsWithoutFnTrait(d) => handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d), AnyDiagnostic::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d), AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&ctx, &d), AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d), @@ -520,13 +512,7 @@ pub fn semantic_diagnostics( // The edition isn't accurate (each diagnostics may have its own edition due to macros), // but it's okay as it's only being used for error recovery. - handle_lints( - &ctx.sema, - &mut FxHashMap::default(), - &mut lints, - &mut Vec::new(), - editioned_file_id.edition(db), - ); + handle_lints(&ctx.sema, &mut lints, editioned_file_id.edition(db)); res.retain(|d| d.severity != Severity::Allow); @@ -591,8 +577,6 @@ fn handle_diag_from_macros( true } -// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros - struct BuiltLint { lint: &'static Lint, groups: Vec<&'static str>, @@ -636,9 +620,7 @@ fn build_lints_map( fn handle_lints( sema: &Semantics<'_, RootDatabase>, - cache: &mut FxHashMap>, diagnostics: &mut [(InFile, &mut Diagnostic)], - cache_stack: &mut Vec, edition: Edition, ) { for (node, diag) in diagnostics { @@ -652,7 +634,8 @@ fn handle_lints( diag.severity = default_severity; } - let mut diag_severity = fill_lint_attrs(sema, node, cache, cache_stack, diag, edition); + let mut diag_severity = + lint_severity_at(sema, node, &lint_groups(&diag.code, edition), edition); if let outline_diag_severity @ Some(_) = find_outline_mod_lint_severity(sema, node, diag, edition) @@ -705,155 +688,22 @@ fn find_outline_mod_lint_severity( result } -#[derive(Debug, Clone, Copy)] -struct SeverityAttr { - severity: Severity, - /// This field counts how far we are from the main node. Bigger values mean more far. - /// - /// Note this isn't accurate: there can be gaps between values (created when merging severity maps). - /// The important thing is that if an attr is closer to the main node, it will have smaller value. - /// - /// This is necessary even though we take care to never overwrite a value from deeper nesting - /// because of lint groups. For example, in the following code: - /// ``` - /// #[warn(non_snake_case)] - /// mod foo { - /// #[allow(nonstandard_style)] - /// mod bar {} - /// } - /// ``` - /// We want to not warn on non snake case inside `bar`. If we are traversing this for the first - /// time, everything will be fine, because we will set `diag_severity` on the first matching group - /// and never overwrite it since then. But if `bar` is cached, the cache will contain both - /// `#[warn(non_snake_case)]` and `#[allow(nonstandard_style)]`, and without this field, we have - /// no way of differentiating between the two. - depth: u32, -} - -fn fill_lint_attrs( +fn lint_severity_at( sema: &Semantics<'_, RootDatabase>, node: &InFile, - cache: &mut FxHashMap>, - cache_stack: &mut Vec, - diag: &Diagnostic, + lint_groups: &LintGroups, edition: Edition, ) -> Option { - let mut collected_lint_attrs = FxHashMap::::default(); - let mut diag_severity = None; - - let mut ancestors = node.value.ancestors().peekable(); - let mut depth = 0; - loop { - let ancestor = ancestors.next().expect("we always return from top-level nodes"); - depth += 1; - - if ancestors.peek().is_none() { - // We don't want to insert too many nodes into cache, but top level nodes (aka. outline modules - // or macro expansions) need to touch the database so they seem like a good fit to cache. - - if let Some(cached) = cache.get_mut(&node.file_id) { - // This node (and everything above it) is already cached; the attribute is either here or nowhere. - - // Workaround for the borrow checker. - let cached = std::mem::take(cached); - - cached.iter().for_each(|(lint, severity)| { - for item in &*cache_stack { - let node_cache_entry = cache - .get_mut(item) - .expect("we always insert cached nodes into the cache map"); - let lint_cache_entry = node_cache_entry.entry(lint.clone()); - if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { - // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs - // overwrite top attrs. - lint_cache_entry.insert(SeverityAttr { - severity: severity.severity, - depth: severity.depth + depth, - }); - } - } - }); - - let lints = lint_groups(&diag.code, edition); - let all_matching_groups = - lints.iter().filter_map(|lint_group| cached.get(lint_group)); - let cached_severity = - all_matching_groups.min_by_key(|it| it.depth).map(|it| it.severity); - - cache.insert(node.file_id, cached); - - return diag_severity.or(cached_severity); - } - - // Insert this node's descendants' attributes into any outline descendant, but not including this node. - // This must come before inserting this node's own attributes to preserve order. - collected_lint_attrs.drain().for_each(|(lint, severity)| { - if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) { - diag_severity = Some(severity.severity); - } - - for item in &*cache_stack { - let node_cache_entry = cache - .get_mut(item) - .expect("we always insert cached nodes into the cache map"); - let lint_cache_entry = node_cache_entry.entry(lint.clone()); - if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { - // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs - // overwrite top attrs. - lint_cache_entry.insert(severity); - } - } - }); - - cache_stack.push(node.file_id); - cache.insert(node.file_id, FxHashMap::default()); - - if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) { - // Insert this node's attributes into any outline descendant, including this node. - lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| { - if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) { - diag_severity = Some(severity); - } - - for item in &*cache_stack { - let node_cache_entry = cache - .get_mut(item) - .expect("we always insert cached nodes into the cache map"); - let lint_cache_entry = node_cache_entry.entry(lint.clone()); - if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { - // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs - // overwrite top attrs. - lint_cache_entry.insert(SeverityAttr { severity, depth }); - } - } - }); - } - - let parent_node = sema.find_parent_file(node.file_id); - if let Some(parent_node) = parent_node { - let parent_severity = - fill_lint_attrs(sema, &parent_node, cache, cache_stack, diag, edition); - if diag_severity.is_none() { - diag_severity = parent_severity; - } - } - cache_stack.pop(); - return diag_severity; - } else if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) { - lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| { - if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) { - diag_severity = Some(severity); - } - - let lint_cache_entry = collected_lint_attrs.entry(lint); - if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { - // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs - // overwrite top attrs. - lint_cache_entry.insert(SeverityAttr { severity, depth }); - } - }); - } - } + node.value + .ancestors() + .filter_map(ast::AnyHasAttrs::cast) + .find_map(|ancestor| { + lint_attrs(sema, ancestor, edition) + .find_map(|(lint, severity)| lint_groups.contains(&lint).then_some(severity)) + }) + .or_else(|| { + lint_severity_at(sema, &sema.find_parent_file(node.file_id)?, lint_groups, edition) + }) } fn lint_attrs<'a>( @@ -952,10 +802,6 @@ impl LintGroups { fn contains(&self, group: &str) -> bool { self.groups.contains(&group) || (self.inside_warnings && group == "warnings") } - - fn iter(&self) -> impl Iterator { - self.groups.iter().copied().chain(self.inside_warnings.then_some("warnings")) - } } fn lint_groups(lint: &DiagnosticCode, edition: Edition) -> LintGroups { diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index 339c199ec29a..43c56ac8bec5 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -287,7 +287,7 @@ impl<'db> MatchFinder<'db> { if let Some(expanded) = self.sema.expand_macro_call(¯o_call) { if let Some(tt) = macro_call.token_tree() { self.output_debug_for_nodes_at_range( - &expanded, + &expanded.value, range, &Some(self.sema.original_range(tt.syntax())), out, diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs index d89911fca403..9afbedbb1ab4 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs @@ -194,7 +194,7 @@ impl MatchFinder<'_> { // nodes that originated entirely from within the token tree of the macro call. // i.e. we don't want to match something that came from the macro itself. if let Some(range) = self.sema.original_range_opt(tt.syntax()) { - self.slow_scan_node(&expanded, rule, &Some(range), matches_out); + self.slow_scan_node(&expanded.value, rule, &Some(range), matches_out); } } } 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 f0247f32d7ec..2c983287d89c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -5,17 +5,21 @@ mod tests; mod intra_doc_links; +use std::ops::Range; + use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; use pulldown_cmark_to_cmark::{Options as CMarkOptions, cmark_resume_with_options}; use stdx::format_to; use url::Url; -use hir::{Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs, db::HirDatabase, sym}; +use hir::{ + Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrsWithOwner, HasAttrs, db::HirDatabase, sym, +}; use ide_db::{ RootDatabase, base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb}, defs::{Definition, NameClass, NameRefClass}, - documentation::{Documentation, HasDocs, docs_with_rangemap}, + documentation::{DocsRangeMap, Documentation, HasDocs, docs_with_rangemap}, helpers::pick_best_token, }; use syntax::{ @@ -46,11 +50,17 @@ const MARKDOWN_OPTIONS: Options = Options::ENABLE_FOOTNOTES.union(Options::ENABLE_TABLES).union(Options::ENABLE_TASKLISTS); /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) -pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Definition) -> String { +pub(crate) fn rewrite_links( + db: &RootDatabase, + markdown: &str, + definition: Definition, + range_map: Option, +) -> String { let mut cb = broken_link_clone_cb; - let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb)); + let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb)) + .into_offset_iter(); - let doc = map_links(doc, |target, title| { + let doc = map_links(doc, |target, title, range| { // This check is imperfect, there's some overlap between valid intra-doc links // and valid URLs so we choose to be too eager to try to resolve what might be // a URL. @@ -60,7 +70,16 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Defin // Two possibilities: // * path-based links: `../../module/struct.MyStruct.html` // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` - if let Some((target, title)) = rewrite_intra_doc_link(db, definition, target, title) { + let text_range = + TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()); + let is_inner_doc = range_map + .as_ref() + .and_then(|range_map| range_map.map(text_range)) + .map(|(_, attr_id)| attr_id.is_inner_attr()) + .unwrap_or(false); + if let Some((target, title)) = + rewrite_intra_doc_link(db, definition, target, title, is_inner_doc) + { (None, target, title) } else if let Some(target) = rewrite_url_link(db, definition, target) { (Some(LinkType::Inline), target, title.to_owned()) @@ -195,22 +214,23 @@ pub(crate) fn resolve_doc_path_for_def( def: Definition, link: &str, ns: Option, + is_inner_doc: bool, ) -> Option { match def { - Definition::Module(it) => it.resolve_doc_path(db, link, ns), - Definition::Crate(it) => it.resolve_doc_path(db, link, ns), - Definition::Function(it) => it.resolve_doc_path(db, link, ns), - Definition::Adt(it) => it.resolve_doc_path(db, link, ns), - Definition::Variant(it) => it.resolve_doc_path(db, link, ns), - Definition::Const(it) => it.resolve_doc_path(db, link, ns), - Definition::Static(it) => it.resolve_doc_path(db, link, ns), - Definition::Trait(it) => it.resolve_doc_path(db, link, ns), - Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns), - Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns), - Definition::Macro(it) => it.resolve_doc_path(db, link, ns), - Definition::Field(it) => it.resolve_doc_path(db, link, ns), - Definition::SelfType(it) => it.resolve_doc_path(db, link, ns), - Definition::ExternCrateDecl(it) => it.resolve_doc_path(db, link, ns), + Definition::Module(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Crate(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Function(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Adt(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Variant(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Static(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Trait(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Macro(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::Field(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::SelfType(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + Definition::ExternCrateDecl(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) @@ -289,31 +309,58 @@ impl DocCommentToken { let relative_comment_offset = offset - original_start - prefix_len; sema.descend_into_macros(doc_token).into_iter().find_map(|t| { - let (node, descended_prefix_len) = match_ast! { + let (node, descended_prefix_len, is_inner) = match_ast!{ match t { - ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?), - ast::String(string) => (t.parent_ancestors().skip_while(|n| n.kind() != ATTR).nth(1)?, string.open_quote_text_range()?.len()), + ast::Comment(comment) => { + (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?, comment.is_inner()) + }, + ast::String(string) => { + let attr = t.parent_ancestors().find_map(ast::Attr::cast)?; + let attr_is_inner = attr.excl_token().map(|excl| excl.kind() == BANG).unwrap_or(false); + (attr.syntax().parent()?, string.open_quote_text_range()?.len(), attr_is_inner) + }, _ => return None, } }; let token_start = t.text_range().start(); let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len; - - let (attributes, def) = doc_attributes(sema, &node)?; + let (attributes, def) = Self::doc_attributes(sema, &node, is_inner)?; let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?; - let (in_expansion_range, link, ns) = + let (in_expansion_range, link, ns, is_inner) = extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| { - let mapped = doc_mapping.map(range)?; - (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns)) + let (mapped, idx) = doc_mapping.map(range)?; + (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns, idx.is_inner_attr())) })?; // get the relative range to the doc/attribute in the expansion let in_expansion_relative_range = in_expansion_range - descended_prefix_len - token_start; // Apply relative range to the original input comment let absolute_range = in_expansion_relative_range + original_start + prefix_len; - let def = resolve_doc_path_for_def(sema.db, def, &link, ns)?; + let def = resolve_doc_path_for_def(sema.db, def, &link, ns, is_inner)?; cb(def, node, absolute_range) }) } + + /// When we hover a inner doc item, this find a attached definition. + /// ``` + /// // node == ITEM_LIST + /// // node.parent == EXPR_BLOCK + /// // node.parent().parent() == FN + /// fn f() { + /// //! [`S$0`] + /// } + /// ``` + fn doc_attributes( + sema: &Semantics<'_, RootDatabase>, + node: &SyntaxNode, + is_inner_doc: bool, + ) -> Option<(AttrsWithOwner, Definition)> { + if is_inner_doc && node.kind() != SOURCE_FILE { + let parent = node.parent()?; + doc_attributes(sema, &parent).or(doc_attributes(sema, &parent.parent()?)) + } else { + doc_attributes(sema, node) + } + } } fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)> { @@ -369,6 +416,7 @@ fn rewrite_intra_doc_link( def: Definition, target: &str, title: &str, + is_inner_doc: bool, ) -> Option<(String, String)> { let (link, ns) = parse_intra_doc_link(target); @@ -377,7 +425,7 @@ fn rewrite_intra_doc_link( None => (link, None), }; - let resolved = resolve_doc_path_for_def(db, def, link, ns)?; + let resolved = resolve_doc_path_for_def(db, def, link, ns, is_inner_doc)?; let mut url = get_doc_base_urls(db, resolved, None, None).0?; let (_, file, frag) = filename_and_frag_for_def(db, resolved)?; @@ -421,8 +469,8 @@ fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { /// Rewrites a markdown document, applying 'callback' to each link. fn map_links<'e>( - events: impl Iterator>, - callback: impl Fn(&str, &str) -> (Option, String, String), + events: impl Iterator, Range)>, + callback: impl Fn(&str, &str, Range) -> (Option, String, String), ) -> impl Iterator> { let mut in_link = false; // holds the origin link target on start event and the rewritten one on end event @@ -432,7 +480,7 @@ fn map_links<'e>( // `Shortcut` type parsed from Start/End tags doesn't make sense for url links let mut end_link_type: Option = None; - events.map(move |evt| match evt { + events.map(move |(evt, range)| match evt { Event::Start(Tag::Link(link_type, ref target, _)) => { in_link = true; end_link_target = Some(target.clone()); @@ -449,7 +497,7 @@ fn map_links<'e>( } Event::Text(s) if in_link => { let (link_type, link_target_s, link_name) = - callback(&end_link_target.take().unwrap(), &s); + callback(&end_link_target.take().unwrap(), &s, range); end_link_target = Some(CowStr::Boxed(link_target_s.into())); if !matches!(end_link_type, Some(LinkType::Autolink)) { end_link_type = link_type; @@ -458,7 +506,7 @@ fn map_links<'e>( } Event::Code(s) if in_link => { let (link_type, link_target_s, link_name) = - callback(&end_link_target.take().unwrap(), &s); + callback(&end_link_target.take().unwrap(), &s, range); end_link_target = Some(CowStr::Boxed(link_target_s.into())); if !matches!(end_link_type, Some(LinkType::Autolink)) { end_link_type = link_type; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 91785be8d8ba..6af156fa668f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -5,7 +5,7 @@ use hir::Semantics; use ide_db::{ FilePosition, FileRange, RootDatabase, defs::Definition, - documentation::{Documentation, HasDocs}, + documentation::{DocsRangeMap, Documentation, HasDocs}, }; use itertools::Itertools; use syntax::{AstNode, SyntaxNode, ast, match_ast}; @@ -45,8 +45,8 @@ fn check_external_docs( fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let sema = &Semantics::new(&analysis.db); - let (cursor_def, docs) = def_under_cursor(sema, &position); - let res = rewrite_links(sema.db, docs.as_str(), cursor_def); + let (cursor_def, docs, range) = def_under_cursor(sema, &position); + let res = rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range)); expect.assert_eq(&res) } @@ -56,12 +56,14 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, mut expected) = fixture::annotations(ra_fixture); expected.sort_by_key(key_fn); let sema = &Semantics::new(&analysis.db); - let (cursor_def, docs) = def_under_cursor(sema, &position); + let (cursor_def, docs, range) = def_under_cursor(sema, &position); let defs = extract_definitions_from_docs(&docs); let actual: Vec<_> = defs .into_iter() - .flat_map(|(_, link, ns)| { - let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns) + .flat_map(|(text_range, link, ns)| { + let attr = range.map(text_range); + let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false); + let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) .unwrap_or_else(|| panic!("Failed to resolve {link}")); def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) }) @@ -78,7 +80,7 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { fn def_under_cursor( sema: &Semantics<'_, RootDatabase>, position: &FilePosition, -) -> (Definition, Documentation) { +) -> (Definition, Documentation, DocsRangeMap) { let (docs, def) = sema .parse_guess_edition(position.file_id) .syntax() @@ -89,31 +91,31 @@ fn def_under_cursor( .find_map(|it| node_to_def(sema, &it)) .expect("no def found") .unwrap(); - let docs = docs.expect("no docs found for cursor def"); - (def, docs) + let (docs, range) = docs.expect("no docs found for cursor def"); + (def, docs, range) } fn node_to_def( sema: &Semantics<'_, RootDatabase>, node: &SyntaxNode, -) -> Option, Definition)>> { +) -> Option, Definition)>> { Some(match_ast! { match node { - ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))), - ast::Module(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))), - ast::Fn(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Function(def))), - ast::Struct(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Struct(def)))), - ast::Union(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Union(def)))), - ast::Enum(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Enum(def)))), - ast::Variant(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Variant(def))), - ast::Trait(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Trait(def))), - ast::Static(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Static(def))), - ast::Const(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Const(def))), - ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::TypeAlias(def))), - ast::Impl(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::SelfType(def))), - ast::RecordField(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Field(def))), - ast::TupleField(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Field(def))), - ast::Macro(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Macro(def))), + ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Module(def))), + ast::Module(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Module(def))), + ast::Fn(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Function(def))), + ast::Struct(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Adt(hir::Adt::Struct(def)))), + ast::Union(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Adt(hir::Adt::Union(def)))), + ast::Enum(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Adt(hir::Adt::Enum(def)))), + ast::Variant(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Variant(def))), + ast::Trait(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Trait(def))), + ast::Static(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Static(def))), + ast::Const(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Const(def))), + ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::TypeAlias(def))), + ast::Impl(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::SelfType(def))), + ast::RecordField(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Field(def))), + ast::TupleField(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Field(def))), + ast::Macro(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Macro(def))), // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), _ => return None, } @@ -575,6 +577,40 @@ struct S$0(i32); ); } +#[test] +fn doc_links_module() { + check_doc_links( + r#" +/// [`M`] +/// [`M::f`] +mod M$0 { + //^ M + #![doc = "inner_item[`S`]"] + + pub fn f() {} + //^ M::f + pub struct S; + //^ S +} +"#, + ); + + check_doc_links( + r#" +mod M$0 { + //^ super::M + //! [`super::M`] + //! [`super::M::f`] + //! [`super::M::S`] + pub fn f() {} + //^ super::M::f + pub struct S; + //^ super::M::S +} +"#, + ); +} + #[test] fn rewrite_html_root_url() { check_rewrite( @@ -690,6 +726,29 @@ fn rewrite_intra_doc_link_with_anchor() { ); } +#[test] +fn rewrite_module() { + check_rewrite( + r#" +//- /main.rs crate:foo +/// [Foo] +pub mod $0Foo{ +}; +"#, + expect![[r#"[Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); + + check_rewrite( + r#" +//- /main.rs crate:foo +pub mod $0Foo{ + //! [super::Foo] +}; +"#, + expect![[r#"[super::Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); +} + #[test] fn rewrite_intra_doc_link_to_associated_item() { check_rewrite( diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 241a702038da..7c396339c145 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,10 +1,10 @@ use hir::db::ExpandDatabase; -use hir::{ExpandResult, InFile, Semantics}; +use hir::{ExpandResult, InFile, InRealFile, Semantics}; use ide_db::{ FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, }; -use span::{Edition, SpanMap, SyntaxContext, TextRange, TextSize}; +use span::{SpanMap, SyntaxContext, TextRange, TextSize}; use stdx::format_to; use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted}; @@ -26,8 +26,9 @@ pub struct ExpandedMacro { // ![Expand Macro Recursively](https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif) pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); - let file = sema.parse_guess_edition(position.file_id); - let krate = sema.file_to_module_def(position.file_id)?.krate().into(); + let file_id = sema.attach_first_edition(position.file_id)?; + let file = sema.parse(file_id); + let krate = sema.file_to_module_def(file_id.file_id(db))?.krate().into(); let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { SyntaxKind::IDENT => 1, @@ -86,7 +87,10 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< return derive; } - let mut anc = tok.parent_ancestors(); + let mut anc = sema + .descend_token_into_include_expansion(InRealFile::new(file_id, tok)) + .value + .parent_ancestors(); let mut span_map = SpanMap::empty(); let mut error = String::new(); let (name, expanded, kind) = loop { @@ -95,14 +99,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< if let Some(item) = ast::Item::cast(node.clone()) { if let Some(def) = sema.resolve_attr_macro_call(&item) { break ( - def.name(db) - .display( - db, - sema.attach_first_edition(position.file_id) - .map(|it| it.edition(db)) - .unwrap_or(Edition::CURRENT), - ) - .to_string(), + def.name(db).display(db, file_id.edition(db)).to_string(), expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, SyntaxKind::MACRO_ITEMS, ); @@ -146,10 +143,11 @@ fn expand_macro_recur( offset_in_original_node: TextSize, ) -> Option { let ExpandResult { value: expanded, err } = match macro_call { - item @ ast::Item::MacroCall(macro_call) => { - sema.expand_attr_macro(item).or_else(|| sema.expand_allowed_builtins(macro_call))? - } - item => sema.expand_attr_macro(item)?, + item @ ast::Item::MacroCall(macro_call) => sema + .expand_attr_macro(item) + .map(|it| it.map(|it| it.value)) + .or_else(|| sema.expand_allowed_builtins(macro_call))?, + item => sema.expand_attr_macro(item)?.map(|it| it.value), }; let expanded = expanded.clone_for_update(); if let Some(err) = err { @@ -718,4 +716,88 @@ __log!(written:%; "Test"$0); "#]], ); } + + #[test] + fn assoc_call() { + check( + r#" +macro_rules! mac { + () => { fn assoc() {} } +} +impl () { + mac$0!(); +} + "#, + expect![[r#" + mac! + fn assoc(){}"#]], + ); + } + + #[test] + fn eager() { + check( + r#" +//- minicore: concat +macro_rules! my_concat { + ($head:expr, $($tail:tt)*) => { concat!($head, $($tail)*) }; +} + + +fn test() { + _ = my_concat!( + conc$0at!("<", ">"), + "hi", + ); +} + "#, + expect![[r#" + my_concat! + "<>hi""#]], + ); + } + + #[test] + fn in_included() { + check( + r#" +//- minicore: include +//- /main.rs crate:main +include!("./included.rs"); +//- /included.rs +macro_rules! foo { + () => { fn item() {} }; +} +foo$0!(); +"#, + expect![[r#" + foo! + fn item(){}"#]], + ); + } + + #[test] + fn include() { + check( + r#" +//- minicore: include +//- /main.rs crate:main +include$0!("./included.rs"); +//- /included.rs +macro_rules! foo { + () => { fn item() {} }; +} +foo(); +"#, + expect![[r#" + include! + macro_rules! foo { + () => { + fn item(){} + + }; + } + foo();"#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index b894e857522f..c60ca3562f66 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -1922,6 +1922,74 @@ pub fn foo() { } ) } + #[test] + fn goto_def_for_intra_doc_link_outer_same_file() { + check( + r#" +/// [`S$0`] +mod m { + //! [`super::S`] +} +struct S; + //^ + "#, + ); + + check( + r#" +/// [`S$0`] +mod m {} +struct S; + //^ + "#, + ); + + check( + r#" +/// [`S$0`] +fn f() { + //! [`S`] +} +struct S; + //^ + "#, + ); + } + + #[test] + fn goto_def_for_intra_doc_link_inner_same_file() { + check( + r#" +/// [`S`] +mod m { + //! [`super::S$0`] +} +struct S; + //^ + "#, + ); + + check( + r#" +mod m { + //! [`super::S$0`] +} +struct S; + //^ + "#, + ); + + check( + r#" +fn f() { + //! [`S$0`] +} +struct S; + //^ + "#, + ); + } + #[test] fn goto_def_for_intra_doc_link_inner() { check( 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 80624eeae80c..520ba39a238b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -89,6 +89,9 @@ pub(crate) fn highlight_related( T![break] | T![loop] | T![while] | T![continue] if config.break_points => { highlight_break_points(sema, token).remove(&file_id) } + T![unsafe] if token.parent().and_then(ast::BlockExpr::cast).is_some() => { + highlight_unsafe_points(sema, token).remove(&file_id) + } T![|] if config.closure_captures => { highlight_closure_captures(sema, token, file_id, span_file_id.file_id()) } @@ -650,7 +653,7 @@ impl<'a> WalkExpandedExprCtx<'a> { expr.macro_call().and_then(|call| self.sema.expand_macro_call(&call)) { match_ast! { - match expanded { + match (expanded.value) { ast::MacroStmts(it) => { self.handle_expanded(it, cb); }, @@ -706,6 +709,44 @@ impl<'a> WalkExpandedExprCtx<'a> { } } +pub(crate) fn highlight_unsafe_points( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> FxHashMap> { + fn hl( + sema: &Semantics<'_, RootDatabase>, + unsafe_token: &SyntaxToken, + block_expr: Option, + ) -> Option>> { + let mut highlights: FxHashMap> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().push(hrange); + } + }; + + // highlight unsafe keyword itself + let unsafe_token_file_id = sema.hir_file_for(&unsafe_token.parent()?); + push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range())); + + // highlight unsafe operations + if let Some(block) = block_expr { + if let Some(body) = sema.body_for(InFile::new(unsafe_token_file_id, block.syntax())) { + let unsafe_ops = sema.get_unsafe_ops(body); + for unsafe_op in unsafe_ops { + push_to_highlights(unsafe_op.file_id, Some(unsafe_op.value.text_range())); + } + } + } + + Some(highlights) + } + + hl(sema, &token, token.parent().and_then(ast::BlockExpr::cast)).unwrap_or_default() +} + #[cfg(test)] mod tests { use itertools::Itertools; @@ -754,6 +795,32 @@ mod tests { assert_eq!(expected, actual); } + #[test] + fn test_hl_unsafe_block() { + check( + r#" +fn foo() { + unsafe fn this_is_unsafe_function() {} + + unsa$0fe { + //^^^^^^ + let raw_ptr = &42 as *const i32; + let val = *raw_ptr; + //^^^^^^^^ + + let mut_ptr = &mut 5 as *mut i32; + *mut_ptr = 10; + //^^^^^^^^ + + this_is_unsafe_function(); + //^^^^^^^^^^^^^^^^^^^^^^^^^ + } + +} +"#, + ); + } + #[test] fn test_hl_tuple_fields() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 075afcec019f..8bb1c708e25d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -58,6 +58,7 @@ pub struct MemoryLayoutHoverConfig { pub size: Option, pub offset: Option, pub alignment: Option, + pub padding: Option, pub niches: bool, } @@ -456,7 +457,7 @@ pub(crate) fn hover_for_definition( let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default(); let subst_types = subst.map(|subst| subst.types(db)); - let markup = render::definition( + let (markup, range_map) = render::definition( sema.db, def, famous_defs.as_ref(), @@ -469,7 +470,7 @@ pub(crate) fn hover_for_definition( display_target, ); HoverResult { - markup: render::process_markup(sema.db, def, &markup, config), + markup: render::process_markup(sema.db, def, &markup, range_map, config), actions: [ show_fn_references_action(sema.db, def), show_implementations_action(sema.db, def), 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 69b83f3b12d8..c24864a18bdf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -11,7 +11,7 @@ use hir::{ use ide_db::{ RootDatabase, defs::Definition, - documentation::HasDocs, + documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, syntax_helpers::prettify_macro_expansion, @@ -21,7 +21,7 @@ use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, }; -use span::Edition; +use span::{Edition, TextSize}; use stdx::format_to; use syntax::{AstNode, AstToken, Direction, SyntaxToken, T, algo, ast, match_ast}; @@ -276,13 +276,10 @@ pub(super) fn keyword( keyword_hints(sema, token, parent, edition, display_target); let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?; - let docs = doc_owner.docs(sema.db)?; - let markup = process_markup( - sema.db, - Definition::Module(doc_owner), - &markup(Some(docs.into()), description, None, None, String::new()), - config, - ); + let (docs, range_map) = doc_owner.docs_with_rangemap(sema.db)?; + let (markup, range_map) = + markup(Some(docs.into()), Some(range_map), description, None, None, String::new()); + let markup = process_markup(sema.db, Definition::Module(doc_owner), &markup, range_map, config); Some(HoverResult { markup, actions }) } @@ -371,11 +368,15 @@ pub(super) fn process_markup( db: &RootDatabase, def: Definition, markup: &Markup, + markup_range_map: Option, config: &HoverConfig, ) -> Markup { let markup = markup.as_str(); - let markup = - if config.links_in_hover { rewrite_links(db, markup, def) } else { remove_links(markup) }; + let markup = if config.links_in_hover { + rewrite_links(db, markup, def, markup_range_map) + } else { + remove_links(markup) + }; Markup::from(markup) } @@ -482,7 +483,7 @@ pub(super) fn definition( config: &HoverConfig, edition: Edition, display_target: DisplayTarget, -) -> Markup { +) -> (Markup, Option) { let mod_path = definition_path(db, &def, edition); let label = match def { Definition::Trait(trait_) => trait_ @@ -518,7 +519,12 @@ pub(super) fn definition( } _ => def.label(db, display_target), }; - let docs = def.docs(db, famous_defs, display_target); + let (docs, range_map) = + if let Some((docs, doc_range)) = def.docs_with_rangemap(db, famous_defs, display_target) { + (Some(docs), doc_range) + } else { + (None, None) + }; let value = || match def { Definition::Variant(it) => { if !it.parent_enum(db).is_data_carrying(db) { @@ -624,27 +630,57 @@ pub(super) fn definition( } }, |_| None, + |_| None, + ), + Definition::Adt(it @ Adt::Struct(strukt)) => render_memory_layout( + config.memory_layout, + || it.layout(db), + |_| None, + |layout| { + let mut field_size = + |i: usize| Some(strukt.fields(db).get(i)?.layout(db).ok()?.size()); + if strukt.repr(db).is_some_and(|it| it.inhibit_struct_field_reordering()) { + Some(("tail padding", layout.tail_padding(&mut field_size)?)) + } else { + Some(("largest padding", layout.largest_padding(&mut field_size)?)) + } + }, + |_| None, + ), + Definition::Adt(it) => render_memory_layout( + config.memory_layout, + || it.layout(db), + |_| None, + |_| None, + |_| None, ), - Definition::Adt(it) => { - render_memory_layout(config.memory_layout, || it.layout(db), |_| None, |_| None) - } Definition::Variant(it) => render_memory_layout( config.memory_layout, || it.layout(db), |_| None, + |_| None, |layout| layout.enum_tag_size(), ), - Definition::TypeAlias(it) => { - render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None) - } - Definition::Local(it) => { - render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None) - } + Definition::TypeAlias(it) => render_memory_layout( + config.memory_layout, + || it.ty(db).layout(db), + |_| None, + |_| None, + |_| None, + ), + Definition::Local(it) => render_memory_layout( + config.memory_layout, + || it.ty(db).layout(db), + |_| None, + |_| None, + |_| None, + ), Definition::SelfType(it) => render_memory_layout( config.memory_layout, || it.self_ty(db).layout(db), |_| None, |_| None, + |_| None, ), _ => None, }; @@ -807,6 +843,7 @@ pub(super) fn definition( markup( docs.map(Into::into), + range_map, desc, extra.is_empty().not().then_some(extra), mod_path, @@ -1048,9 +1085,13 @@ fn closure_ty( if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) { push_new_def(hir::Trait::from(trait_).into()) } - if let Some(layout) = - render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) - { + if let Some(layout) = render_memory_layout( + config.memory_layout, + || original.layout(sema.db), + |_| None, + |_| None, + |_| None, + ) { format_to!(markup, "\n___\n{layout}"); } format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,); @@ -1083,11 +1124,12 @@ fn definition_path(db: &RootDatabase, &def: &Definition, edition: Edition) -> Op fn markup( docs: Option, + range_map: Option, rust: String, extra: Option, mod_path: Option, subst_types: String, -) -> Markup { +) -> (Markup, Option) { let mut buf = String::new(); if let Some(mod_path) = mod_path { @@ -1106,9 +1148,15 @@ fn markup( } if let Some(doc) = docs { - format_to!(buf, "\n___\n\n{}", doc); + format_to!(buf, "\n___\n\n"); + let offset = TextSize::new(buf.len() as u32); + let buf_range_map = range_map.map(|range_map| range_map.shift_docstring_line_range(offset)); + format_to!(buf, "{}", doc); + + (buf.into(), buf_range_map) + } else { + (buf.into(), None) } - buf.into() } fn find_std_module( @@ -1128,6 +1176,7 @@ fn render_memory_layout( config: Option, layout: impl FnOnce() -> Result, offset: impl FnOnce(&Layout) -> Option, + padding: impl FnOnce(&Layout) -> Option<(&str, u64)>, tag: impl FnOnce(&Layout) -> Option, ) -> Option { let config = config?; @@ -1185,6 +1234,23 @@ fn render_memory_layout( } } + if let Some(render) = config.padding { + if let Some((padding_name, padding)) = padding(&layout) { + format_to!(label, "{padding_name} = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{padding}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{padding:#X}"), + MemoryLayoutHoverRenderKind::Both if padding >= 10 => { + format_to!(label, "{padding} ({padding:#X})") + } + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{padding}") + } + } + format_to!(label, ", "); + } + } + if config.niches { if let Some(niches) = layout.niches() { if niches > 1024 { 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 7b7eef9d5793..a281a491525c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -12,6 +12,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { size: Some(MemoryLayoutHoverRenderKind::Both), offset: Some(MemoryLayoutHoverRenderKind::Both), alignment: Some(MemoryLayoutHoverRenderKind::Both), + padding: Some(MemoryLayoutHoverRenderKind::Both), niches: true, }), documentation: true, @@ -933,7 +934,7 @@ struct Foo$0(pub u32) where u32: Copy; --- - size = 4, align = 4, no Drop + size = 4, align = 4, largest padding = 0, no Drop "#]], ); } @@ -959,7 +960,7 @@ struct Foo$0 { field: u32 } --- - size = 4, align = 4, no Drop + size = 4, align = 4, largest padding = 0, no Drop "#]], ); check( @@ -984,7 +985,7 @@ struct Foo$0 where u32: Copy { field: u32 } --- - size = 4, align = 4, no Drop + size = 4, align = 4, largest padding = 0, no Drop "#]], ); } @@ -1013,7 +1014,7 @@ fn hover_record_struct_limit() { --- - size = 12 (0xC), align = 4, no Drop + size = 12 (0xC), align = 4, largest padding = 0, no Drop "#]], ); check_hover_fields_limit( @@ -1036,7 +1037,7 @@ fn hover_record_struct_limit() { --- - size = 4, align = 4, no Drop + size = 4, align = 4, largest padding = 0, no Drop "#]], ); check_hover_fields_limit( @@ -1062,7 +1063,7 @@ fn hover_record_struct_limit() { --- - size = 16 (0x10), align = 4, no Drop + size = 16 (0x10), align = 4, largest padding = 0, no Drop "#]], ); check_hover_fields_limit( @@ -1083,7 +1084,7 @@ fn hover_record_struct_limit() { --- - size = 12 (0xC), align = 4, no Drop + size = 12 (0xC), align = 4, largest padding = 0, no Drop "#]], ); check_hover_fields_limit( @@ -1104,7 +1105,7 @@ fn hover_record_struct_limit() { --- - size = 12 (0xC), align = 4, no Drop + size = 12 (0xC), align = 4, largest padding = 0, no Drop "#]], ); @@ -3114,7 +3115,7 @@ struct S$0(core::marker::PhantomData); --- - size = 0, align = 1, no Drop + size = 0, align = 1, largest padding = 0, no Drop "#]], ); } @@ -3147,6 +3148,111 @@ fn test_hover_layout_of_enum() { ); } +#[test] +fn test_hover_layout_padding_info() { + check( + r#"struct $0Foo { + x: bool, + y: i64, + z: u32, + }"#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo { + x: bool, + y: i64, + z: u32, + } + ``` + + --- + + size = 16 (0x10), align = 8, largest padding = 3, niches = 254, no Drop + "#]], + ); + + check( + r#"#[repr(align(32))] + struct $0Foo { + x: bool, + y: i64, + z: u32, + }"#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo { + x: bool, + y: i64, + z: u32, + } + ``` + + --- + + size = 32 (0x20), align = 32 (0x20), largest padding = 19 (0x13), niches = 254, no Drop + "#]], + ); + + check( + r#"#[repr(C)] + struct $0Foo { + x: bool, + y: i64, + z: u32, + }"#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo { + x: bool, + y: i64, + z: u32, + } + ``` + + --- + + size = 24 (0x18), align = 8, tail padding = 4, niches = 254, no Drop + "#]], + ); + + check( + r#"struct $0Foo(i16, u128, u64)"#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo(i16, u128, u64) + ``` + + --- + + size = 32 (0x20), align = 8, largest padding = 6, no Drop + "#]], + ); +} + #[test] fn test_hover_no_memory_layout() { check_hover_no_memory_layout( @@ -7374,6 +7480,128 @@ pub struct Foo(i32); ); } +#[test] +fn hover_intra_inner_attr() { + check( + r#" +/// outer comment for [`Foo`] +#[doc = "Doc outer comment for [`Foo`]"] +pub fn Foo { + //! inner comment for [`Foo$0`] + #![doc = "Doc inner comment for [`Foo`]"] +} +"#, + expect![[r#" + *[`Foo`]* + + ```rust + ra_test_fixture + ``` + + ```rust + pub fn Foo() + ``` + + --- + + outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + Doc inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + "#]], + ); + + check( + r#" +/// outer comment for [`Foo`] +#[doc = "Doc outer comment for [`Foo`]"] +pub mod Foo { + //! inner comment for [`super::Foo$0`] + #![doc = "Doc inner comment for [`super::Foo`]"] +} +"#, + expect![[r#" + *[`super::Foo`]* + + ```rust + ra_test_fixture + ``` + + ```rust + pub mod Foo + ``` + + --- + + outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + Doc inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + "#]], + ); +} + +#[test] +fn hover_intra_outer_attr() { + check( + r#" +/// outer comment for [`Foo$0`] +#[doc = "Doc outer comment for [`Foo`]"] +pub fn Foo() { + //! inner comment for [`Foo`] + #![doc = "Doc inner comment for [`Foo`]"] +} +"#, + expect![[r#" + *[`Foo`]* + + ```rust + ra_test_fixture + ``` + + ```rust + pub fn Foo() + ``` + + --- + + outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + Doc inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html) + "#]], + ); + + check( + r#" +/// outer comment for [`Foo$0`] +#[doc = "Doc outer comment for [`Foo`]"] +pub mod Foo { + //! inner comment for [`super::Foo`] + #![doc = "Doc inner comment for [`super::Foo`]"] +} +"#, + expect![[r#" + *[`Foo`]* + + ```rust + ra_test_fixture + ``` + + ```rust + pub mod Foo + ``` + + --- + + outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + Doc inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html) + "#]], + ); +} + #[test] fn hover_intra_generics() { check( @@ -9076,7 +9304,7 @@ struct Pedro$0<'a> { --- - size = 16 (0x10), align = 8, niches = 1, no Drop + size = 16 (0x10), align = 8, largest padding = 0, niches = 1, no Drop "#]], ) } @@ -10437,7 +10665,7 @@ struct DropField$0 { --- - size = 4, align = 4, needs Drop + size = 4, align = 4, largest padding = 0, needs Drop "#]], ); check( diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index aa525a86123d..d649dffbd90c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -62,7 +62,7 @@ use std::panic::{AssertUnwindSafe, UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::{ChangeWithProcMacros, EditionedFileId, sym}; +use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ @@ -627,7 +627,7 @@ impl Analysis { /// Returns true if this crate has `no_std` or `no_core` specified. pub fn is_crate_no_std(&self, crate_id: Crate) -> Cancellable { - self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std()) + self.with_db(|db| crate_def_map(db, crate_id).is_no_std()) } /// Returns the root file of the given crate. diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index 6dc01c450633..50219cee57db 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -1,4 +1,4 @@ -use hir::{Semantics, db::DefDatabase}; +use hir::{Semantics, crate_def_map}; use ide_db::{ FileId, FilePosition, RootDatabase, base_db::{Crate, RootQueryDb}, @@ -58,7 +58,7 @@ pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec { .iter() .copied() .filter(|&crate_id| { - db.crate_def_map(crate_id).modules_for_file(db, file_id).next().is_some() + crate_def_map(db, crate_id).modules_for_file(db, file_id).next().is_some() }) .sorted() .collect() 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 0998e14c87ba..7f5c2c1ec849 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 @@ -129,11 +129,18 @@ pub(super) fn doc_comment( extract_definitions_from_docs(&docs) .into_iter() .filter_map(|(range, link, ns)| { - doc_mapping.map(range).filter(|mapping| mapping.file_id == src_file_id).and_then( - |InFile { value: mapped_range, .. }| { - Some(mapped_range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns)) - }, - ) + doc_mapping + .map(range) + .filter(|(mapping, _)| mapping.file_id == src_file_id) + .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { + Some(mapped_range).zip(resolve_doc_path_for_def( + sema.db, + def, + &link, + ns, + attr_id.is_inner_attr(), + )) + }) }) .for_each(|(range, def)| { hl.add(HlRange { 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 abde48d15127..fc922dd849fb 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -164,7 +164,6 @@ define_symbols! { completion, compile_error, concat_bytes, - concat_idents, concat, const_format_args, const_panic_fmt, diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 2686a75c7c86..30e2d5416cf6 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -512,10 +512,6 @@ impl ProcMacroExpander for Expander { Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), } } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().downcast_ref::().is_some_and(|other| self == other) - } } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index a5672e4e0504..3369dfff2816 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -356,3 +356,120 @@ fn expr_2021() { ;"#]], ); } + +#[test] +fn minus_belongs_to_literal() { + let decl = r#" +(-1) => {-1}; +(- 2) => {- 2}; +(- 3.0) => {- 3.0}; +(@$lit:literal) => {$lit} +"#; + let check = |args, expect| check(Edition::CURRENT, Edition::CURRENT, decl, args, expect); + check( + "-1", + expect![[r#" + SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024 + PUNCH - [alone] 0:0@10..11#ROOT2024 + LITERAL Integer 1 0:0@11..12#ROOT2024 + + -1"#]], + ); + check( + "- 1", + expect![[r#" + SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024 + PUNCH - [alone] 0:0@10..11#ROOT2024 + LITERAL Integer 1 0:0@11..12#ROOT2024 + + -1"#]], + ); + check( + "-2", + expect![[r#" + SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024 + PUNCH - [alone] 0:0@25..26#ROOT2024 + LITERAL Integer 2 0:0@27..28#ROOT2024 + + -2"#]], + ); + check( + "- 2", + expect![[r#" + SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024 + PUNCH - [alone] 0:0@25..26#ROOT2024 + LITERAL Integer 2 0:0@27..28#ROOT2024 + + -2"#]], + ); + check( + "-3.0", + expect![[r#" + SUBTREE $$ 1:0@0..4#ROOT2024 1:0@0..4#ROOT2024 + PUNCH - [alone] 0:0@43..44#ROOT2024 + LITERAL Float 3.0 0:0@45..48#ROOT2024 + + -3.0"#]], + ); + check( + "- 3.0", + expect![[r#" + SUBTREE $$ 1:0@0..5#ROOT2024 1:0@0..5#ROOT2024 + PUNCH - [alone] 0:0@43..44#ROOT2024 + LITERAL Float 3.0 0:0@45..48#ROOT2024 + + -3.0"#]], + ); + check( + "@1", + expect![[r#" + SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024 + LITERAL Integer 1 1:0@1..2#ROOT2024 + + 1"#]], + ); + check( + "@-1", + expect![[r#" + SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024 + PUNCH - [alone] 1:0@1..2#ROOT2024 + LITERAL Integer 1 1:0@2..3#ROOT2024 + + -1"#]], + ); + check( + "@1.0", + expect![[r#" + SUBTREE $$ 1:0@0..4#ROOT2024 1:0@0..4#ROOT2024 + LITERAL Float 1.0 1:0@1..4#ROOT2024 + + 1.0"#]], + ); + check( + "@-1.0", + expect![[r#" + SUBTREE $$ 1:0@0..5#ROOT2024 1:0@0..5#ROOT2024 + PUNCH - [alone] 1:0@1..2#ROOT2024 + LITERAL Float 1.0 1:0@2..5#ROOT2024 + + -1.0"#]], + ); + check( + "@--1.0", + expect![[r#" + ExpandError { + inner: ( + 1:0@1..2#ROOT2024, + BindingError( + "expected literal", + ), + ), + } + + SUBTREE $$ 1:0@0..6#ROOT2024 1:0@0..6#ROOT2024 + PUNCH - [joint] 1:0@1..2#ROOT2024 + PUNCH - [alone] 1:0@2..3#ROOT2024 + + --"#]], + ); +} 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 5faf6fc2759e..8cc332d4633d 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 @@ -381,10 +381,14 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { op.complete(p, ASM_REG_OPERAND); op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat_contextual_kw(T![label]) { + // test asm_label + // fn foo() { + // builtin#asm("", label {}); + // } dir_spec.abandon(p); block_expr(p); - op.complete(p, ASM_OPERAND_NAMED); - op_n.complete(p, ASM_LABEL); + op.complete(p, ASM_LABEL); + op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat(T![const]) { dir_spec.abandon(p); expr(p); diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 0a5c16dc4c49..0fa9a264545d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -179,7 +179,10 @@ impl<'a> Converter<'a> { COMMENT } - rustc_lexer::TokenKind::Frontmatter { has_invalid_preceding_whitespace, invalid_infostring } => { + rustc_lexer::TokenKind::Frontmatter { + has_invalid_preceding_whitespace, + invalid_infostring, + } => { if *has_invalid_preceding_whitespace { err = "invalid preceding whitespace for frontmatter opening" } else if *invalid_infostring { 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 24db9478ee56..030d8e0f04dd 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 @@ -21,6 +21,8 @@ mod ok { #[test] fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); } #[test] + fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.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_label.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rast new file mode 100644 index 000000000000..38999c9cd34f --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rast @@ -0,0 +1,37 @@ +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 "(" + LITERAL + STRING "\"\"" + COMMA "," + WHITESPACE " " + ASM_OPERAND_NAMED + ASM_LABEL + LABEL_KW "label" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rs new file mode 100644 index 000000000000..996c1c8477b6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rs @@ -0,0 +1,3 @@ +fn foo() { + builtin#asm("", label {}); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index dfdbb4c95fca..6820e4b33539 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -31,12 +31,17 @@ pub fn fn_like_mk_literals(_args: TokenStream) -> TokenStream { TokenTree::from(Literal::byte_string(b"byte_string")), TokenTree::from(Literal::character('c')), TokenTree::from(Literal::string("string")), + TokenTree::from(Literal::c_string(c"cstring")), // as of 2022-07-21, there's no method on `Literal` to build a raw // string or a raw byte string TokenTree::from(Literal::f64_suffixed(3.14)), + TokenTree::from(Literal::f64_suffixed(-3.14)), TokenTree::from(Literal::f64_unsuffixed(3.14)), + TokenTree::from(Literal::f64_unsuffixed(-3.14)), TokenTree::from(Literal::i64_suffixed(123)), + TokenTree::from(Literal::i64_suffixed(-123)), TokenTree::from(Literal::i64_unsuffixed(123)), + TokenTree::from(Literal::i64_unsuffixed(-123)), ]; TokenStream::from_iter(trees) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs index 3d999421794b..11dbd9200915 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs @@ -16,9 +16,8 @@ mod token_stream; pub use token_stream::TokenStream; pub mod rust_analyzer_span; -// mod symbol; pub mod token_id; -// pub use symbol::*; + use tt::Spacing; #[derive(Clone)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 47555a5db2f7..e0c6e68f8037 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -168,16 +168,38 @@ impl server::TokenStream for RaSpanServer { } bridge::TokenTree::Literal(literal) => { - let literal = tt::Literal { - symbol: literal.symbol, - suffix: literal.suffix, - span: literal.span, - kind: literal_kind_to_internal(literal.kind), - }; + let token_trees = + if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') { + let punct = tt::Punct { + spacing: tt::Spacing::Alone, + span: literal.span, + char: '-' as char, + }; + let leaf: tt::Leaf = tt::Leaf::from(punct); + let minus_tree = tt::TokenTree::from(leaf); - let leaf: tt::Leaf = tt::Leaf::from(literal); - let tree = tt::TokenTree::from(leaf); - TokenStream { token_trees: vec![tree] } + let literal = tt::Literal { + symbol: Symbol::intern(symbol), + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![minus_tree, tree] + } else { + let literal = tt::Literal { + symbol: literal.symbol, + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![tree] + }; + TokenStream { token_trees } } bridge::TokenTree::Punct(p) => { @@ -236,7 +258,9 @@ impl server::TokenStream for RaSpanServer { &mut self, stream: Self::TokenStream, ) -> Vec> { - stream.into_bridge() + stream.into_bridge(&mut |first, second| { + server::Span::join(self, first, second).unwrap_or(first) + }) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs deleted file mode 100644 index 6863ce959973..000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Symbol interner for proc-macro-srv - -use std::{cell::RefCell, collections::HashMap, thread::LocalKey}; - -thread_local! { - pub(crate) static SYMBOL_INTERNER: RefCell = Default::default(); -} - -// ID for an interned symbol. -#[derive(Hash, Eq, PartialEq, Copy, Clone)] -pub struct Symbol(u32); - -pub(crate) type SymbolInternerRef = &'static LocalKey>; - -impl Symbol { - pub(super) fn intern(interner: SymbolInternerRef, data: &str) -> Symbol { - interner.with(|i| i.borrow_mut().intern(data)) - } - - pub(super) fn text(&self, interner: SymbolInternerRef) -> SmolStr { - interner.with(|i| i.borrow().get(self).clone()) - } -} - -#[derive(Default)] -pub(crate) struct SymbolInterner { - idents: HashMap, - ident_data: Vec, -} - -impl SymbolInterner { - fn intern(&mut self, data: &str) -> Symbol { - if let Some(index) = self.idents.get(data) { - return Symbol(*index); - } - - let index = self.idents.len() as u32; - let data = SmolStr::from(data); - self.ident_data.push(data.clone()); - self.idents.insert(data, index); - Symbol(index) - } - - fn get(&self, sym: &Symbol) -> &SmolStr { - &self.ident_data[sym.0 as usize] - } -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index c002be4be6ff..d55b269f868d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -153,16 +153,38 @@ impl server::TokenStream for TokenIdServer { } bridge::TokenTree::Literal(literal) => { - let literal = Literal { - symbol: literal.symbol, - suffix: literal.suffix, - span: literal.span, - kind: literal_kind_to_internal(literal.kind), - }; + let token_trees = + if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') { + let punct = tt::Punct { + spacing: tt::Spacing::Alone, + span: literal.span, + char: '-' as char, + }; + let leaf: tt::Leaf = tt::Leaf::from(punct); + let minus_tree = tt::TokenTree::from(leaf); - let leaf = tt::Leaf::from(literal); - let tree = TokenTree::from(leaf); - TokenStream { token_trees: vec![tree] } + let literal = Literal { + symbol: Symbol::intern(symbol), + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![minus_tree, tree] + } else { + let literal = Literal { + symbol: literal.symbol, + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![tree] + }; + TokenStream { token_trees } } bridge::TokenTree::Punct(p) => { @@ -216,7 +238,8 @@ impl server::TokenStream for TokenIdServer { &mut self, stream: Self::TokenStream, ) -> Vec> { - stream.into_bridge() + // Can't join with `TokenId`. + stream.into_bridge(&mut |first, _second| first) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs index 4946a4f2a621..c5019a591722 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs @@ -56,7 +56,10 @@ impl TokenStream { self.token_trees.is_empty() } - pub(crate) fn into_bridge(self) -> Vec> { + pub(crate) fn into_bridge( + self, + join_spans: &mut dyn FnMut(S, S) -> S, + ) -> Vec> { let mut result = Vec::new(); let mut iter = self.token_trees.into_iter(); while let Some(tree) = iter.next() { @@ -68,6 +71,11 @@ impl TokenStream { span: ident.span, })) } + // Note, we do not have to assemble our `-` punct and literal split into a single + // negative bridge literal here. As the proc-macro docs state + // > Literals created from negative numbers might not survive round-trips through + // > TokenStream or strings and may be broken into two tokens (- and positive + // > literal). tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { result.push(bridge::TokenTree::Literal(bridge::Literal { span: lit.span, @@ -93,7 +101,11 @@ impl TokenStream { token_trees: iter.by_ref().take(subtree.usize_len()).collect(), }) }, - span: bridge::DelimSpan::from_single(subtree.delimiter.open), + span: bridge::DelimSpan { + open: subtree.delimiter.open, + close: subtree.delimiter.close, + entire: join_spans(subtree.delimiter.open, subtree.delimiter.close), + }, })) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index a81fea7bec69..3a6ce639d135 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -11,8 +11,24 @@ fn test_derive_empty() { assert_expand( "DeriveEmpty", r#"struct S;"#, - expect!["SUBTREE $$ 1 1"], - expect!["SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024"], + expect![[r#" + SUBTREE $$ 1 1 + IDENT struct 1 + IDENT S 1 + PUNCH ; [alone] 1 + + + + SUBTREE $$ 1 1"#]], + expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT struct 42:2@0..6#ROOT2024 + IDENT S 42:2@7..8#ROOT2024 + PUNCH ; [alone] 42:2@8..9#ROOT2024 + + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024"#]], ); } @@ -22,6 +38,13 @@ fn test_derive_error() { "DeriveError", r#"struct S;"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT struct 1 + IDENT S 1 + PUNCH ; [alone] 1 + + + SUBTREE $$ 1 1 IDENT compile_error 1 PUNCH ! [alone] 1 @@ -29,6 +52,13 @@ fn test_derive_error() { LITERAL Str #[derive(DeriveError)] struct S ; 1 PUNCH ; [alone] 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT struct 42:2@0..6#ROOT2024 + IDENT S 42:2@7..8#ROOT2024 + PUNCH ; [alone] 42:2@8..9#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT compile_error 42:2@0..100#ROOT2024 PUNCH ! [alone] 42:2@0..100#ROOT2024 @@ -44,6 +74,17 @@ fn test_fn_like_macro_noop() { "fn_like_noop", r#"ident, 0, 1, []"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT ident 1 + PUNCH , [alone] 1 + LITERAL Integer 0 1 + PUNCH , [alone] 1 + LITERAL Integer 1 1 + PUNCH , [alone] 1 + SUBTREE [] 1 1 + + + SUBTREE $$ 1 1 IDENT ident 1 PUNCH , [alone] 1 @@ -53,6 +94,17 @@ fn test_fn_like_macro_noop() { PUNCH , [alone] 1 SUBTREE [] 1 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT ident 42:2@0..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + LITERAL Integer 0 42:2@7..8#ROOT2024 + PUNCH , [alone] 42:2@8..9#ROOT2024 + LITERAL Integer 1 42:2@10..11#ROOT2024 + PUNCH , [alone] 42:2@11..12#ROOT2024 + SUBTREE [] 42:2@13..14#ROOT2024 42:2@14..15#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT ident 42:2@0..5#ROOT2024 PUNCH , [alone] 42:2@5..6#ROOT2024 @@ -70,6 +122,13 @@ fn test_fn_like_macro_clone_ident_subtree() { "fn_like_clone_tokens", r#"ident, []"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT ident 1 + PUNCH , [alone] 1 + SUBTREE [] 1 1 + + + SUBTREE $$ 1 1 IDENT ident 1 PUNCH , [alone] 1 @@ -78,7 +137,14 @@ fn test_fn_like_macro_clone_ident_subtree() { SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT ident 42:2@0..5#ROOT2024 PUNCH , [alone] 42:2@5..6#ROOT2024 - SUBTREE [] 42:2@7..8#ROOT2024 42:2@7..8#ROOT2024"#]], + SUBTREE [] 42:2@7..8#ROOT2024 42:2@8..9#ROOT2024 + + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT ident 42:2@0..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + SUBTREE [] 42:2@7..9#ROOT2024 42:2@7..9#ROOT2024"#]], ); } @@ -88,9 +154,19 @@ fn test_fn_like_macro_clone_raw_ident() { "fn_like_clone_tokens", "r#async", expect![[r#" + SUBTREE $$ 1 1 + IDENT r#async 1 + + + SUBTREE $$ 1 1 IDENT r#async 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT r#async 42:2@0..7#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT r#async 42:2@0..7#ROOT2024"#]], ); @@ -102,9 +178,21 @@ fn test_fn_like_fn_like_span_join() { "fn_like_span_join", "foo bar", expect![[r#" + SUBTREE $$ 1 1 + IDENT foo 1 + IDENT bar 1 + + + SUBTREE $$ 1 1 IDENT r#joined 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT foo 42:2@0..3#ROOT2024 + IDENT bar 42:2@8..11#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT r#joined 42:2@0..11#ROOT2024"#]], ); @@ -116,11 +204,25 @@ fn test_fn_like_fn_like_span_ops() { "fn_like_span_ops", "set_def_site resolved_at_def_site start_span", expect![[r#" + SUBTREE $$ 1 1 + IDENT set_def_site 1 + IDENT resolved_at_def_site 1 + IDENT start_span 1 + + + SUBTREE $$ 1 1 IDENT set_def_site 0 IDENT resolved_at_def_site 1 IDENT start_span 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT set_def_site 42:2@0..12#ROOT2024 + IDENT resolved_at_def_site 42:2@13..33#ROOT2024 + IDENT start_span 42:2@34..44#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT set_def_site 41:1@0..150#ROOT2024 IDENT resolved_at_def_site 42:2@13..33#ROOT2024 @@ -134,22 +236,48 @@ fn test_fn_like_mk_literals() { "fn_like_mk_literals", r#""#, expect![[r#" + SUBTREE $$ 1 1 + + + SUBTREE $$ 1 1 LITERAL ByteStr byte_string 1 LITERAL Char c 1 LITERAL Str string 1 + LITERAL CStr cstring 1 + LITERAL Float 3.14f64 1 + PUNCH - [alone] 1 LITERAL Float 3.14f64 1 LITERAL Float 3.14 1 + PUNCH - [alone] 1 + LITERAL Float 3.14 1 LITERAL Integer 123i64 1 + PUNCH - [alone] 1 + LITERAL Integer 123i64 1 + LITERAL Integer 123 1 + PUNCH - [alone] 1 LITERAL Integer 123 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 LITERAL ByteStr byte_string 42:2@0..100#ROOT2024 LITERAL Char c 42:2@0..100#ROOT2024 LITERAL Str string 42:2@0..100#ROOT2024 + LITERAL CStr cstring 42:2@0..100#ROOT2024 + LITERAL Float 3.14f64 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 LITERAL Float 3.14f64 42:2@0..100#ROOT2024 LITERAL Float 3.14 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Float 3.14 42:2@0..100#ROOT2024 LITERAL Integer 123i64 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Integer 123i64 42:2@0..100#ROOT2024 + LITERAL Integer 123 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 LITERAL Integer 123 42:2@0..100#ROOT2024"#]], ); } @@ -160,10 +288,18 @@ fn test_fn_like_mk_idents() { "fn_like_mk_idents", r#""#, expect![[r#" + SUBTREE $$ 1 1 + + + SUBTREE $$ 1 1 IDENT standard 1 IDENT r#raw 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT standard 42:2@0..100#ROOT2024 IDENT r#raw 42:2@0..100#ROOT2024"#]], @@ -176,6 +312,30 @@ fn test_fn_like_macro_clone_literals() { "fn_like_clone_tokens", r###"1u16, 2_u32, -4i64, 3.14f32, "hello bridge", "suffixed"suffix, r##"raw"##, 'a', b'b', c"null""###, expect![[r#" + SUBTREE $$ 1 1 + LITERAL Integer 1u16 1 + PUNCH , [alone] 1 + LITERAL Integer 2_u32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Integer 4i64 1 + PUNCH , [alone] 1 + LITERAL Float 3.14f32 1 + PUNCH , [alone] 1 + LITERAL Str hello bridge 1 + PUNCH , [alone] 1 + LITERAL Str suffixedsuffix 1 + PUNCH , [alone] 1 + LITERAL StrRaw(2) raw 1 + PUNCH , [alone] 1 + LITERAL Char a 1 + PUNCH , [alone] 1 + LITERAL Byte b 1 + PUNCH , [alone] 1 + LITERAL CStr null 1 + + + SUBTREE $$ 1 1 LITERAL Integer 1u16 1 PUNCH , [alone] 1 @@ -198,6 +358,30 @@ fn test_fn_like_macro_clone_literals() { PUNCH , [alone] 1 LITERAL CStr null 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + LITERAL Integer 1u16 42:2@0..4#ROOT2024 + PUNCH , [alone] 42:2@4..5#ROOT2024 + LITERAL Integer 2_u32 42:2@6..11#ROOT2024 + PUNCH , [alone] 42:2@11..12#ROOT2024 + PUNCH - [alone] 42:2@13..14#ROOT2024 + LITERAL Integer 4i64 42:2@14..18#ROOT2024 + PUNCH , [alone] 42:2@18..19#ROOT2024 + LITERAL Float 3.14f32 42:2@20..27#ROOT2024 + PUNCH , [alone] 42:2@27..28#ROOT2024 + LITERAL Str hello bridge 42:2@29..43#ROOT2024 + PUNCH , [alone] 42:2@43..44#ROOT2024 + LITERAL Str suffixedsuffix 42:2@45..61#ROOT2024 + PUNCH , [alone] 42:2@61..62#ROOT2024 + LITERAL StrRaw(2) raw 42:2@63..73#ROOT2024 + PUNCH , [alone] 42:2@73..74#ROOT2024 + LITERAL Char a 42:2@75..78#ROOT2024 + PUNCH , [alone] 42:2@78..79#ROOT2024 + LITERAL Byte b 42:2@80..84#ROOT2024 + PUNCH , [alone] 42:2@84..85#ROOT2024 + LITERAL CStr null 42:2@86..93#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 LITERAL Integer 1u16 42:2@0..4#ROOT2024 PUNCH , [alone] 42:2@4..5#ROOT2024 @@ -222,6 +406,70 @@ fn test_fn_like_macro_clone_literals() { ); } +#[test] +fn test_fn_like_macro_negative_literals() { + assert_expand( + "fn_like_clone_tokens", + r###"-1u16, - 2_u32, -3.14f32, - 2.7"###, + expect![[r#" + SUBTREE $$ 1 1 + PUNCH - [alone] 1 + LITERAL Integer 1u16 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Integer 2_u32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 3.14f32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 2.7 1 + + + + SUBTREE $$ 1 1 + PUNCH - [alone] 1 + LITERAL Integer 1u16 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Integer 2_u32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 3.14f32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 2.7 1"#]], + expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..1#ROOT2024 + LITERAL Integer 1u16 42:2@1..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + PUNCH - [alone] 42:2@7..8#ROOT2024 + LITERAL Integer 2_u32 42:2@9..14#ROOT2024 + PUNCH , [alone] 42:2@14..15#ROOT2024 + PUNCH - [alone] 42:2@16..17#ROOT2024 + LITERAL Float 3.14f32 42:2@17..24#ROOT2024 + PUNCH , [alone] 42:2@24..25#ROOT2024 + PUNCH - [alone] 42:2@26..27#ROOT2024 + LITERAL Float 2.7 42:2@28..31#ROOT2024 + + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..1#ROOT2024 + LITERAL Integer 1u16 42:2@1..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + PUNCH - [alone] 42:2@7..8#ROOT2024 + LITERAL Integer 2_u32 42:2@9..14#ROOT2024 + PUNCH , [alone] 42:2@14..15#ROOT2024 + PUNCH - [alone] 42:2@16..17#ROOT2024 + LITERAL Float 3.14f32 42:2@17..24#ROOT2024 + PUNCH , [alone] 42:2@24..25#ROOT2024 + PUNCH - [alone] 42:2@26..27#ROOT2024 + LITERAL Float 2.7 42:2@28..31#ROOT2024"#]], + ); +} + #[test] fn test_attr_macro() { // Corresponds to @@ -232,6 +480,15 @@ fn test_attr_macro() { r#"mod m {}"#, r#"some arguments"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT mod 1 + IDENT m 1 + SUBTREE {} 1 1 + + SUBTREE $$ 1 1 + IDENT some 1 + IDENT arguments 1 + SUBTREE $$ 1 1 IDENT compile_error 1 PUNCH ! [alone] 1 @@ -239,6 +496,15 @@ fn test_attr_macro() { LITERAL Str #[attr_error(some arguments)] mod m {} 1 PUNCH ; [alone] 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT mod 42:2@0..3#ROOT2024 + IDENT m 42:2@4..5#ROOT2024 + SUBTREE {} 42:2@6..7#ROOT2024 42:2@7..8#ROOT2024 + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT some 42:2@0..4#ROOT2024 + IDENT arguments 42:2@5..14#ROOT2024 + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT compile_error 42:2@0..100#ROOT2024 PUNCH ! [alone] 42:2@0..100#ROOT2024 diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index a476a70a7409..a0a45b269e4a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -32,9 +32,9 @@ pub fn assert_expand( macro_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, - expect_s: Expect, + expect_spanned: Expect, ) { - assert_expand_impl(macro_name, ra_fixture, None, expect, expect_s); + assert_expand_impl(macro_name, ra_fixture, None, expect, expect_spanned); } pub fn assert_expand_attr( @@ -42,9 +42,9 @@ pub fn assert_expand_attr( #[rust_analyzer::rust_fixture] ra_fixture: &str, attr_args: &str, expect: Expect, - expect_s: Expect, + expect_spanned: Expect, ) { - assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect, expect_s); + assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect, expect_spanned); } fn assert_expand_impl( @@ -52,7 +52,7 @@ fn assert_expand_impl( input: &str, attr: Option<&str>, expect: Expect, - expect_s: Expect, + expect_spanned: Expect, ) { let path = proc_macro_test_dylib_path(); let expander = dylib::Expander::new(&path).unwrap(); @@ -60,20 +60,17 @@ fn assert_expand_impl( let def_site = TokenId(0); let call_site = TokenId(1); let mixed_site = TokenId(2); - let input_ts = parse_string(call_site, input); + let input_ts = parse_string(call_site, input).into_subtree(call_site); let attr_ts = attr.map(|attr| parse_string(call_site, attr).into_subtree(call_site)); + let input_ts_string = format!("{input_ts:?}"); + let attr_ts_string = attr_ts.as_ref().map(|it| format!("{it:?}")); - let res = expander - .expand( - macro_name, - input_ts.into_subtree(call_site), - attr_ts, - def_site, - call_site, - mixed_site, - ) - .unwrap(); - expect.assert_eq(&format!("{res:?}")); + let res = + expander.expand(macro_name, input_ts, attr_ts, def_site, call_site, mixed_site).unwrap(); + expect.assert_eq(&format!( + "{input_ts_string}\n\n{}\n\n{res:?}", + attr_ts_string.unwrap_or_default() + )); let def_site = Span { range: TextRange::new(0.into(), 150.into()), @@ -93,15 +90,17 @@ fn assert_expand_impl( }; let mixed_site = call_site; - let fixture = parse_string_spanned(call_site.anchor, call_site.ctx, input); + let fixture = + parse_string_spanned(call_site.anchor, call_site.ctx, input).into_subtree(call_site); let attr = attr.map(|attr| { parse_string_spanned(call_site.anchor, call_site.ctx, attr).into_subtree(call_site) }); + let fixture_string = format!("{fixture:?}"); + let attr_string = attr.as_ref().map(|it| format!("{it:?}")); - let res = expander - .expand(macro_name, fixture.into_subtree(call_site), attr, def_site, call_site, mixed_site) - .unwrap(); - expect_s.assert_eq(&format!("{res:#?}")); + let res = expander.expand(macro_name, fixture, attr, def_site, call_site, mixed_site).unwrap(); + expect_spanned + .assert_eq(&format!("{fixture_string}\n\n{}\n\n{res:#?}", attr_string.unwrap_or_default())); } pub(crate) fn list() -> Vec { diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index e7293b0b2ef6..450def5461da 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -18,6 +18,7 @@ pub(crate) fn inject_cargo_package_env(env: &mut Env, package: &PackageData) { let manifest_dir = package.manifest.parent(); env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str()); + env.set("CARGO_MANIFEST_PATH", package.manifest.as_str()); env.set("CARGO_PKG_VERSION", package.version.to_string()); env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string()); diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index c7c1b0431867..d4055d9a0af9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -137,7 +137,12 @@ impl Sysroot { } let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir, envs); - cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root)); + if !envs.contains_key("RUSTUP_TOOLCHAIN") + && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() + { + cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root)); + } + cmd } _ => toolchain::command(tool.path(), current_dir, envs), diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index 4ef9d8161197..3722e2c72168 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -52,6 +52,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -136,6 +137,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -220,6 +222,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "an_example", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -304,6 +307,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "it", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -384,6 +388,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "libc", "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_MANIFEST_PATH": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml", "CARGO_PKG_AUTHORS": "The Rust Project Developers", "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index 4ef9d8161197..3722e2c72168 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -52,6 +52,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -136,6 +137,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -220,6 +222,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "an_example", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -304,6 +307,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "it", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -384,6 +388,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "libc", "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_MANIFEST_PATH": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml", "CARGO_PKG_AUTHORS": "The Rust Project Developers", "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 52089d1dbc2c..7b156ea63a58 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -51,6 +51,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -134,6 +135,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -217,6 +219,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "an_example", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -300,6 +303,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "it", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -380,6 +384,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "libc", "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_MANIFEST_PATH": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml", "CARGO_PKG_AUTHORS": "The Rust Project Developers", "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs index bade0c2cd6fa..71af63a0d3b8 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs @@ -1,33 +1,41 @@ use std::sync::{Arc, Mutex}; #[salsa_macros::db] -#[derive(Default, Clone)] +#[derive(Clone)] pub(crate) struct LoggerDb { storage: salsa::Storage, logger: Logger, } +impl Default for LoggerDb { + fn default() -> Self { + let logger = Logger::default(); + Self { + storage: salsa::Storage::new(Some(Box::new({ + let logger = logger.clone(); + move |event| match event.kind { + salsa::EventKind::WillExecute { .. } + | salsa::EventKind::WillCheckCancellation + | salsa::EventKind::DidValidateMemoizedValue { .. } + | salsa::EventKind::WillDiscardStaleOutput { .. } + | salsa::EventKind::DidDiscard { .. } => { + logger.logs.lock().unwrap().push(format!("salsa_event({:?})", event.kind)); + } + _ => {} + } + }))), + logger, + } + } +} + #[derive(Default, Clone)] struct Logger { logs: Arc>>, } #[salsa_macros::db] -impl salsa::Database for LoggerDb { - fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) { - let event = event(); - match event.kind { - salsa::EventKind::WillExecute { .. } - | salsa::EventKind::WillCheckCancellation - | salsa::EventKind::DidValidateMemoizedValue { .. } - | salsa::EventKind::WillDiscardStaleOutput { .. } - | salsa::EventKind::DidDiscard { .. } => { - self.push_log(format!("salsa_event({:?})", event.kind)); - } - _ => {} - } - } -} +impl salsa::Database for LoggerDb {} impl LoggerDb { /// Log an event from inside a tracked function. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index a1e4adf0844a..12b393b80c0d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -4,6 +4,7 @@ use std::{ env, fmt, ops::AddAssign, + panic::{AssertUnwindSafe, catch_unwind}, time::{SystemTime, UNIX_EPOCH}, }; @@ -721,6 +722,7 @@ impl flags::AnalysisStats { let mut num_pats_unknown = 0; let mut num_pats_partially_unknown = 0; let mut num_pat_type_mismatches = 0; + let mut panics = 0; for &body_id in bodies { let name = body_id.name(db).unwrap_or_else(Name::missing); let module = body_id.module(db); @@ -774,7 +776,20 @@ impl flags::AnalysisStats { } bar.set_message(msg); let body = db.body(body_id.into()); - let inference_result = db.infer(body_id.into()); + let inference_result = catch_unwind(AssertUnwindSafe(|| db.infer(body_id.into()))); + let inference_result = match inference_result { + Ok(inference_result) => inference_result, + Err(p) => { + if let Some(s) = p.downcast_ref::<&str>() { + eprintln!("infer panicked for {}: {}", full_name(), s); + } else if let Some(s) = p.downcast_ref::() { + eprintln!("infer panicked for {}: {}", full_name(), s); + } + panics += 1; + bar.inc(1); + continue; + } + }; // This query is LRU'd, so actually calling it will skew the timing results. let sm = || db.body_with_source_map(body_id.into()).1; @@ -1008,6 +1023,7 @@ impl flags::AnalysisStats { percentage(num_pats_partially_unknown, num_pats), num_pat_type_mismatches ); + eprintln!(" panics: {panics}"); eprintln!("{:<20} {}", "Inference:", inference_time); report_metric("unknown type", num_exprs_unknown, "#"); report_metric("type mismatches", num_expr_type_mismatches, "#"); 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 57f95d114d9d..16f351272b69 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 @@ -150,8 +150,8 @@ xflags::xflags! { optional --disable-proc-macros /// Run the proc-macro-srv binary at the specified path. optional --proc-macro-srv path: PathBuf - /// Run cache priming in parallel. - optional --parallel + /// The number of threads to use. Defaults to the number of physical cores. + optional --num-threads num_threads: usize } cmd ssr { @@ -299,7 +299,7 @@ pub struct PrimeCaches { pub disable_build_scripts: bool, pub disable_proc_macros: bool, pub proc_macro_srv: Option, - pub parallel: bool, + pub num_threads: Option, } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs index 46fb701ab426..467d8a53884a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs @@ -52,7 +52,7 @@ impl flags::PrimeCaches { elapsed.memory.allocated.megabytes() as u64 ); - let threads = if self.parallel { num_cpus::get() } else { 1 }; + let threads = self.num_threads.unwrap_or_else(num_cpus::get_physical); ide_db::prime_caches::parallel_prime_caches(&db, threads, &|_| ()); let elapsed = stop_watch.elapsed(); 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 03e5b1f6f4b6..d1ca8c1a91a3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -149,6 +149,8 @@ config_data! { 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 padding information in a memory layout hover. + hover_memoryLayout_padding: Option = None, /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), @@ -544,7 +546,7 @@ config_data! { /// Whether to prefer import paths containing a `prelude` module. imports_preferPrelude: bool = false, /// The path structure for newly inserted paths to use. - imports_prefix: ImportPrefixDef = ImportPrefixDef::Plain, + imports_prefix: ImportPrefixDef = ImportPrefixDef::ByCrate, /// Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". imports_prefixExternPrelude: bool = false, } @@ -1635,6 +1637,7 @@ impl Config { size: self.hover_memoryLayout_size().map(mem_kind), offset: self.hover_memoryLayout_offset().map(mem_kind), alignment: self.hover_memoryLayout_alignment().map(mem_kind), + padding: self.hover_memoryLayout_padding().map(mem_kind), niches: self.hover_memoryLayout_niches().unwrap_or_default(), }), documentation: self.hover_documentation_enable().to_owned(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index fc312439d58c..0e418240db0c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -470,7 +470,11 @@ impl FlycheckActor { let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env); if let Some(sysroot_root) = &self.sysroot_root { - cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); + if !options.extra_env.contains_key("RUSTUP_TOOLCHAIN") + && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() + { + cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); + } } cmd.arg(command); 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 3b3b9c879754..a870232d4a0e 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 @@ -3,7 +3,11 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::{ops::Not as _, time::Instant}; +use std::{ + ops::Not as _, + panic::AssertUnwindSafe, + time::{Duration, Instant}, +}; use crossbeam_channel::{Receiver, Sender, unbounded}; use hir::ChangeWithProcMacros; @@ -19,6 +23,7 @@ use parking_lot::{ use proc_macro_api::ProcMacroClient; use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; use rustc_hash::{FxHashMap, FxHashSet}; +use stdx::thread; use tracing::{Level, span, trace}; use triomphe::Arc; use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs, VfsPath}; @@ -40,6 +45,7 @@ use crate::{ test_runner::{CargoTestHandle, CargoTestMessage}, }; +#[derive(Debug)] pub(crate) struct FetchWorkspaceRequest { pub(crate) path: Option, pub(crate) force_crate_graph_reload: bool, @@ -78,6 +84,7 @@ pub(crate) struct GlobalState { pub(crate) task_pool: Handle, Receiver>, pub(crate) fmt_pool: Handle, Receiver>, + pub(crate) cancellation_pool: thread::Pool, pub(crate) config: Arc, pub(crate) config_errors: Option, @@ -114,6 +121,11 @@ pub(crate) struct GlobalState { pub(crate) discover_sender: Sender, pub(crate) discover_receiver: Receiver, + // Debouncing channel for fetching the workspace + // we want to delay it until the VFS looks stable-ish (and thus is not currently in the middle + // of a VCS operation like `git switch`) + pub(crate) fetch_ws_receiver: Option<(Receiver, FetchWorkspaceRequest)>, + // VFS pub(crate) loader: Handle, Receiver>, pub(crate) vfs: Arc)>>, @@ -210,6 +222,7 @@ impl GlobalState { let handle = TaskPool::new_with_threads(sender, 1); Handle { handle, receiver } }; + let cancellation_pool = thread::Pool::new(1); let task_queue = { let (sender, receiver) = unbounded(); @@ -230,6 +243,7 @@ impl GlobalState { req_queue: ReqQueue::default(), task_pool, fmt_pool, + cancellation_pool, loader, config: Arc::new(config.clone()), analysis_host, @@ -264,6 +278,8 @@ impl GlobalState { discover_sender, discover_receiver, + fetch_ws_receiver: None, + vfs: Arc::new(RwLock::new((vfs::Vfs::default(), Default::default()))), vfs_config_version: 0, vfs_progress_config_version: 0, @@ -290,7 +306,6 @@ impl GlobalState { pub(crate) fn process_changes(&mut self) -> bool { let _p = span!(Level::INFO, "GlobalState::process_changes").entered(); - // We cannot directly resolve a change in a ratoml file to a format // that can be used by the config module because config talks // in `SourceRootId`s instead of `FileId`s and `FileId` -> `SourceRootId` @@ -298,66 +313,75 @@ impl GlobalState { let mut modified_ratoml_files: FxHashMap = FxHashMap::default(); - let (change, modified_rust_files, workspace_structure_change) = { - let mut change = ChangeWithProcMacros::default(); - let mut guard = self.vfs.write(); - let changed_files = guard.0.take_changes(); - if changed_files.is_empty() { - return false; - } + let mut change = ChangeWithProcMacros::default(); + let mut guard = self.vfs.write(); + let changed_files = guard.0.take_changes(); + if changed_files.is_empty() { + return false; + } - // downgrade to read lock to allow more readers while we are normalizing text - let guard = RwLockWriteGuard::downgrade_to_upgradable(guard); - let vfs: &Vfs = &guard.0; + let (change, modified_rust_files, workspace_structure_change) = + self.cancellation_pool.scoped(|s| { + // start cancellation in parallel, this will kick off lru eviction + // allowing us to do meaningful work while waiting + let analysis_host = AssertUnwindSafe(&mut self.analysis_host); + s.spawn(thread::ThreadIntent::LatencySensitive, || { + { analysis_host }.0.request_cancellation() + }); - let mut workspace_structure_change = None; - // A file was added or deleted - let mut has_structure_changes = false; - let mut bytes = vec![]; - let mut modified_rust_files = vec![]; - for file in changed_files.into_values() { - let vfs_path = vfs.file_path(file.file_id); - if let Some(("rust-analyzer", Some("toml"))) = vfs_path.name_and_extension() { - // Remember ids to use them after `apply_changes` - modified_ratoml_files.insert(file.file_id, (file.kind(), vfs_path.clone())); - } + // downgrade to read lock to allow more readers while we are normalizing text + let guard = RwLockWriteGuard::downgrade_to_upgradable(guard); + let vfs: &Vfs = &guard.0; - if let Some(path) = vfs_path.as_path() { - has_structure_changes |= file.is_created_or_deleted(); - - if file.is_modified() && path.extension() == Some("rs") { - modified_rust_files.push(file.file_id); + let mut workspace_structure_change = None; + // A file was added or deleted + let mut has_structure_changes = false; + let mut bytes = vec![]; + let mut modified_rust_files = vec![]; + for file in changed_files.into_values() { + let vfs_path = vfs.file_path(file.file_id); + if let Some(("rust-analyzer", Some("toml"))) = vfs_path.name_and_extension() { + // Remember ids to use them after `apply_changes` + modified_ratoml_files.insert(file.file_id, (file.kind(), vfs_path.clone())); } - let additional_files = self - .config - .discover_workspace_config() - .map(|cfg| { - cfg.files_to_watch.iter().map(String::as_str).collect::>() - }) - .unwrap_or_default(); + if let Some(path) = vfs_path.as_path() { + has_structure_changes |= file.is_created_or_deleted(); - let path = path.to_path_buf(); - if file.is_created_or_deleted() { - workspace_structure_change.get_or_insert((path, false)).1 |= - self.crate_graph_file_dependencies.contains(vfs_path); - } else if reload::should_refresh_for_change( - &path, - file.kind(), - &additional_files, - ) { - trace!(?path, kind = ?file.kind(), "refreshing for a change"); - workspace_structure_change.get_or_insert((path.clone(), false)); + if file.is_modified() && path.extension() == Some("rs") { + modified_rust_files.push(file.file_id); + } + + let additional_files = self + .config + .discover_workspace_config() + .map(|cfg| { + cfg.files_to_watch.iter().map(String::as_str).collect::>() + }) + .unwrap_or_default(); + + let path = path.to_path_buf(); + if file.is_created_or_deleted() { + workspace_structure_change.get_or_insert((path, false)).1 |= + self.crate_graph_file_dependencies.contains(vfs_path); + } else if reload::should_refresh_for_change( + &path, + file.kind(), + &additional_files, + ) { + trace!(?path, kind = ?file.kind(), "refreshing for a change"); + workspace_structure_change.get_or_insert((path.clone(), false)); + } } - } - // Clear native diagnostics when their file gets deleted - if !file.exists() { - self.diagnostics.clear_native_for(file.file_id); - } + // Clear native diagnostics when their file gets deleted + if !file.exists() { + self.diagnostics.clear_native_for(file.file_id); + } - let text = - if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) = file.change { + let text = if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) = + file.change + { String::from_utf8(v).ok().map(|text| { // FIXME: Consider doing normalization in the `vfs` instead? That allows // getting rid of some locking @@ -367,29 +391,28 @@ impl GlobalState { } else { None }; - // delay `line_endings_map` changes until we are done normalizing the text - // this allows delaying the re-acquisition of the write lock - bytes.push((file.file_id, text)); - } - let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard); - bytes.into_iter().for_each(|(file_id, text)| { - let text = match text { - None => None, - Some((text, line_endings)) => { - line_endings_map.insert(file_id, line_endings); - Some(text) - } - }; - change.change_file(file_id, text); + // delay `line_endings_map` changes until we are done normalizing the text + // this allows delaying the re-acquisition of the write lock + bytes.push((file.file_id, text)); + } + let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard); + bytes.into_iter().for_each(|(file_id, text)| { + let text = match text { + None => None, + Some((text, line_endings)) => { + line_endings_map.insert(file_id, line_endings); + Some(text) + } + }; + change.change_file(file_id, text); + }); + if has_structure_changes { + let roots = self.source_root_config.partition(vfs); + change.set_roots(roots); + } + (change, modified_rust_files, workspace_structure_change) }); - if has_structure_changes { - let roots = self.source_root_config.partition(vfs); - change.set_roots(roots); - } - (change, modified_rust_files, workspace_structure_change) - }; - let _p = span!(Level::INFO, "GlobalState::process_changes/apply_change").entered(); self.analysis_host.apply_change(change); if !modified_ratoml_files.is_empty() || !self.config.same_source_root_parent_map(&self.local_roots_parent_map) @@ -508,11 +531,7 @@ impl GlobalState { if let Some((path, force_crate_graph_reload)) = workspace_structure_change { let _p = span!(Level::INFO, "GlobalState::process_changes/ws_structure_change") .entered(); - - self.fetch_workspaces_queue.request_op( - format!("workspace vfs file change: {path}"), - FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload }, - ); + self.enqueue_workspace_fetch(path, force_crate_graph_reload); } } @@ -660,6 +679,30 @@ impl GlobalState { None }) } + + fn enqueue_workspace_fetch(&mut self, path: AbsPathBuf, force_crate_graph_reload: bool) { + let already_requested = self.fetch_workspaces_queue.op_requested() + && !self.fetch_workspaces_queue.op_in_progress(); + if self.fetch_ws_receiver.is_none() && already_requested { + // Don't queue up a new fetch request if we already have done so + // Otherwise we will re-fetch in quick succession which is unnecessary + // Note though, that if one is already in progress, we *want* to re-queue + // as the in-progress fetch might not have the latest changes in it anymore + // FIXME: We should cancel the in-progress fetch here + return; + } + + self.fetch_ws_receiver = Some(( + crossbeam_channel::after(Duration::from_millis(100)), + FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload }, + )); + } + + pub(crate) fn debounce_workspace_fetch(&mut self) { + if let Some((fetch_receiver, _)) = &mut self.fetch_ws_receiver { + *fetch_receiver = crossbeam_channel::after(Duration::from_millis(100)); + } + } } impl Drop for GlobalState { 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 49ebffa909ac..84b7888258f8 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 @@ -147,7 +147,7 @@ fn integrated_completion_benchmark() { let _it = stdx::timeit("change"); let mut text = host.analysis().file_text(file_id).unwrap().to_string(); let completion_offset = - patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)") + patch(&mut text, "db.struct_signature(self.id)", "sel;\ndb.struct_signature(self.id)") + "sel".len(); let mut change = ChangeWithProcMacros::default(); change.change_file(file_id, Some(text)); @@ -197,9 +197,11 @@ fn integrated_completion_benchmark() { let completion_offset = { let _it = stdx::timeit("change"); let mut text = host.analysis().file_text(file_id).unwrap().to_string(); - let completion_offset = - patch(&mut text, "sel;\ndb.struct_data(self.id)", ";sel;\ndb.struct_data(self.id)") - + ";sel".len(); + let completion_offset = patch( + &mut text, + "sel;\ndb.struct_signature(self.id)", + ";sel;\ndb.struct_signature(self.id)", + ) + ";sel".len(); let mut change = ChangeWithProcMacros::default(); change.change_file(file_id, Some(text)); host.apply_change(change); @@ -247,9 +249,11 @@ fn integrated_completion_benchmark() { let completion_offset = { let _it = stdx::timeit("change"); let mut text = host.analysis().file_text(file_id).unwrap().to_string(); - let completion_offset = - patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)") - + "self.".len(); + let completion_offset = patch( + &mut text, + "sel;\ndb.struct_signature(self.id)", + "self.;\ndb.struct_signature(self.id)", + ) + "self.".len(); let mut change = ChangeWithProcMacros::default(); change.change_file(file_id, Some(text)); host.apply_change(change); @@ -366,7 +370,7 @@ fn integrated_diagnostics_benchmark() { { let _it = stdx::timeit("change"); let mut text = host.analysis().file_text(file_id).unwrap().to_string(); - patch(&mut text, "db.struct_data(self.id)", "();\ndb.struct_data(self.id)"); + patch(&mut text, "db.struct_signature(self.id)", "();\ndb.struct_signature(self.id)"); let mut change = ChangeWithProcMacros::default(); change.change_file(file_id, Some(text)); host.apply_change(change); 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 bd213ffa57a1..0c0438c4b8ff 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 @@ -8,7 +8,7 @@ use std::{ time::{Duration, Instant}, }; -use crossbeam_channel::{Receiver, select}; +use crossbeam_channel::{Receiver, never, select}; use ide_db::base_db::{SourceDatabase, VfsPath, salsa::Database as _}; use lsp_server::{Connection, Notification, Request}; use lsp_types::{TextDocumentIdentifier, notification::Notification as _}; @@ -71,6 +71,7 @@ enum Event { Flycheck(FlycheckMessage), TestResult(CargoTestMessage), DiscoverProject(DiscoverProjectMessage), + FetchWorkspaces(FetchWorkspaceRequest), } impl fmt::Display for Event { @@ -83,6 +84,7 @@ impl fmt::Display for Event { Event::QueuedTask(_) => write!(f, "Event::QueuedTask"), Event::TestResult(_) => write!(f, "Event::TestResult"), Event::DiscoverProject(_) => write!(f, "Event::DiscoverProject"), + Event::FetchWorkspaces(_) => write!(f, "Event::SwitchWorkspaces"), } } } @@ -150,6 +152,7 @@ impl fmt::Debug for Event { } _ => (), } + match self { Event::Lsp(it) => fmt::Debug::fmt(it, f), Event::Task(it) => fmt::Debug::fmt(it, f), @@ -158,6 +161,7 @@ impl fmt::Debug for Event { Event::Flycheck(it) => fmt::Debug::fmt(it, f), Event::TestResult(it) => fmt::Debug::fmt(it, f), Event::DiscoverProject(it) => fmt::Debug::fmt(it, f), + Event::FetchWorkspaces(it) => fmt::Debug::fmt(it, f), } } } @@ -251,7 +255,7 @@ impl GlobalState { } fn next_event( - &self, + &mut self, inbox: &Receiver, ) -> Result, crossbeam_channel::RecvError> { // Make sure we reply to formatting requests ASAP so the editor doesn't block @@ -283,6 +287,10 @@ impl GlobalState { recv(self.discover_receiver) -> task => task.map(Event::DiscoverProject), + + recv(self.fetch_ws_receiver.as_ref().map_or(&never(), |(chan, _)| chan)) -> _instant => { + Ok(Event::FetchWorkspaces(self.fetch_ws_receiver.take().unwrap().1)) + }, } .map(Some) } @@ -412,6 +420,9 @@ impl GlobalState { self.handle_discover_msg(message); } } + Event::FetchWorkspaces(req) => { + self.fetch_workspaces_queue.request_op("project structure change".to_owned(), req) + } } let event_handling_duration = loop_start.elapsed(); let (state_changed, memdocs_added_or_removed) = if self.vfs_done { @@ -830,6 +841,7 @@ impl GlobalState { match message { vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => { let _p = tracing::info_span!("GlobalState::handle_vfs_msg{changed/load}").entered(); + self.debounce_workspace_fetch(); let vfs = &mut self.vfs.write().0; for (path, contents) in files { let path = VfsPath::from(path); 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 709d99bda751..7af5b48bb7a7 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 @@ -36,7 +36,7 @@ impl Default for OpQueue { } } -impl 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)); 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 55ed1923653f..ae9e3e998746 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -69,6 +69,7 @@ impl GlobalState { /// are ready to do semantic work. pub(crate) fn is_quiescent(&self) -> bool { self.vfs_done + && self.fetch_ws_receiver.is_none() && !self.fetch_workspaces_queue.op_in_progress() && !self.fetch_build_data_queue.op_in_progress() && !self.fetch_proc_macros_queue.op_in_progress() @@ -659,6 +660,10 @@ impl GlobalState { .chain( ws.sysroot .root() + .filter(|_| { + !self.config.extra_env(None).contains_key("RUSTUP_TOOLCHAIN") + && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() + }) .map(|it| ("RUSTUP_TOOLCHAIN".to_owned(), Some(it.to_string()))), ) .collect(), diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index 7bda106764b9..b37aded6f68c 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -17,6 +17,7 @@ jod-thread = "1.0.0" crossbeam-channel.workspace = true itertools.workspace = true tracing.workspace = true +crossbeam-utils = "0.8.21" # Think twice before adding anything here [target.'cfg(unix)'.dependencies] diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs index a8de4db624f1..8d76c5fd1fb3 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs @@ -8,6 +8,7 @@ //! the threading utilities in [`crate::thread`]. use std::{ + marker::PhantomData, panic::{self, UnwindSafe}, sync::{ Arc, @@ -16,8 +17,9 @@ use std::{ }; use crossbeam_channel::{Receiver, Sender}; +use crossbeam_utils::sync::WaitGroup; -use super::{Builder, JoinHandle, ThreadIntent}; +use crate::thread::{Builder, JoinHandle, ThreadIntent}; pub struct Pool { // `_handles` is never read: the field is present @@ -79,9 +81,6 @@ impl Pool { Self { _handles: handles.into_boxed_slice(), extant_tasks, job_sender } } - /// # Panics - /// - /// Panics if job panics pub fn spawn(&self, intent: ThreadIntent, f: F) where F: FnOnce() + Send + UnwindSafe + 'static, @@ -97,6 +96,17 @@ impl Pool { self.job_sender.send(job).unwrap(); } + pub fn scoped<'pool, 'scope, F, R>(&'pool self, f: F) -> R + where + F: FnOnce(&Scope<'pool, 'scope>) -> R, + { + let wg = WaitGroup::new(); + let scope = Scope { pool: self, wg, _marker: PhantomData }; + let r = f(&scope); + scope.wg.wait(); + r + } + #[must_use] pub fn len(&self) -> usize { self.extant_tasks.load(Ordering::SeqCst) @@ -107,3 +117,36 @@ impl Pool { self.len() == 0 } } + +pub struct Scope<'pool, 'scope> { + pool: &'pool Pool, + wg: WaitGroup, + _marker: PhantomData &'scope ()>, +} + +impl<'scope> Scope<'_, 'scope> { + pub fn spawn(&self, intent: ThreadIntent, f: F) + where + F: 'scope + FnOnce() + Send + UnwindSafe, + { + let wg = self.wg.clone(); + let f = Box::new(move || { + if cfg!(debug_assertions) { + intent.assert_is_used_on_current_thread(); + } + f(); + drop(wg); + }); + + let job = Job { + requested_intent: intent, + f: unsafe { + std::mem::transmute::< + Box, + Box, + >(f) + }, + }; + self.pool.job_sender.send(job).unwrap(); + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index da0bfd4f37f5..e60243f2c917 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -109,6 +109,67 @@ impl GenericParamsOwnerEdit for ast::Trait { } } +impl GenericParamsOwnerEdit for ast::TraitAlias { + fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { + match self.generic_param_list() { + Some(it) => it, + None => { + let position = if let Some(name) = self.name() { + Position::after(name.syntax) + } else if let Some(trait_token) = self.trait_token() { + Position::after(trait_token) + } else { + Position::last_child_of(self.syntax()) + }; + create_generic_param_list(position) + } + } + } + + fn get_or_create_where_clause(&self) -> ast::WhereClause { + if self.where_clause().is_none() { + let position = match self.semicolon_token() { + Some(tok) => Position::before(tok), + None => Position::last_child_of(self.syntax()), + }; + create_where_clause(position); + } + self.where_clause().unwrap() + } +} + +impl GenericParamsOwnerEdit for ast::TypeAlias { + fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { + match self.generic_param_list() { + Some(it) => it, + None => { + let position = if let Some(name) = self.name() { + Position::after(name.syntax) + } else if let Some(trait_token) = self.type_token() { + Position::after(trait_token) + } else { + Position::last_child_of(self.syntax()) + }; + create_generic_param_list(position) + } + } + } + + fn get_or_create_where_clause(&self) -> ast::WhereClause { + if self.where_clause().is_none() { + let position = match self.eq_token() { + Some(tok) => Position::before(tok), + None => match self.semicolon_token() { + Some(tok) => Position::before(tok), + None => Position::last_child_of(self.syntax()), + }, + }; + create_where_clause(position); + } + self.where_clause().unwrap() + } +} + impl GenericParamsOwnerEdit for ast::Struct { fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { match self.generic_param_list() { 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 596f73e0b103..fab4cb287c3d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -13,6 +13,7 @@ mod quote; +use either::Either; use itertools::Itertools; use parser::{Edition, T}; use rowan::NodeOrToken; @@ -881,7 +882,7 @@ pub fn match_arm_list(arms: impl IntoIterator) -> ast::Mat } pub fn where_pred( - path: ast::Type, + path: Either, bounds: impl IntoIterator, ) -> ast::WherePred { let bounds = bounds.into_iter().join(" + "); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 8dee3964d448..429e51ba3621 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -585,6 +585,18 @@ impl SyntaxFactory { ast } + pub fn expr_underscore(&self) -> ast::UnderscoreExpr { + let ast::Expr::UnderscoreExpr(ast) = make::ext::expr_underscore().clone_for_update() else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + SyntaxMappingBuilder::new(ast.syntax().clone()).finish(&mut mapping); + } + + ast + } + pub fn expr_if( &self, condition: ast::Expr, 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 f6ca5ab6c8c5..96e1301f227e 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -662,10 +662,6 @@ impl ProcMacroExpander for IdentityProcMacroExpander { ) -> Result { Ok(subtree.clone()) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Expands to a macro_rules! macro, for issue #18089. @@ -697,10 +693,6 @@ impl ProcMacroExpander for Issue18089ProcMacroExpander { #subtree }) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Pastes the attribute input as its output @@ -721,10 +713,6 @@ impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { .cloned() .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into())) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } #[derive(Debug)] @@ -756,10 +744,6 @@ impl ProcMacroExpander for Issue18840ProcMacroExpander { top_subtree_delimiter_mut.close = def_site; Ok(result) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } #[derive(Debug)] @@ -791,10 +775,6 @@ impl ProcMacroExpander for MirrorProcMacroExpander { traverse(&mut builder, input.iter()); Ok(builder.build()) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Replaces every literal with an empty string literal and every identifier with its first letter, @@ -835,10 +815,6 @@ impl ProcMacroExpander for ShortenProcMacroExpander { } } } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Reads ident type within string quotes, for issue #17479. @@ -864,10 +840,6 @@ impl ProcMacroExpander for Issue17479ProcMacroExpander { #symbol() }) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Reads ident type within string quotes, for issue #17479. @@ -919,10 +891,6 @@ impl ProcMacroExpander for Issue18898ProcMacroExpander { } }) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Reads ident type within string quotes, for issue #17479. @@ -950,8 +918,4 @@ impl ProcMacroExpander for DisallowCfgProcMacroExpander { } Ok(subtree.clone()) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 1dbc07c0929c..14574a6456bd 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -817,6 +817,58 @@ impl fmt::Display for Ident { } } +impl Literal { + pub fn display_no_minus(&self) -> impl fmt::Display { + struct NoMinus<'a, S>(&'a Literal); + impl fmt::Display for NoMinus<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let symbol = + self.0.symbol.as_str().strip_prefix('-').unwrap_or(self.0.symbol.as_str()); + match self.0.kind { + LitKind::Byte => write!(f, "b'{symbol}'"), + LitKind::Char => write!(f, "'{symbol}'"), + LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{symbol}"), + LitKind::Str => write!(f, "\"{symbol}\""), + LitKind::ByteStr => write!(f, "b\"{symbol}\""), + LitKind::CStr => write!(f, "c\"{symbol}\""), + LitKind::StrRaw(num_of_hashes) => { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"r{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"br{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"cr{0:# fmt::Display for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind { diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 2ae73df61d0a..0e07dadfb7c9 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -763,6 +763,13 @@ Default: `"hexadecimal"` How to render the offset information in a memory layout hover. +## rust-analyzer.hover.memoryLayout.padding {#hover.memoryLayout.padding} + +Default: `null` + +How to render the padding information in a memory layout hover. + + ## rust-analyzer.hover.memoryLayout.size {#hover.memoryLayout.size} Default: `"both"` @@ -835,7 +842,7 @@ Whether to prefer import paths containing a `prelude` module. ## rust-analyzer.imports.prefix {#imports.prefix} -Default: `"plain"` +Default: `"crate"` The path structure for newly inserted paths to use. diff --git a/src/tools/rust-analyzer/docs/book/src/other_editors.md b/src/tools/rust-analyzer/docs/book/src/other_editors.md index 1eac7dd2c256..896df52af5f9 100644 --- a/src/tools/rust-analyzer/docs/book/src/other_editors.md +++ b/src/tools/rust-analyzer/docs/book/src/other_editors.md @@ -364,30 +364,6 @@ binary](./rust_analyzer_binary.html). There are multiple rust-analyzer extensions for Visual Studio 2022 on Windows: -### rust-analyzer.vs - -(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 -International) - -[Visual Studio -Marketplace](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) - -[GitHub](https://github.com/kitamstudios/rust-analyzer/) - -Support for Rust development in the Visual Studio IDE is enabled by the -[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) -package. Either click on the download link or install from IDE’s -extension manager. For now [Visual Studio -2022](https://visualstudio.microsoft.com/downloads/) is required. All -editions are supported viz. Community, Professional & Enterprise. The -package aims to provide 0-friction installation and therefore comes -loaded with most things required including rust-analyzer binary. If -anything it needs is missing, appropriate errors / warnings will guide -the user. E.g. cargo.exe needs to be in path and the package will tell -you as much. This package is under rapid active development. So if you -encounter any issues please file it at -[rust-analyzer.vs](https://github.com/kitamstudios/rust-analyzer/). - ### VS RustAnalyzer (License: GPL) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 11a37c218f7f..18fb097aad7f 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -5730,9 +5730,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "6.21.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", - "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", "dev": true, "license": "MIT", "engines": { diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index a282eea99973..c8c36cd85c8e 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1779,6 +1779,33 @@ } } }, + { + "title": "hover", + "properties": { + "rust-analyzer.hover.memoryLayout.padding": { + "markdownDescription": "How to render the padding information in a memory layout hover.", + "default": null, + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + } + } + }, { "title": "hover", "properties": { @@ -1927,7 +1954,7 @@ "properties": { "rust-analyzer.imports.prefix": { "markdownDescription": "The path structure for newly inserted paths to use.", - "default": "plain", + "default": "crate", "type": "string", "enum": [ "plain", diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 90e4d65bf961..5b47d1bbaed7 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -6e23095adf9209614a45f7f75fea36dad7b92afb +a8e4c68dcb4dc1e48a0db294c5323cab0227fcb9 diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index 6195de5d2025..bb7d83c4b7d9 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/xtask/Cargo.toml @@ -14,7 +14,7 @@ write-json = "0.1.4" xshell.workspace = true xflags = "0.3.2" time = { version = "0.3", default-features = false } -zip = { version = "2.4", default-features = false, features = ["deflate-flate2", "flate2", "time"] } +zip = { version = "3.0", default-features = false, features = ["deflate-flate2", "time"] } stdx.workspace = true proc-macro2 = "1.0.94" quote = "1.0.40" diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 0b3893770111..8893846b5fa5 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -343,17 +343,6 @@ dependencies = [ "regex", ] -[[package]] -name = "dbus" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" -dependencies = [ - "libc", - "libdbus-sys", - "winapi", -] - [[package]] name = "derive_builder" version = "0.20.2" @@ -823,16 +812,6 @@ version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" -[[package]] -name = "libdbus-sys" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "linereader" version = "0.4.0" @@ -906,9 +885,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.49" +version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1daacee059634081dee4250d2814763a365b92dfe14bfdef964bc27835209d4" +checksum = "a87e65420ab45ca9c1b8cdf698f95b710cc826d373fa550f0f7fad82beac9328" dependencies = [ "ammonia", "anyhow", @@ -921,7 +900,6 @@ dependencies = [ "hex", "log", "memchr", - "once_cell", "opener", "pulldown-cmark 0.10.3", "regex", @@ -1048,11 +1026,11 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "onig" -version = "6.4.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "libc", "once_cell", "onig_sys", @@ -1060,9 +1038,9 @@ dependencies = [ [[package]] name = "onig_sys" -version = "69.8.1" +version = "69.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc" dependencies = [ "cc", "pkg-config", @@ -1070,12 +1048,11 @@ dependencies = [ [[package]] name = "opener" -version = "0.7.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0812e5e4df08da354c851a3376fead46db31c2214f849d3de356d774d057681" +checksum = "de96cad6ee771be7f68df884d3767460b4684012308d8342ed5623fe62b2628c" dependencies = [ "bstr", - "dbus", "normpath", "windows-sys", ] @@ -1905,22 +1882,6 @@ dependencies = [ "string_cache_codegen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.9" @@ -1930,12 +1891,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.61.0" diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index 10fde31306df..69c0cfaf5c99 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -15,6 +15,6 @@ mdbook-i18n-helpers = "0.3.3" mdbook-spec = { path = "../../doc/reference/mdbook-spec" } [dependencies.mdbook] -version = "0.4.49" +version = "0.4.51" default-features = false features = ["search"] diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf index c0f3b53c8e5d..6a70166b92a1 160000 --- a/src/tools/rustc-perf +++ b/src/tools/rustc-perf @@ -1 +1 @@ -Subproject commit c0f3b53c8e5de87714d18a5f42998859302ae03a +Subproject commit 6a70166b92a1b1560cb3cf056427b011b2a1f2bf diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index e79b7803c607..1a3897b51cb8 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1110,10 +1110,10 @@ impl<'a> StructParts<'a> { pub(crate) fn from_item(item: &'a ast::Item) -> Self { let (prefix, def, ident, generics) = match item.kind { - ast::ItemKind::Struct(ident, ref def, ref generics) => { + ast::ItemKind::Struct(ident, ref generics, ref def) => { ("struct ", def, ident, generics) } - ast::ItemKind::Union(ident, ref def, ref generics) => ("union ", def, ident, generics), + ast::ItemKind::Union(ident, ref generics, ref def) => ("union ", def, ident, generics), _ => unreachable!(), }; StructParts { diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs index 08cda6913b9e..942b42ec5f20 100644 --- a/src/tools/rustfmt/src/lib.rs +++ b/src/tools/rustfmt/src/lib.rs @@ -8,7 +8,6 @@ // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_builtin_macros; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_expand; diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 1a9614bacec8..bfa9c6300c46 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::{AsmArg, parse_asm_args}; +use rustc_parse::parser::asm::{AsmArg, parse_asm_args}; use crate::rewrite::RewriteContext; diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 16d1f5105d5f..f6a9a3f2cd17 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -521,7 +521,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ast::ItemKind::Struct(..) | ast::ItemKind::Union(..) => { self.visit_struct(&StructParts::from_item(item)); } - ast::ItemKind::Enum(ident, ref def, ref generics) => { + ast::ItemKind::Enum(ident, ref generics, ref def) => { self.format_missing_with_indent(source!(self, item.span).lo()); self.visit_enum(ident, &item.vis, def, generics, item.span); self.last_pos = source!(self, item.span).hi(); diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index dfdbc0878f26..4835c2202104 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -15,7 +15,7 @@ semver = "1.0" serde = { version = "1.0.125", features = ["derive"], optional = true } termcolor = "1.1.3" rustc-hash = "2.0.0" -fluent-syntax = "0.11.1" +fluent-syntax = "0.12" similar = "2.5.0" toml = "0.7.8" diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 9bb06c31c5c0..9f333cc43cf8 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -200,6 +200,7 @@ const EXCEPTIONS_CRANELIFT: ExceptionList = &[ ("cranelift-module", "Apache-2.0 WITH LLVM-exception"), ("cranelift-native", "Apache-2.0 WITH LLVM-exception"), ("cranelift-object", "Apache-2.0 WITH LLVM-exception"), + ("cranelift-srcgen", "Apache-2.0 WITH LLVM-exception"), ("foldhash", "Zlib"), ("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"), ("regalloc2", "Apache-2.0 WITH LLVM-exception"), @@ -525,6 +526,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "cranelift-module", "cranelift-native", "cranelift-object", + "cranelift-srcgen", "crc32fast", "equivalent", "fallible-iterator", diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index fcd7943e6e0a..6093e7fd2632 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -54,6 +54,7 @@ pub struct Feature { pub tracking_issue: Option, pub file: PathBuf, pub line: usize, + pub description: Option, } impl Feature { fn tracking_issue_display(&self) -> impl fmt::Display { @@ -296,6 +297,7 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba let mut prev_names = vec![]; let lines = contents.lines().zip(1..); + let mut doc_comments: Vec = Vec::new(); for (line, line_number) in lines { let line = line.trim(); @@ -332,6 +334,13 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba continue; } + if in_feature_group { + if let Some(doc_comment) = line.strip_prefix("///") { + doc_comments.push(doc_comment.trim().to_string()); + continue; + } + } + let mut parts = line.split(','); let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) { Some("unstable") => Status::Unstable, @@ -438,9 +447,15 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba tracking_issue, file: path.to_path_buf(), line: line_number, + description: if doc_comments.is_empty() { + None + } else { + Some(doc_comments.join(" ")) + }, }); } } + doc_comments.clear(); } } @@ -564,6 +579,7 @@ fn map_lib_features( tracking_issue: find_attr_val(line, "issue").and_then(handle_issue_none), file: file.to_path_buf(), line: i + 1, + description: None, }; mf(Ok((feature_name, feature)), file, i + 1); continue; @@ -600,6 +616,7 @@ fn map_lib_features( tracking_issue, file: file.to_path_buf(), line: i + 1, + description: None, }; if line.contains(']') { mf(Ok((feature_name, feature)), file, i + 1); diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 1d0ddd56eec5..3e9d79224fdd 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -939,7 +939,6 @@ ui/enum-discriminant/auxiliary/issue-41394.rs ui/enum-discriminant/issue-104519.rs ui/enum-discriminant/issue-41394-rpass.rs ui/enum-discriminant/issue-41394.rs -ui/enum-discriminant/issue-43398.rs ui/enum-discriminant/issue-46519.rs ui/enum-discriminant/issue-50689.rs ui/enum-discriminant/issue-51582.rs diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index ca45f8bb84ba..e8a12d563358 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -82,6 +82,7 @@ pub mod mir_opt_tests; pub mod pal; pub mod rustdoc_css_themes; pub mod rustdoc_gui_tests; +pub mod rustdoc_js; pub mod rustdoc_templates; pub mod style; pub mod target_policy; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 48122129b017..776f1bde2eb7 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -35,6 +35,7 @@ fn main() { let library_path = root_path.join("library"); let compiler_path = root_path.join("compiler"); let librustdoc_path = src_path.join("librustdoc"); + let tools_path = src_path.join("tools"); let crashes_path = tests_path.join("crashes"); let args: Vec = env::args().skip(1).collect(); @@ -108,6 +109,7 @@ fn main() { check!(rustdoc_gui_tests, &tests_path); check!(rustdoc_css_themes, &librustdoc_path); check!(rustdoc_templates, &librustdoc_path); + check!(rustdoc_js, &librustdoc_path, &tools_path, &src_path); check!(known_bug, &crashes_path); check!(unknown_revision, &tests_path); diff --git a/src/tools/tidy/src/rustdoc_js.rs b/src/tools/tidy/src/rustdoc_js.rs new file mode 100644 index 000000000000..2517e2de12ce --- /dev/null +++ b/src/tools/tidy/src/rustdoc_js.rs @@ -0,0 +1,99 @@ +//! Tidy check to ensure that rustdoc templates didn't forget a `{# #}` to strip extra whitespace +//! characters. + +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use ignore::DirEntry; + +use crate::walk::walk_no_read; + +fn run_eslint(args: &[PathBuf], config_folder: PathBuf, bad: &mut bool) { + let mut child = match Command::new("npx") + .arg("eslint") + .arg("-c") + .arg(config_folder.join(".eslintrc.js")) + .args(args) + .spawn() + { + Ok(child) => child, + Err(error) => { + *bad = true; + eprintln!("failed to run eslint: {error:?}"); + return; + } + }; + match child.wait() { + Ok(exit_status) => { + if exit_status.success() { + return; + } + eprintln!("eslint command failed"); + } + Err(error) => eprintln!("eslint command failed: {error:?}"), + } + *bad = true; +} + +fn get_eslint_version_inner(global: bool) -> Option { + let mut command = Command::new("npm"); + command.arg("list").arg("--parseable").arg("--long").arg("--depth=0"); + if global { + command.arg("--global"); + } + let output = command.output().ok()?; + let lines = String::from_utf8_lossy(&output.stdout); + lines.lines().find_map(|l| l.split(':').nth(1)?.strip_prefix("eslint@")).map(|v| v.to_owned()) +} + +fn get_eslint_version() -> Option { + get_eslint_version_inner(false).or_else(|| get_eslint_version_inner(true)) +} + +pub fn check(librustdoc_path: &Path, tools_path: &Path, src_path: &Path, bad: &mut bool) { + let eslint_version_path = + src_path.join("ci/docker/host-x86_64/mingw-check-tidy/eslint.version"); + let eslint_version = match std::fs::read_to_string(&eslint_version_path) { + Ok(version) => version.trim().to_string(), + Err(error) => { + *bad = true; + eprintln!("failed to read `{}`: {error:?}", eslint_version_path.display()); + return; + } + }; + match get_eslint_version() { + Some(version) => { + if version != eslint_version { + *bad = true; + eprintln!( + "⚠️ Installed version of eslint (`{version}`) is different than the \ + one used in the CI (`{eslint_version}`)", + ); + eprintln!( + "You can install this version using `npm update eslint` or by using \ + `npm install eslint@{eslint_version}`", + ); + return; + } + } + None => { + eprintln!("`eslint` doesn't seem to be installed. Skipping tidy check for JS files."); + eprintln!("You can install it using `npm install eslint@{eslint_version}`"); + return; + } + } + let mut files_to_check = Vec::new(); + walk_no_read( + &[&librustdoc_path.join("html/static/js")], + |path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("js")), + &mut |path: &DirEntry| { + files_to_check.push(path.path().into()); + }, + ); + println!("Running eslint on rustdoc JS files"); + run_eslint(&files_to_check, librustdoc_path.join("html/static"), bad); + + run_eslint(&[tools_path.join("rustdoc-js/tester.js")], tools_path.join("rustdoc-js"), bad); + run_eslint(&[tools_path.join("rustdoc-gui/tester.js")], tools_path.join("rustdoc-gui"), bad); +} diff --git a/src/tools/unstable-book-gen/src/SUMMARY.md b/src/tools/unstable-book-gen/src/SUMMARY.md index 933c928e2f09..fd4ea1dada62 100644 --- a/src/tools/unstable-book-gen/src/SUMMARY.md +++ b/src/tools/unstable-book-gen/src/SUMMARY.md @@ -1,5 +1,7 @@ [The Unstable Book](the-unstable-book.md) +- [Compiler environment variables](compiler-environment-variables.md) +{compiler_env_vars} - [Compiler flags](compiler-flags.md) {compiler_flags} - [Language features](language-features.md) diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index 6cbdc83d5b5f..159a1d0fa174 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -12,13 +12,18 @@ use tidy::unstable_book::{ collect_unstable_feature_names, }; -fn generate_stub_issue(path: &Path, name: &str, issue: u32) { - let content = format!(include_str!("stub-issue.md"), name = name, issue = issue); +fn generate_stub_issue(path: &Path, name: &str, issue: u32, description: &str) { + let content = format!( + include_str!("stub-issue.md"), + name = name, + issue = issue, + description = description + ); t!(write(path, content), path); } -fn generate_stub_no_issue(path: &Path, name: &str) { - let content = format!(include_str!("stub-no-issue.md"), name = name); +fn generate_stub_no_issue(path: &Path, name: &str, description: &str) { + let content = format!(include_str!("stub-no-issue.md"), name = name, description = description); t!(write(path, content), path); } @@ -30,8 +35,12 @@ fn set_to_summary_str(set: &BTreeSet, dir: &str) -> String { fn generate_summary(path: &Path, lang_features: &Features, lib_features: &Features) { let compiler_flags = collect_unstable_book_section_file_names(&path.join("src/compiler-flags")); + let compiler_env_vars = + collect_unstable_book_section_file_names(&path.join("src/compiler-environment-variables")); let compiler_flags_str = set_to_summary_str(&compiler_flags, "compiler-flags"); + let compiler_env_vars_str = + set_to_summary_str(&compiler_env_vars, "compiler-environment-variables"); let unstable_lang_features = collect_unstable_feature_names(&lang_features); let unstable_lib_features = collect_unstable_feature_names(&lib_features); @@ -42,6 +51,7 @@ fn generate_summary(path: &Path, lang_features: &Features, lib_features: &Featur let summary_path = path.join("src/SUMMARY.md"); let content = format!( include_str!("SUMMARY.md"), + compiler_env_vars = compiler_env_vars_str, compiler_flags = compiler_flags_str, language_features = lang_features_str, library_features = lib_features_str @@ -58,11 +68,17 @@ fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) { let file_name = format!("{feature_name}.md"); let out_file_path = out.join(&file_name); let feature = &features[&feature_name_underscore]; + let description = feature.description.as_deref().unwrap_or_default(); if let Some(issue) = feature.tracking_issue { - generate_stub_issue(&out_file_path, &feature_name_underscore, issue.get()); + generate_stub_issue( + &out_file_path, + &feature_name_underscore, + issue.get(), + &description, + ); } else { - generate_stub_no_issue(&out_file_path, &feature_name_underscore); + generate_stub_no_issue(&out_file_path, &feature_name_underscore, &description); } } } diff --git a/src/tools/unstable-book-gen/src/stub-issue.md b/src/tools/unstable-book-gen/src/stub-issue.md index 8698fb7278f6..f1e91b4ac172 100644 --- a/src/tools/unstable-book-gen/src/stub-issue.md +++ b/src/tools/unstable-book-gen/src/stub-issue.md @@ -1,5 +1,7 @@ # `{name}` +{description} + The tracking issue for this feature is: [#{issue}] [#{issue}]: https://github.com/rust-lang/rust/issues/{issue} diff --git a/src/tools/unstable-book-gen/src/stub-no-issue.md b/src/tools/unstable-book-gen/src/stub-no-issue.md index 3da140633d0f..3674d0048aeb 100644 --- a/src/tools/unstable-book-gen/src/stub-no-issue.md +++ b/src/tools/unstable-book-gen/src/stub-no-issue.md @@ -1,5 +1,7 @@ # `{name}` +{description} + This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ diff --git a/tests/assembly/aarch64-xray.rs b/tests/assembly/aarch64-xray.rs new file mode 100644 index 000000000000..d5ee01118439 --- /dev/null +++ b/tests/assembly/aarch64-xray.rs @@ -0,0 +1,25 @@ +//@ assembly-output: emit-asm +//@ compile-flags: -Zinstrument-xray=always + +//@ revisions: aarch64-linux +//@[aarch64-linux] compile-flags: --target=aarch64-unknown-linux-gnu +//@[aarch64-linux] needs-llvm-components: aarch64 +//@[aarch64-linux] only-aarch64-unknown-linux-gnu + +//@ revisions: aarch64-darwin +//@[aarch64-darwin] compile-flags: --target=aarch64-apple-darwin +//@[aarch64-darwin] needs-llvm-components: aarch64 +//@[aarch64-darwin] only-aarch64-apple-darwin + +#![crate_type = "lib"] + +// CHECK-LABEL: xray_func: +#[no_mangle] +pub fn xray_func() { + // CHECK: nop + + std::hint::black_box(()); + + // CHECK: b #32 + // CHECK-NEXT: nop +} diff --git a/tests/assembly/asm/aarch64-types.rs b/tests/assembly/asm/aarch64-types.rs index ad2770d43e37..b7abeb022986 100644 --- a/tests/assembly/asm/aarch64-types.rs +++ b/tests/assembly/asm/aarch64-types.rs @@ -86,12 +86,11 @@ pub unsafe fn sym_static() { // Regression test for #75761 // CHECK-LABEL: {{("#)?}}issue_75761{{"?}} -// aarch64: str {{.*}}x30 -// arm64ec: stp {{.*}}x30 +// x29 holds the frame pointer, right next to x30, so ldp/stp happens sometimes +// CHECK: st[[MAY_PAIR:(r|p).*]]x30 // CHECK: //APP // CHECK: //NO_APP -// aarch64: ldr {{.*}}x30 -// arm64ec: ldp {{.*}}x30 +// CHECK: ld[[MAY_PAIR]]x30 #[no_mangle] pub unsafe fn issue_75761() { asm!("", out("v0") _, out("x30") _); diff --git a/tests/assembly/x86_64-xray.rs b/tests/assembly/x86_64-xray.rs new file mode 100644 index 000000000000..4cf3e8cda13c --- /dev/null +++ b/tests/assembly/x86_64-xray.rs @@ -0,0 +1,25 @@ +//@ assembly-output: emit-asm +//@ compile-flags: -Zinstrument-xray=always -Cllvm-args=-x86-asm-syntax=intel + +//@ revisions: x86_64-linux +//@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu +//@[x86_64-linux] needs-llvm-components: x86 +//@[x86_64-linux] only-x86_64-unknown-linux-gnu + +//@ revisions: x86_64-darwin +//@[x86_64-darwin] compile-flags: --target=x86_64-apple-darwin +//@[x86_64-darwin] needs-llvm-components: x86 +//@[x86_64-darwin] only-x86_64-apple-darwin + +#![crate_type = "lib"] + +// CHECK-LABEL: xray_func: +#[no_mangle] +pub fn xray_func() { + // CHECK: nop word ptr [rax + rax + 512] + + std::hint::black_box(()); + + // CHECK: ret + // CHECK-NEXT: nop word ptr cs:[rax + rax + 512] +} diff --git a/tests/codegen/align-byval-alignment-mismatch.rs b/tests/codegen/align-byval-alignment-mismatch.rs index 46cfb2972df3..c69fc2de9d25 100644 --- a/tests/codegen/align-byval-alignment-mismatch.rs +++ b/tests/codegen/align-byval-alignment-mismatch.rs @@ -2,9 +2,10 @@ //@ add-core-stubs //@ revisions:i686-linux x86_64-linux -//@[i686-linux] compile-flags: --target i686-unknown-linux-gnu -C panic=abort +//@ compile-flags: -Cno-prepopulate-passes -Copt-level=1 -Cpanic=abort +//@[i686-linux] compile-flags: --target i686-unknown-linux-gnu //@[i686-linux] needs-llvm-components: x86 -//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu -C panic=abort +//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64-linux] needs-llvm-components: x86 // Tests that we correctly copy arguments into allocas when the alignment of the byval argument @@ -54,8 +55,10 @@ extern "C" { pub unsafe fn rust_to_c_increases_alignment(x: Align1) { // i686-linux: start: // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca [48 x i8], align 4 + // i686-linux-NEXT: call void @llvm.lifetime.start.p0(i64 48, ptr {{.*}}[[ALLOCA]]) // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 4 {{.*}}[[ALLOCA]], ptr {{.*}}align 1 {{.*}}%x // i686-linux-NEXT: call void @extern_c_align1({{.+}} [[ALLOCA]]) + // i686-linux-NEXT: call void @llvm.lifetime.end.p0(i64 48, ptr {{.*}}[[ALLOCA]]) // x86_64-linux: start: // x86_64-linux-NEXT: call void @extern_c_align1 diff --git a/tests/codegen/autodiff/batched.rs b/tests/codegen/autodiff/batched.rs index e00471164055..d27aed50e6cc 100644 --- a/tests/codegen/autodiff/batched.rs +++ b/tests/codegen/autodiff/batched.rs @@ -11,11 +11,11 @@ #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_forward; -#[autodiff(d_square3, Forward, Dual, DualOnly)] -#[autodiff(d_square2, Forward, 4, Dual, DualOnly)] -#[autodiff(d_square1, Forward, 4, Dual, Dual)] +#[autodiff_forward(d_square3, Dual, DualOnly)] +#[autodiff_forward(d_square2, 4, Dual, DualOnly)] +#[autodiff_forward(d_square1, 4, Dual, Dual)] #[no_mangle] fn square(x: &f32) -> f32 { x * x diff --git a/tests/codegen/autodiff/generic.rs b/tests/codegen/autodiff/generic.rs new file mode 100644 index 000000000000..2f674079be02 --- /dev/null +++ b/tests/codegen/autodiff/generic.rs @@ -0,0 +1,42 @@ +//@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[autodiff_reverse(d_square, Duplicated, Active)] +fn square + Copy>(x: &T) -> T { + *x * *x +} + +// Ensure that `d_square::` code is generated even if `square::` was never called +// +// CHECK: ; generic::square +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define internal {{.*}} double +// CHECK-NEXT: start: +// CHECK-NOT: ret +// CHECK: fmul double + +// Ensure that `d_square::` code is generated +// +// CHECK: ; generic::square +// CHECK-NEXT: ; Function Attrs: {{.*}} +// CHECK-NEXT: define internal {{.*}} float +// CHECK-NEXT: start: +// CHECK-NOT: ret +// CHECK: fmul float + +fn main() { + let xf32: f32 = std::hint::black_box(3.0); + let xf64: f64 = std::hint::black_box(3.0); + + let outputf32 = square::(&xf32); + assert_eq!(9.0, outputf32); + + let mut df_dxf64: f64 = std::hint::black_box(0.0); + + let output_f64 = d_square::(&xf64, &mut df_dxf64, 1.0); + assert_eq!(6.0, df_dxf64); +} diff --git a/tests/codegen/autodiff/identical_fnc.rs b/tests/codegen/autodiff/identical_fnc.rs index 1c3277f52b48..1c25b3d09ab0 100644 --- a/tests/codegen/autodiff/identical_fnc.rs +++ b/tests/codegen/autodiff/identical_fnc.rs @@ -11,14 +11,14 @@ // identical function calls in the LLVM-IR, while having two different calls in the Rust code. #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] fn square(x: &f64) -> f64 { x * x } -#[autodiff(d_square2, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square2, Duplicated, Active)] fn square2(x: &f64) -> f64 { x * x } diff --git a/tests/codegen/autodiff/inline.rs b/tests/codegen/autodiff/inline.rs index e90faa4aa380..65bed170207c 100644 --- a/tests/codegen/autodiff/inline.rs +++ b/tests/codegen/autodiff/inline.rs @@ -4,9 +4,9 @@ #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] fn square(x: &f64) -> f64 { x * x } diff --git a/tests/codegen/autodiff/scalar.rs b/tests/codegen/autodiff/scalar.rs index 85358f5fcb69..096b4209e84a 100644 --- a/tests/codegen/autodiff/scalar.rs +++ b/tests/codegen/autodiff/scalar.rs @@ -3,9 +3,9 @@ //@ needs-enzyme #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] #[no_mangle] fn square(x: &f64) -> f64 { x * x diff --git a/tests/codegen/autodiff/sret.rs b/tests/codegen/autodiff/sret.rs index 5ead90041edc..d2fa85e3e378 100644 --- a/tests/codegen/autodiff/sret.rs +++ b/tests/codegen/autodiff/sret.rs @@ -9,10 +9,10 @@ #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; #[no_mangle] -#[autodiff(df, Reverse, Active, Active, Active)] +#[autodiff_reverse(df, Active, Active, Active)] fn primal(x: f32, y: f32) -> f64 { (x * x * y) as f64 } diff --git a/tests/codegen/call-tmps-lifetime.rs b/tests/codegen/call-tmps-lifetime.rs new file mode 100644 index 000000000000..7b7b6e17bddd --- /dev/null +++ b/tests/codegen/call-tmps-lifetime.rs @@ -0,0 +1,68 @@ +// Test that temporary allocas used for call arguments have their lifetimes described by +// intrinsics. +// +//@ add-core-stubs +//@ compile-flags: -Copt-level=1 -Cno-prepopulate-passes --crate-type=lib --target i686-unknown-linux-gnu +//@ needs-llvm-components: x86 +#![feature(no_core)] +#![no_std] +#![no_core] +extern crate minicore; +use minicore::*; + +// Const operand. Regression test for #98156. +// +// CHECK-LABEL: define void @const_indirect( +// CHECK-NEXT: start: +// CHECK-NEXT: [[B:%.*]] = alloca +// CHECK-NEXT: [[A:%.*]] = alloca +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4096, ptr [[A]]) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 {{.*}}, i32 4096, i1 false) +// CHECK-NEXT: call void %h(ptr {{.*}} [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4096, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4096, ptr [[B]]) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[B]], ptr align 4 {{.*}}, i32 4096, i1 false) +// CHECK-NEXT: call void %h(ptr {{.*}} [[B]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4096, ptr [[B]]) +#[no_mangle] +pub fn const_indirect(h: extern "C" fn([u32; 1024])) { + const C: [u32; 1024] = [0; 1024]; + h(C); + h(C); +} + +#[repr(C)] +pub struct Str { + pub ptr: *const u8, + pub len: usize, +} + +// Pair of immediates. Regression test for #132014. +// +// CHECK-LABEL: define void @immediate_indirect(ptr {{.*}}%s.0, i32 {{.*}}%s.1, ptr {{.*}}%g) +// CHECK-NEXT: start: +// CHECK-NEXT: [[A:%.*]] = alloca +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A]]) +// CHECK-NEXT: store ptr %s.0, ptr [[A]] +// CHECK-NEXT: [[B:%.]] = getelementptr inbounds i8, ptr [[A]], i32 4 +// CHECK-NEXT: store i32 %s.1, ptr [[B]] +// CHECK-NEXT: call void %g(ptr {{.*}} [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[A]]) +#[no_mangle] +pub fn immediate_indirect(s: Str, g: extern "C" fn(Str)) { + g(s); +} + +// Indirect argument with a higher alignment requirement than the type's. +// +// CHECK-LABEL: define void @align_indirect(ptr{{.*}} align 1{{.*}} %a, ptr{{.*}} %fun) +// CHECK-NEXT: start: +// CHECK-NEXT: [[A:%.*]] = alloca [1024 x i8], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 1024, ptr [[A]]) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 1 %a, i32 1024, i1 false) +// CHECK-NEXT: call void %fun(ptr {{.*}} [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 1024, ptr [[A]]) +#[no_mangle] +pub fn align_indirect(a: [u8; 1024], fun: extern "C" fn([u8; 1024])) { + fun(a); +} diff --git a/tests/codegen/enum/enum-u128.rs b/tests/codegen/enum/enum-u128.rs index ecdff3c5ce31..2676669f3e30 100644 --- a/tests/codegen/enum/enum-u128.rs +++ b/tests/codegen/enum/enum-u128.rs @@ -13,9 +13,6 @@ // CHECK: {{.*}}DIEnumerator{{.*}}name: "Hi",{{.*}}value: 18446744073709551616,{{.*}} // CHECK: {{.*}}DIEnumerator{{.*}}name: "Bar",{{.*}}value: 18446745000000000123,{{.*}} -#![allow(incomplete_features)] -#![feature(repr128)] - #[repr(u128)] pub enum Foo { Lo, diff --git a/tests/codegen/frame-pointer.rs b/tests/codegen/frame-pointer.rs index 1f7c9a59c988..23989653fa8e 100644 --- a/tests/codegen/frame-pointer.rs +++ b/tests/codegen/frame-pointer.rs @@ -26,8 +26,10 @@ pub fn peach(x: u32) -> u32 { // CHECK: attributes [[PEACH_ATTRS]] = { // x64-linux-NOT: {{.*}}"frame-pointer"{{.*}} -// aarch64-linux-NOT: {{.*}}"frame-pointer"{{.*}} // x64-apple-SAME: {{.*}}"frame-pointer"="all" // force-SAME: {{.*}}"frame-pointer"="all" +// +// AAPCS64 demands frame pointers: +// aarch64-linux-SAME: {{.*}}"frame-pointer"="non-leaf" // aarch64-apple-SAME: {{.*}}"frame-pointer"="non-leaf" // CHECK-SAME: } diff --git a/tests/codegen/issues/issue-98156-const-arg-temp-lifetime.rs b/tests/codegen/issues/issue-98156-const-arg-temp-lifetime.rs deleted file mode 100644 index aecb81caf22b..000000000000 --- a/tests/codegen/issues/issue-98156-const-arg-temp-lifetime.rs +++ /dev/null @@ -1,27 +0,0 @@ -// This test checks that temporaries for indirectly-passed arguments get lifetime markers. - -//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes -Zmir-opt-level=0 - -#![crate_type = "lib"] - -extern "Rust" { - fn f(x: [u8; 1024]); -} - -const A: [u8; 1024] = [0; 1024]; - -// CHECK-LABEL: @const_arg_indirect -#[no_mangle] -pub unsafe fn const_arg_indirect() { - // Ensure that the live ranges for the two argument temporaries don't overlap. - - // CHECK: call void @llvm.lifetime.start - // CHECK: call void @f - // CHECK: call void @llvm.lifetime.end - // CHECK: call void @llvm.lifetime.start - // CHECK: call void @f - // CHECK: call void @llvm.lifetime.end - - f(A); - f(A); -} diff --git a/tests/codegen/option-niche-eq.rs b/tests/codegen/option-niche-eq.rs index 9c5ed9ce57a5..3900cb79aa2a 100644 --- a/tests/codegen/option-niche-eq.rs +++ b/tests/codegen/option-niche-eq.rs @@ -1,4 +1,7 @@ +//@ revisions: REGULAR LLVM21 +//@ min-llvm-version: 20 //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled +//@ [LLVM21] min-llvm-version: 21 #![crate_type = "lib"] extern crate core; @@ -24,6 +27,18 @@ pub fn non_zero_signed_eq(l: Option>, r: Option>) -> b l == r } +// FIXME(#49892) +// This currently relies on a manual implementation of `PartialOrd`/`Ord` for `Option` +// Once LLVM is better able to optimize this pattern, we can return to using a derive. +// CHECK-LABEL: @non_zero_ord +#[no_mangle] +pub fn non_zero_ord(a: Option>, b: Option>) -> bool { + // CHECK: start: + // CHECK-NEXT: icmp ult i32 + // CHECK-NEXT: ret i1 + a < b +} + // CHECK-LABEL: @non_null_eq #[no_mangle] pub fn non_null_eq(l: Option>, r: Option>) -> bool { @@ -62,12 +77,11 @@ pub fn niche_eq(l: Option, r: Option) -> bool { l == r } -// FIXME: This should work too -// // FIXME-CHECK-LABEL: @bool_eq -// #[no_mangle] -// pub fn bool_eq(l: Option, r: Option) -> bool { -// // FIXME-CHECK: start: -// // FIXME-CHECK-NEXT: icmp eq i8 -// // FIXME-CHECK-NEXT: ret i1 -// l == r -// } +// LLVM21-LABEL: @bool_eq +#[no_mangle] +pub fn bool_eq(l: Option, r: Option) -> bool { + // LLVM21: start: + // LLVM21-NEXT: icmp eq i8 + // LLVM21-NEXT: ret i1 + l == r +} diff --git a/tests/codegen/option-niche-unfixed/option-nonzero-eq.rs b/tests/codegen/option-niche-unfixed/option-nonzero-eq.rs new file mode 100644 index 000000000000..308856cfb7e9 --- /dev/null +++ b/tests/codegen/option-niche-unfixed/option-nonzero-eq.rs @@ -0,0 +1,24 @@ +//@ should-fail +//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled +//! FIXME(#49892) +//! Test that the derived implementation of `PartialEq` for `Option` is not fully +//! optimized by LLVM. If this starts passing, the test and manual impl should +//! be removed. +#![crate_type = "lib"] + +use std::num::NonZero; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum Option { + None, + Some(T), +} + +// CHECK-LABEL: @non_zero_eq +#[no_mangle] +pub fn non_zero_eq(l: Option>, r: Option>) -> bool { + // CHECK: start: + // CHECK-NEXT: icmp eq i32 + // CHECK-NEXT: ret i1 + l == r +} diff --git a/tests/codegen/thread-local.rs b/tests/codegen/thread-local.rs index 9ce34473b915..41df8c9be1b9 100644 --- a/tests/codegen/thread-local.rs +++ b/tests/codegen/thread-local.rs @@ -14,13 +14,14 @@ use std::cell::Cell; thread_local!(static A: Cell = const { Cell::new(1) }); -// CHECK: [[TLS_AUX:@.+]] = external thread_local local_unnamed_addr global i64 -// CHECK: [[TLS:@.+]] = internal thread_local unnamed_addr global +// CHECK: [[TLS_AUX:@.+]] = external thread_local{{.*}} global i64 +// CHECK: [[TLS:@.+]] = internal thread_local{{.*}} global // CHECK-LABEL: @get #[no_mangle] fn get() -> u32 { - // CHECK: [[RET_0:%.+]] = load i32, {{.*}}[[TLS]]{{.*}} + // CHECK: [[PTR:%.+]] = tail call {{.*}} ptr @llvm.threadlocal.address.p0(ptr [[TLS]]) + // CHECK-NEXT: [[RET_0:%.+]] = load i32, ptr [[PTR]] // CHECK-NEXT: ret i32 [[RET_0]] A.with(|a| a.get()) } @@ -28,7 +29,8 @@ fn get() -> u32 { // CHECK-LABEL: @set #[no_mangle] fn set(v: u32) { - // CHECK: store i32 %0, {{.*}}[[TLS]]{{.*}} + // CHECK: [[PTR:%.+]] = tail call {{.*}} ptr @llvm.threadlocal.address.p0(ptr [[TLS]]) + // CHECK-NEXT: store i32 %0, ptr [[PTR]] // CHECK-NEXT: ret void A.with(|a| a.set(v)) } @@ -36,7 +38,8 @@ fn set(v: u32) { // CHECK-LABEL: @get_aux #[no_mangle] fn get_aux() -> u64 { - // CHECK: [[RET_1:%.+]] = load i64, {{.*}}[[TLS_AUX]] + // CHECK: [[PTR:%.+]] = tail call {{.*}} ptr @llvm.threadlocal.address.p0(ptr [[TLS_AUX]]) + // CHECK-NEXT: [[RET_1:%.+]] = load i64, ptr [[PTR]] // CHECK-NEXT: ret i64 [[RET_1]] aux::A.with(|a| a.get()) } @@ -44,7 +47,8 @@ fn get_aux() -> u64 { // CHECK-LABEL: @set_aux #[no_mangle] fn set_aux(v: u64) { - // CHECK: store i64 %0, {{.*}}[[TLS_AUX]] + // CHECK: [[PTR:%.+]] = tail call {{.*}} ptr @llvm.threadlocal.address.p0(ptr [[TLS_AUX]]) + // CHECK-NEXT: store i64 %0, ptr [[PTR]] // CHECK-NEXT: ret void aux::A.with(|a| a.set(v)) } diff --git a/tests/crashes/113379.rs b/tests/crashes/113379.rs deleted file mode 100644 index 7163cbc39342..000000000000 --- a/tests/crashes/113379.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ known-bug: #113379 - -async fn f999() -> Vec { - 'b: { - continue 'b; - } -} diff --git a/tests/crashes/121623.rs b/tests/crashes/121623.rs deleted file mode 100644 index 3c01a7f452ca..000000000000 --- a/tests/crashes/121623.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #121623 -fn main() { - match () { - _ => 'b: { - continue 'b; - } - } -} diff --git a/tests/crashes/122704.rs b/tests/crashes/122704.rs deleted file mode 100644 index d6c07be83188..000000000000 --- a/tests/crashes/122704.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ known-bug: #122704 -use std::any::Any; - -pub struct Foo { - bar: Box Fn(&'a usize) -> Box>, -} - -impl Foo { - pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - self.bar = Box::new(|baz| Box::new(f(baz))); - } -} - -fn main() {} diff --git a/tests/crashes/133199.rs b/tests/crashes/133199.rs deleted file mode 100644 index 76535fa83a6d..000000000000 --- a/tests/crashes/133199.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: #133199 -//@ aux-build: aux133199.rs - -extern crate aux133199; - -use aux133199::FixedBitSet; - -fn main() { - FixedBitSet::<7>::new(); - //~^ ERROR -} diff --git a/tests/crashes/136894.rs b/tests/crashes/136894.rs deleted file mode 100644 index 26bbb78717e1..000000000000 --- a/tests/crashes/136894.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #136894 -#![feature(generic_const_exprs)] -#![crate_type = "lib"] -#![allow(incomplete_features, dead_code)] - -struct X([(); f::()]) where [(); f::()]:; - -const fn f() -> usize { panic!() } diff --git a/tests/crashes/137813.rs b/tests/crashes/137813.rs deleted file mode 100644 index 5d205ee53312..000000000000 --- a/tests/crashes/137813.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ known-bug: #137813 -trait AssocConst { - const A: u8; -} - -impl AssocConst for (T,) { - const A: u8 = 0; -} - -trait Trait {} - -impl Trait for () where (U,): AssocConst {} - -fn foo() -where - (): Trait, -{ -} diff --git a/tests/crashes/139905.rs b/tests/crashes/139905.rs new file mode 100644 index 000000000000..7da622aaabac --- /dev/null +++ b/tests/crashes/139905.rs @@ -0,0 +1,6 @@ +//@ known-bug: #139905 +trait a {} +impl a<{}> for () {} +trait c {} +impl c for () where (): a {} +impl c for () {} diff --git a/tests/crashes/140011.rs b/tests/crashes/140011.rs new file mode 100644 index 000000000000..b9d57a2822d2 --- /dev/null +++ b/tests/crashes/140011.rs @@ -0,0 +1,11 @@ +//@ known-bug: #140011 +//@compile-flags: -Wrust-2021-incompatible-closure-captures +enum b { + c(d), + e(f), +} +struct f; +fn g() { + let h; + || b::e(a) = h; +} diff --git a/tests/crashes/140099.rs b/tests/crashes/140099.rs new file mode 100644 index 000000000000..fca129100555 --- /dev/null +++ b/tests/crashes/140099.rs @@ -0,0 +1,6 @@ +//@ known-bug: #140099 +struct a; +impl From for a where for<'any> &'any mut (): Clone {} +fn b() -> Result<(), std::convert::Infallible> { + || -> Result<_, a> { b()? } +} diff --git a/tests/crashes/140100.rs b/tests/crashes/140100.rs new file mode 100644 index 000000000000..0836ffe2d92f --- /dev/null +++ b/tests/crashes/140100.rs @@ -0,0 +1,7 @@ +//@ known-bug: #140100 +fn a() +where + b: Sized, +{ + println!() +} diff --git a/tests/crashes/140123-2.rs b/tests/crashes/140123-2.rs new file mode 100644 index 000000000000..6ed10b9dcc34 --- /dev/null +++ b/tests/crashes/140123-2.rs @@ -0,0 +1,12 @@ +//@ known-bug: #140123 +//@ compile-flags: --crate-type lib + +trait Trait {} + +impl Trait for [(); 0] {} + +const ICE: [&mut dyn Trait; 2] = [const { empty_mut() }; 2]; + +const fn empty_mut() -> &'static mut [(); 0] { + &mut [] +} diff --git a/tests/crashes/140123-3.rs b/tests/crashes/140123-3.rs new file mode 100644 index 000000000000..a1dcd7fc39fa --- /dev/null +++ b/tests/crashes/140123-3.rs @@ -0,0 +1,10 @@ +//@ known-bug: #140123 +//@ compile-flags: --crate-type lib + +const ICE: [&mut [()]; 2] = [const { empty_mut() }; 2]; + +const fn empty_mut() -> &'static mut [()] { + unsafe { + std::slice::from_raw_parts_mut(std::ptr::dangling_mut(), 0) + } +} diff --git a/tests/crashes/140123-4.rs b/tests/crashes/140123-4.rs new file mode 100644 index 000000000000..39042d897ee2 --- /dev/null +++ b/tests/crashes/140123-4.rs @@ -0,0 +1,13 @@ +//@ known-bug: #140123 +//@ compile-flags: --crate-type lib + +const ICE: [&mut [(); 0]; 2] = [const { empty_mut() }; 2]; + +const fn empty_mut() -> &'static mut [(); 0] { + &mut [] +} +// https://github.com/rust-lang/rust/issues/140123#issuecomment-2820664450 +const ICE2: [&mut [(); 0]; 2] = [const { + let x = &mut []; + x +}; 2]; diff --git a/tests/crashes/140123.rs b/tests/crashes/140123.rs new file mode 100644 index 000000000000..337b5f3cef04 --- /dev/null +++ b/tests/crashes/140123.rs @@ -0,0 +1,10 @@ +//@ known-bug: #140123 +//@ compile-flags: --crate-type lib + +const OK: [&mut [()]; 2] = [empty_mut(), empty_mut()]; +const ICE: [&mut [()]; 2] = [const { empty_mut() }; 2]; + +// Any kind of fn call gets around E0764. +const fn empty_mut() -> &'static mut [()] { + &mut [] +} diff --git a/tests/crashes/140255.rs b/tests/crashes/140255.rs new file mode 100644 index 000000000000..6b0ec1718b0b --- /dev/null +++ b/tests/crashes/140255.rs @@ -0,0 +1,3 @@ +//@ known-bug: #140255 +#[unsafe(macro_use::VAR2)] +fn dead_code() {} diff --git a/tests/crashes/140275.rs b/tests/crashes/140275.rs new file mode 100644 index 000000000000..5ea04af0c8e0 --- /dev/null +++ b/tests/crashes/140275.rs @@ -0,0 +1,5 @@ +//@ known-bug: #140275 +#![feature(generic_const_exprs)] +trait T{} +trait V{} +impl T for [i32; N::<&mut V>] {} diff --git a/tests/crashes/140303.rs b/tests/crashes/140303.rs new file mode 100644 index 000000000000..43a20b5e58ed --- /dev/null +++ b/tests/crashes/140303.rs @@ -0,0 +1,22 @@ +//@ known-bug: #140303 +//@compile-flags: -Zvalidate-mir +use std::future::Future; +async fn a() -> impl Sized { + b(c) +} +async fn c(); // kaboom +fn b(e: d) -> impl Sized +where + d: f, +{ + || -> ::h { panic!() } +} +trait f { + type h; +} +impl f for d +where + d: Fn() -> g, + g: Future, +{ +} diff --git a/tests/crashes/140333.rs b/tests/crashes/140333.rs new file mode 100644 index 000000000000..cec1100e6ada --- /dev/null +++ b/tests/crashes/140333.rs @@ -0,0 +1,9 @@ +//@ known-bug: #140333 +fn a() -> impl b< + [c; { + struct d { + #[a] + bar: e, + } + }], +>; diff --git a/tests/crashes/140365.rs b/tests/crashes/140365.rs new file mode 100644 index 000000000000..809ceaf35a05 --- /dev/null +++ b/tests/crashes/140365.rs @@ -0,0 +1,8 @@ +//@ known-bug: #140365 +//@compile-flags: -C opt-level=1 -Zvalidate-mir +fn f() -> &'static str +where + Self: Sized, +{ + "" +} diff --git a/tests/crashes/140381.rs b/tests/crashes/140381.rs new file mode 100644 index 000000000000..439ca694d563 --- /dev/null +++ b/tests/crashes/140381.rs @@ -0,0 +1,16 @@ +//@ known-bug: #140381 +pub trait Foo {} +pub trait Lend { + type From<'a> + where + Self: 'a; + fn lend(from: Self::From<'_>) -> impl Foo>; +} + +impl Lend for (T, F) { + type From<'a> = (); + + fn lend(from: Self::From<'_>) -> impl Foo> { + from + } +} diff --git a/tests/crashes/140429.rs b/tests/crashes/140429.rs new file mode 100644 index 000000000000..041eaf86c5c3 --- /dev/null +++ b/tests/crashes/140429.rs @@ -0,0 +1,6 @@ +//@ known-bug: #140429 +//@ compile-flags: -Zlint-mir --crate-type lib +//@ edition:2024 + +#![feature(async_drop)] +async fn a(x: T) {} diff --git a/tests/crashes/140479.rs b/tests/crashes/140479.rs new file mode 100644 index 000000000000..ed3ca887546f --- /dev/null +++ b/tests/crashes/140479.rs @@ -0,0 +1,5 @@ +//@ known-bug: #140479 +macro_rules! a { ( $( { $ [ $b:c ] } )) => ( $(${ concat(d, $b)} ))} +fn e() { + a!({}) +} diff --git a/tests/crashes/140484.rs b/tests/crashes/140484.rs new file mode 100644 index 000000000000..92ec19843982 --- /dev/null +++ b/tests/crashes/140484.rs @@ -0,0 +1,14 @@ +//@ known-bug: #140484 +//@edition:2024 +#![feature(async_drop)] +use std::future::AsyncDrop; +struct a; +impl Drop for a { + fn b() {} +} +impl AsyncDrop for a { + type c; +} +async fn bar() { + a; +} diff --git a/tests/crashes/140500.rs b/tests/crashes/140500.rs new file mode 100644 index 000000000000..ee5b93ab8213 --- /dev/null +++ b/tests/crashes/140500.rs @@ -0,0 +1,14 @@ +//@ known-bug: #140500 + +#![feature(async_drop)] +use std::future::AsyncDrop; +struct a; +impl Drop for a { + fn b() {} +} +impl AsyncDrop for a { + fn c(d: impl Sized) {} +} +async fn bar() { + a; +} diff --git a/tests/crashes/140530.rs b/tests/crashes/140530.rs new file mode 100644 index 000000000000..7e0372a4bd86 --- /dev/null +++ b/tests/crashes/140530.rs @@ -0,0 +1,8 @@ +//@ known-bug: #140530 +//@ edition: 2024 + +#![feature(async_drop, gen_blocks)] +async gen fn a() { + _ = async {} +} +fn main() {} diff --git a/tests/crashes/140531.rs b/tests/crashes/140531.rs new file mode 100644 index 000000000000..f664481d4402 --- /dev/null +++ b/tests/crashes/140531.rs @@ -0,0 +1,7 @@ +//@ known-bug: #140531 +//@compile-flags: -Zlint-mir --crate-type lib +//@ edition:2024 +#![feature(async_drop)] +async fn call_once(f: impl AsyncFnOnce()) { + let fut = Box::pin(f()); +} diff --git a/tests/crashes/140571.rs b/tests/crashes/140571.rs new file mode 100644 index 000000000000..97fa1d8432dd --- /dev/null +++ b/tests/crashes/140571.rs @@ -0,0 +1,14 @@ +//@ known-bug: #140571 +pub trait IsVoid { + const IS_VOID: bool; +} +impl IsVoid for T { + default const IS_VOID: bool = false; +} +impl Maybe for () where T: NotVoid + ?Sized {} + +pub trait NotVoid {} +impl NotVoid for T where T: IsVoid + ?Sized {} + +pub trait Maybe {} +impl Maybe for T {} diff --git a/tests/crashes/140577.rs b/tests/crashes/140577.rs new file mode 100644 index 000000000000..21e6b1e1522f --- /dev/null +++ b/tests/crashes/140577.rs @@ -0,0 +1,32 @@ +//@ known-bug: #140577 +//@ compile-flags: -Znext-solver=globally +//@ edition:2021 + +use std::future::Future; +use std::pin::Pin; +trait Acquire { + type Connection; +} +impl Acquire for &'static () { + type Connection = (); +} +fn b() -> impl Future + Send { + let x: Pin + Send>> = todo!(); + x +} +fn main() { + async { + b::<&()>().await; + } + .aa(); +} + +impl Filter for F where F: Send {} + +trait Filter { + fn aa(self) + where + Self: Sized, + { + } +} diff --git a/tests/crashes/140609.rs b/tests/crashes/140609.rs new file mode 100644 index 000000000000..ee8a4bb30489 --- /dev/null +++ b/tests/crashes/140609.rs @@ -0,0 +1,13 @@ +//@ known-bug: #140609 +#![feature(with_negative_coherence)] +#![feature(generic_const_exprs)] +#![crate_type = "lib"] +trait Trait {} +struct A; + +trait C {} + +impl Trait for E where A<{ D <= 2 }>: FnOnce(&isize) {} +struct E; + +impl Trait for E where A<{ D <= 2 }>: C {} diff --git a/tests/crashes/140729.rs b/tests/crashes/140729.rs new file mode 100644 index 000000000000..a436ec58e8e8 --- /dev/null +++ b/tests/crashes/140729.rs @@ -0,0 +1,11 @@ +//@ known-bug: #140729 +#![feature(min_generic_const_args)] + +const C: usize = 0; +pub struct A {} +impl A { + fn fun1() {} +} +impl A { + fn fun1() {} +} diff --git a/tests/crashes/140850.rs b/tests/crashes/140850.rs new file mode 100644 index 000000000000..fd26097deda0 --- /dev/null +++ b/tests/crashes/140850.rs @@ -0,0 +1,7 @@ +//@ known-bug: #140850 +//@ compile-flags: -Zvalidate-mir +fn A() -> impl { + while A() {} + loop {} +} +fn main() {} diff --git a/tests/crashes/140860.rs b/tests/crashes/140860.rs new file mode 100644 index 000000000000..04da6bd832c3 --- /dev/null +++ b/tests/crashes/140860.rs @@ -0,0 +1,10 @@ +//@ known-bug: #140860 +#![feature(min_generic_const_args)] +#![feature(unsized_const_params)] +#![feature(with_negative_coherence, negative_impls)] +trait a < const b : &'static str> {} trait c {} struct d< e >(e); +impl c for e where e: a<""> {} +impl c for d {} +impl !a for e {} +const f : &str = ""; +fn main() {} diff --git a/tests/crashes/140891.rs b/tests/crashes/140891.rs new file mode 100644 index 000000000000..421919403eff --- /dev/null +++ b/tests/crashes/140891.rs @@ -0,0 +1,6 @@ +//@ known-bug: #140891 +struct A {} +impl Iterator for A { + fn next() -> [(); std::mem::size_of::>] {} +} +fn main() {} diff --git a/tests/crashes/141124.rs b/tests/crashes/141124.rs new file mode 100644 index 000000000000..38a2a55e1c4a --- /dev/null +++ b/tests/crashes/141124.rs @@ -0,0 +1,16 @@ +//@ known-bug: #141124 +struct S; +trait SimpleTrait {} +trait TraitAssoc { + type Assoc; +} + +impl TraitAssoc for T +where + T: SimpleTrait, +{ + type Assoc = <(T,) as TraitAssoc>::Assoc; +} +impl SimpleTrait for ::Assoc {} + +pub fn main() {} diff --git a/tests/debuginfo/by-value-non-immediate-argument.rs b/tests/debuginfo/by-value-non-immediate-argument.rs index 192f6efe7db6..5233b95f1f4a 100644 --- a/tests/debuginfo/by-value-non-immediate-argument.rs +++ b/tests/debuginfo/by-value-non-immediate-argument.rs @@ -2,6 +2,7 @@ //@ min-gdb-version: 13.0 //@ compile-flags:-g //@ ignore-windows-gnu: #128973 +//@ ignore-aarch64-unknown-linux-gnu (gdb tries to read from 0x0; FIXME: #128973) // === GDB TESTS =================================================================================== diff --git a/tests/debuginfo/msvc-pretty-enums.rs b/tests/debuginfo/msvc-pretty-enums.rs index 06bc25dc5d59..aa6629ef1e1b 100644 --- a/tests/debuginfo/msvc-pretty-enums.rs +++ b/tests/debuginfo/msvc-pretty-enums.rs @@ -231,8 +231,6 @@ // cdb-command: dx c_style_i128_d // cdb-check: c_style_i128_d : D [Type: enum2$] #![feature(rustc_attrs)] -#![feature(repr128)] -#![feature(arbitrary_enum_discriminant)] use std::num::NonZero; diff --git a/tests/debuginfo/multiline-calls.rs b/tests/debuginfo/multiline-calls.rs new file mode 100644 index 000000000000..724ad29729fd --- /dev/null +++ b/tests/debuginfo/multiline-calls.rs @@ -0,0 +1,35 @@ +//@ compile-flags:-g +//@ min-gdb-version: 16.0 + +// === GDB TESTS =================================================================================== + +// gdb-command: run +// gdb-check:[...]#break[...] +// gdb-command: up +// gdb-check:[...]zzz[...] + +// === LLDB TESTS ================================================================================== + +// lldb-command:run +// lldb-check:[...]#break[...] +// lldb-command: up +// lldb-check:[...]zzz[...] + +struct Foo; + +impl Foo { + fn bar(self) -> Foo { + println!("bar"); + self + } + fn baz(self) -> Foo { + println!("baz"); // #break + self + } +} + +fn main() { + let f = Foo; + f.bar() // aaa + .baz(); // zzz +} diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index 5fc77f95eaf7..8afb6ad250e0 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -1,34 +1,34 @@ // MIR for `address_of_reborrow` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] -| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send -| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] +| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send +| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] | 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] | 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] -| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send -| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] +| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send +| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] | 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] | 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] -| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send -| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] +| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send +| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] | 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send | 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] | 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] | diff --git a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir index 6e349a2a24ff..744553c7cd26 100644 --- a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir +++ b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir @@ -3,8 +3,8 @@ | User Type Annotations | 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test | 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test -| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test -| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test +| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test +| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/dataflow.main.maybe_init.borrowck.dot b/tests/mir-opt/dataflow.main.maybe_init.borrowck.dot deleted file mode 100644 index 7c7d8921fb36..000000000000 --- a/tests/mir-opt/dataflow.main.maybe_init.borrowck.dot +++ /dev/null @@ -1,6 +0,0 @@ -digraph graph_for_def_id_0_3 { - graph[fontname="Courier, monospace"]; - node[fontname="Courier, monospace"]; - edge[fontname="Courier, monospace"]; - bb_0[label=<
bb0
MIRSTATE
(on start){}
0_0 = const ()+_0
Treturn
(on end){_0}
>][shape="none"]; -} diff --git a/tests/mir-opt/dataflow.main.maybe_uninit.borrowck.dot b/tests/mir-opt/dataflow.main.maybe_uninit.borrowck.dot new file mode 100644 index 000000000000..258404b8da78 --- /dev/null +++ b/tests/mir-opt/dataflow.main.maybe_uninit.borrowck.dot @@ -0,0 +1,6 @@ +digraph graph_for_def_id_0_3 { + graph[fontname="Courier, monospace"]; + node[fontname="Courier, monospace"]; + edge[fontname="Courier, monospace"]; + bb_0[label=<
bb0
MIRSTATE
(on start){_0}
0_0 = const ()-_0
Treturn
(on end){}
>][shape="none"]; +} diff --git a/tests/mir-opt/dataflow.rs b/tests/mir-opt/dataflow.rs index 3a28f5d47b9a..5ed3da4c531a 100644 --- a/tests/mir-opt/dataflow.rs +++ b/tests/mir-opt/dataflow.rs @@ -2,5 +2,5 @@ // Test graphviz dataflow output //@ compile-flags: -Z dump-mir=main -Z dump-mir-dataflow -// EMIT_MIR dataflow.main.maybe_init.borrowck.dot +// EMIT_MIR dataflow.main.maybe_uninit.borrowck.dot fn main() {} diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff index 35eb4fbd106a..25ffff619e60 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff @@ -59,7 +59,7 @@ scope 10 (inlined ::allocate) { } } - scope 9 (inlined Layout::from_size_align_unchecked) { + scope 9 (inlined #[track_caller] Layout::from_size_align_unchecked) { let mut _19: bool; let _20: (); let mut _21: std::ptr::Alignment; diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff index 4427a5fcc7de..839b53e3b0b3 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff @@ -59,7 +59,7 @@ scope 10 (inlined ::allocate) { } } - scope 9 (inlined Layout::from_size_align_unchecked) { + scope 9 (inlined #[track_caller] Layout::from_size_align_unchecked) { let mut _19: bool; let _20: (); let mut _21: std::ptr::Alignment; diff --git a/tests/mir-opt/enum_opt.rs b/tests/mir-opt/enum_opt.rs index e42be8ac06dc..81390567c2bb 100644 --- a/tests/mir-opt/enum_opt.rs +++ b/tests/mir-opt/enum_opt.rs @@ -3,8 +3,6 @@ // EMIT_MIR_FOR_EACH_BIT_WIDTH //@ compile-flags: -Zunsound-mir-opts -Zdump-mir-exclude-alloc-bytes -#![feature(arbitrary_enum_discriminant, repr128)] - // Tests that an enum with a variant with no data gets correctly transformed. pub enum NoData { Large([u8; 8196]), diff --git a/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff b/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff new file mode 100644 index 000000000000..fd0478252811 --- /dev/null +++ b/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff @@ -0,0 +1,18 @@ +- // MIR for `repeat_local` before GVN ++ // MIR for `repeat_local` after GVN + + fn repeat_local(_1: usize, _2: usize, _3: i32) -> i32 { + let mut _0: i32; + let mut _4: [i32; 5]; + let mut _5: &i32; + + bb0: { + _4 = [copy _3; 5]; + _5 = &_4[_1]; + _1 = copy _2; +- _0 = copy (*_5); ++ _0 = copy _3; + return; + } + } + diff --git a/tests/mir-opt/gvn_repeat.repeat_place.GVN.diff b/tests/mir-opt/gvn_repeat.repeat_place.GVN.diff new file mode 100644 index 000000000000..e490925bc119 --- /dev/null +++ b/tests/mir-opt/gvn_repeat.repeat_place.GVN.diff @@ -0,0 +1,17 @@ +- // MIR for `repeat_place` before GVN ++ // MIR for `repeat_place` after GVN + + fn repeat_place(_1: usize, _2: usize, _3: &i32) -> i32 { + let mut _0: i32; + let mut _4: [i32; 5]; + let mut _5: &i32; + + bb0: { + _4 = [copy (*_3); 5]; + _5 = &_4[_1]; + _1 = copy _2; + _0 = copy (*_5); + return; + } + } + diff --git a/tests/mir-opt/gvn_repeat.rs b/tests/mir-opt/gvn_repeat.rs new file mode 100644 index 000000000000..bbbb2a7ccbaf --- /dev/null +++ b/tests/mir-opt/gvn_repeat.rs @@ -0,0 +1,49 @@ +//@ test-mir-pass: GVN + +#![feature(custom_mir, core_intrinsics)] + +// Check that we do not introduce out-of-bounds access. + +use std::intrinsics::mir::*; + +// EMIT_MIR gvn_repeat.repeat_place.GVN.diff +#[custom_mir(dialect = "runtime")] +pub fn repeat_place(mut idx1: usize, idx2: usize, val: &i32) -> i32 { + // CHECK-LABEL: fn repeat_place( + // CHECK: let mut [[ELEM:.*]]: &i32; + // CHECK: _0 = copy (*[[ELEM]]) + mir! { + let array; + let elem; + { + array = [*val; 5]; + elem = &array[idx1]; + idx1 = idx2; + RET = *elem; + Return() + } + } +} + +// EMIT_MIR gvn_repeat.repeat_local.GVN.diff +#[custom_mir(dialect = "runtime")] +pub fn repeat_local(mut idx1: usize, idx2: usize, val: i32) -> i32 { + // CHECK-LABEL: fn repeat_local( + // CHECK: _0 = copy _3 + mir! { + let array; + let elem; + { + array = [val; 5]; + elem = &array[idx1]; + idx1 = idx2; + RET = *elem; + Return() + } + } +} + +fn main() { + assert_eq!(repeat_place(0, 5, &0), 0); + assert_eq!(repeat_local(0, 5, 0), 0); +} diff --git a/tests/mir-opt/inline/unchecked_shifts.rs b/tests/mir-opt/inline/unchecked_shifts.rs index 3c4e73bf7ea6..122f099da4b7 100644 --- a/tests/mir-opt/inline/unchecked_shifts.rs +++ b/tests/mir-opt/inline/unchecked_shifts.rs @@ -11,7 +11,7 @@ // EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.mir pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 { // CHECK-LABEL: fn unchecked_shl_unsigned_smaller( - // CHECK: (inlined core::num::::unchecked_shl) + // CHECK: (inlined #[track_caller] core::num::::unchecked_shl) a.unchecked_shl(b) } @@ -19,6 +19,6 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 { // EMIT_MIR unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.mir pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 { // CHECK-LABEL: fn unchecked_shr_signed_bigger( - // CHECK: (inlined core::num::::unchecked_shr) + // CHECK: (inlined #[track_caller] core::num::::unchecked_shr) a.unchecked_shr(b) } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff index 39ba480d2033..813796657b24 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff @@ -7,7 +7,7 @@ let mut _0: u16; let mut _3: u16; let mut _4: u32; -+ scope 1 (inlined core::num::::unchecked_shl) { ++ scope 1 (inlined #[track_caller] core::num::::unchecked_shl) { + let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { + let mut _6: bool; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff index 5a758d357406..61fdb69f74b7 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff @@ -7,7 +7,7 @@ let mut _0: u16; let mut _3: u16; let mut _4: u32; -+ scope 1 (inlined core::num::::unchecked_shl) { ++ scope 1 (inlined #[track_caller] core::num::::unchecked_shl) { + let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { + let mut _6: bool; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir index 611273ab08d7..0fc7c4b7947e 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir @@ -4,7 +4,7 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 { debug a => _1; debug b => _2; let mut _0: u16; - scope 1 (inlined core::num::::unchecked_shl) { + scope 1 (inlined #[track_caller] core::num::::unchecked_shl) { scope 2 (inlined core::ub_checks::check_language_ub) { scope 3 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir index 611273ab08d7..0fc7c4b7947e 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir @@ -4,7 +4,7 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 { debug a => _1; debug b => _2; let mut _0: u16; - scope 1 (inlined core::num::::unchecked_shl) { + scope 1 (inlined #[track_caller] core::num::::unchecked_shl) { scope 2 (inlined core::ub_checks::check_language_ub) { scope 3 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff index a0caf141f2d0..5ea99e8301b8 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff @@ -7,7 +7,7 @@ let mut _0: i64; let mut _3: i64; let mut _4: u32; -+ scope 1 (inlined core::num::::unchecked_shr) { ++ scope 1 (inlined #[track_caller] core::num::::unchecked_shr) { + let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { + let mut _6: bool; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff index 633089e7b2a2..b13531ab148f 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff @@ -7,7 +7,7 @@ let mut _0: i64; let mut _3: i64; let mut _4: u32; -+ scope 1 (inlined core::num::::unchecked_shr) { ++ scope 1 (inlined #[track_caller] core::num::::unchecked_shr) { + let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { + let mut _6: bool; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir index f4ddd0bca04d..bef7fa7b1df7 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir @@ -4,7 +4,7 @@ fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 { debug a => _1; debug b => _2; let mut _0: i64; - scope 1 (inlined core::num::::unchecked_shr) { + scope 1 (inlined #[track_caller] core::num::::unchecked_shr) { scope 2 (inlined core::ub_checks::check_language_ub) { scope 3 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir index f4ddd0bca04d..bef7fa7b1df7 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir @@ -4,7 +4,7 @@ fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 { debug a => _1; debug b => _2; let mut _0: i64; - scope 1 (inlined core::num::::unchecked_shr) { + scope 1 (inlined #[track_caller] core::num::::unchecked_shr) { scope 2 (inlined core::ub_checks::check_language_ub) { scope 3 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff index a5986a4315a2..0119dd799704 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff @@ -9,7 +9,7 @@ + let mut _3: isize; + scope 2 { + } -+ scope 3 (inlined unreachable_unchecked) { ++ scope 3 (inlined #[track_caller] unreachable_unchecked) { + let _4: (); + scope 4 (inlined core::ub_checks::check_language_ub) { + let mut _5: bool; diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff index 12b03a6b6d92..d6a5eab1d6e9 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff @@ -9,7 +9,7 @@ + let mut _3: isize; + scope 2 { + } -+ scope 3 (inlined unreachable_unchecked) { ++ scope 3 (inlined #[track_caller] unreachable_unchecked) { + let _4: (); + scope 4 (inlined core::ub_checks::check_language_ub) { + let mut _5: bool; diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 66ab5e1b9622..b7b892c177c3 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -7,7 +7,7 @@ fn unwrap_unchecked(_1: Option) -> T { let mut _2: isize; scope 2 { } - scope 3 (inlined unreachable_unchecked) { + scope 3 (inlined #[track_caller] unreachable_unchecked) { scope 4 (inlined core::ub_checks::check_language_ub) { scope 5 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 66ab5e1b9622..b7b892c177c3 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -7,7 +7,7 @@ fn unwrap_unchecked(_1: Option) -> T { let mut _2: isize; scope 2 { } - scope 3 (inlined unreachable_unchecked) { + scope 3 (inlined #[track_caller] unreachable_unchecked) { scope 4 (inlined core::ub_checks::check_language_ub) { scope 5 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff index 82353a2d5404..2c9071e6e207 100644 --- a/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff +++ b/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff @@ -9,7 +9,7 @@ let mut _3: isize; scope 2 { } - scope 3 (inlined unreachable_unchecked) { + scope 3 (inlined #[track_caller] unreachable_unchecked) { let _4: (); scope 4 (inlined core::ub_checks::check_language_ub) { let mut _5: bool; diff --git a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff index 7f325245bce6..d73cc3d214e4 100644 --- a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff +++ b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff @@ -45,7 +45,7 @@ _9 = copy _10; _8 = move _9 as *mut i32 (PtrToPtr); StorageDead(_9); -- _3 = copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; +- _3 = std::intrinsics::copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; + copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); + goto -> bb1; } diff --git a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff index 7f325245bce6..d73cc3d214e4 100644 --- a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff +++ b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff @@ -45,7 +45,7 @@ _9 = copy _10; _8 = move _9 as *mut i32 (PtrToPtr); StorageDead(_9); -- _3 = copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; +- _3 = std::intrinsics::copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; + copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); + goto -> bb1; } diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 5afddc5ff730..1e4f20241947 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -1,9 +1,11 @@ //@ test-mir-pass: LowerIntrinsics // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -#![feature(core_intrinsics, intrinsics, rustc_attrs)] +#![feature(core_intrinsics)] #![crate_type = "lib"] +use std::intrinsics::copy_nonoverlapping; + // EMIT_MIR lower_intrinsics.wrapping.LowerIntrinsics.diff pub fn wrapping(a: i32, b: i32) { // CHECK-LABEL: fn wrapping( @@ -153,11 +155,6 @@ pub fn discriminant(t: T) { core::intrinsics::discriminant_value(&E::B); } -// Cannot use `std::intrinsics::copy_nonoverlapping` as that is a wrapper function -#[rustc_nounwind] -#[rustc_intrinsic] -unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); - // EMIT_MIR lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff pub fn f_copy_nonoverlapping() { // CHECK-LABEL: fn f_copy_nonoverlapping( @@ -266,3 +263,24 @@ pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug let _usize = ptr_metadata(b); let _vtable = ptr_metadata(c); } + +// EMIT_MIR lower_intrinsics.slice_get.LowerIntrinsics.diff +pub unsafe fn slice_get<'a, 'b>( + r: &'a [i8], + rm: &'b mut [i16], + p: *const [i32], + pm: *mut [i64], + i: usize, +) -> (&'a i8, &'b mut i16, *const i32, *mut i64) { + use std::intrinsics::slice_get_unchecked; + // CHECK: = &(*_{{[0-9]+}})[_{{[0-9]+}}] + // CHECK: = &mut (*_{{[0-9]+}})[_{{[0-9]+}}] + // CHECK: = &raw const (*_{{[0-9]+}})[_{{[0-9]+}}] + // CHECK: = &raw mut (*_{{[0-9]+}})[_{{[0-9]+}}] + ( + slice_get_unchecked(r, i), + slice_get_unchecked(rm, i), + slice_get_unchecked(p, i), + slice_get_unchecked(pm, i), + ) +} diff --git a/tests/mir-opt/lower_intrinsics.slice_get.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.slice_get.LowerIntrinsics.panic-abort.diff new file mode 100644 index 000000000000..d18bdc431683 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.slice_get.LowerIntrinsics.panic-abort.diff @@ -0,0 +1,85 @@ +- // MIR for `slice_get` before LowerIntrinsics ++ // MIR for `slice_get` after LowerIntrinsics + + fn slice_get(_1: &[i8], _2: &mut [i16], _3: *const [i32], _4: *mut [i64], _5: usize) -> (&i8, &mut i16, *const i32, *mut i64) { + debug r => _1; + debug rm => _2; + debug p => _3; + debug pm => _4; + debug i => _5; + let mut _0: (&i8, &mut i16, *const i32, *mut i64); + let mut _6: &i8; + let mut _7: &[i8]; + let mut _8: usize; + let mut _9: &mut i16; + let mut _10: &mut [i16]; + let mut _11: usize; + let mut _12: *const i32; + let mut _13: *const [i32]; + let mut _14: usize; + let mut _15: *mut i64; + let mut _16: *mut [i64]; + let mut _17: usize; + + bb0: { + StorageLive(_6); + StorageLive(_7); + _7 = copy _1; + StorageLive(_8); + _8 = copy _5; +- _6 = slice_get_unchecked::<&i8, &[i8], i8>(move _7, move _8) -> [return: bb1, unwind unreachable]; ++ _6 = &(*_7)[_8]; ++ goto -> bb1; + } + + bb1: { + StorageDead(_8); + StorageDead(_7); + StorageLive(_9); + StorageLive(_10); + _10 = move _2; + StorageLive(_11); + _11 = copy _5; +- _9 = slice_get_unchecked::<&mut i16, &mut [i16], i16>(move _10, move _11) -> [return: bb2, unwind unreachable]; ++ _9 = &mut (*_10)[_11]; ++ goto -> bb2; + } + + bb2: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = copy _3; + StorageLive(_14); + _14 = copy _5; +- _12 = slice_get_unchecked::<*const i32, *const [i32], i32>(move _13, move _14) -> [return: bb3, unwind unreachable]; ++ _12 = &raw const (*_13)[_14]; ++ goto -> bb3; + } + + bb3: { + StorageDead(_14); + StorageDead(_13); + StorageLive(_15); + StorageLive(_16); + _16 = copy _4; + StorageLive(_17); + _17 = copy _5; +- _15 = slice_get_unchecked::<*mut i64, *mut [i64], i64>(move _16, move _17) -> [return: bb4, unwind unreachable]; ++ _15 = &raw mut (*_16)[_17]; ++ goto -> bb4; + } + + bb4: { + StorageDead(_17); + StorageDead(_16); + _0 = (move _6, move _9, move _12, move _15); + StorageDead(_15); + StorageDead(_12); + StorageDead(_9); + StorageDead(_6); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.slice_get.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.slice_get.LowerIntrinsics.panic-unwind.diff new file mode 100644 index 000000000000..d18bdc431683 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.slice_get.LowerIntrinsics.panic-unwind.diff @@ -0,0 +1,85 @@ +- // MIR for `slice_get` before LowerIntrinsics ++ // MIR for `slice_get` after LowerIntrinsics + + fn slice_get(_1: &[i8], _2: &mut [i16], _3: *const [i32], _4: *mut [i64], _5: usize) -> (&i8, &mut i16, *const i32, *mut i64) { + debug r => _1; + debug rm => _2; + debug p => _3; + debug pm => _4; + debug i => _5; + let mut _0: (&i8, &mut i16, *const i32, *mut i64); + let mut _6: &i8; + let mut _7: &[i8]; + let mut _8: usize; + let mut _9: &mut i16; + let mut _10: &mut [i16]; + let mut _11: usize; + let mut _12: *const i32; + let mut _13: *const [i32]; + let mut _14: usize; + let mut _15: *mut i64; + let mut _16: *mut [i64]; + let mut _17: usize; + + bb0: { + StorageLive(_6); + StorageLive(_7); + _7 = copy _1; + StorageLive(_8); + _8 = copy _5; +- _6 = slice_get_unchecked::<&i8, &[i8], i8>(move _7, move _8) -> [return: bb1, unwind unreachable]; ++ _6 = &(*_7)[_8]; ++ goto -> bb1; + } + + bb1: { + StorageDead(_8); + StorageDead(_7); + StorageLive(_9); + StorageLive(_10); + _10 = move _2; + StorageLive(_11); + _11 = copy _5; +- _9 = slice_get_unchecked::<&mut i16, &mut [i16], i16>(move _10, move _11) -> [return: bb2, unwind unreachable]; ++ _9 = &mut (*_10)[_11]; ++ goto -> bb2; + } + + bb2: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = copy _3; + StorageLive(_14); + _14 = copy _5; +- _12 = slice_get_unchecked::<*const i32, *const [i32], i32>(move _13, move _14) -> [return: bb3, unwind unreachable]; ++ _12 = &raw const (*_13)[_14]; ++ goto -> bb3; + } + + bb3: { + StorageDead(_14); + StorageDead(_13); + StorageLive(_15); + StorageLive(_16); + _16 = copy _4; + StorageLive(_17); + _17 = copy _5; +- _15 = slice_get_unchecked::<*mut i64, *mut [i64], i64>(move _16, move _17) -> [return: bb4, unwind unreachable]; ++ _15 = &raw mut (*_16)[_17]; ++ goto -> bb4; + } + + bb4: { + StorageDead(_17); + StorageDead(_16); + _0 = (move _6, move _9, move _12, move _15); + StorageDead(_15); + StorageDead(_12); + StorageDead(_9); + StorageDead(_6); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff new file mode 100644 index 000000000000..81e900a34c08 --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff @@ -0,0 +1,29 @@ +- // MIR for `match_non_int_failed` before MatchBranchSimplification ++ // MIR for `match_non_int_failed` after MatchBranchSimplification + + fn match_non_int_failed(_1: char) -> u8 { + let mut _0: u8; + + bb0: { + switchInt(copy _1) -> [97: bb1, 98: bb2, otherwise: bb3]; + } + + bb1: { + _0 = const 97_u8; + goto -> bb4; + } + + bb2: { + _0 = const 98_u8; + goto -> bb4; + } + + bb3: { + unreachable; + } + + bb4: { + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index 3372ae2f2a61..89ef3bfb3085 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -1,6 +1,5 @@ //@ test-mir-pass: MatchBranchSimplification -#![feature(repr128)] #![feature(core_intrinsics)] #![feature(custom_mir)] #![allow(non_camel_case_types)] @@ -628,6 +627,37 @@ fn match_i128_u128(i: EnumAi128) -> u128 { } } +// EMIT_MIR matches_reduce_branches.match_non_int_failed.MatchBranchSimplification.diff +#[custom_mir(dialect = "runtime")] +fn match_non_int_failed(i: char) -> u8 { + // CHECK-LABEL: fn match_non_int_failed( + // CHECK: switchInt + // CHECK: return + mir! { + { + match i { + 'a' => bb1, + 'b' => bb2, + _ => unreachable_bb, + } + } + bb1 = { + RET = 97; + Goto(ret) + } + bb2 = { + RET = 98; + Goto(ret) + } + unreachable_bb = { + Unreachable() + } + ret = { + Return() + } + } +} + fn main() { let _ = foo(None); let _ = foo(Some(())); @@ -665,4 +695,5 @@ fn main() { let _ = match_i128_u128(EnumAi128::A); let _ = my_is_some(None); + let _ = match_non_int_failed('a'); } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir index a9dd8886577d..18eeb8e4d3b6 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir @@ -7,7 +7,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { scope 1 (inlined core::num::::checked_shl) { let mut _3: bool; let mut _4: u32; - scope 2 (inlined core::num::::unchecked_shl) { + scope 2 (inlined #[track_caller] core::num::::unchecked_shl) { scope 3 (inlined core::ub_checks::check_language_ub) { scope 4 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir index a9dd8886577d..18eeb8e4d3b6 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir @@ -7,7 +7,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { scope 1 (inlined core::num::::checked_shl) { let mut _3: bool; let mut _4: u32; - scope 2 (inlined core::num::::unchecked_shl) { + scope 2 (inlined #[track_caller] core::num::::unchecked_shl) { scope 3 (inlined core::ub_checks::check_language_ub) { scope 4 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index 518fedffc169..8a6732d5f745 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -4,7 +4,7 @@ fn ub_if_b(_1: Thing) -> Thing { debug t => _1; let mut _0: Thing; let mut _2: isize; - scope 1 (inlined unreachable_unchecked) { + scope 1 (inlined #[track_caller] unreachable_unchecked) { scope 2 (inlined core::ub_checks::check_language_ub) { scope 3 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index 1f9c464d633b..154cbd3791cb 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -29,7 +29,7 @@ fn int_range(_1: usize, _2: usize) -> () { scope 8 (inlined ::forward_unchecked) { debug start => _11; debug n => const 1_usize; - scope 9 (inlined core::num::::unchecked_add) { + scope 9 (inlined #[track_caller] core::num::::unchecked_add) { debug self => _11; debug rhs => const 1_usize; scope 10 (inlined core::ub_checks::check_language_ub) { diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir index 5faa1e210cf4..a6dad00bbdb1 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir @@ -4,12 +4,12 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { debug p => _1; debug n => _2; let mut _0: *const [u32]; - scope 1 (inlined std::ptr::const_ptr::::byte_add) { + scope 1 (inlined #[track_caller] std::ptr::const_ptr::::byte_add) { let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { } - scope 3 (inlined std::ptr::const_ptr::::add) { + scope 3 (inlined #[track_caller] std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { let mut _5: usize; diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir index 5faa1e210cf4..a6dad00bbdb1 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir @@ -4,12 +4,12 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { debug p => _1; debug n => _2; let mut _0: *const [u32]; - scope 1 (inlined std::ptr::const_ptr::::byte_add) { + scope 1 (inlined #[track_caller] std::ptr::const_ptr::::byte_add) { let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { } - scope 3 (inlined std::ptr::const_ptr::::add) { + scope 3 (inlined #[track_caller] std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { let mut _5: usize; diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir index 9429785045a2..cb7f15657463 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir @@ -4,12 +4,12 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { debug p => _1; debug n => _2; let mut _0: *const u32; - scope 1 (inlined std::ptr::const_ptr::::byte_add) { + scope 1 (inlined #[track_caller] std::ptr::const_ptr::::byte_add) { let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { } - scope 3 (inlined std::ptr::const_ptr::::add) { + scope 3 (inlined #[track_caller] std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { scope 5 (inlined std::ptr::metadata::) { diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir index 9429785045a2..cb7f15657463 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir @@ -4,12 +4,12 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { debug p => _1; debug n => _2; let mut _0: *const u32; - scope 1 (inlined std::ptr::const_ptr::::byte_add) { + scope 1 (inlined #[track_caller] std::ptr::const_ptr::::byte_add) { let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { } - scope 3 (inlined std::ptr::const_ptr::::add) { + scope 3 (inlined #[track_caller] std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { scope 5 (inlined std::ptr::metadata::) { diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir index 0aa37203c1b0..dfe618612ab9 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -24,7 +24,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { let mut _8: u32; scope 6 { scope 8 (inlined ::forward_unchecked) { - scope 9 (inlined core::num::::unchecked_add) { + scope 9 (inlined #[track_caller] core::num::::unchecked_add) { scope 10 (inlined core::ub_checks::check_language_ub) { scope 11 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 699d8bc8feab..e0fcfcaffc59 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -24,7 +24,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { let mut _8: u32; scope 6 { scope 8 (inlined ::forward_unchecked) { - scope 9 (inlined core::num::::unchecked_add) { + scope 9 (inlined #[track_caller] core::num::::unchecked_add) { scope 10 (inlined core::ub_checks::check_language_ub) { scope 11 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir index f3033d4a2fa8..1f82fc59ac2c 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir @@ -10,7 +10,7 @@ fn range_iter_next(_1: &mut std::ops::Range) -> Option { let mut _6: u32; scope 3 { scope 5 (inlined ::forward_unchecked) { - scope 6 (inlined core::num::::unchecked_add) { + scope 6 (inlined #[track_caller] core::num::::unchecked_add) { scope 7 (inlined core::ub_checks::check_language_ub) { scope 8 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir index f3033d4a2fa8..1f82fc59ac2c 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir @@ -10,7 +10,7 @@ fn range_iter_next(_1: &mut std::ops::Range) -> Option { let mut _6: u32; scope 3 { scope 5 (inlined ::forward_unchecked) { - scope 6 (inlined core::num::::unchecked_add) { + scope 6 (inlined #[track_caller] core::num::::unchecked_add) { scope 7 (inlined core::ub_checks::check_language_ub) { scope 8 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir index ec67193bc794..d1b1e3d7dd73 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir @@ -8,19 +8,11 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { scope 2 (inlined >::get_mut) { let mut _3: usize; let mut _4: bool; - let mut _5: *mut [u32]; - let mut _7: *mut u32; - let mut _8: &mut u32; - scope 3 (inlined core::slice::index::get_mut_noubcheck::) { - let _6: *mut u32; - scope 4 { - } - } + let mut _5: &mut u32; } } bb0: { - StorageLive(_8); StorageLive(_4); StorageLive(_3); _3 = PtrMetadata(copy _1); @@ -36,23 +28,15 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { bb2: { StorageDead(_3); - StorageLive(_7); StorageLive(_5); - _5 = &raw mut (*_1); - StorageLive(_6); - _6 = copy _5 as *mut u32 (PtrToPtr); - _7 = Offset(copy _6, copy _2); - StorageDead(_6); + _5 = &mut (*_1)[_2]; + _0 = Option::<&mut u32>::Some(move _5); StorageDead(_5); - _8 = &mut (*_7); - _0 = Option::<&mut u32>::Some(copy _8); - StorageDead(_7); goto -> bb3; } bb3: { StorageDead(_4); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir index ec67193bc794..d1b1e3d7dd73 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir @@ -8,19 +8,11 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { scope 2 (inlined >::get_mut) { let mut _3: usize; let mut _4: bool; - let mut _5: *mut [u32]; - let mut _7: *mut u32; - let mut _8: &mut u32; - scope 3 (inlined core::slice::index::get_mut_noubcheck::) { - let _6: *mut u32; - scope 4 { - } - } + let mut _5: &mut u32; } } bb0: { - StorageLive(_8); StorageLive(_4); StorageLive(_3); _3 = PtrMetadata(copy _1); @@ -36,23 +28,15 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { bb2: { StorageDead(_3); - StorageLive(_7); StorageLive(_5); - _5 = &raw mut (*_1); - StorageLive(_6); - _6 = copy _5 as *mut u32 (PtrToPtr); - _7 = Offset(copy _6, copy _2); - StorageDead(_6); + _5 = &mut (*_1)[_2]; + _0 = Option::<&mut u32>::Some(move _5); StorageDead(_5); - _8 = &mut (*_7); - _0 = Option::<&mut u32>::Some(copy _8); - StorageDead(_7); goto -> bb3; } bb3: { StorageDead(_4); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir index 220e881f8664..6fb1637a6e02 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir @@ -6,21 +6,19 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> let mut _0: &mut [u32]; let mut _3: usize; let mut _4: usize; - scope 1 (inlined core::slice::::get_unchecked_mut::>) { + scope 1 (inlined #[track_caller] core::slice::::get_unchecked_mut::>) { let mut _5: *mut [u32]; let mut _11: *mut [u32]; - scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { + scope 2 (inlined #[track_caller] as SliceIndex<[u32]>>::get_unchecked_mut) { let mut _6: usize; let _7: (); let _8: usize; scope 3 { scope 6 (inlined core::slice::index::get_offset_len_mut_noubcheck::) { - let _10: *mut u32; + let _9: *mut u32; scope 7 { - } - scope 8 (inlined core::slice::index::get_mut_noubcheck::) { - let _9: *mut u32; - scope 9 { + let _10: *mut u32; + scope 8 { } } } @@ -47,13 +45,13 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> bb1: { StorageDead(_6); _8 = SubUnchecked(copy _4, copy _3); - StorageLive(_10); StorageLive(_9); + StorageLive(_10); _9 = copy _5 as *mut u32 (PtrToPtr); _10 = Offset(copy _9, copy _3); - StorageDead(_9); _11 = *mut [u32] from (copy _10, copy _8); StorageDead(_10); + StorageDead(_9); StorageDead(_8); StorageDead(_5); _0 = &mut (*_11); diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir index 220e881f8664..6fb1637a6e02 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir @@ -6,21 +6,19 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> let mut _0: &mut [u32]; let mut _3: usize; let mut _4: usize; - scope 1 (inlined core::slice::::get_unchecked_mut::>) { + scope 1 (inlined #[track_caller] core::slice::::get_unchecked_mut::>) { let mut _5: *mut [u32]; let mut _11: *mut [u32]; - scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { + scope 2 (inlined #[track_caller] as SliceIndex<[u32]>>::get_unchecked_mut) { let mut _6: usize; let _7: (); let _8: usize; scope 3 { scope 6 (inlined core::slice::index::get_offset_len_mut_noubcheck::) { - let _10: *mut u32; + let _9: *mut u32; scope 7 { - } - scope 8 (inlined core::slice::index::get_mut_noubcheck::) { - let _9: *mut u32; - scope 9 { + let _10: *mut u32; + scope 8 { } } } @@ -47,13 +45,13 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> bb1: { StorageDead(_6); _8 = SubUnchecked(copy _4, copy _3); - StorageLive(_10); StorageLive(_9); + StorageLive(_10); _9 = copy _5 as *mut u32 (PtrToPtr); _10 = Offset(copy _9, copy _3); - StorageDead(_9); _11 = *mut [u32] from (copy _10, copy _8); StorageDead(_10); + StorageDead(_9); StorageDead(_8); StorageDead(_5); _0 = &mut (*_11); diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir index 1e0df94b67fb..ad1ca5dff43a 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir @@ -7,18 +7,16 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - let mut _3: usize; let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { - scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { + scope 2 (inlined #[track_caller] as SliceIndex<[u32]>>::get_unchecked) { let mut _5: usize; let _6: (); let _7: usize; scope 3 { scope 6 (inlined core::slice::index::get_offset_len_noubcheck::) { - let _9: *const u32; + let _8: *const u32; scope 7 { - } - scope 8 (inlined core::slice::index::get_noubcheck::) { - let _8: *const u32; - scope 9 { + let _9: *const u32; + scope 8 { } } } @@ -42,13 +40,13 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - bb1: { StorageDead(_5); _7 = SubUnchecked(copy _4, copy _3); - StorageLive(_9); StorageLive(_8); + StorageLive(_9); _8 = copy _1 as *const u32 (PtrToPtr); _9 = Offset(copy _8, copy _3); - StorageDead(_8); _0 = *const [u32] from (copy _9, copy _7); StorageDead(_9); + StorageDead(_8); StorageDead(_7); return; } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir index 1e0df94b67fb..ad1ca5dff43a 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir @@ -7,18 +7,16 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - let mut _3: usize; let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { - scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { + scope 2 (inlined #[track_caller] as SliceIndex<[u32]>>::get_unchecked) { let mut _5: usize; let _6: (); let _7: usize; scope 3 { scope 6 (inlined core::slice::index::get_offset_len_noubcheck::) { - let _9: *const u32; + let _8: *const u32; scope 7 { - } - scope 8 (inlined core::slice::index::get_noubcheck::) { - let _8: *const u32; - scope 9 { + let _9: *const u32; + scope 8 { } } } @@ -42,13 +40,13 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - bb1: { StorageDead(_5); _7 = SubUnchecked(copy _4, copy _3); - StorageLive(_9); StorageLive(_8); + StorageLive(_9); _8 = copy _1 as *const u32 (PtrToPtr); _9 = Offset(copy _8, copy _3); - StorageDead(_8); _0 = *const [u32] from (copy _9, copy _7); StorageDead(_9); + StorageDead(_8); StorageDead(_7); return; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index c75edde711e1..d389e4069d05 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -58,7 +58,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { scope 30 { let _23: usize; scope 31 { - scope 34 (inlined core::num::::unchecked_sub) { + scope 34 (inlined #[track_caller] core::num::::unchecked_sub) { scope 35 (inlined core::ub_checks::check_language_ub) { scope 36 (inlined core::ub_checks::check_language_ub::runtime) { } @@ -115,7 +115,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } scope 13 (inlined NonNull::::as_ptr) { } - scope 14 (inlined std::ptr::mut_ptr::::add) { + scope 14 (inlined #[track_caller] std::ptr::mut_ptr::::add) { } } scope 8 (inlined NonNull::<[T]>::from_ref) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index bc72181b77c4..3b58f1d61f4b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -40,7 +40,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } scope 13 (inlined NonNull::::as_ptr) { } - scope 14 (inlined std::ptr::mut_ptr::::add) { + scope 14 (inlined #[track_caller] std::ptr::mut_ptr::::add) { } } scope 8 (inlined NonNull::<[T]>::from_ref) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 38c509d0b534..216e05ec5b79 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -30,7 +30,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 18 { let _22: usize; scope 19 { - scope 22 (inlined core::num::::unchecked_sub) { + scope 22 (inlined #[track_caller] core::num::::unchecked_sub) { scope 23 (inlined core::ub_checks::check_language_ub) { scope 24 (inlined core::ub_checks::check_language_ub::runtime) { } @@ -86,7 +86,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 13 (inlined NonNull::::as_ptr) { } - scope 14 (inlined std::ptr::mut_ptr::::add) { + scope 14 (inlined #[track_caller] std::ptr::mut_ptr::::add) { } } scope 8 (inlined NonNull::<[T]>::from_ref) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 158cc284b1ae..001023919804 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -30,7 +30,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 18 { let _22: usize; scope 19 { - scope 22 (inlined core::num::::unchecked_sub) { + scope 22 (inlined #[track_caller] core::num::::unchecked_sub) { scope 23 (inlined core::ub_checks::check_language_ub) { scope 24 (inlined core::ub_checks::check_language_ub::runtime) { } @@ -86,7 +86,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 13 (inlined NonNull::::as_ptr) { } - scope 14 (inlined std::ptr::mut_ptr::::add) { + scope 14 (inlined #[track_caller] std::ptr::mut_ptr::::add) { } } scope 8 (inlined NonNull::<[T]>::from_ref) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir index f8d11df5185c..41e273151eca 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir @@ -29,7 +29,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _8: usize; scope 7 { scope 9 (inlined ::forward_unchecked) { - scope 10 (inlined core::num::::unchecked_add) { + scope 10 (inlined #[track_caller] core::num::::unchecked_add) { scope 11 (inlined core::ub_checks::check_language_ub) { scope 12 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir index 2c249197894a..ec781c1480c7 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir @@ -29,7 +29,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _8: usize; scope 7 { scope 9 (inlined ::forward_unchecked) { - scope 10 (inlined core::num::::unchecked_add) { + scope 10 (inlined #[track_caller] core::num::::unchecked_add) { scope 11 (inlined core::ub_checks::check_language_ub) { scope 12 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index 003667621080..b09e36223441 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -40,7 +40,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 13 (inlined NonNull::::as_ptr) { } - scope 14 (inlined std::ptr::mut_ptr::::add) { + scope 14 (inlined #[track_caller] std::ptr::mut_ptr::::add) { } } scope 8 (inlined NonNull::<[T]>::from_ref) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index e1d710fb689c..12b54b57b844 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -40,7 +40,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 13 (inlined NonNull::::as_ptr) { } - scope 14 (inlined std::ptr::mut_ptr::::add) { + scope 14 (inlined #[track_caller] std::ptr::mut_ptr::::add) { } } scope 8 (inlined NonNull::<[T]>::from_ref) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir index b6df2300efb1..c0ed0aea1e26 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir @@ -15,7 +15,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { scope 3 { let _11: usize; scope 4 { - scope 7 (inlined core::num::::unchecked_sub) { + scope 7 (inlined #[track_caller] core::num::::unchecked_sub) { scope 8 (inlined core::ub_checks::check_language_ub) { scope 9 (inlined core::ub_checks::check_language_ub::runtime) { } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir index b6df2300efb1..c0ed0aea1e26 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir @@ -15,7 +15,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { scope 3 { let _11: usize; scope 4 { - scope 7 (inlined core::num::::unchecked_sub) { + scope 7 (inlined #[track_caller] core::num::::unchecked_sub) { scope 8 (inlined core::ub_checks::check_language_ub) { scope 9 (inlined core::ub_checks::check_language_ub::runtime) { } 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 927deabd253f..30eafe8594b3 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 @@ -29,7 +29,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } } } - scope 12 (inlined std::slice::from_raw_parts::<'_, u8>) { + scope 12 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { debug data => _3; debug len => _4; let _5: *const [u8]; 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 927deabd253f..30eafe8594b3 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 @@ -29,7 +29,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } } } - scope 12 (inlined std::slice::from_raw_parts::<'_, u8>) { + scope 12 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { debug data => _3; debug len => _4; let _5: *const [u8]; diff --git a/tests/pretty/autodiff/autodiff_forward.pp b/tests/pretty/autodiff/autodiff_forward.pp index 713b8f541ae0..a2525abc8320 100644 --- a/tests/pretty/autodiff/autodiff_forward.pp +++ b/tests/pretty/autodiff/autodiff_forward.pp @@ -13,7 +13,7 @@ extern crate std; // Test that forward mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::{autodiff_forward, autodiff_reverse}; #[rustc_autodiff] #[inline(never)] @@ -31,6 +31,8 @@ pub fn f1(x: &[f64], y: f64) -> f64 { // We want to make sure that we can use the macro for functions defined inside of functions + // Make sure we can handle generics + ::core::panicking::panic("not implemented") } #[rustc_autodiff(Forward, 1, Dual, Const, Dual)] @@ -181,4 +183,16 @@ pub fn f9() { ::core::hint::black_box(::default()) } } +#[rustc_autodiff] +#[inline(never)] +pub fn f10 + Copy>(x: &T) -> T { *x * *x } +#[rustc_autodiff(Reverse, 1, Duplicated, Active)] +#[inline(never)] +pub fn d_square + + Copy>(x: &T, dx_0: &mut T, dret: T) -> T { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f10::(x)); + ::core::hint::black_box((dx_0, dret)); + ::core::hint::black_box(f10::(x)) +} fn main() {} diff --git a/tests/pretty/autodiff/autodiff_forward.rs b/tests/pretty/autodiff/autodiff_forward.rs index 5a0660a08e52..e23a1b3e241e 100644 --- a/tests/pretty/autodiff/autodiff_forward.rs +++ b/tests/pretty/autodiff/autodiff_forward.rs @@ -7,48 +7,48 @@ // Test that forward mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::{autodiff_forward, autodiff_reverse}; -#[autodiff(df1, Forward, Dual, Const, Dual)] +#[autodiff_forward(df1, Dual, Const, Dual)] pub fn f1(x: &[f64], y: f64) -> f64 { unimplemented!() } -#[autodiff(df2, Forward, Dual, Const, Const)] +#[autodiff_forward(df2, Dual, Const, Const)] pub fn f2(x: &[f64], y: f64) -> f64 { unimplemented!() } -#[autodiff(df3, Forward, Dual, Const, Const)] +#[autodiff_forward(df3, Dual, Const, Const)] pub fn f3(x: &[f64], y: f64) -> f64 { unimplemented!() } // Not the most interesting derivative, but who are we to judge -#[autodiff(df4, Forward)] +#[autodiff_forward(df4)] pub fn f4() {} // We want to be sure that the same function can be differentiated in different ways -#[autodiff(df5_rev, Reverse, Duplicated, Const, Active)] -#[autodiff(df5_x, Forward, Dual, Const, Const)] -#[autodiff(df5_y, Forward, Const, Dual, Const)] +#[autodiff_reverse(df5_rev, Duplicated, Const, Active)] +#[autodiff_forward(df5_x, Dual, Const, Const)] +#[autodiff_forward(df5_y, Const, Dual, Const)] pub fn f5(x: &[f64], y: f64) -> f64 { unimplemented!() } struct DoesNotImplDefault; -#[autodiff(df6, Forward, Const)] +#[autodiff_forward(df6, Const)] pub fn f6() -> DoesNotImplDefault { unimplemented!() } // Make sure, that we add the None for the default return. -#[autodiff(df7, Forward, Const)] +#[autodiff_forward(df7, Const)] pub fn f7(x: f32) -> () {} -#[autodiff(f8_1, Forward, Dual, DualOnly)] -#[autodiff(f8_2, Forward, 4, Dual, DualOnly)] -#[autodiff(f8_3, Forward, 4, Dual, Dual)] +#[autodiff_forward(f8_1, Dual, DualOnly)] +#[autodiff_forward(f8_2, 4, Dual, DualOnly)] +#[autodiff_forward(f8_3, 4, Dual, Dual)] #[no_mangle] fn f8(x: &f32) -> f32 { unimplemented!() @@ -56,11 +56,17 @@ fn f8(x: &f32) -> f32 { // We want to make sure that we can use the macro for functions defined inside of functions pub fn f9() { - #[autodiff(d_inner_1, Forward, Dual, DualOnly)] - #[autodiff(d_inner_2, Forward, Dual, Dual)] + #[autodiff_forward(d_inner_1, Dual, DualOnly)] + #[autodiff_forward(d_inner_2, Dual, Dual)] fn inner(x: f32) -> f32 { x * x } } +// Make sure we can handle generics +#[autodiff_reverse(d_square, Duplicated, Active)] +pub fn f10 + Copy>(x: &T) -> T { + *x * *x +} + fn main() {} diff --git a/tests/pretty/autodiff/autodiff_reverse.pp b/tests/pretty/autodiff/autodiff_reverse.pp index 31920694a3ad..e67c3443ddef 100644 --- a/tests/pretty/autodiff/autodiff_reverse.pp +++ b/tests/pretty/autodiff/autodiff_reverse.pp @@ -13,7 +13,7 @@ extern crate std; // Test that reverse mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; #[rustc_autodiff] #[inline(never)] diff --git a/tests/pretty/autodiff/autodiff_reverse.rs b/tests/pretty/autodiff/autodiff_reverse.rs index 3c024272f407..d37e5e3eb4ce 100644 --- a/tests/pretty/autodiff/autodiff_reverse.rs +++ b/tests/pretty/autodiff/autodiff_reverse.rs @@ -7,18 +7,18 @@ // Test that reverse mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(df1, Reverse, Duplicated, Const, Active)] +#[autodiff_reverse(df1, Duplicated, Const, Active)] pub fn f1(x: &[f64], y: f64) -> f64 { unimplemented!() } // Not the most interesting derivative, but who are we to judge -#[autodiff(df2, Reverse)] +#[autodiff_reverse(df2)] pub fn f2() {} -#[autodiff(df3, Reverse, Duplicated, Const, Active)] +#[autodiff_reverse(df3, Duplicated, Const, Active)] pub fn f3(x: &[f64], y: f64) -> f64 { unimplemented!() } @@ -27,12 +27,12 @@ enum Foo { Reverse } use Foo::Reverse; // What happens if we already have Reverse in type (enum variant decl) and value (enum variant // constructor) namespace? > It's expected to work normally. -#[autodiff(df4, Reverse, Const)] +#[autodiff_reverse(df4, Const)] pub fn f4(x: f32) { unimplemented!() } -#[autodiff(df5, Reverse, DuplicatedOnly, Duplicated)] +#[autodiff_reverse(df5, DuplicatedOnly, Duplicated)] pub fn f5(x: *const f32, y: &f32) { unimplemented!() } diff --git a/tests/pretty/autodiff/inherent_impl.pp b/tests/pretty/autodiff/inherent_impl.pp index 97ac766b6b99..d18061b2dbde 100644 --- a/tests/pretty/autodiff/inherent_impl.pp +++ b/tests/pretty/autodiff/inherent_impl.pp @@ -11,7 +11,7 @@ extern crate std; //@ pretty-compare-only //@ pp-exact:inherent_impl.pp -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; struct Foo { a: f64, diff --git a/tests/pretty/autodiff/inherent_impl.rs b/tests/pretty/autodiff/inherent_impl.rs index 59de93f7e0ff..11ff209f9d89 100644 --- a/tests/pretty/autodiff/inherent_impl.rs +++ b/tests/pretty/autodiff/inherent_impl.rs @@ -5,7 +5,7 @@ //@ pretty-compare-only //@ pp-exact:inherent_impl.pp -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; struct Foo { a: f64, @@ -17,7 +17,7 @@ trait MyTrait { } impl MyTrait for Foo { - #[autodiff(df, Reverse, Const, Active, Active)] + #[autodiff_reverse(df, Const, Active, Active)] fn f(&self, x: f64) -> f64 { self.a * 0.25 * (x * x - 1.0 - 2.0 * x.ln()) } diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index bcaef33344e5..36c9db106ec4 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -30,9 +30,9 @@ pub unsafe extern "C" fn check_list_0(mut ap: VaList) -> usize { #[no_mangle] pub unsafe extern "C" fn check_list_1(mut ap: VaList) -> usize { continue_if!(ap.arg::() == -1); - continue_if!(ap.arg::() == 'A' as c_char); - continue_if!(ap.arg::() == '4' as c_char); - continue_if!(ap.arg::() == ';' as c_char); + continue_if!(ap.arg::() == 'A' as c_int); + continue_if!(ap.arg::() == '4' as c_int); + continue_if!(ap.arg::() == ';' as c_int); continue_if!(ap.arg::() == 0x32); continue_if!(ap.arg::() == 0x10000001); continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Valid!")); @@ -43,7 +43,7 @@ pub unsafe extern "C" fn check_list_1(mut ap: VaList) -> usize { pub unsafe extern "C" fn check_list_2(mut ap: VaList) -> usize { continue_if!(ap.arg::().floor() == 3.14f64.floor()); continue_if!(ap.arg::() == 12); - continue_if!(ap.arg::() == 'a' as c_char); + continue_if!(ap.arg::() == 'a' as c_int); continue_if!(ap.arg::().floor() == 6.18f64.floor()); continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello")); continue_if!(ap.arg::() == 42); @@ -55,7 +55,7 @@ pub unsafe extern "C" fn check_list_2(mut ap: VaList) -> usize { pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize { continue_if!(ap.arg::().floor() == 6.28f64.floor()); continue_if!(ap.arg::() == 16); - continue_if!(ap.arg::() == 'A' as c_char); + continue_if!(ap.arg::() == 'A' as c_int); continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Skip Me!")); ap.with_copy( |mut ap| { @@ -75,7 +75,7 @@ pub unsafe extern "C" fn check_varargs_0(_: c_int, mut ap: ...) -> usize { pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize { continue_if!(ap.arg::().floor() == 3.14f64.floor()); continue_if!(ap.arg::() == 12); - continue_if!(ap.arg::() == 'A' as c_char); + continue_if!(ap.arg::() == 'A' as c_int); continue_if!(ap.arg::() == 1); 0 } @@ -112,6 +112,9 @@ pub unsafe extern "C" fn check_varargs_4(_: c_double, mut ap: ...) -> usize { continue_if!(ap.arg::() == 8.0); continue_if!(ap.arg::() == 9.0); continue_if!(ap.arg::() == 10.0); + continue_if!(ap.arg::() == 11.0); + continue_if!(ap.arg::() == 12.0); + continue_if!(ap.arg::() == 13.0); 0 } @@ -137,5 +140,11 @@ pub unsafe extern "C" fn check_varargs_5(_: c_int, mut ap: ...) -> usize { continue_if!(ap.arg::() == 9.0); continue_if!(ap.arg::() == 10); continue_if!(ap.arg::() == 10.0); + continue_if!(ap.arg::() == 11); + continue_if!(ap.arg::() == 11.0); + continue_if!(ap.arg::() == 12); + continue_if!(ap.arg::() == 12.0); + continue_if!(ap.arg::() == 13); + continue_if!(ap.arg::() == 13.0); 0 } diff --git a/tests/run-make/c-link-to-rust-va-list-fn/test.c b/tests/run-make/c-link-to-rust-va-list-fn/test.c index 5bdb51680a65..b47a9357880f 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/test.c +++ b/tests/run-make/c-link-to-rust-va-list-fn/test.c @@ -41,10 +41,11 @@ int main(int argc, char* argv[]) { assert(check_varargs_3(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 0); - assert(check_varargs_4(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0) == 0); + assert(check_varargs_4(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0) == 0); assert(check_varargs_5(0, 1.0, 1, 2.0, 2, 3.0, 3, 4.0, 4, 5, 5.0, 6, 6.0, 7, 7.0, 8, 8.0, - 9, 9.0, 10, 10.0) == 0); + 9, 9.0, 10, 10.0, 11, 11.0, 12, 12.0, 13, 13.0) == 0); return 0; } diff --git a/tests/run-make/crate-loading/multiple-dep-versions.stderr b/tests/run-make/crate-loading/multiple-dep-versions.stderr index 6e1d6111b581..dea08bb96c97 100644 --- a/tests/run-make/crate-loading/multiple-dep-versions.stderr +++ b/tests/run-make/crate-loading/multiple-dep-versions.stderr @@ -39,7 +39,7 @@ error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in t --> replaced | LL | Type.foo(); - | ^^^ method not found in `Type` + | ^^^ method not found in `dep_2_reexport::Type` | note: there are multiple different versions of crate `dependency` in the dependency graph --> replaced @@ -63,7 +63,7 @@ error[E0599]: no function or associated item named `bar` found for struct `dep_2 --> replaced | LL | Type::bar(); - | ^^^ function or associated item not found in `Type` + | ^^^ function or associated item not found in `dep_2_reexport::Type` | note: there are multiple different versions of crate `dependency` in the dependency graph --> replaced diff --git a/tests/run-make/mte-ffi/bar.h b/tests/run-make/mte-ffi/bar.h index a2292ae02a30..9b030c618d18 100644 --- a/tests/run-make/mte-ffi/bar.h +++ b/tests/run-make/mte-ffi/bar.h @@ -1,3 +1,5 @@ +// FIXME(#141600) the mte-ffi test doesn't fail in aarch64-gnu + #ifndef __BAR_H #define __BAR_H diff --git a/tests/run-make/mte-ffi/bar_float.c b/tests/run-make/mte-ffi/bar_float.c index a1590f62765a..acc2f5d9266d 100644 --- a/tests/run-make/mte-ffi/bar_float.c +++ b/tests/run-make/mte-ffi/bar_float.c @@ -3,9 +3,9 @@ #include #include "bar.h" -extern void foo(char*); +extern void foo(float*); -void bar(char *ptr) { +void bar(float *ptr) { if (((uintptr_t)ptr >> 56) != 0x1f) { fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); exit(1); diff --git a/tests/run-make/mte-ffi/bar_int.c b/tests/run-make/mte-ffi/bar_int.c index d1c79e95dc9c..c92e765302c1 100644 --- a/tests/run-make/mte-ffi/bar_int.c +++ b/tests/run-make/mte-ffi/bar_int.c @@ -5,7 +5,7 @@ extern void foo(unsigned int *); -void bar(char *ptr) { +void bar(unsigned int *ptr) { if (((uintptr_t)ptr >> 56) != 0x1f) { fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); exit(1); diff --git a/tests/run-make/mte-ffi/bar_string.c b/tests/run-make/mte-ffi/bar_string.c index 5669ffd6695e..8e1202f6fd15 100644 --- a/tests/run-make/mte-ffi/bar_string.c +++ b/tests/run-make/mte-ffi/bar_string.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "bar.h" extern void foo(char*); @@ -32,7 +33,7 @@ int main(void) // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), // and a different value in the ignored top 4 bits. - ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); + ptr = (char *)((uintptr_t)ptr | 0x1fl << 56); if (mte_enabled()) { set_tag(ptr); diff --git a/tests/run-make/repr128-dwarf/main.rs b/tests/run-make/repr128-dwarf/main.rs index 9842ab4a3426..a8a414fd72e4 100644 --- a/tests/run-make/repr128-dwarf/main.rs +++ b/tests/run-make/repr128-dwarf/main.rs @@ -1,5 +1,3 @@ -#![feature(repr128)] - // Use .to_le() to ensure that the bytes are in the same order on both little- and big-endian // platforms. diff --git a/tests/run-make/version-check/rmake.rs b/tests/run-make/version-check/rmake.rs new file mode 100644 index 000000000000..e6a14d2e95c3 --- /dev/null +++ b/tests/run-make/version-check/rmake.rs @@ -0,0 +1,13 @@ +use run_make_support::bare_rustc; + +fn main() { + let signalled_version = "Ceci n'est pas une rustc"; + let rustc_out = bare_rustc() + .env("RUSTC_OVERRIDE_VERSION_STRING", signalled_version) + .arg("--version") + .run() + .stdout_utf8(); + + let version = rustc_out.strip_prefix("rustc ").unwrap().trim_end(); + assert_eq!(version, signalled_version); +} diff --git a/tests/rustdoc-gui/collapse-trait-impl.goml b/tests/rustdoc-gui/collapse-trait-impl.goml new file mode 100644 index 000000000000..4db02e5239b5 --- /dev/null +++ b/tests/rustdoc-gui/collapse-trait-impl.goml @@ -0,0 +1,26 @@ +// Checks that individual trait impls can only be collapsed via clicking directly on the summary element + +go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" +assert-attribute: ("details:has(#trait-impl-link-in-summary)", {"open": ""}) + +// Collapse the trait impl doc. The actual clickable area is outside the element, hence offset. +click-with-offset: ("summary:has(#trait-impl-link-in-summary)", {"x": -15, "y": 5}) +assert-attribute-false: ("details:has(#trait-impl-link-in-summary)", {"open": ""}) +click-with-offset: ("summary:has(#trait-impl-link-in-summary)", {"x": -15, "y": 5}) +assert-attribute: ("details:has(#trait-impl-link-in-summary)", {"open": ""}) + +// Clicks on the text should be ignored +click: "summary:has(#trait-impl-link-in-summary) > .impl" +assert-attribute: ("details:has(#trait-impl-link-in-summary)", {"open": ""}) + +// But links should still work +click: "summary:has(#trait-impl-link-in-summary) a:has(#trait-impl-link-in-summary)" +assert-window-property-false: ({"pageYOffset": "0"}) +assert-attribute: ("details:has(#trait-impl-link-in-summary)", {"open": ""}) + +// As well as clicks on elements within links +go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" +assert-window-property: ({"pageYOffset": "0"}) +click: "#trait-impl-link-in-summary" +assert-window-property-false: ({"pageYOffset": "0"}) +assert-attribute: ("details:has(#trait-impl-link-in-summary)", {"open": ""}) diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml index 4624fb80b377..7fc70e0675df 100644 --- a/tests/rustdoc-gui/notable-trait.goml +++ b/tests/rustdoc-gui/notable-trait.goml @@ -244,10 +244,6 @@ click: ".sidebar-menu-toggle" assert: "//*[@class='sidebar shown']" assert-count: ("//*[@class='tooltip popover']", 0) assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" -// Clicking a notable trait tooltip popover should close the sidebar. -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 settings button. set-window-size: (1100, 600) diff --git a/tests/rustdoc-gui/pocket-menu.goml b/tests/rustdoc-gui/pocket-menu.goml index 4a062fec7516..073172dd8a79 100644 --- a/tests/rustdoc-gui/pocket-menu.goml +++ b/tests/rustdoc-gui/pocket-menu.goml @@ -68,16 +68,3 @@ assert-css: ("#settings-menu .popover", {"display": "block"}) click: ".sidebar-menu-toggle" assert: "//*[@class='sidebar shown']" assert-css: ("#settings-menu .popover", {"display": "none"}) -// Opening the settings popover should close the sidebar. -click: "#settings-menu a" -assert-css: ("#settings-menu .popover", {"display": "block"}) -assert-false: "//*[@class='sidebar shown']" - -// Opening the settings popover at start (which async loads stuff) should also close. -reload: -click: ".sidebar-menu-toggle" -assert: "//*[@class='sidebar shown']" -assert-false: "#settings-menu .popover" -click: "#settings-menu a" -assert-false: "//*[@class='sidebar shown']" -wait-for: "#settings-menu .popover" diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index 4ada4837a577..6ddc07c6481c 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -32,8 +32,8 @@ assert-css: ( {"display": "block"} ) -// Click elsewhere. -click: "body" +// Click the toggle to close it +click: ".sidebar-menu-toggle" assert-css: (".sidebar", {"display": "block", "left": "-1000px"}) // Open the sidebar menu, and make sure pressing Escape closes it. @@ -57,6 +57,8 @@ scroll-to: ".block.keyword li:nth-child(1)" compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 544}) // Now checking the background color of the sidebar. +// Close the sidebar menu. +press-key: "Escape" show-text: true define-function: ( @@ -72,6 +74,10 @@ define-function: ( "background-color": |background|, "color": |color|, }) + // Make sure the sidebar is full width + compare-elements-size: (".sidebar", "body", ["width"]) + // Close the sidebar menu. + press-key: "Escape" }, ) diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index bb0015b8f9c4..e8afe8b56872 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -79,6 +79,7 @@ impl Foo { pub fn warning2() {} } +///
A collapsible trait impl with a link impl AsRef for Foo { fn as_ref(&self) -> &str { "hello" diff --git a/tests/rustdoc-js/auxiliary/interner.rs b/tests/rustdoc-js/auxiliary/interner.rs index e4e4ff6276d6..8af3b732ef7a 100644 --- a/tests/rustdoc-js/auxiliary/interner.rs +++ b/tests/rustdoc-js/auxiliary/interner.rs @@ -18,7 +18,7 @@ pub trait Interner: Sized { type Binder>: BoundVars + TypeSuperVisitable; type BoundVars: IntoIterator; type BoundVar; - type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator>; + type CanonicalVarKinds: Copy + Debug + Hash + Eq + IntoIterator>; type Ty: Copy + DebugWithInfcx + Hash @@ -77,7 +77,7 @@ pub trait Interner: Sized { type ClosureKind: Copy + Debug + Hash + Eq; // Required method - fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo]) -> Self::CanonicalVars; + fn mk_canonical_var_kinds(self, kinds: &[CanonicalVarKind]) -> Self::CanonicalVarKinds; } pub trait DebugWithInfcx: Debug { @@ -104,10 +104,6 @@ pub trait TypeSuperVisitable: TypeVisitable { fn super_visit_with>(&self, visitor: &mut V) -> V::Result; } -pub struct CanonicalVarInfo { - pub kind: CanonicalVarKind, -} - pub struct CanonicalVarKind(std::marker::PhantomData); pub struct TyKind(std::marker::PhantomData); diff --git a/tests/rustdoc-js/looks-like-rustc-interner.js b/tests/rustdoc-js/looks-like-rustc-interner.js index d6d2764c3aed..7d0511944546 100644 --- a/tests/rustdoc-js/looks-like-rustc-interner.js +++ b/tests/rustdoc-js/looks-like-rustc-interner.js @@ -3,13 +3,13 @@ const EXPECTED = [ { - 'query': 'canonicalvarinfo, intoiterator -> intoiterator', + 'query': 'CanonicalVarKind, intoiterator -> intoiterator', 'others': [], }, { - 'query': '[canonicalvarinfo], interner -> intoiterator', + 'query': '[CanonicalVarKind], interner -> intoiterator', 'others': [ - { 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' }, + { 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_kinds' }, ], }, ]; diff --git a/tests/rustdoc-json/attrs/inline.rs b/tests/rustdoc-json/attrs/inline.rs new file mode 100644 index 000000000000..74f5f36f03f3 --- /dev/null +++ b/tests/rustdoc-json/attrs/inline.rs @@ -0,0 +1,11 @@ +//@ is "$.index[?(@.name=='just_inline')].attrs" '["#[inline]"]' +#[inline] +pub fn just_inline() {} + +//@ is "$.index[?(@.name=='inline_always')].attrs" '["#[inline(always)]"]' +#[inline(always)] +pub fn inline_always() {} + +//@ is "$.index[?(@.name=='inline_never')].attrs" '["#[inline(never)]"]' +#[inline(never)] +pub fn inline_never() {} diff --git a/tests/rustdoc-json/attrs/repr_combination.rs b/tests/rustdoc-json/attrs/repr_combination.rs index 0e8e2ef0d83e..6fe29c5eac09 100644 --- a/tests/rustdoc-json/attrs/repr_combination.rs +++ b/tests/rustdoc-json/attrs/repr_combination.rs @@ -77,3 +77,7 @@ pub enum AlignedExplicitRepr { pub enum ReorderedAlignedExplicitRepr { First, } + +//@ is "$.index[?(@.name=='Transparent')].attrs" '["#[repr(transparent)]"]' +#[repr(transparent)] +pub struct Transparent(i64); diff --git a/tests/rustdoc-json/attrs/repr_transparent.rs b/tests/rustdoc-json/attrs/repr_transparent.rs deleted file mode 100644 index 1e634ca901dc..000000000000 --- a/tests/rustdoc-json/attrs/repr_transparent.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![no_std] - -// Rustdoc JSON *only* includes `#[repr(transparent)]` -// if the transparency is public API: -// - if a non-1-ZST field exists, it has to be public -// - otherwise, all fields are 1-ZST and at least one of them is public -// -// More info: https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent - -// Here, the non-1-ZST field is public. -// We expect `#[repr(transparent)]` in the attributes. -// -//@ is "$.index[?(@.name=='Transparent')].attrs" '["#[repr(transparent)]"]' -#[repr(transparent)] -pub struct Transparent(pub i64); - -// Here the non-1-ZST field isn't public, so the attribute isn't included. -// -//@ has "$.index[?(@.name=='TransparentNonPub')]" -//@ is "$.index[?(@.name=='TransparentNonPub')].attrs" '[]' -#[repr(transparent)] -pub struct TransparentNonPub(i64); - -// Only 1-ZST fields here, and one of them is public. -// We expect `#[repr(transparent)]` in the attributes. -// -//@ is "$.index[?(@.name=='AllZst')].attrs" '["#[repr(transparent)]"]' -#[repr(transparent)] -pub struct AllZst<'a>(pub core::marker::PhantomData<&'a ()>, ()); - -// Only 1-ZST fields here but none of them are public. -// The attribute isn't included. -// -//@ has "$.index[?(@.name=='AllZstNotPublic')]" -//@ is "$.index[?(@.name=='AllZstNotPublic')].attrs" '[]' -#[repr(transparent)] -pub struct AllZstNotPublic<'a>(core::marker::PhantomData<&'a ()>, ()); diff --git a/tests/rustdoc-json/enums/discriminant/limits.rs b/tests/rustdoc-json/enums/discriminant/limits.rs index c84181334e35..e98271b7c806 100644 --- a/tests/rustdoc-json/enums/discriminant/limits.rs +++ b/tests/rustdoc-json/enums/discriminant/limits.rs @@ -1,6 +1,3 @@ -#![feature(repr128)] -#![allow(incomplete_features)] - #[repr(u64)] pub enum U64 { //@ is "$.index[?(@.name=='U64Min')].inner.variant.discriminant.value" '"0"' diff --git a/tests/rustdoc-ui/doc-cfg-check-cfg.cfg_empty.stderr b/tests/rustdoc-ui/doc-cfg-check-cfg.cfg_empty.stderr new file mode 100644 index 000000000000..0878f7edbf48 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-check-cfg.cfg_empty.stderr @@ -0,0 +1,30 @@ +warning: unexpected `cfg` condition name: `foo` + --> $DIR/doc-cfg-check-cfg.rs:12:12 + | +LL | #![doc(cfg(foo))] + | ^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(foo)` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition name: `foo` + --> $DIR/doc-cfg-check-cfg.rs:19:11 + | +LL | #[doc(cfg(foo))] + | ^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(foo)` + = note: see for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `foo` + --> $DIR/doc-cfg-check-cfg.rs:15:11 + | +LL | #[doc(cfg(foo))] + | ^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(foo)` + = note: see for more information about checking conditional configuration + +warning: 3 warnings emitted + diff --git a/tests/rustdoc-ui/doc-cfg-check-cfg.rs b/tests/rustdoc-ui/doc-cfg-check-cfg.rs index e3420dc07897..7d37077a32dd 100644 --- a/tests/rustdoc-ui/doc-cfg-check-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg-check-cfg.rs @@ -1,16 +1,25 @@ // Ensure that `doc(cfg())` respects `check-cfg` // Currently not properly working -#![feature(doc_cfg)] -#![deny(unexpected_cfgs)] -//@revisions: no_check cfg_empty cfg_foo +//@ check-pass +//@ no-auto-check-cfg + +//@ revisions: no_check cfg_empty cfg_foo //@[cfg_empty] compile-flags: --check-cfg cfg() //@[cfg_foo] compile-flags: --check-cfg cfg(foo) -//@[no_check] check-pass -//@[cfg_empty] check-pass -//@[cfg_empty] known-bug: #138358 -//@[cfg_foo] check-pass +#![feature(doc_cfg)] +#![doc(cfg(foo))] +//[cfg_empty]~^ WARN unexpected `cfg` condition name: `foo` #[doc(cfg(foo))] +//[cfg_empty]~^ WARN unexpected `cfg` condition name: `foo` pub fn foo() {} + +#[doc(cfg(foo))] +//[cfg_empty]~^ WARN unexpected `cfg` condition name: `foo` +pub mod module { + #[allow(unexpected_cfgs)] + #[doc(cfg(bar))] + pub fn bar() {} +} diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index 354d76bc3c43..14943bbc3418 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -3,7 +3,9 @@ #[doc(cfg(), cfg(foo, bar))] //~^ ERROR //~^^ ERROR -#[doc(cfg(foo), cfg(bar))] // ok! +#[doc(cfg(foo), cfg(bar))] +//~^ WARN unexpected `cfg` condition name: `foo` +//~^^ WARN unexpected `cfg` condition name: `bar` #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index 14b7b17e04d3..1233ee010de2 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -11,16 +11,36 @@ LL | #[doc(cfg(), cfg(foo, bar))] | ^^^ error: `cfg` predicate is not specified - --> $DIR/doc-cfg.rs:7:7 + --> $DIR/doc-cfg.rs:9:7 | LL | #[doc(cfg())] | ^^^^^ help: expected syntax is: `cfg(/* predicate */)` error: multiple `cfg` predicates are specified - --> $DIR/doc-cfg.rs:8:16 + --> $DIR/doc-cfg.rs:10:16 | LL | #[doc(cfg(foo, bar))] | ^^^ -error: aborting due to 4 previous errors +warning: unexpected `cfg` condition name: `foo` + --> $DIR/doc-cfg.rs:6:11 + | +LL | #[doc(cfg(foo), cfg(bar))] + | ^^^ + | + = help: expected names are: `FALSE` and `test` and 31 more + = help: to expect this configuration use `--check-cfg=cfg(foo)` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition name: `bar` + --> $DIR/doc-cfg.rs:6:21 + | +LL | #[doc(cfg(foo), cfg(bar))] + | ^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(bar)` + = note: see for more information about checking conditional configuration + +error: aborting due to 4 previous errors; 2 warnings emitted diff --git a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr new file mode 100644 index 000000000000..113fb7ccb60e --- /dev/null +++ b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr @@ -0,0 +1,8 @@ +warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function + --> $DIR/failed-doctest-extra-semicolon-on-item.rs:11:1 + | +11 | /// ```rust + | ^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr new file mode 100644 index 000000000000..d90a289ca698 --- /dev/null +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr @@ -0,0 +1,14 @@ +warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function + --> $DIR/main-alongside-stmts.rs:17:1 + | +17 | //! ``` + | ^^^^^^^ + +warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function + --> $DIR/main-alongside-stmts.rs:26:1 + | +26 | //! ``` + | ^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stderr b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stderr new file mode 100644 index 000000000000..0dc7c2a2eea9 --- /dev/null +++ b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stderr @@ -0,0 +1,8 @@ +warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function + --> $DIR/test-main-alongside-exprs.rs:15:1 + | +15 | //! ``` + | ^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.rs b/tests/rustdoc-ui/doctest/warn-main-not-called.rs new file mode 100644 index 000000000000..25d92e9cee9f --- /dev/null +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.rs @@ -0,0 +1,22 @@ +//@ check-pass +//@ compile-flags:--test --test-args --test-threads=1 +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" + +// In case there is a `main` function in the doctest alongside expressions, +// the whole doctest will be wrapped into a function and the `main` function +// won't be called. + +//! ``` +//! macro_rules! bla { +//! ($($x:tt)*) => {} +//! } +//! +//! let x = 12; +//! bla!(fn main ()); +//! ``` +//! +//! ``` +//! let x = 12; +//! fn main() {} +//! ``` diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr new file mode 100644 index 000000000000..3a079f47555b --- /dev/null +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr @@ -0,0 +1,14 @@ +warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function + --> $DIR/warn-main-not-called.rs:10:1 + | +10 | //! ``` + | ^^^^^^^ + +warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function + --> $DIR/warn-main-not-called.rs:19:1 + | +19 | //! ``` + | ^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.stdout b/tests/rustdoc-ui/doctest/warn-main-not-called.stdout new file mode 100644 index 000000000000..07cdddc7b945 --- /dev/null +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/warn-main-not-called.rs - (line 10) ... ok +test $DIR/warn-main-not-called.rs - (line 19) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.rs b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.rs new file mode 100644 index 000000000000..b449465768ef --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.rs @@ -0,0 +1,21 @@ +//@ compile-flags: --crate-type=proc-macro --document-private-items +#![deny(rustdoc::broken_intra_doc_links)] + +//! Link to [`m`]. +//~^ ERROR `m` is both a module and a macro + +// test a further edge case related to https://github.com/rust-lang/rust/issues/91274 + +// we need to make sure that when there is actually an ambiguity +// in a proc-macro crate, we print out a sensible error. +// because proc macro crates can't normally export modules, +// this can only happen in --document-private-items mode. + +extern crate proc_macro; + +mod m {} + +#[proc_macro] +pub fn m(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + input +} diff --git a/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.stderr b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.stderr new file mode 100644 index 000000000000..09a5d26ededc --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.stderr @@ -0,0 +1,22 @@ +error: `m` is both a module and a macro + --> $DIR/bad-link-to-proc-macro.rs:4:15 + | +LL | //! Link to [`m`]. + | ^ ambiguous link + | +note: the lint level is defined here + --> $DIR/bad-link-to-proc-macro.rs:2:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to link to the module, prefix with `mod@` + | +LL | //! Link to [`mod@m`]. + | ++++ +help: to link to the macro, add an exclamation mark + | +LL | //! Link to [`m!`]. + | + + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc-ui/intra-doc/warning.stderr b/tests/rustdoc-ui/intra-doc/warning.stderr index 3a06f1787e09..9b3f6f822d75 100644 --- a/tests/rustdoc-ui/intra-doc/warning.stderr +++ b/tests/rustdoc-ui/intra-doc/warning.stderr @@ -69,29 +69,19 @@ LL | bar [BarC] bar = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarD` - --> $DIR/warning.rs:45:9 + --> $DIR/warning.rs:45:20 | LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ no item named `BarD` in scope | - = note: the link appears in this line: - - bar [BarD] bar - ^^^^ - = note: no item named `BarD` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarF` - --> $DIR/warning.rs:54:4 + --> $DIR/warning.rs:54:15 | LL | f!("Foo\nbar [BarF] bar\nbaz"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ no item named `BarF` in scope | - = note: the link appears in this line: - - bar [BarF] bar - ^^^^ - = note: no item named `BarF` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` = note: this warning originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -112,29 +102,19 @@ LL | * time to introduce a link [error] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` - --> $DIR/warning.rs:68:9 + --> $DIR/warning.rs:68:23 | LL | #[doc = "single line [error]"] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ no item named `error` in scope | - = note: the link appears in this line: - - single line [error] - ^^^^^ - = note: no item named `error` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` - --> $DIR/warning.rs:71:9 + --> $DIR/warning.rs:71:41 | LL | #[doc = "single line with \"escaping\" [error]"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ no item named `error` in scope | - = note: the link appears in this line: - - single line with "escaping" [error] - ^^^^^ - = note: no item named `error` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout index 790e58b0df9f..30aadfe89f42 100644 --- a/tests/rustdoc-ui/issues/issue-91713.stdout +++ b/tests/rustdoc-ui/issues/issue-91713.stdout @@ -1,4 +1,5 @@ Available passes for running rustdoc: + check-doc-cfg - checks `#[doc(cfg(...))]` for stability feature and unexpected cfgs check_doc_test_visibility - run various visibility-related lints on doctests strip-aliased-non-local - strips all non-local private aliased items from the output strip-hidden - strips all `#[doc(hidden)]` items from the output @@ -14,6 +15,7 @@ calculate-doc-coverage - counts the number of items with and without documentati Default passes for rustdoc: collect-trait-impls check_doc_test_visibility + check-doc-cfg strip-aliased-non-local strip-hidden (when not --document-hidden-items) strip-private (when not --document-private-items) diff --git a/tests/rustdoc-ui/lints/bare-urls-limit.rs b/tests/rustdoc-ui/lints/bare-urls-limit.rs new file mode 100644 index 000000000000..f64154b04969 --- /dev/null +++ b/tests/rustdoc-ui/lints/bare-urls-limit.rs @@ -0,0 +1,12 @@ +//@ check-fail + +#![deny(rustdoc::bare_urls)] + +// examples of bare urls that are beyond our ability to generate suggestions for + +// this falls through every heuristic in `source_span_for_markdown_range`, +// and thus does not get any suggestion. +#[doc = "good: \n\n"] +//~^ ERROR this URL is not a hyperlink +#[doc = "bad: https://example.com/"] +pub fn duplicate_raw() {} diff --git a/tests/rustdoc-ui/lints/bare-urls-limit.stderr b/tests/rustdoc-ui/lints/bare-urls-limit.stderr new file mode 100644 index 000000000000..9573665cb131 --- /dev/null +++ b/tests/rustdoc-ui/lints/bare-urls-limit.stderr @@ -0,0 +1,18 @@ +error: this URL is not a hyperlink + --> $DIR/bare-urls-limit.rs:9:9 + | +LL | #[doc = "good: \n\n"] + | _________^ +LL | | +LL | | #[doc = "bad: https://example.com/"] + | |___________________________________^ + | + = note: bare URLs are not automatically turned into clickable links +note: the lint level is defined here + --> $DIR/bare-urls-limit.rs:3:9 + | +LL | #![deny(rustdoc::bare_urls)] + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc-ui/lints/bare-urls.fixed b/tests/rustdoc-ui/lints/bare-urls.fixed index 7938d715199d..a91573146b82 100644 --- a/tests/rustdoc-ui/lints/bare-urls.fixed +++ b/tests/rustdoc-ui/lints/bare-urls.fixed @@ -38,6 +38,16 @@ //~^ ERROR this URL is not a hyperlink pub fn c() {} +#[doc = "here's a thing: "] +//~^ ERROR this URL is not a hyperlink +pub fn f() {} + +/// +//~^ ERROR this URL is not a hyperlink +#[doc = ""] +//~^ ERROR this URL is not a hyperlink +pub fn mixed() {} + /// /// [a](http://a.com) /// [b] diff --git a/tests/rustdoc-ui/lints/bare-urls.rs b/tests/rustdoc-ui/lints/bare-urls.rs index 75f42b78ffbb..5b008cdafa23 100644 --- a/tests/rustdoc-ui/lints/bare-urls.rs +++ b/tests/rustdoc-ui/lints/bare-urls.rs @@ -38,6 +38,16 @@ //~^ ERROR this URL is not a hyperlink pub fn c() {} +#[doc = "here's a thing: https://example.com/"] +//~^ ERROR this URL is not a hyperlink +pub fn f() {} + +/// https://example.com/sugar +//~^ ERROR this URL is not a hyperlink +#[doc = "https://example.com/raw"] +//~^ ERROR this URL is not a hyperlink +pub fn mixed() {} + /// /// [a](http://a.com) /// [b] diff --git a/tests/rustdoc-ui/lints/bare-urls.stderr b/tests/rustdoc-ui/lints/bare-urls.stderr index ddfc387eaf66..e1108c7e7f84 100644 --- a/tests/rustdoc-ui/lints/bare-urls.stderr +++ b/tests/rustdoc-ui/lints/bare-urls.stderr @@ -207,5 +207,41 @@ help: use an automatic link instead LL | /// hey! | + + -error: aborting due to 17 previous errors +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:41:26 + | +LL | #[doc = "here's a thing: https://example.com/"] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | #[doc = "here's a thing: "] + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:45:5 + | +LL | /// https://example.com/sugar + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:47:10 + | +LL | #[doc = "https://example.com/raw"] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | #[doc = ""] + | + + + +error: aborting due to 20 previous errors diff --git a/tests/rustdoc-ui/unescaped_backticks.stderr b/tests/rustdoc-ui/unescaped_backticks.stderr index d93aaf5f3ca9..1bcb88e108f2 100644 --- a/tests/rustdoc-ui/unescaped_backticks.stderr +++ b/tests/rustdoc-ui/unescaped_backticks.stderr @@ -628,10 +628,10 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:108:9 + --> $DIR/unescaped_backticks.rs:108:10 | LL | #[doc = "`"] - | ^^^ + | ^ | = help: the opening or closing backtick of an inline code may be missing = help: if you meant to use a literal backtick, escape it @@ -639,10 +639,10 @@ LL | #[doc = "`"] to this: \` error: unescaped backtick - --> $DIR/unescaped_backticks.rs:115:9 + --> $DIR/unescaped_backticks.rs:115:26 | LL | #[doc = concat!("\\", "`")] - | ^^^^^^^^^^^^^^^^^^^^ + | ^ | = help: the opening backtick of an inline code may be missing change: \` diff --git a/tests/rustdoc/cfg-bool.rs b/tests/rustdoc/cfg-bool.rs new file mode 100644 index 000000000000..34fdfbe930e1 --- /dev/null +++ b/tests/rustdoc/cfg-bool.rs @@ -0,0 +1,13 @@ +#![feature(doc_cfg)] +#![crate_name = "foo"] + +// regression test for https://github.com/rust-lang/rust/issues/138112 + +//@ has 'foo/fn.foo.html' '//div[@class="stab portability"]' 'Available nowhere' +#[doc(cfg(false))] +pub fn foo() {} + +// a cfg(true) will simply be ommited, as it is the same as no cfg. +//@ !has 'foo/fn.bar.html' '//div[@class="stab portability"]' '' +#[doc(cfg(true))] +pub fn bar() {} diff --git a/tests/rustdoc/doctest/ignore-sometimes.rs b/tests/rustdoc/doctest/ignore-sometimes.rs new file mode 100644 index 000000000000..0f8586d221c1 --- /dev/null +++ b/tests/rustdoc/doctest/ignore-sometimes.rs @@ -0,0 +1,23 @@ +#![crate_name = "foo"] + +// test for https://github.com/rust-lang/rust/issues/141092 + +//@ has 'foo/fn.f.html' '//a[@title="This example is not tested on wasm"]' 'ⓘ' +/// Example +/// +/// ```ignore-wasm +/// let x = 1; +/// ``` +pub fn f() {} + +//@ has 'foo/fn.g.html' '//a[@title="This example is not tested on wasm or windows"]' 'ⓘ' +/// ```ignore-wasm,ignore-windows +/// let x = 1; +/// ``` +pub fn g() {} + +//@ has 'foo/fn.h.html' '//a[@title="This example is not tested on wasm, windows, or unix"]' 'ⓘ' +/// ```ignore-wasm,ignore-windows,ignore-unix +/// let x = 1; +/// ``` +pub fn h() {} diff --git a/tests/rustdoc/intra-doc/link-to-proc-macro.rs b/tests/rustdoc/intra-doc/link-to-proc-macro.rs new file mode 100644 index 000000000000..6c289078db86 --- /dev/null +++ b/tests/rustdoc/intra-doc/link-to-proc-macro.rs @@ -0,0 +1,13 @@ +//@ compile-flags: --crate-type=proc-macro +//@ has 'foo/index.html' '//a[@href="macro.my_macro.html"]' 'my_macro' +//! Link to [`my_macro`]. +#![crate_name = "foo"] + +// regression test for https://github.com/rust-lang/rust/issues/91274 + +extern crate proc_macro; + +#[proc_macro] +pub fn my_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + input +} diff --git a/tests/rustdoc/type-alias/repr.rs b/tests/rustdoc/type-alias/repr.rs new file mode 100644 index 000000000000..cf9079803609 --- /dev/null +++ b/tests/rustdoc/type-alias/repr.rs @@ -0,0 +1,42 @@ +// This test ensures that the `repr` attribute is displayed in type aliases. +// +// Regression test for . + +#![crate_name = "foo"] + +/// bla +#[repr(C)] +pub struct Foo1; + +//@ has 'foo/type.Bar1.html' +//@ has - '//*[@class="rust item-decl"]/code' '#[repr(C)]pub struct Bar1;' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar1 = Foo1; + +/// bla +#[repr(C)] +pub union Foo2 { + pub a: u8, +} + +//@ has 'foo/type.Bar2.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]\npub union Bar2 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar2 = Foo2; + +/// bla +#[repr(C)] +pub enum Foo3 { + A, +} + +//@ has 'foo/type.Bar3.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]pub enum Bar3 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar3 = Foo3; diff --git a/tests/rustdoc/type-layout.rs b/tests/rustdoc/type-layout.rs index 6de435dbcc14..482b8b597dd3 100644 --- a/tests/rustdoc/type-layout.rs +++ b/tests/rustdoc/type-layout.rs @@ -61,7 +61,7 @@ pub type TypeAlias = X; pub type GenericTypeAlias = (Generic<(u32, ())>, Generic); // Regression test for the rustdoc equivalent of #85103. -//@ hasraw type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.' +//@ hasraw type_layout/type.Edges.html 'Unable to compute type layout, possibly due to this type having generic parameters. Layout can only be computed for concrete, fully-instantiated types.' pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>; //@ !hasraw type_layout/trait.MyTrait.html 'Size: ' diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 4a866560e798..f5cfa9e0bccf 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -34,7 +34,7 @@ extern crate thin_vec; extern crate rustc_driver; use parser::parse_expr; -use rustc_ast::mut_visit::{visit_clobber, MutVisitor}; +use rustc_ast::mut_visit::MutVisitor; use rustc_ast::ptr::P; use rustc_ast::*; use rustc_ast_pretty::pprust; @@ -202,15 +202,9 @@ struct AddParens; impl MutVisitor for AddParens { fn visit_expr(&mut self, e: &mut P) { mut_visit::walk_expr(self, e); - visit_clobber(e, |e| { - P(Expr { - id: DUMMY_NODE_ID, - kind: ExprKind::Paren(e), - span: DUMMY_SP, - attrs: AttrVec::new(), - tokens: None, - }) - }); + let expr = std::mem::replace(e, Expr::dummy()); + + e.kind = ExprKind::Paren(expr); } } diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index 2b41020d3071..c566ac459e0d 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -42,7 +42,7 @@ use std::process::ExitCode; use parser::parse_expr; use rustc_ast::ast::{Expr, ExprKind}; -use rustc_ast::mut_visit::{self, DummyAstNode as _, MutVisitor}; +use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast_pretty::pprust; use rustc_session::parse::ParseSess; @@ -154,7 +154,7 @@ struct Unparenthesize; impl MutVisitor for Unparenthesize { fn visit_expr(&mut self, e: &mut P) { while let ExprKind::Paren(paren) = &mut e.kind { - **e = mem::replace(&mut *paren, Expr::dummy()); + *e = mem::replace(paren, Expr::dummy()); } mut_visit::walk_expr(self, e); } diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs index 35e1d037f388..622f99aa1b1e 100644 --- a/tests/ui/asm/aarch64/parse-error.rs +++ b/tests/ui/asm/aarch64/parse-error.rs @@ -1,57 +1,11 @@ //@ only-aarch64 -use std::arch::{asm, global_asm}; +use std::arch::asm; fn main() { let mut foo = 0; let mut bar = 0; unsafe { - asm!(); - //~^ ERROR requires at least a template string argument - asm!(foo); - //~^ ERROR asm template must be a string literal - asm!("{}" foo); - //~^ ERROR expected token: `,` - asm!("{}", foo); - //~^ ERROR expected operand, clobber_abi, options, or additional template string - asm!("{}", in foo); - //~^ ERROR expected `(`, found `foo` - asm!("{}", in(reg foo)); - //~^ ERROR expected `)`, found `foo` - asm!("{}", in(reg)); - //~^ ERROR expected expression, found end of macro arguments - asm!("{}", inout(=) foo => bar); - //~^ ERROR expected register class or explicit register - asm!("{}", inout(reg) foo =>); - //~^ ERROR expected expression, found end of macro arguments - asm!("{}", in(reg) foo => bar); - //~^ ERROR expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` - asm!("{}", sym foo + bar); - //~^ ERROR expected a path for argument to `sym` - asm!("", options(foo)); - //~^ ERROR expected one of - asm!("", options(nomem foo)); - //~^ ERROR expected one of - asm!("", options(nomem, foo)); - //~^ ERROR expected one of - asm!("{}", options(), const foo); - //~^ ERROR attempt to use a non-constant value in a constant - asm!("", clobber_abi(foo)); - //~^ ERROR expected string literal - asm!("", clobber_abi("C" foo)); - //~^ ERROR expected one of `)` or `,`, found `foo` - asm!("", clobber_abi("C", foo)); - //~^ ERROR expected string literal - asm!("{}", clobber_abi("C"), const foo); - //~^ ERROR attempt to use a non-constant value in a constant - asm!("", options(), clobber_abi("C")); - asm!("{}", options(), clobber_abi("C"), const foo); - //~^ ERROR attempt to use a non-constant value in a constant - asm!("{a}", a = const foo, a = const bar); - //~^ ERROR duplicate argument named `a` - //~^^ ERROR argument never used - //~^^^ ERROR attempt to use a non-constant value in a constant - //~^^^^ ERROR attempt to use a non-constant value in a constant asm!("", a = in("x0") foo); //~^ ERROR explicit register arguments cannot have names asm!("{a}", in("x0") foo, a = const bar); @@ -61,64 +15,5 @@ fn main() { asm!("{1}", in("x0") foo, const bar); //~^ ERROR positional arguments cannot follow named arguments or explicit register arguments //~^^ ERROR attempt to use a non-constant value in a constant - asm!("", options(), ""); - //~^ ERROR expected one of - asm!("{}", in(reg) foo, "{}", out(reg) foo); - //~^ ERROR expected one of - asm!(format!("{{{}}}", 0), in(reg) foo); - //~^ ERROR asm template must be a string literal - asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); - //~^ ERROR asm template must be a string literal - asm!("{}", in(reg) _); - //~^ ERROR _ cannot be used for input operands - asm!("{}", inout(reg) _); - //~^ ERROR _ cannot be used for input operands - asm!("{}", inlateout(reg) _); - //~^ ERROR _ cannot be used for input operands } } - -const FOO: i32 = 1; -const BAR: i32 = 2; -global_asm!(); -//~^ ERROR requires at least a template string argument -global_asm!(FOO); -//~^ ERROR asm template must be a string literal -global_asm!("{}" FOO); -//~^ ERROR expected token: `,` -global_asm!("{}", FOO); -//~^ ERROR expected operand, options, or additional template string -global_asm!("{}", const); -//~^ ERROR expected expression, found end of macro arguments -global_asm!("{}", const(reg) FOO); -//~^ ERROR expected one of -global_asm!("", options(FOO)); -//~^ ERROR expected one of -global_asm!("", options(nomem FOO)); -//~^ ERROR expected one of -global_asm!("", options(nomem, FOO)); -//~^ ERROR expected one of -global_asm!("{}", options(), const FOO); -global_asm!("", clobber_abi(FOO)); -//~^ ERROR expected string literal -global_asm!("", clobber_abi("C" FOO)); -//~^ ERROR expected one of `)` or `,`, found `FOO` -global_asm!("", clobber_abi("C", FOO)); -//~^ ERROR expected string literal -global_asm!("{}", clobber_abi("C"), const FOO); -//~^ ERROR `clobber_abi` cannot be used with `global_asm!` -global_asm!("", options(), clobber_abi("C")); -//~^ ERROR `clobber_abi` cannot be used with `global_asm!` -global_asm!("{}", options(), clobber_abi("C"), const FOO); -//~^ ERROR `clobber_abi` cannot be used with `global_asm!` -global_asm!("{a}", a = const FOO, a = const BAR); -//~^ ERROR duplicate argument named `a` -//~^^ ERROR argument never used -global_asm!("", options(), ""); -//~^ ERROR expected one of -global_asm!("{}", const FOO, "{}", const FOO); -//~^ ERROR expected one of -global_asm!(format!("{{{}}}", 0), const FOO); -//~^ ERROR asm template must be a string literal -global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); -//~^ ERROR asm template must be a string literal diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index 45f9e7989c2e..ca21311f87f9 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -1,377 +1,19 @@ -error: requires at least a template string argument - --> $DIR/parse-error.rs:9:9 - | -LL | asm!(); - | ^^^^^^ - -error: asm template must be a string literal - --> $DIR/parse-error.rs:11:14 - | -LL | asm!(foo); - | ^^^ - -error: expected token: `,` - --> $DIR/parse-error.rs:13:19 - | -LL | asm!("{}" foo); - | ^^^ expected `,` - -error: expected operand, clobber_abi, options, or additional template string - --> $DIR/parse-error.rs:15:20 - | -LL | asm!("{}", foo); - | ^^^ expected operand, clobber_abi, options, or additional template string - -error: expected `(`, found `foo` - --> $DIR/parse-error.rs:17:23 - | -LL | asm!("{}", in foo); - | ^^^ expected `(` - -error: expected `)`, found `foo` - --> $DIR/parse-error.rs:19:27 - | -LL | asm!("{}", in(reg foo)); - | ^^^ expected `)` - -error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:21:27 - | -LL | asm!("{}", in(reg)); - | ^ expected expression - -error: expected register class or explicit register - --> $DIR/parse-error.rs:23:26 - | -LL | asm!("{}", inout(=) foo => bar); - | ^ - -error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:25:37 - | -LL | asm!("{}", inout(reg) foo =>); - | ^ expected expression - -error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` - --> $DIR/parse-error.rs:27:32 - | -LL | asm!("{}", in(reg) foo => bar); - | ^^ expected one of 7 possible tokens - -error: expected a path for argument to `sym` - --> $DIR/parse-error.rs:29:24 - | -LL | asm!("{}", sym foo + bar); - | ^^^^^^^^^ - -error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:31:26 - | -LL | asm!("", options(foo)); - | ^^^ expected one of 10 possible tokens - -error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:33:32 - | -LL | asm!("", options(nomem foo)); - | ^^^ expected one of `)` or `,` - -error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:35:33 - | -LL | asm!("", options(nomem, foo)); - | ^^^ expected one of 10 possible tokens - -error: expected string literal - --> $DIR/parse-error.rs:39:30 - | -LL | asm!("", clobber_abi(foo)); - | ^^^ not a string literal - -error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:41:34 - | -LL | asm!("", clobber_abi("C" foo)); - | ^^^ expected one of `)` or `,` - -error: expected string literal - --> $DIR/parse-error.rs:43:35 - | -LL | asm!("", clobber_abi("C", foo)); - | ^^^ not a string literal - -error: duplicate argument named `a` - --> $DIR/parse-error.rs:50:36 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ------------- ^^^^^^^^^^^^^ duplicate argument - | | - | previously here - -error: argument never used - --> $DIR/parse-error.rs:50:36 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ^^^^^^^^^^^^^ argument never used - | - = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` - error: explicit register arguments cannot have names - --> $DIR/parse-error.rs:55:18 + --> $DIR/parse-error.rs:9:18 | LL | asm!("", a = in("x0") foo); | ^^^^^^^^^^^^^^^^ error: positional arguments cannot follow named arguments or explicit register arguments - --> $DIR/parse-error.rs:61:35 + --> $DIR/parse-error.rs:15:35 | LL | asm!("{1}", in("x0") foo, const bar); | ------------ ^^^^^^^^^ positional argument | | | explicit register argument -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` - --> $DIR/parse-error.rs:64:29 - | -LL | asm!("", options(), ""); - | ^^ expected one of 10 possible tokens - -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:66:33 - | -LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); - | ^^^^ expected one of 10 possible tokens - -error: asm template must be a string literal - --> $DIR/parse-error.rs:68:14 - | -LL | asm!(format!("{{{}}}", 0), in(reg) foo); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: asm template must be a string literal - --> $DIR/parse-error.rs:70:21 - | -LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: _ cannot be used for input operands - --> $DIR/parse-error.rs:72:28 - | -LL | asm!("{}", in(reg) _); - | ^ - -error: _ cannot be used for input operands - --> $DIR/parse-error.rs:74:31 - | -LL | asm!("{}", inout(reg) _); - | ^ - -error: _ cannot be used for input operands - --> $DIR/parse-error.rs:76:35 - | -LL | asm!("{}", inlateout(reg) _); - | ^ - -error: requires at least a template string argument - --> $DIR/parse-error.rs:83:1 - | -LL | global_asm!(); - | ^^^^^^^^^^^^^ - -error: asm template must be a string literal - --> $DIR/parse-error.rs:85:13 - | -LL | global_asm!(FOO); - | ^^^ - -error: expected token: `,` - --> $DIR/parse-error.rs:87:18 - | -LL | global_asm!("{}" FOO); - | ^^^ expected `,` - -error: expected operand, options, or additional template string - --> $DIR/parse-error.rs:89:19 - | -LL | global_asm!("{}", FOO); - | ^^^ expected operand, options, or additional template string - -error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:91:24 - | -LL | global_asm!("{}", const); - | ^ expected expression - -error: expected one of `,`, `.`, `?`, or an operator, found `FOO` - --> $DIR/parse-error.rs:93:30 - | -LL | global_asm!("{}", const(reg) FOO); - | ^^^ expected one of `,`, `.`, `?`, or an operator - -error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:95:25 - | -LL | global_asm!("", options(FOO)); - | ^^^ expected one of `)`, `att_syntax`, or `raw` - -error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:97:31 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^ expected one of `)` or `,` - -error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:99:32 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^ expected one of `)`, `att_syntax`, or `raw` - -error: expected string literal - --> $DIR/parse-error.rs:102:29 - | -LL | global_asm!("", clobber_abi(FOO)); - | ^^^ not a string literal - -error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:104:33 - | -LL | global_asm!("", clobber_abi("C" FOO)); - | ^^^ expected one of `)` or `,` - -error: expected string literal - --> $DIR/parse-error.rs:106:34 - | -LL | global_asm!("", clobber_abi("C", FOO)); - | ^^^ not a string literal - -error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:108:19 - | -LL | global_asm!("{}", clobber_abi("C"), const FOO); - | ^^^^^^^^^^^^^^^^ - -error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:110:28 - | -LL | global_asm!("", options(), clobber_abi("C")); - | ^^^^^^^^^^^^^^^^ - -error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:112:30 - | -LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); - | ^^^^^^^^^^^^^^^^ - -error: duplicate argument named `a` - --> $DIR/parse-error.rs:114:35 - | -LL | global_asm!("{a}", a = const FOO, a = const BAR); - | ------------- ^^^^^^^^^^^^^ duplicate argument - | | - | previously here - -error: argument never used - --> $DIR/parse-error.rs:114:35 - | -LL | global_asm!("{a}", a = const FOO, a = const BAR); - | ^^^^^^^^^^^^^ argument never used - | - = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` - -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:117:28 - | -LL | global_asm!("", options(), ""); - | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` - -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:119:30 - | -LL | global_asm!("{}", const FOO, "{}", const FOO); - | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` - -error: asm template must be a string literal - --> $DIR/parse-error.rs:121:13 - | -LL | global_asm!(format!("{{{}}}", 0), const FOO); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: asm template must be a string literal - --> $DIR/parse-error.rs:123:20 - | -LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:37:37 - | -LL | asm!("{}", options(), const foo); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:45:44 - | -LL | asm!("{}", clobber_abi("C"), const foo); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:48:55 - | -LL | asm!("{}", options(), clobber_abi("C"), const foo); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:50:31 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:50:46 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut bar = 0; -LL + const bar: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:57:45 + --> $DIR/parse-error.rs:11:45 | LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value @@ -383,7 +25,7 @@ LL + const bar: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:59:45 + --> $DIR/parse-error.rs:13:45 | LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value @@ -395,7 +37,7 @@ LL + const bar: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:61:41 + --> $DIR/parse-error.rs:15:41 | LL | asm!("{1}", in("x0") foo, const bar); | ^^^ non-constant value @@ -406,6 +48,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 57 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/cfg-parse-error.rs b/tests/ui/asm/cfg-parse-error.rs new file mode 100644 index 000000000000..9b79d16a76dd --- /dev/null +++ b/tests/ui/asm/cfg-parse-error.rs @@ -0,0 +1,57 @@ +//@ needs-asm-support +#![feature(asm_cfg)] + +use std::arch::asm; + +fn main() { + unsafe { + asm!( + "", + #[cfg(false)] + clobber_abi("C"), + #[cfg(false)] + options(att_syntax), + #[cfg(false)] + a = out(reg) x, + "", + //~^ ERROR expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` + ); + asm!( + #[cfg(false)] + "", + #[cfg(false)] + const { + 5 + }, + "", + //~^ ERROR expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` + ); + + asm!( + #[cfg_attr(true, cfg(false))] + const { + 5 + }, + "", + ); + + // This is not accepted because `a = out(reg) x` is not a valid expression. + asm!( + #[cfg(false)] + a = out(reg) x, //~ ERROR expected token: `,` + "", + ); + + // For now, any non-cfg attributes are rejected + asm!( + #[rustfmt::skip] //~ ERROR this attribute is not supported on assembly + "", + ); + + // For now, any non-cfg attributes are rejected + asm!( + #![rustfmt::skip] //~ ERROR an inner attribute is not permitted in this context + "", + ); + } +} diff --git a/tests/ui/asm/cfg-parse-error.stderr b/tests/ui/asm/cfg-parse-error.stderr new file mode 100644 index 000000000000..8a70d39a43dc --- /dev/null +++ b/tests/ui/asm/cfg-parse-error.stderr @@ -0,0 +1,39 @@ +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` + --> $DIR/cfg-parse-error.rs:16:13 + | +LL | a = out(reg) x, + | - expected one of 11 possible tokens +LL | "", + | ^^ unexpected token + +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` + --> $DIR/cfg-parse-error.rs:26:13 + | +LL | }, + | - expected one of 11 possible tokens +LL | "", + | ^^ unexpected token + +error: expected token: `,` + --> $DIR/cfg-parse-error.rs:41:26 + | +LL | a = out(reg) x, + | ^ expected `,` + +error: this attribute is not supported on assembly + --> $DIR/cfg-parse-error.rs:47:13 + | +LL | #[rustfmt::skip] + | ^^^^^^^^^^^^^^^^ + +error: an inner attribute is not permitted in this context + --> $DIR/cfg-parse-error.rs:53:13 + | +LL | #![rustfmt::skip] + | ^^^^^^^^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + = note: outer attributes, like `#[test]`, annotate the item following them + +error: aborting due to 5 previous errors + diff --git a/tests/ui/asm/cfg.rs b/tests/ui/asm/cfg.rs new file mode 100644 index 000000000000..bcf86340b9d8 --- /dev/null +++ b/tests/ui/asm/cfg.rs @@ -0,0 +1,125 @@ +// Check that `cfg` and `cfg_attr` work as expected. +// +//@ revisions: reva revb +//@ only-x86_64 +//@ run-pass +#![feature(asm_cfg, cfg_select)] + +use std::arch::{asm, naked_asm}; + +#[unsafe(naked)] +extern "C" fn ignore_const_operand() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg(revb)] + "mov rax, {a}", + "ret", + #[cfg(revb)] + a = const 10, + ) +} + +#[unsafe(naked)] +extern "C" fn ignore_const_operand_cfg_attr() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg_attr(true, cfg(revb))] + "mov rax, {a}", + "ret", + #[cfg_attr(true, cfg(revb))] + a = const 10, + ) +} + +#[unsafe(naked)] +extern "C" fn const_operand() -> u64 { + naked_asm!( + "mov rax, {a}", + "ret", + #[cfg(reva)] + a = const 5, + #[cfg(revb)] + a = const 10, + ) +} + +fn options() { + // Without the cfg, this throws an error that the `att_syntax` option is provided twice. + unsafe { + asm!( + "nop", + #[cfg(false)] + options(att_syntax), + options(att_syntax) + ) + } +} + +fn clobber_abi() { + // Without the cfg, this throws an error that the "C" abi is provided twice. + unsafe { + asm!( + "nop", + #[cfg(false)] + clobber_abi("C"), + clobber_abi("C"), + ); + } +} + +#[unsafe(naked)] +extern "C" fn first_template() -> u64 { + naked_asm!( + #[cfg(reva)] + "mov rax, 5", + #[cfg(revb)] + "mov rax, 10", + "ret", + ) +} + +#[unsafe(naked)] +extern "C" fn true_and_false() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg(true)] + #[cfg(false)] + "mov rax, 10", + "ret", + ) +} + +#[unsafe(naked)] +extern "C" fn false_and_true() -> u64 { + naked_asm!( + "mov rax, 5", + #[cfg(false)] + #[cfg(true)] + "mov rax, 10", + "ret", + ) +} + +pub fn main() { + std::cfg_select! { + reva => { + assert_eq!(const_operand(), 5); + assert_eq!(ignore_const_operand_cfg_attr(), 5); + assert_eq!(ignore_const_operand(), 5); + assert_eq!(first_template(), 5); + + } + revb => { + assert_eq!(const_operand(), 10); + assert_eq!(ignore_const_operand_cfg_attr(), 10); + assert_eq!(ignore_const_operand(), 10); + assert_eq!(first_template(), 10); + + } + } + options(); + clobber_abi(); + + assert_eq!(true_and_false(), 5); + assert_eq!(false_and_true(), 5); +} diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 0bba1fd8d9b6..dff85a601b73 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -176,17 +176,17 @@ LL | asm!("{a}", a = const foo, a = const bar); | = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` --> $DIR/parse-error.rs:80:29 | LL | asm!("", options(), ""); - | ^^ expected one of 10 possible tokens + | ^^ expected one of 11 possible tokens -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` --> $DIR/parse-error.rs:82:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); - | ^^^^ expected one of 10 possible tokens + | ^^^^ expected one of 11 possible tokens error: asm template must be a string literal --> $DIR/parse-error.rs:84:14 @@ -340,17 +340,17 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` +error: expected one of `#`, `clobber_abi`, `const`, `options`, or `sym`, found `""` --> $DIR/parse-error.rs:137:28 | LL | global_asm!("", options(), ""); - | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` + | ^^ expected one of `#`, `clobber_abi`, `const`, `options`, or `sym` -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` +error: expected one of `#`, `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` --> $DIR/parse-error.rs:139:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); - | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` + | ^^^^ expected one of `#`, `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal --> $DIR/parse-error.rs:141:13 diff --git a/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr index a8256f775a68..19b63a041d61 100644 --- a/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr +++ b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr @@ -26,6 +26,19 @@ LL | pub fn test(arg: EFoo) { LL | B::X => println!("B::X"), | ^^^^ `const` depends on a generic parameter +error[E0158]: constant pattern cannot depend on generic parameters + --> $DIR/associated-const-type-parameter-pattern.rs:28:48 + | +LL | pub trait Foo { + | ------------- +LL | const X: EFoo; + | ------------- constant defined here +... +LL | pub fn test_let_pat(arg: EFoo, A::X: EFoo) { + | - ^^^^ `const` depends on a generic parameter + | | + | constant depends on this generic parameter + error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/associated-const-type-parameter-pattern.rs:30:9 | @@ -40,19 +53,6 @@ LL | LL | let A::X = arg; | ^^^^ `const` depends on a generic parameter -error[E0158]: constant pattern cannot depend on generic parameters - --> $DIR/associated-const-type-parameter-pattern.rs:28:48 - | -LL | pub trait Foo { - | ------------- -LL | const X: EFoo; - | ------------- constant defined here -... -LL | pub fn test_let_pat(arg: EFoo, A::X: EFoo) { - | - ^^^^ `const` depends on a generic parameter - | | - | constant depends on this generic parameter - error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr index 978305c2ce35..3e914e0538d0 100644 --- a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr @@ -2,7 +2,13 @@ error[E0223]: ambiguous associated type --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:12 | LL | let _: S::::Pr = (); - | ^^^^^^^^^^^^^ help: use fully-qualified syntax: ` as Tr>::Pr` + | ^^^^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: S::::Pr = (); +LL + let _: as Tr>::Pr = (); + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-item/ambiguous-associated-type-with-generics.stderr b/tests/ui/associated-item/ambiguous-associated-type-with-generics.stderr index 9e1dd7398079..fcf6b4c3e736 100644 --- a/tests/ui/associated-item/ambiguous-associated-type-with-generics.stderr +++ b/tests/ui/associated-item/ambiguous-associated-type-with-generics.stderr @@ -2,7 +2,12 @@ error[E0223]: ambiguous associated type --> $DIR/ambiguous-associated-type-with-generics.rs:13:13 | LL | let _x: >::Ty; - | ^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: ` as Assoc>::Ty` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL | let _x: as Assoc>::Ty; + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/associated-item/associated-item-duplicate-names-3.stderr b/tests/ui/associated-item/associated-item-duplicate-names-3.stderr index a2346e292ac4..84a9da099883 100644 --- a/tests/ui/associated-item/associated-item-duplicate-names-3.stderr +++ b/tests/ui/associated-item/associated-item-duplicate-names-3.stderr @@ -13,7 +13,13 @@ error[E0223]: ambiguous associated type --> $DIR/associated-item-duplicate-names-3.rs:18:12 | LL | let x: Baz::Bar = 5; - | ^^^^^^^^ help: use fully-qualified syntax: `::Bar` + | ^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let x: Baz::Bar = 5; +LL + let x: ::Bar = 5; + | error: aborting due to 2 previous errors diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr index edac09db89da..677fc0e10bbd 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/path-missing.stderr @@ -8,9 +8,7 @@ error[E0575]: expected method or associated constant, found associated type `A:: --> $DIR/path-missing.rs:12:5 | LL | ::bad(..): Send, - | ^^^^^^^^^^^^^^^^^ - | - = note: can't use a type alias as a constructor + | ^^^^^^^^^^^^^^^^^ not a method or associated constant error[E0220]: associated function `method` not found for `T` --> $DIR/path-missing.rs:19:8 diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr index aad6dfc496b7..a13fdbda1bf7 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr @@ -7,7 +7,7 @@ LL | Trait::method(..): Send, help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path | LL - Trait::method(..): Send, -LL + ::method: Send, +LL + ::method(..): Send, | error: aborting due to 1 previous error diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr index 38202bdbf072..4c4c2c24079b 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/path-non-param-qself.stderr @@ -3,18 +3,35 @@ error[E0223]: ambiguous associated function | LL | <()>::method(..): Send, | ^^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated function `method` implemented for `()`, you could use the fully-qualified path + | +LL | <() as Example>::method(..): Send, + | ++++++++++ error[E0223]: ambiguous associated function --> $DIR/path-non-param-qself.rs:13:5 | LL | i32::method(..): Send, | ^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated function `method` implemented for `i32`, you could use the fully-qualified path + | +LL - i32::method(..): Send, +LL + ::method(..): Send, + | error[E0223]: ambiguous associated function --> $DIR/path-non-param-qself.rs:15:5 | LL | Adt::method(..): Send, | ^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated function `method` implemented for `Adt`, you could use the fully-qualified path + | +LL - Adt::method(..): Send, +LL + ::method(..): Send, + | error: aborting due to 3 previous errors diff --git a/tests/ui/associated-types/associated-type-tuple-struct-construction.rs b/tests/ui/associated-types/associated-type-tuple-struct-construction.rs deleted file mode 100644 index d5809ecd55d8..000000000000 --- a/tests/ui/associated-types/associated-type-tuple-struct-construction.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Users cannot yet construct structs through associated types -// in both expressions and patterns - -#![feature(more_qualified_paths)] - -fn main() { - let ::Assoc(n) = ::Assoc(2); - //~^ ERROR expected method or associated constant, found associated type - //~| ERROR expected method or associated constant, found associated type - assert!(n == 2); -} - -struct TupleStruct(i8); - -struct Foo; - - -trait A { - type Assoc; -} - -impl A for Foo { - type Assoc = TupleStruct; -} diff --git a/tests/ui/associated-types/associated-type-tuple-struct-construction.stderr b/tests/ui/associated-types/associated-type-tuple-struct-construction.stderr deleted file mode 100644 index bca7deeb5128..000000000000 --- a/tests/ui/associated-types/associated-type-tuple-struct-construction.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0575]: expected method or associated constant, found associated type `A::Assoc` - --> $DIR/associated-type-tuple-struct-construction.rs:7:32 - | -LL | let ::Assoc(n) = ::Assoc(2); - | ^^^^^^^^^^^^^^^^^ - | - = note: can't use a type alias as a constructor - -error[E0575]: expected method or associated constant, found associated type `A::Assoc` - --> $DIR/associated-type-tuple-struct-construction.rs:7:9 - | -LL | let ::Assoc(n) = ::Assoc(2); - | ^^^^^^^^^^^^^^^^^ - | - = note: can't use a type alias as a constructor - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0575`. diff --git a/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr b/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr index 1be8db5ddf44..a7647cf26aad 100644 --- a/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr +++ b/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr @@ -14,7 +14,13 @@ error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:22:17 | LL | trait Foo where Foo::Assoc: Bar { - | ^^^^^^^^^^ help: use fully-qualified syntax: `::Assoc` + | ^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - trait Foo where Foo::Assoc: Bar { +LL + trait Foo where ::Assoc: Bar { + | error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:27:10 @@ -42,7 +48,13 @@ error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:13:23 | LL | fn grab(&self) -> Grab::Value; - | ^^^^^^^^^^^ help: use fully-qualified syntax: `::Value` + | ^^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - fn grab(&self) -> Grab::Value; +LL + fn grab(&self) -> ::Value; + | error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:16:22 diff --git a/tests/ui/associated-types/issue-43924.stderr b/tests/ui/associated-types/issue-43924.stderr index ab1a9511ec6c..526f425b21e7 100644 --- a/tests/ui/associated-types/issue-43924.stderr +++ b/tests/ui/associated-types/issue-43924.stderr @@ -14,7 +14,7 @@ error[E0599]: no function or associated item named `default` found for trait obj --> $DIR/issue-43924.rs:14:39 | LL | assert_eq!(<() as Foo>::Out::default().to_string(), "false"); - | ^^^^^^^ function or associated item not found in `dyn ToString` + | ^^^^^^^ function or associated item not found in `(dyn ToString + 'static)` error: aborting due to 2 previous errors diff --git a/tests/ui/associated-types/tuple-struct-expr-pat.fixed b/tests/ui/associated-types/tuple-struct-expr-pat.fixed new file mode 100644 index 000000000000..d6e2385f8210 --- /dev/null +++ b/tests/ui/associated-types/tuple-struct-expr-pat.fixed @@ -0,0 +1,48 @@ +// Check that fully qualified syntax can **not** be used in tuple struct expressions (calls) and +// patterns. Both tuple struct expressions and patterns are resolved in value namespace and thus +// can't be resolved through associated *types*. +// +//@ run-rustfix + +#![feature(more_qualified_paths)] + +fn main() { + let as Trait>::Assoc {} = as Trait>::Assoc {}; + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type + let as Trait>::Assoc { 0: _a } = as Trait>::Assoc { 0: 0 }; + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type + let as Trait>::Assoc { 0: _a, 1: _b } = as Trait>::Assoc { 0: 0, 1: 1 }; + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type + let as Trait>::Assoc { 0: ref _a, 1: ref mut _b, 2: mut _c } = as Trait>::Assoc { 0: 0, 1: 1, 2: 2 }; + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type +} + + +struct T; + +struct T0(); +struct T1(u8); +struct T2(u8, u8); +struct T3(u8, u8, u8); + +trait Trait { + type Assoc; +} + +impl Trait for T<0> { + type Assoc = T0; +} + +impl Trait for T<1> { + type Assoc = T1; +} +impl Trait for T<2> { + type Assoc = T2; +} +impl Trait for T<3> { + type Assoc = T3; +} diff --git a/tests/ui/associated-types/tuple-struct-expr-pat.rs b/tests/ui/associated-types/tuple-struct-expr-pat.rs new file mode 100644 index 000000000000..f27a5fe17539 --- /dev/null +++ b/tests/ui/associated-types/tuple-struct-expr-pat.rs @@ -0,0 +1,48 @@ +// Check that fully qualified syntax can **not** be used in tuple struct expressions (calls) and +// patterns. Both tuple struct expressions and patterns are resolved in value namespace and thus +// can't be resolved through associated *types*. +// +//@ run-rustfix + +#![feature(more_qualified_paths)] + +fn main() { + let as Trait>::Assoc() = as Trait>::Assoc(); + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type + let as Trait>::Assoc(_a) = as Trait>::Assoc(0); + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type + let as Trait>::Assoc(_a, _b) = as Trait>::Assoc(0, 1); + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type + let as Trait>::Assoc(ref _a, ref mut _b, mut _c) = as Trait>::Assoc(0, 1, 2); + //~^ error: expected method or associated constant, found associated type + //~| error: expected tuple struct or tuple variant, found associated type +} + + +struct T; + +struct T0(); +struct T1(u8); +struct T2(u8, u8); +struct T3(u8, u8, u8); + +trait Trait { + type Assoc; +} + +impl Trait for T<0> { + type Assoc = T0; +} + +impl Trait for T<1> { + type Assoc = T1; +} +impl Trait for T<2> { + type Assoc = T2; +} +impl Trait for T<3> { + type Assoc = T3; +} diff --git a/tests/ui/associated-types/tuple-struct-expr-pat.stderr b/tests/ui/associated-types/tuple-struct-expr-pat.stderr new file mode 100644 index 000000000000..135dfcb3447e --- /dev/null +++ b/tests/ui/associated-types/tuple-struct-expr-pat.stderr @@ -0,0 +1,97 @@ +error[E0575]: expected method or associated constant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:10:36 + | +LL | let as Trait>::Assoc() = as Trait>::Assoc(); + | ^^^^^^^^^^^^^^^^^^^^^^-- help: use struct expression instead: `{}` + | + = note: can't use a type alias as a constructor + +error[E0575]: expected tuple struct or tuple variant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:10:9 + | +LL | let as Trait>::Assoc() = as Trait>::Assoc(); + | ^^^^^^^^^^^^^^^^^^^^^^-- help: use struct pattern instead: `{}` + | + = note: can't use a type alias as tuple pattern + +error[E0575]: expected method or associated constant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:13:38 + | +LL | let as Trait>::Assoc(_a) = as Trait>::Assoc(0); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as a constructor +help: use struct expression instead + | +LL - let as Trait>::Assoc(_a) = as Trait>::Assoc(0); +LL + let as Trait>::Assoc(_a) = as Trait>::Assoc { 0: 0 }; + | + +error[E0575]: expected tuple struct or tuple variant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:13:9 + | +LL | let as Trait>::Assoc(_a) = as Trait>::Assoc(0); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as tuple pattern +help: use struct pattern instead + | +LL - let as Trait>::Assoc(_a) = as Trait>::Assoc(0); +LL + let as Trait>::Assoc { 0: _a } = as Trait>::Assoc(0); + | + +error[E0575]: expected method or associated constant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:16:42 + | +LL | let as Trait>::Assoc(_a, _b) = as Trait>::Assoc(0, 1); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as a constructor +help: use struct expression instead + | +LL - let as Trait>::Assoc(_a, _b) = as Trait>::Assoc(0, 1); +LL + let as Trait>::Assoc(_a, _b) = as Trait>::Assoc { 0: 0, 1: 1 }; + | + +error[E0575]: expected tuple struct or tuple variant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:16:9 + | +LL | let as Trait>::Assoc(_a, _b) = as Trait>::Assoc(0, 1); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as tuple pattern +help: use struct pattern instead + | +LL - let as Trait>::Assoc(_a, _b) = as Trait>::Assoc(0, 1); +LL + let as Trait>::Assoc { 0: _a, 1: _b } = as Trait>::Assoc(0, 1); + | + +error[E0575]: expected method or associated constant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:19:62 + | +LL | let as Trait>::Assoc(ref _a, ref mut _b, mut _c) = as Trait>::Assoc(0, 1, 2); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as a constructor +help: use struct expression instead + | +LL - let as Trait>::Assoc(ref _a, ref mut _b, mut _c) = as Trait>::Assoc(0, 1, 2); +LL + let as Trait>::Assoc(ref _a, ref mut _b, mut _c) = as Trait>::Assoc { 0: 0, 1: 1, 2: 2 }; + | + +error[E0575]: expected tuple struct or tuple variant, found associated type `Trait::Assoc` + --> $DIR/tuple-struct-expr-pat.rs:19:9 + | +LL | let as Trait>::Assoc(ref _a, ref mut _b, mut _c) = as Trait>::Assoc(0, 1, 2); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as tuple pattern +help: use struct pattern instead + | +LL - let as Trait>::Assoc(ref _a, ref mut _b, mut _c) = as Trait>::Assoc(0, 1, 2); +LL + let as Trait>::Assoc { 0: ref _a, 1: ref mut _b, 2: mut _c } = as Trait>::Assoc(0, 1, 2); + | + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0575`. diff --git a/tests/ui/async-await/async-closures/async-fn-mut-impl-fn-once.rs b/tests/ui/async-await/async-closures/async-fn-mut-impl-fn-once.rs new file mode 100644 index 000000000000..59d034953d70 --- /dev/null +++ b/tests/ui/async-await/async-closures/async-fn-mut-impl-fn-once.rs @@ -0,0 +1,15 @@ +//@ edition:2021 +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +fn call_once(_: impl FnOnce() -> F) {} + +fn main() { + let mut i = 0; + let c = async || { + i += 1; + }; + call_once(c); +} diff --git a/tests/ui/async-await/async-closures/promote-in-body.rs b/tests/ui/async-await/async-closures/promote-in-body.rs new file mode 100644 index 000000000000..ea95d680987e --- /dev/null +++ b/tests/ui/async-await/async-closures/promote-in-body.rs @@ -0,0 +1,13 @@ +//@ build-pass +//@ compile-flags: --crate-type=lib +//@ edition: 2024 + +union U { + f: i32, +} + +fn foo() { + async || { + &U { f: 1 } + }; +} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.rs b/tests/ui/async-await/async-drop/dependency-dropped.rs index f763bb32b173..d7f415e19aad 100644 --- a/tests/ui/async-await/async-drop/dependency-dropped.rs +++ b/tests/ui/async-await/async-drop/dependency-dropped.rs @@ -1,9 +1,12 @@ //@ run-pass //@ check-run-results +//@ revisions: with_feature without_feature //@ aux-build:async-drop-dep.rs //@ edition:2021 -#![feature(async_drop)] +#![cfg_attr(with_feature, feature(async_drop))] +//[without_feature]~^ WARN found async drop types in dependency `async_drop_dep`, but async_drop feature is disabled for `dependency_dropped` + #![allow(incomplete_features)] extern crate async_drop_dep; diff --git a/tests/ui/async-await/async-drop/dependency-dropped.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout similarity index 100% rename from tests/ui/async-await/async-drop/dependency-dropped.run.stdout rename to tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout diff --git a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout new file mode 100644 index 000000000000..80eeeefc2220 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout @@ -0,0 +1 @@ +Sync drop diff --git a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr new file mode 100644 index 000000000000..96a4572055c6 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr @@ -0,0 +1,10 @@ +warning: found async drop types in dependency `async_drop_dep`, but async_drop feature is disabled for `dependency_dropped` + --> $DIR/dependency-dropped.rs:7:1 + | +LL | #![cfg_attr(with_feature, feature(async_drop))] + | ^ + | + = help: if async drop type will be dropped in a crate without `feature(async_drop)`, sync Drop will be used + +warning: 1 warning emitted + diff --git a/tests/ui/async-await/async-drop/deref-later-projection.rs b/tests/ui/async-await/async-drop/deref-later-projection.rs new file mode 100644 index 000000000000..baf81daf766b --- /dev/null +++ b/tests/ui/async-await/async-drop/deref-later-projection.rs @@ -0,0 +1,26 @@ +// Ex-ICE: #140975 +//@ compile-flags: -Zvalidate-mir +//@ build-pass +//@ edition:2021 +#![crate_type = "lib"] +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::{future::AsyncDrop, pin::Pin}; + +struct HasAsyncDrop ; +impl Drop for HasAsyncDrop { + fn drop(&mut self) {} +} +impl AsyncDrop for HasAsyncDrop { + async fn drop(self: Pin<&mut Self>) {} +} + +struct Holder { + inner: HasAsyncDrop, +} +async fn bar() { + Holder { + inner: HasAsyncDrop + }; +} diff --git a/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs new file mode 100644 index 000000000000..bd0a95eb1e49 --- /dev/null +++ b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs @@ -0,0 +1,17 @@ +//@ edition: 2024 +// Ex-ICE: #140974 +#![crate_type = "lib"] +#![allow(incomplete_features)] +#![feature(async_drop)] +use core::future::AsyncDrop; + +async fn fun(_: HasIncompleteAsyncDrop) {} + +struct HasIncompleteAsyncDrop; +impl Drop for HasIncompleteAsyncDrop { + fn drop(&mut self) {} +} +impl AsyncDrop for HasIncompleteAsyncDrop { + //~^ ERROR: not all trait items implemented, missing: `drop` [E0046] + // not implemented yet.. +} diff --git a/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.stderr b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.stderr new file mode 100644 index 000000000000..d8582398c797 --- /dev/null +++ b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.stderr @@ -0,0 +1,11 @@ +error[E0046]: not all trait items implemented, missing: `drop` + --> $DIR/elaborate-index-out-of-bounds.rs:14:1 + | +LL | impl AsyncDrop for HasIncompleteAsyncDrop { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation + | + = help: implement the missing item: `async fn drop(self: Pin<&mut Self>) { todo!() }` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/async-await/async-drop/open-drop-error2.rs b/tests/ui/async-await/async-drop/open-drop-error2.rs new file mode 100644 index 000000000000..b2a7b68190ec --- /dev/null +++ b/tests/ui/async-await/async-drop/open-drop-error2.rs @@ -0,0 +1,21 @@ +//@compile-flags: -Zvalidate-mir -Zinline-mir=yes --crate-type=lib + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::{ + future::{Future, async_drop_in_place}, + pin::pin, + task::Context, +}; + +fn wrong() -> impl Sized { + //~^ ERROR: the size for values of type `str` cannot be known at compilation time + *"abc" // Doesn't implement Sized +} +fn weird(context: &mut Context<'_>) { + let mut e = wrong(); + let h = unsafe { async_drop_in_place(&raw mut e) }; + let i = pin!(h); + i.poll(context); +} diff --git a/tests/ui/async-await/async-drop/open-drop-error2.stderr b/tests/ui/async-await/async-drop/open-drop-error2.stderr new file mode 100644 index 000000000000..e849829b1c78 --- /dev/null +++ b/tests/ui/async-await/async-drop/open-drop-error2.stderr @@ -0,0 +1,19 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/open-drop-error2.rs:12:15 + | +LL | fn wrong() -> impl Sized { + | ^^^^^^^^^^ doesn't have a size known at compile-time +LL | +LL | *"abc" // Doesn't implement Sized + | ------ return type was inferred to be `str` here + | + = help: the trait `Sized` is not implemented for `str` +help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression + | +LL - *"abc" // Doesn't implement Sized +LL + "abc" // Doesn't implement Sized + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs b/tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs index d222ddc081ef..60a7dff7d499 100644 --- a/tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs +++ b/tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs @@ -1,3 +1,4 @@ +//@ edition:2015 //@ check-pass // Make sure that we don't eagerly recover `async ::Bound` in edition 2015. diff --git a/tests/ui/async-await/async-fn/edition-2015.rs b/tests/ui/async-await/async-fn/edition-2015.rs index 341b9b10e671..5a81df3a4e4d 100644 --- a/tests/ui/async-await/async-fn/edition-2015.rs +++ b/tests/ui/async-await/async-fn/edition-2015.rs @@ -1,3 +1,4 @@ +//@ edition:2015 fn foo(x: impl async Fn()) -> impl async Fn() { x } //~^ ERROR `async` trait bounds are only allowed in Rust 2018 or later //~| ERROR `async` trait bounds are only allowed in Rust 2018 or later diff --git a/tests/ui/async-await/async-fn/edition-2015.stderr b/tests/ui/async-await/async-fn/edition-2015.stderr index ca9e64cd1bb0..0bec00c162c9 100644 --- a/tests/ui/async-await/async-fn/edition-2015.stderr +++ b/tests/ui/async-await/async-fn/edition-2015.stderr @@ -1,5 +1,5 @@ error: `async` trait bounds are only allowed in Rust 2018 or later - --> $DIR/edition-2015.rs:1:16 + --> $DIR/edition-2015.rs:2:16 | LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } | ^^^^^ @@ -8,7 +8,7 @@ LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } = note: for more on editions, read https://doc.rust-lang.org/edition-guide error: `async` trait bounds are only allowed in Rust 2018 or later - --> $DIR/edition-2015.rs:1:36 + --> $DIR/edition-2015.rs:2:36 | LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } | ^^^^^ @@ -17,7 +17,7 @@ LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0658]: `async` trait bounds are unstable - --> $DIR/edition-2015.rs:1:16 + --> $DIR/edition-2015.rs:2:16 | LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } | ^^^^^ @@ -28,7 +28,7 @@ LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } = help: use the desugared name of the async trait, such as `AsyncFn` error[E0658]: `async` trait bounds are unstable - --> $DIR/edition-2015.rs:1:36 + --> $DIR/edition-2015.rs:2:36 | LL | fn foo(x: impl async Fn()) -> impl async Fn() { x } | ^^^^^ diff --git a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.rs b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.rs index 50c1639996ee..714a3328ecf6 100644 --- a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.rs +++ b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.rs @@ -1,3 +1,4 @@ +//@ edition:2015 #![allow(non_camel_case_types)] #![deny(keyword_idents)] diff --git a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr index 8cea73f86514..70900e612f48 100644 --- a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr +++ b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr @@ -1,5 +1,5 @@ error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:5:13 + --> $DIR/2015-edition-error-various-positions.rs:6:13 | LL | pub mod await { | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -7,14 +7,14 @@ LL | pub mod await { = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #49716 note: the lint level is defined here - --> $DIR/2015-edition-error-various-positions.rs:2:9 + --> $DIR/2015-edition-error-various-positions.rs:3:9 | LL | #![deny(keyword_idents)] | ^^^^^^^^^^^^^^ = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]` error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:7:20 + --> $DIR/2015-edition-error-various-positions.rs:8:20 | LL | pub struct await; | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -23,7 +23,7 @@ LL | pub struct await; = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:11:16 + --> $DIR/2015-edition-error-various-positions.rs:12:16 | LL | use outer_mod::await::await; | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -32,7 +32,7 @@ LL | use outer_mod::await::await; = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:11:23 + --> $DIR/2015-edition-error-various-positions.rs:12:23 | LL | use outer_mod::await::await; | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -41,7 +41,7 @@ LL | use outer_mod::await::await; = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:16:14 + --> $DIR/2015-edition-error-various-positions.rs:17:14 | LL | struct Foo { await: () } | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -50,7 +50,7 @@ LL | struct Foo { await: () } = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:20:15 + --> $DIR/2015-edition-error-various-positions.rs:21:15 | LL | impl Foo { fn await() {} } | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -59,7 +59,7 @@ LL | impl Foo { fn await() {} } = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:24:14 + --> $DIR/2015-edition-error-various-positions.rs:25:14 | LL | macro_rules! await { | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -68,7 +68,7 @@ LL | macro_rules! await { = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:31:5 + --> $DIR/2015-edition-error-various-positions.rs:32:5 | LL | await!(); | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -77,7 +77,7 @@ LL | await!(); = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:34:11 + --> $DIR/2015-edition-error-various-positions.rs:35:11 | LL | match await { await => {} } | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -86,7 +86,7 @@ LL | match await { await => {} } = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-error-various-positions.rs:34:19 + --> $DIR/2015-edition-error-various-positions.rs:35:19 | LL | match await { await => {} } | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` diff --git a/tests/ui/async-await/await-keyword/2015-edition-warning.fixed b/tests/ui/async-await/await-keyword/2015-edition-warning.fixed index 4cb8017c7ee3..45758cb10391 100644 --- a/tests/ui/async-await/await-keyword/2015-edition-warning.fixed +++ b/tests/ui/async-await/await-keyword/2015-edition-warning.fixed @@ -1,3 +1,4 @@ +//@ edition:2015 //@ run-rustfix #![allow(non_camel_case_types)] diff --git a/tests/ui/async-await/await-keyword/2015-edition-warning.rs b/tests/ui/async-await/await-keyword/2015-edition-warning.rs index d591a5af8218..ea26abe072b3 100644 --- a/tests/ui/async-await/await-keyword/2015-edition-warning.rs +++ b/tests/ui/async-await/await-keyword/2015-edition-warning.rs @@ -1,3 +1,4 @@ +//@ edition:2015 //@ run-rustfix #![allow(non_camel_case_types)] diff --git a/tests/ui/async-await/await-keyword/2015-edition-warning.stderr b/tests/ui/async-await/await-keyword/2015-edition-warning.stderr index 70b7fa52a19c..9d19a09092b9 100644 --- a/tests/ui/async-await/await-keyword/2015-edition-warning.stderr +++ b/tests/ui/async-await/await-keyword/2015-edition-warning.stderr @@ -1,5 +1,5 @@ error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-warning.rs:7:13 + --> $DIR/2015-edition-warning.rs:8:13 | LL | pub mod await { | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -7,14 +7,14 @@ LL | pub mod await { = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #49716 note: the lint level is defined here - --> $DIR/2015-edition-warning.rs:4:9 + --> $DIR/2015-edition-warning.rs:5:9 | LL | #![deny(keyword_idents)] | ^^^^^^^^^^^^^^ = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]` error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-warning.rs:10:20 + --> $DIR/2015-edition-warning.rs:11:20 | LL | pub struct await; | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -23,7 +23,7 @@ LL | pub struct await; = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-warning.rs:15:16 + --> $DIR/2015-edition-warning.rs:16:16 | LL | use outer_mod::await::await; | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -32,7 +32,7 @@ LL | use outer_mod::await::await; = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-warning.rs:15:23 + --> $DIR/2015-edition-warning.rs:16:23 | LL | use outer_mod::await::await; | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -41,7 +41,7 @@ LL | use outer_mod::await::await; = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-warning.rs:22:11 + --> $DIR/2015-edition-warning.rs:23:11 | LL | match await { await => {} } | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` @@ -50,7 +50,7 @@ LL | match await { await => {} } = note: for more information, see issue #49716 error: `await` is a keyword in the 2018 edition - --> $DIR/2015-edition-warning.rs:22:19 + --> $DIR/2015-edition-warning.rs:23:19 | LL | match await { await => {} } | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` diff --git a/tests/ui/async-await/dyn/mut-is-pointer-like.stderr b/tests/ui/async-await/dyn/mut-is-pointer-like.stderr index bf20473924bc..6689539ff453 100644 --- a/tests/ui/async-await/dyn/mut-is-pointer-like.stderr +++ b/tests/ui/async-await/dyn/mut-is-pointer-like.stderr @@ -8,10 +8,10 @@ LL | #![feature(async_fn_in_dyn_trait)] = note: `#[warn(incomplete_features)]` on by default error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/mut-is-pointer-like.rs:35:16 + --> $DIR/mut-is-pointer-like.rs:35:29 | LL | let x: Pin<&mut dyn AsyncTrait> = f; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | ^^^^^^^^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -24,48 +24,6 @@ LL | async fn async_dispatch(self: Pin<&mut Self>) -> Self::Output; | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` = help: consider moving `async_dispatch` to another trait -error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/mut-is-pointer-like.rs:35:56 - | -LL | let x: Pin<&mut dyn AsyncTrait> = f; - | ^ `AsyncTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/mut-is-pointer-like.rs:16:14 - | -LL | trait AsyncTrait { - | ---------- this trait is not dyn compatible... -... -LL | async fn async_dispatch(self: Pin<&mut Self>) -> Self::Output; - | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` - = help: consider moving `async_dispatch` to another trait - = note: required for the cast from `Pin<&mut {async block@$DIR/mut-is-pointer-like.rs:32:32: 32:37}>` to `Pin<&mut dyn AsyncTrait>` +error: aborting due to 1 previous error; 1 warning emitted -error[E0277]: the trait bound `dyn AsyncTrait: AsyncTrait` is not satisfied - --> $DIR/mut-is-pointer-like.rs:36:11 - | -LL | x.async_dispatch().await; - | ^^^^^^^^^^^^^^ the trait `AsyncTrait` is not implemented for `dyn AsyncTrait` - -error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/mut-is-pointer-like.rs:36:9 - | -LL | x.async_dispatch().await; - | ^^^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/mut-is-pointer-like.rs:16:14 - | -LL | trait AsyncTrait { - | ---------- this trait is not dyn compatible... -... -LL | async fn async_dispatch(self: Pin<&mut Self>) -> Self::Output; - | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` - = help: consider moving `async_dispatch` to another trait - -error: aborting due to 4 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0038, E0277. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/dyn/works.stderr b/tests/ui/async-await/dyn/works.stderr index 47abeab5aacb..338479d8b70a 100644 --- a/tests/ui/async-await/dyn/works.stderr +++ b/tests/ui/async-await/dyn/works.stderr @@ -8,28 +8,10 @@ LL | #![feature(async_fn_in_dyn_trait)] = note: `#[warn(incomplete_features)]` on by default error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/works.rs:27:34 + --> $DIR/works.rs:27:21 | LL | let x: &dyn AsyncTrait = &"hello, world!"; - | ^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/works.rs:14:14 - | -LL | trait AsyncTrait { - | ---------- this trait is not dyn compatible... -LL | async fn async_dispatch(&self); - | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` - = help: consider moving `async_dispatch` to another trait - = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. - = note: required for the cast from `&&'static str` to `&dyn AsyncTrait` - -error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/works.rs:27:16 - | -LL | let x: &dyn AsyncTrait = &"hello, world!"; - | ^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | ^^^^^^^^^^ `AsyncTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -42,40 +24,6 @@ LL | async fn async_dispatch(&self); = help: consider moving `async_dispatch` to another trait = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. -error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/works.rs:28:11 - | -LL | x.async_dispatch().await; - | ^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/works.rs:14:14 - | -LL | trait AsyncTrait { - | ---------- this trait is not dyn compatible... -LL | async fn async_dispatch(&self); - | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` - = help: consider moving `async_dispatch` to another trait - = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. - -error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/works.rs:28:9 - | -LL | x.async_dispatch().await; - | ^^^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/works.rs:14:14 - | -LL | trait AsyncTrait { - | ---------- this trait is not dyn compatible... -LL | async fn async_dispatch(&self); - | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` - = help: consider moving `async_dispatch` to another trait - = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. - -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/dyn/wrong-size.stderr b/tests/ui/async-await/dyn/wrong-size.stderr index b4684f4fc174..a465f91f62af 100644 --- a/tests/ui/async-await/dyn/wrong-size.stderr +++ b/tests/ui/async-await/dyn/wrong-size.stderr @@ -8,28 +8,10 @@ LL | #![feature(async_fn_in_dyn_trait)] = note: `#[warn(incomplete_features)]` on by default error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/wrong-size.rs:21:30 + --> $DIR/wrong-size.rs:21:17 | LL | let x: &dyn AsyncTrait = &"hello, world!"; - | ^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/wrong-size.rs:9:14 - | -LL | trait AsyncTrait { - | ---------- this trait is not dyn compatible... -LL | async fn async_dispatch(&self); - | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` - = help: consider moving `async_dispatch` to another trait - = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. - = note: required for the cast from `&&'static str` to `&dyn AsyncTrait` - -error[E0038]: the trait `AsyncTrait` is not dyn compatible - --> $DIR/wrong-size.rs:21:12 - | -LL | let x: &dyn AsyncTrait = &"hello, world!"; - | ^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | ^^^^^^^^^^ `AsyncTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -42,6 +24,6 @@ LL | async fn async_dispatch(&self); = help: consider moving `async_dispatch` to another trait = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/for-await-2015.rs b/tests/ui/async-await/for-await-2015.rs index 89ff256da1dd..c2bd39c84466 100644 --- a/tests/ui/async-await/for-await-2015.rs +++ b/tests/ui/async-await/for-await-2015.rs @@ -1,3 +1,4 @@ +//@ edition:2015 //@ check-pass #![feature(async_for_loop)] diff --git a/tests/ui/async-await/format-await-send.rs b/tests/ui/async-await/format-await-send.rs new file mode 100644 index 000000000000..13ae7233fd68 --- /dev/null +++ b/tests/ui/async-await/format-await-send.rs @@ -0,0 +1,24 @@ +// regression test for +// assert that Future which has format!() with an async function is Send + +#![allow(unused)] + +//@ check-pass +//@ edition: 2018 + +use core::future::Future; +use core::pin::Pin; + +fn build_string() -> Pin + Send>> { + Box::pin(async move { + let mut string_builder = String::new(); + string_builder += &format!("Hello {}", helper().await); + string_builder + }) +} + +async fn helper() -> String { + "World".to_string() +} + +fn main() {} diff --git a/tests/ui/async-await/in-trait/dyn-compatibility.stderr b/tests/ui/async-await/in-trait/dyn-compatibility.stderr index 553bcbf89d59..f0c5dc534788 100644 --- a/tests/ui/async-await/in-trait/dyn-compatibility.stderr +++ b/tests/ui/async-await/in-trait/dyn-compatibility.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/dyn-compatibility.rs:9:12 + --> $DIR/dyn-compatibility.rs:9:17 | LL | let x: &dyn Foo = todo!(); - | ^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit diff --git a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr index 2bdc1347c815..8ce4d4ea06ac 100644 --- a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr +++ b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr @@ -1,16 +1,16 @@ error[E0034]: multiple applicable items in scope - --> $DIR/issue-65634-raw-ident-suggestion.rs:24:13 + --> $DIR/issue-65634-raw-ident-suggestion.rs:25:13 | LL | r#fn {}.r#struct(); | ^^^^^^^^ multiple `r#struct` found | note: candidate #1 is defined in an impl of the trait `async` for the type `r#fn` - --> $DIR/issue-65634-raw-ident-suggestion.rs:7:5 + --> $DIR/issue-65634-raw-ident-suggestion.rs:8:5 | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn` - --> $DIR/issue-65634-raw-ident-suggestion.rs:13:5 + --> $DIR/issue-65634-raw-ident-suggestion.rs:14:5 | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr index ab10ab749fed..d910ef9872cd 100644 --- a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr +++ b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr @@ -1,16 +1,16 @@ error[E0034]: multiple applicable items in scope - --> $DIR/issue-65634-raw-ident-suggestion.rs:24:13 + --> $DIR/issue-65634-raw-ident-suggestion.rs:25:13 | LL | r#fn {}.r#struct(); | ^^^^^^^^ multiple `r#struct` found | note: candidate #1 is defined in an impl of the trait `r#async` for the type `r#fn` - --> $DIR/issue-65634-raw-ident-suggestion.rs:7:5 + --> $DIR/issue-65634-raw-ident-suggestion.rs:8:5 | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ note: candidate #2 is defined in an impl of the trait `r#await` for the type `r#fn` - --> $DIR/issue-65634-raw-ident-suggestion.rs:13:5 + --> $DIR/issue-65634-raw-ident-suggestion.rs:14:5 | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/issue-65634-raw-ident-suggestion.rs b/tests/ui/async-await/issue-65634-raw-ident-suggestion.rs index ef5760f4846b..98f5b6dd9f98 100644 --- a/tests/ui/async-await/issue-65634-raw-ident-suggestion.rs +++ b/tests/ui/async-await/issue-65634-raw-ident-suggestion.rs @@ -1,4 +1,5 @@ //@ revisions: edition2015 edition2018 +//@[edition2015]edition:2015 //@[edition2018]edition:2018 #![allow(non_camel_case_types)] diff --git a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs index bcb5cb94b77c..555b01105215 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs +++ b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs @@ -1,3 +1,4 @@ +//@ edition:2015 //@ rustc-env:CARGO_CRATE_NAME=foo use std::pin::Pin; diff --git a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr index 11f5825c232e..c5f1793731ea 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr +++ b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `await` on type `await_on_struct_missing::S` - --> $DIR/suggest-switching-edition-on-await-cargo.rs:11:7 + --> $DIR/suggest-switching-edition-on-await-cargo.rs:12:7 | LL | x.await; | ^^^^^ unknown field @@ -9,7 +9,7 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0609]: no field `await` on type `await_on_struct_similar::S` - --> $DIR/suggest-switching-edition-on-await-cargo.rs:24:7 + --> $DIR/suggest-switching-edition-on-await-cargo.rs:25:7 | LL | x.await; | ^^^^^ unknown field @@ -24,7 +24,7 @@ LL + x.awai; | error[E0609]: no field `await` on type `Pin<&mut dyn Future>` - --> $DIR/suggest-switching-edition-on-await-cargo.rs:34:7 + --> $DIR/suggest-switching-edition-on-await-cargo.rs:35:7 | LL | x.await; | ^^^^^ unknown field @@ -34,7 +34,7 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0609]: no field `await` on type `impl Future` - --> $DIR/suggest-switching-edition-on-await-cargo.rs:43:7 + --> $DIR/suggest-switching-edition-on-await-cargo.rs:44:7 | LL | x.await; | ^^^^^ unknown field diff --git a/tests/ui/async-await/suggest-switching-edition-on-await.rs b/tests/ui/async-await/suggest-switching-edition-on-await.rs index 0907a87e02cf..6b639fa0b4ea 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await.rs +++ b/tests/ui/async-await/suggest-switching-edition-on-await.rs @@ -1,3 +1,4 @@ +//@ edition:2015 use std::pin::Pin; use std::future::Future; diff --git a/tests/ui/async-await/suggest-switching-edition-on-await.stderr b/tests/ui/async-await/suggest-switching-edition-on-await.stderr index 2ede8d5b7f42..bf03016222ea 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await.stderr +++ b/tests/ui/async-await/suggest-switching-edition-on-await.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `await` on type `await_on_struct_missing::S` - --> $DIR/suggest-switching-edition-on-await.rs:9:7 + --> $DIR/suggest-switching-edition-on-await.rs:10:7 | LL | x.await; | ^^^^^ unknown field @@ -9,7 +9,7 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0609]: no field `await` on type `await_on_struct_similar::S` - --> $DIR/suggest-switching-edition-on-await.rs:22:7 + --> $DIR/suggest-switching-edition-on-await.rs:23:7 | LL | x.await; | ^^^^^ unknown field @@ -24,7 +24,7 @@ LL + x.awai; | error[E0609]: no field `await` on type `Pin<&mut dyn Future>` - --> $DIR/suggest-switching-edition-on-await.rs:32:7 + --> $DIR/suggest-switching-edition-on-await.rs:33:7 | LL | x.await; | ^^^^^ unknown field @@ -34,7 +34,7 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0609]: no field `await` on type `impl Future` - --> $DIR/suggest-switching-edition-on-await.rs:41:7 + --> $DIR/suggest-switching-edition-on-await.rs:42:7 | LL | x.await; | ^^^^^ unknown field diff --git a/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs b/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs new file mode 100644 index 000000000000..4c06d0fdfe39 --- /dev/null +++ b/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs @@ -0,0 +1,24 @@ +#[doc(alias="DocAliasS1")] +pub struct S1; + +#[doc(alias="DocAliasS2")] +#[doc(alias("DocAliasS3", "DocAliasS4"))] +pub struct S2; + +#[doc(alias("doc_alias_f1", "doc_alias_f2"))] +pub fn f() {} + +pub mod m { + #[doc(alias="DocAliasS5")] + pub struct S5; + + pub mod n { + #[doc(alias("DocAliasX"))] + pub mod x { + pub mod y { + #[doc(alias="DocAliasS6")] + pub struct S6; + } + } + } +} diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index 55c9219a08a8..aba384ff8ac8 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -42,13 +42,13 @@ error[E0599]: no method named `foo` found for struct `rustc_confusables_across_c --> $DIR/rustc_confusables.rs:15:7 | LL | x.foo(); - | ^^^ method not found in `BTreeSet` + | ^^^ method not found in `rustc_confusables_across_crate::BTreeSet` error[E0599]: no method named `push` found for struct `rustc_confusables_across_crate::BTreeSet` in the current scope --> $DIR/rustc_confusables.rs:17:7 | LL | x.push(); - | ^^^^ method not found in `BTreeSet` + | ^^^^ method not found in `rustc_confusables_across_crate::BTreeSet` | help: you might have meant to use `insert` | @@ -60,7 +60,7 @@ error[E0599]: no method named `test` found for struct `rustc_confusables_across_ --> $DIR/rustc_confusables.rs:20:7 | LL | x.test(); - | ^^^^ method not found in `BTreeSet` + | ^^^^ method not found in `rustc_confusables_across_crate::BTreeSet` error[E0599]: no method named `pulled` found for struct `rustc_confusables_across_crate::BTreeSet` in the current scope --> $DIR/rustc_confusables.rs:22:7 diff --git a/tests/ui/attributes/use-doc-alias-name.rs b/tests/ui/attributes/use-doc-alias-name.rs new file mode 100644 index 000000000000..1fc9199b6e38 --- /dev/null +++ b/tests/ui/attributes/use-doc-alias-name.rs @@ -0,0 +1,67 @@ +//@ aux-build: use-doc-alias-name-extern.rs + +// issue#124273 + +extern crate use_doc_alias_name_extern; + +use use_doc_alias_name_extern::*; + +#[doc(alias="LocalDocAliasS")] +struct S; + +fn main() { + LocalDocAliasS; // don't show help in local crate + //~^ ERROR: cannot find value `LocalDocAliasS` in this scope + + DocAliasS1; + //~^ ERROR: cannot find value `DocAliasS1` in this scope + //~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + + DocAliasS2; + //~^ ERROR: cannot find value `DocAliasS2` in this scope + //~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS2` + + DocAliasS3; + //~^ ERROR: cannot find value `DocAliasS3` in this scope + //~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS3` + + DocAliasS4; + //~^ ERROR: cannot find value `DocAliasS4` in this scope + //~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS4` + + doc_alias_f1(); + //~^ ERROR: cannot find function `doc_alias_f1` in this scope + //~| HELP: `f` has a name defined in the doc alias attribute as `doc_alias_f1` + + doc_alias_f2(); + //~^ ERROR: cannot find function `doc_alias_f2` in this scope + //~| HELP: `f` has a name defined in the doc alias attribute as `doc_alias_f2` + + m::DocAliasS5; + //~^ ERROR: cannot find value `DocAliasS5` in module `m` + //~| HELP: `S5` has a name defined in the doc alias attribute as `DocAliasS5` + + not_exist_module::DocAliasS1; + //~^ ERROR: use of unresolved module or unlinked crate `not_exist_module` + //~| HELP: you might be missing a crate named `not_exist_module` + + use_doc_alias_name_extern::DocAliasS1; + //~^ ERROR: cannot find value `DocAliasS1` in crate `use_doc_alias_name_extern + //~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + + m::n::DocAliasX::y::S6; + //~^ ERROR: could not find `DocAliasX` in `n` + //~| HELP: `x` has a name defined in the doc alias attribute as `DocAliasX` + + m::n::x::y::DocAliasS6; + //~^ ERROR: cannot find value `DocAliasS6` in module `m::n::x::y` + //~| HELP: `S6` has a name defined in the doc alias attribute as `DocAliasS6` +} + +trait T { + fn f() { + DocAliasS1; + //~^ ERROR: cannot find value `DocAliasS1` in this scope + //~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + } +} diff --git a/tests/ui/attributes/use-doc-alias-name.stderr b/tests/ui/attributes/use-doc-alias-name.stderr new file mode 100644 index 000000000000..07f4865e4152 --- /dev/null +++ b/tests/ui/attributes/use-doc-alias-name.stderr @@ -0,0 +1,150 @@ +error[E0433]: failed to resolve: could not find `DocAliasX` in `n` + --> $DIR/use-doc-alias-name.rs:52:11 + | +LL | m::n::DocAliasX::y::S6; + | ^^^^^^^^^ could not find `DocAliasX` in `n` + | +help: `x` has a name defined in the doc alias attribute as `DocAliasX` + | +LL - m::n::DocAliasX::y::S6; +LL + m::n::x::y::S6; + | + +error[E0425]: cannot find value `LocalDocAliasS` in this scope + --> $DIR/use-doc-alias-name.rs:13:5 + | +LL | LocalDocAliasS; // don't show help in local crate + | ^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `DocAliasS1` in this scope + --> $DIR/use-doc-alias-name.rs:16:5 + | +LL | DocAliasS1; + | ^^^^^^^^^^ + | +help: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + | +LL - DocAliasS1; +LL + S1; + | + +error[E0425]: cannot find value `DocAliasS2` in this scope + --> $DIR/use-doc-alias-name.rs:20:5 + | +LL | DocAliasS2; + | ^^^^^^^^^^ + | +help: `S2` has a name defined in the doc alias attribute as `DocAliasS2` + | +LL - DocAliasS2; +LL + S2; + | + +error[E0425]: cannot find value `DocAliasS3` in this scope + --> $DIR/use-doc-alias-name.rs:24:5 + | +LL | DocAliasS3; + | ^^^^^^^^^^ + | +help: `S2` has a name defined in the doc alias attribute as `DocAliasS3` + | +LL - DocAliasS3; +LL + S2; + | + +error[E0425]: cannot find value `DocAliasS4` in this scope + --> $DIR/use-doc-alias-name.rs:28:5 + | +LL | DocAliasS4; + | ^^^^^^^^^^ + | +help: `S2` has a name defined in the doc alias attribute as `DocAliasS4` + | +LL - DocAliasS4; +LL + S2; + | + +error[E0425]: cannot find value `DocAliasS5` in module `m` + --> $DIR/use-doc-alias-name.rs:40:8 + | +LL | m::DocAliasS5; + | ^^^^^^^^^^ + | +help: `S5` has a name defined in the doc alias attribute as `DocAliasS5` + | +LL - m::DocAliasS5; +LL + m::S5; + | + +error[E0425]: cannot find value `DocAliasS1` in crate `use_doc_alias_name_extern` + --> $DIR/use-doc-alias-name.rs:48:32 + | +LL | use_doc_alias_name_extern::DocAliasS1; + | ^^^^^^^^^^ + | +help: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + | +LL - use_doc_alias_name_extern::DocAliasS1; +LL + use_doc_alias_name_extern::S1; + | + +error[E0425]: cannot find value `DocAliasS6` in module `m::n::x::y` + --> $DIR/use-doc-alias-name.rs:56:17 + | +LL | m::n::x::y::DocAliasS6; + | ^^^^^^^^^^ + | +help: `S6` has a name defined in the doc alias attribute as `DocAliasS6` + | +LL - m::n::x::y::DocAliasS6; +LL + m::n::x::y::S6; + | + +error[E0425]: cannot find value `DocAliasS1` in this scope + --> $DIR/use-doc-alias-name.rs:63:9 + | +LL | DocAliasS1; + | ^^^^^^^^^^ + | +help: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + | +LL - DocAliasS1; +LL + S1; + | + +error[E0425]: cannot find function `doc_alias_f1` in this scope + --> $DIR/use-doc-alias-name.rs:32:5 + | +LL | doc_alias_f1(); + | ^^^^^^^^^^^^ + | +help: `f` has a name defined in the doc alias attribute as `doc_alias_f1` + | +LL - doc_alias_f1(); +LL + f(); + | + +error[E0425]: cannot find function `doc_alias_f2` in this scope + --> $DIR/use-doc-alias-name.rs:36:5 + | +LL | doc_alias_f2(); + | ^^^^^^^^^^^^ + | +help: `f` has a name defined in the doc alias attribute as `doc_alias_f2` + | +LL - doc_alias_f2(); +LL + f(); + | + +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `not_exist_module` + --> $DIR/use-doc-alias-name.rs:44:5 + | +LL | not_exist_module::DocAliasS1; + | ^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `not_exist_module` + | + = help: you might be missing a crate named `not_exist_module` + +error: aborting due to 13 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/autoderef-full-lval.rs b/tests/ui/autoderef-full-lval.rs deleted file mode 100644 index 0fadc5c98277..000000000000 --- a/tests/ui/autoderef-full-lval.rs +++ /dev/null @@ -1,25 +0,0 @@ -struct Clam { - x: Box, - y: Box, -} - - - -struct Fish { - a: Box, -} - -fn main() { - let a: Clam = Clam{ x: Box::new(1), y: Box::new(2) }; - let b: Clam = Clam{ x: Box::new(10), y: Box::new(20) }; - let z: isize = a.x + b.y; - //~^ ERROR cannot add `Box` to `Box` - println!("{}", z); - assert_eq!(z, 21); - let forty: Fish = Fish{ a: Box::new(40) }; - let two: Fish = Fish{ a: Box::new(2) }; - let answer: isize = forty.a + two.a; - //~^ ERROR cannot add `Box` to `Box` - println!("{}", answer); - assert_eq!(answer, 42); -} diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs index a916bd8b857b..a53b6d5e5898 100644 --- a/tests/ui/autodiff/autodiff_illegal.rs +++ b/tests/ui/autodiff/autodiff_illegal.rs @@ -7,38 +7,38 @@ // Test that invalid ad macros give nice errors and don't ICE. -use std::autodiff::autodiff; +use std::autodiff::{autodiff_forward, autodiff_reverse}; // We can't use Duplicated on scalars -#[autodiff(df1, Reverse, Duplicated)] +#[autodiff_reverse(df1, Duplicated)] pub fn f1(x: f64) { //~^ ERROR Duplicated can not be used for this type unimplemented!() } // Too many activities -#[autodiff(df3, Reverse, Duplicated, Const)] +#[autodiff_reverse(df3, Duplicated, Const)] pub fn f3(x: f64) { //~^^ ERROR expected 1 activities, but found 2 unimplemented!() } // To few activities -#[autodiff(df4, Reverse)] +#[autodiff_reverse(df4)] pub fn f4(x: f64) { //~^^ ERROR expected 1 activities, but found 0 unimplemented!() } // We can't use Dual in Reverse mode -#[autodiff(df5, Reverse, Dual)] +#[autodiff_reverse(df5, Dual)] pub fn f5(x: f64) { //~^^ ERROR Dual can not be used in Reverse Mode unimplemented!() } // We can't use Duplicated in Forward mode -#[autodiff(df6, Forward, Duplicated)] +#[autodiff_forward(df6, Duplicated)] pub fn f6(x: f64) { //~^^ ERROR Duplicated can not be used in Forward Mode //~^^ ERROR Duplicated can not be used for this type @@ -46,36 +46,36 @@ pub fn f6(x: f64) { } fn dummy() { - #[autodiff(df7, Forward, Dual)] + #[autodiff_forward(df7, Dual)] let mut x = 5; //~^ ERROR autodiff must be applied to function - #[autodiff(df7, Forward, Dual)] + #[autodiff_forward(df7, Dual)] x = x + 3; //~^^ ERROR attributes on expressions are experimental [E0658] //~^^ ERROR autodiff must be applied to function - #[autodiff(df7, Forward, Dual)] + #[autodiff_forward(df7, Dual)] let add_one_v2 = |x: u32| -> u32 { x + 1 }; //~^ ERROR autodiff must be applied to function } // Malformed, where args? -#[autodiff] +#[autodiff_forward] pub fn f7(x: f64) { //~^ ERROR autodiff requires at least a name and mode unimplemented!() } // Malformed, where args? -#[autodiff()] +#[autodiff_forward()] pub fn f8(x: f64) { //~^ ERROR autodiff requires at least a name and mode unimplemented!() } // Invalid attribute syntax -#[autodiff = ""] +#[autodiff_forward = ""] pub fn f9(x: f64) { //~^ ERROR autodiff requires at least a name and mode unimplemented!() @@ -84,29 +84,15 @@ pub fn f9(x: f64) { fn fn_exists() {} // We colide with an already existing function -#[autodiff(fn_exists, Reverse, Active)] +#[autodiff_reverse(fn_exists, Active)] pub fn f10(x: f64) { //~^^ ERROR the name `fn_exists` is defined multiple times [E0428] unimplemented!() } -// Malformed, missing a mode -#[autodiff(df11)] -pub fn f11() { - //~^ ERROR autodiff requires at least a name and mode - unimplemented!() -} - -// Invalid Mode -#[autodiff(df12, Debug)] -pub fn f12() { - //~^^ ERROR unknown Mode: `Debug`. Use `Forward` or `Reverse` - unimplemented!() -} - // Invalid, please pick one Mode // or use two autodiff macros. -#[autodiff(df13, Forward, Reverse)] +#[autodiff_reverse(df13, Reverse)] pub fn f13() { //~^^ ERROR did not recognize Activity: `Reverse` unimplemented!() @@ -117,7 +103,7 @@ struct Foo {} // We can't handle Active structs, because that would mean (in the general case), that we would // need to allocate and initialize arbitrary user types. We have Duplicated/Dual input args for // that. FIXME: Give a nicer error and suggest to the user to have a `&mut Foo` input instead. -#[autodiff(df14, Reverse, Active, Active)] +#[autodiff_reverse(df14, Active, Active)] fn f14(x: f32) -> Foo { unimplemented!() } @@ -127,14 +113,14 @@ type MyFloat = f32; // We would like to support type alias to f32/f64 in argument type in the future, // but that requires us to implement our checks at a later stage // like THIR which has type information available. -#[autodiff(df15, Reverse, Active, Active)] +#[autodiff_reverse(df15, Active, Active)] fn f15(x: MyFloat) -> f32 { //~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433] unimplemented!() } // We would like to support type alias to f32/f64 in return type in the future -#[autodiff(df16, Reverse, Active, Active)] +#[autodiff_reverse(df16, Active, Active)] fn f16(x: f32) -> MyFloat { unimplemented!() } @@ -145,40 +131,40 @@ struct F64Trans { } // We would like to support `#[repr(transparent)]` f32/f64 wrapper in return type in the future -#[autodiff(df17, Reverse, Active, Active)] +#[autodiff_reverse(df17, Active, Active)] fn f17(x: f64) -> F64Trans { unimplemented!() } // We would like to support `#[repr(transparent)]` f32/f64 wrapper in argument type in the future -#[autodiff(df18, Reverse, Active, Active)] +#[autodiff_reverse(df18, Active, Active)] fn f18(x: F64Trans) -> f64 { //~^^ ERROR failed to resolve: use of undeclared type `F64Trans` [E0433] unimplemented!() } // Invalid return activity -#[autodiff(df19, Forward, Dual, Active)] +#[autodiff_forward(df19, Dual, Active)] fn f19(x: f32) -> f32 { //~^^ ERROR invalid return activity Active in Forward Mode unimplemented!() } -#[autodiff(df20, Reverse, Active, Dual)] +#[autodiff_reverse(df20, Active, Dual)] fn f20(x: f32) -> f32 { //~^^ ERROR invalid return activity Dual in Reverse Mode unimplemented!() } // Duplicated cannot be used as return activity -#[autodiff(df21, Reverse, Active, Duplicated)] +#[autodiff_reverse(df21, Active, Duplicated)] fn f21(x: f32) -> f32 { //~^^ ERROR invalid return activity Duplicated in Reverse Mode unimplemented!() } struct DoesNotImplDefault; -#[autodiff(df22, Forward, Dual)] +#[autodiff_forward(df22, Dual)] pub fn f22() -> DoesNotImplDefault { //~^^ ERROR the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied unimplemented!() diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr index b119f61b8ae6..ad6f10af4678 100644 --- a/tests/ui/autodiff/autodiff_illegal.stderr +++ b/tests/ui/autodiff/autodiff_illegal.stderr @@ -1,8 +1,8 @@ error[E0658]: attributes on expressions are experimental --> $DIR/autodiff_illegal.rs:53:5 | -LL | #[autodiff(df7, Forward, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_forward(df7, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #15701 for more information = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable @@ -17,26 +17,26 @@ LL | pub fn f1(x: f64) { error: expected 1 activities, but found 2 --> $DIR/autodiff_illegal.rs:20:1 | -LL | #[autodiff(df3, Reverse, Duplicated, Const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df3, Duplicated, Const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected 1 activities, but found 0 --> $DIR/autodiff_illegal.rs:27:1 | -LL | #[autodiff(df4, Reverse)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: Dual can not be used in Reverse Mode --> $DIR/autodiff_illegal.rs:34:1 | -LL | #[autodiff(df5, Reverse, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df5, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Duplicated can not be used in Forward Mode --> $DIR/autodiff_illegal.rs:41:1 | -LL | #[autodiff(df6, Forward, Duplicated)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_forward(df6, Duplicated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Duplicated can not be used for this type --> $DIR/autodiff_illegal.rs:42:14 @@ -95,69 +95,54 @@ error[E0428]: the name `fn_exists` is defined multiple times LL | fn fn_exists() {} | -------------- previous definition of the value `fn_exists` here ... -LL | #[autodiff(fn_exists, Reverse, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fn_exists` redefined here +LL | #[autodiff_reverse(fn_exists, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fn_exists` redefined here | = note: `fn_exists` must be defined only once in the value namespace of this module -error: autodiff requires at least a name and mode - --> $DIR/autodiff_illegal.rs:95:1 - | -LL | / pub fn f11() { -LL | | -LL | | unimplemented!() -LL | | } - | |_^ - -error: unknown Mode: `Debug`. Use `Forward` or `Reverse` - --> $DIR/autodiff_illegal.rs:101:18 - | -LL | #[autodiff(df12, Debug)] - | ^^^^^ - error: did not recognize Activity: `Reverse` - --> $DIR/autodiff_illegal.rs:109:27 + --> $DIR/autodiff_illegal.rs:95:26 | -LL | #[autodiff(df13, Forward, Reverse)] - | ^^^^^^^ +LL | #[autodiff_reverse(df13, Reverse)] + | ^^^^^^^ error: invalid return activity Active in Forward Mode - --> $DIR/autodiff_illegal.rs:161:1 + --> $DIR/autodiff_illegal.rs:147:1 | -LL | #[autodiff(df19, Forward, Dual, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_forward(df19, Dual, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Dual in Reverse Mode - --> $DIR/autodiff_illegal.rs:167:1 + --> $DIR/autodiff_illegal.rs:153:1 | -LL | #[autodiff(df20, Reverse, Active, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df20, Active, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Duplicated in Reverse Mode - --> $DIR/autodiff_illegal.rs:174:1 + --> $DIR/autodiff_illegal.rs:160:1 | -LL | #[autodiff(df21, Reverse, Active, Duplicated)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df21, Active, Duplicated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared type `MyFloat` - --> $DIR/autodiff_illegal.rs:130:1 + --> $DIR/autodiff_illegal.rs:116:1 | -LL | #[autodiff(df15, Reverse, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` +LL | #[autodiff_reverse(df15, Active, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` error[E0433]: failed to resolve: use of undeclared type `F64Trans` - --> $DIR/autodiff_illegal.rs:154:1 + --> $DIR/autodiff_illegal.rs:140:1 | -LL | #[autodiff(df18, Reverse, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` +LL | #[autodiff_reverse(df18, Active, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` error[E0599]: the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied - --> $DIR/autodiff_illegal.rs:181:1 + --> $DIR/autodiff_illegal.rs:167:1 | LL | struct DoesNotImplDefault; | ------------------------- doesn't satisfy `DoesNotImplDefault: Default` -LL | #[autodiff(df22, Forward, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds +LL | #[autodiff_forward(df22, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `DoesNotImplDefault: Default` @@ -168,7 +153,7 @@ LL + #[derive(Default)] LL | struct DoesNotImplDefault; | -error: aborting due to 23 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0428, E0433, E0599, E0658. For more information about an error, try `rustc --explain E0428`. diff --git a/tests/ui/autodiff/auxiliary/my_macro.rs b/tests/ui/autodiff/auxiliary/my_macro.rs index 217631a33c9b..1d5a6de14548 100644 --- a/tests/ui/autodiff/auxiliary/my_macro.rs +++ b/tests/ui/autodiff/auxiliary/my_macro.rs @@ -3,6 +3,6 @@ use proc_macro::TokenStream; #[proc_macro_attribute] #[macro_use] -pub fn autodiff(_attr: TokenStream, item: TokenStream) -> TokenStream { +pub fn autodiff_forward(_attr: TokenStream, item: TokenStream) -> TokenStream { item // identity proc-macro } diff --git a/tests/ui/autodiff/visibility.rs b/tests/ui/autodiff/visibility.rs index dfaec03aef01..a84df75e7996 100644 --- a/tests/ui/autodiff/visibility.rs +++ b/tests/ui/autodiff/visibility.rs @@ -6,12 +6,11 @@ #![feature(autodiff)] #[cfg(std_autodiff)] -use std::autodiff::autodiff; - +use std::autodiff::autodiff_forward; extern crate my_macro; -use my_macro::autodiff; // bring `autodiff` in scope +use my_macro::autodiff_forward; // bring `autodiff_forward` in scope -#[autodiff] -//[std_autodiff]~^^^ ERROR the name `autodiff` is defined multiple times +#[autodiff_forward(dfoo)] +//[std_autodiff]~^^^ ERROR the name `autodiff_forward` is defined multiple times //[std_autodiff]~^^ ERROR this rustc version does not support autodiff fn foo() {} diff --git a/tests/ui/autodiff/visibility.std_autodiff.stderr b/tests/ui/autodiff/visibility.std_autodiff.stderr index 720c9a00170e..e45f1139012a 100644 --- a/tests/ui/autodiff/visibility.std_autodiff.stderr +++ b/tests/ui/autodiff/visibility.std_autodiff.stderr @@ -1,23 +1,23 @@ -error[E0252]: the name `autodiff` is defined multiple times - --> $DIR/visibility.rs:12:5 +error[E0252]: the name `autodiff_forward` is defined multiple times + --> $DIR/visibility.rs:11:5 | -LL | use std::autodiff::autodiff; - | ----------------------- previous import of the macro `autodiff` here -... -LL | use my_macro::autodiff; // bring `autodiff` in scope - | ^^^^^^^^^^^^^^^^^^ `autodiff` reimported here +LL | use std::autodiff::autodiff_forward; + | ------------------------------- previous import of the macro `autodiff_forward` here +LL | extern crate my_macro; +LL | use my_macro::autodiff_forward; // bring `autodiff_forward` in scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `autodiff_forward` reimported here | - = note: `autodiff` must be defined only once in the macro namespace of this module + = note: `autodiff_forward` must be defined only once in the macro namespace of this module help: you can use `as` to change the binding name of the import | -LL | use my_macro::autodiff as other_autodiff; // bring `autodiff` in scope - | +++++++++++++++++ +LL | use my_macro::autodiff_forward as other_autodiff_forward; // bring `autodiff_forward` in scope + | +++++++++++++++++++++++++ error: this rustc version does not support autodiff - --> $DIR/visibility.rs:14:1 + --> $DIR/visibility.rs:13:1 | -LL | #[autodiff] - | ^^^^^^^^^^^ +LL | #[autodiff_forward(dfoo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/autoref-autoderef/autoderef-box-no-add.rs b/tests/ui/autoref-autoderef/autoderef-box-no-add.rs new file mode 100644 index 000000000000..f8085c1ae961 --- /dev/null +++ b/tests/ui/autoref-autoderef/autoderef-box-no-add.rs @@ -0,0 +1,35 @@ +//! Tests that auto-dereferencing does not allow addition of `Box` values. +//! +//! This test ensures that `Box` fields in structs (`Clam` and `Fish`) are not +//! automatically dereferenced to `isize` during addition operations, as `Box` +//! does not implement the `Add` trait. + +struct Clam { + x: Box, + y: Box, +} + +struct Fish { + a: Box, +} + +fn main() { + let a: Clam = Clam { + x: Box::new(1), + y: Box::new(2), + }; + let b: Clam = Clam { + x: Box::new(10), + y: Box::new(20), + }; + let z: isize = a.x + b.y; + //~^ ERROR cannot add `Box` to `Box` + println!("{}", z); + assert_eq!(z, 21); + let forty: Fish = Fish { a: Box::new(40) }; + let two: Fish = Fish { a: Box::new(2) }; + let answer: isize = forty.a + two.a; + //~^ ERROR cannot add `Box` to `Box` + println!("{}", answer); + assert_eq!(answer, 42); +} diff --git a/tests/ui/autoderef-full-lval.stderr b/tests/ui/autoref-autoderef/autoderef-box-no-add.stderr similarity index 91% rename from tests/ui/autoderef-full-lval.stderr rename to tests/ui/autoref-autoderef/autoderef-box-no-add.stderr index d90238a7fb21..20ef3352831a 100644 --- a/tests/ui/autoderef-full-lval.stderr +++ b/tests/ui/autoref-autoderef/autoderef-box-no-add.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `Box` to `Box` - --> $DIR/autoderef-full-lval.rs:15:24 + --> $DIR/autoderef-box-no-add.rs:25:24 | LL | let z: isize = a.x + b.y; | --- ^ --- Box @@ -13,7 +13,7 @@ note: the foreign item type `Box` doesn't implement `Add` = note: not implement `Add` error[E0369]: cannot add `Box` to `Box` - --> $DIR/autoderef-full-lval.rs:21:33 + --> $DIR/autoderef-box-no-add.rs:31:33 | LL | let answer: isize = forty.a + two.a; | ------- ^ ----- Box diff --git a/tests/ui/auxiliary/delegate_macro.rs b/tests/ui/auxiliary/delegate_macro.rs new file mode 100644 index 000000000000..0d752e120395 --- /dev/null +++ b/tests/ui/auxiliary/delegate_macro.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! delegate { + ($method:ident) => { + ::$method(8) + }; +} diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs new file mode 100644 index 000000000000..4fdf5470feac --- /dev/null +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs @@ -0,0 +1,22 @@ +// Regression test for #122704 +use std::any::Any; + +pub struct Foo { + bar: Box Fn(&'a usize) -> Box>, +} + +impl Foo { + pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { + self.bar = Box::new(|baz| Box::new(f(baz))); + //~^ ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + //~| ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + //~| ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + //~| ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + //~| ERROR the parameter type `I` may not live long enough + //~| ERROR the parameter type `I` may not live long enough + //~| ERROR the parameter type `I` may not live long enough + //~| ERROR `f` does not live long enough + } +} + +fn main() {} diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr new file mode 100644 index 000000000000..df86ce79f09c --- /dev/null +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr @@ -0,0 +1,119 @@ +error[E0310]: the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:9 + | +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | ^^^^^^^^ + | | + | the parameter type `impl for<'a> Fn(&'a usize) -> Box` must be valid for the static lifetime... + | ...so that the type `impl for<'a> Fn(&'a usize) -> Box` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box + 'static) { + | +++++++++ + +error[E0310]: the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:9 + | +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | ^^^^^^^^ + | | + | the parameter type `impl for<'a> Fn(&'a usize) -> Box` must be valid for the static lifetime... + | ...so that the type `impl for<'a> Fn(&'a usize) -> Box` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box + 'static) { + | +++++++++ + +error[E0310]: the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:20 + | +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | ^^^^^^^^ + | | + | the parameter type `impl for<'a> Fn(&'a usize) -> Box` must be valid for the static lifetime... + | ...so that the type `impl for<'a> Fn(&'a usize) -> Box` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box + 'static) { + | +++++++++ + +error[E0310]: the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:20 + | +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `impl for<'a> Fn(&'a usize) -> Box` must be valid for the static lifetime... + | ...so that the type `impl for<'a> Fn(&'a usize) -> Box` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box + 'static) { + | +++++++++ + +error[E0310]: the parameter type `I` may not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:35 + | +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | ^^^^^^^^^^^^^^^^ + | | + | the parameter type `I` must be valid for the static lifetime... + | ...so that the type `I` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { + | +++++++++ + +error[E0310]: the parameter type `I` may not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:35 + | +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | ^^^^^^^^^^^^^^^^ + | | + | the parameter type `I` must be valid for the static lifetime... + | ...so that the type `I` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { + | +++++++++ + +error[E0311]: the parameter type `I` may not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:35 + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { + | --------- the parameter type `I` must be valid for the anonymous lifetime defined here... +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | ^^^^^^^^^^^^^^^^ ...so that the type `I` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | pub fn ack<'a, I: 'a>(&'a mut self, f: impl for<'a> Fn(&'a usize) -> Box) { + | +++ ++++ ++ + +error[E0597]: `f` does not live long enough + --> $DIR/unconstrained-closure-lifetime-generic.rs:10:44 + | +LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { + | - binding `f` declared here +LL | self.bar = Box::new(|baz| Box::new(f(baz))); + | -------- ----- ^ borrowed value does not live long enough + | | | + | | value captured here + | coercion requires that `f` is borrowed for `'static` +... +LL | } + | - `f` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box Fn(&'a usize) -> Box<(dyn Any + 'a)>>` actually means `Box<(dyn for<'a> Fn(&'a usize) -> Box<(dyn Any + 'a)> + 'static)>` + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0310, E0311, E0597. +For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-trait-object.rs b/tests/ui/borrowck/unconstrained-closure-lifetime-trait-object.rs new file mode 100644 index 000000000000..3eee98d9bdb2 --- /dev/null +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-trait-object.rs @@ -0,0 +1,11 @@ +// Regression test for #139004 +use std::any::Any; + +type B = Box Fn(&(dyn Any + 'a)) -> Box>; + +fn foo() -> B { + Box::new(|e| Box::new(e.is::())) + //~^ ERROR the parameter type `E` may not live long enough +} + +fn main() {} diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-trait-object.stderr b/tests/ui/borrowck/unconstrained-closure-lifetime-trait-object.stderr new file mode 100644 index 000000000000..c9d5f78828d4 --- /dev/null +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-trait-object.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `E` may not live long enough + --> $DIR/unconstrained-closure-lifetime-trait-object.rs:7:29 + | +LL | Box::new(|e| Box::new(e.is::())) + | ^^ + | | + | the parameter type `E` must be valid for the static lifetime... + | ...so that the type `E` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo() -> B { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/cast/func-pointer-issue-140491.rs b/tests/ui/cast/func-pointer-issue-140491.rs new file mode 100644 index 000000000000..d5d86a66f5a9 --- /dev/null +++ b/tests/ui/cast/func-pointer-issue-140491.rs @@ -0,0 +1,7 @@ +fn my_fn(event: &Event<'_>) {} + +struct Event<'a>(&'a ()); + +fn main() { + const ptr: &fn(&Event<'_>) = &my_fn as _; //~ ERROR non-primitive cast: `&for<'a, 'b> fn(&'a Event<'b>) {my_fn}` as `&for<'a, 'b> fn(&'a Event<'b>)` [E0605] +} diff --git a/tests/ui/cast/func-pointer-issue-140491.stderr b/tests/ui/cast/func-pointer-issue-140491.stderr new file mode 100644 index 000000000000..e1c07010e691 --- /dev/null +++ b/tests/ui/cast/func-pointer-issue-140491.stderr @@ -0,0 +1,11 @@ +error[E0605]: non-primitive cast: `&for<'a, 'b> fn(&'a Event<'b>) {my_fn}` as `&for<'a, 'b> fn(&'a Event<'b>)` + --> $DIR/func-pointer-issue-140491.rs:6:34 + | +LL | ..._>) = &my_fn as _; + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | + = note: casting reference expression `&my_fn` because `&` binds tighter than `as` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/cfg/cfg-version/cfg-version-expand.rs b/tests/ui/cfg/cfg-version/cfg-version-expand.rs new file mode 100644 index 000000000000..8c426b4a41f5 --- /dev/null +++ b/tests/ui/cfg/cfg-version/cfg-version-expand.rs @@ -0,0 +1,30 @@ +//@ run-pass +//@ rustc-env:RUSTC_OVERRIDE_VERSION_STRING=1.50.3 + +#![feature(cfg_version)] + +#[cfg(version("1.49.0"))] +const ON_1_49_0: bool = true; +#[cfg(version("1.50"))] +const ON_1_50_0: bool = true; +#[cfg(not(version("1.51")))] +const ON_1_51_0: bool = false; + +// This one uses the wrong syntax, so doesn't eval to true +#[warn(unexpected_cfgs)] +#[cfg(not(version = "1.48.0"))] //~ WARN unexpected `cfg` condition name: `version` +const ON_1_48_0: bool = false; + +fn main() { + assert!(!ON_1_48_0); + assert!(ON_1_49_0); + assert!(ON_1_50_0); + assert!(!ON_1_51_0); + assert!(cfg!(version("1.1"))); + assert!(cfg!(version("1.49"))); + assert!(cfg!(version("1.50.0"))); + assert!(cfg!(version("1.50.3"))); + assert!(!cfg!(version("1.50.4"))); + assert!(!cfg!(version("1.51"))); + assert!(!cfg!(version("1.100"))); +} diff --git a/tests/ui/cfg/cfg-version/cfg-version-expand.stderr b/tests/ui/cfg/cfg-version/cfg-version-expand.stderr new file mode 100644 index 000000000000..a9a8d86c0fa9 --- /dev/null +++ b/tests/ui/cfg/cfg-version/cfg-version-expand.stderr @@ -0,0 +1,17 @@ +warning: unexpected `cfg` condition name: `version` + --> $DIR/cfg-version-expand.rs:15:11 + | +LL | #[cfg(not(version = "1.48.0"))] + | ^^^^^^^^^^^^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(version, values("1.48.0"))` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default +help: there is a similar config predicate: `version("..")` + | +LL - #[cfg(not(version = "1.48.0"))] +LL + #[cfg(not(version("1.48.0")))] + | + +warning: 1 warning emitted + diff --git a/tests/ui/cfg/cfg-version/syntax.rs b/tests/ui/cfg/cfg-version/syntax.rs new file mode 100644 index 000000000000..22aab47e1ecd --- /dev/null +++ b/tests/ui/cfg/cfg-version/syntax.rs @@ -0,0 +1,152 @@ +//! Check `#[cfg(version(..))]` parsing. + +#![feature(cfg_version)] + +// Overall grammar +// =============== +// +// `#[cfg(version(..))]` accepts only the `version(VERSION_STRING_LITERAL)` predicate form, where +// only a single string literal is permitted. + +#[cfg(version(42))] +//~^ ERROR expected a version literal +fn not_a_string_literal_simple() {} + +#[cfg(version(1.20))] +//~^ ERROR expected a version literal +fn not_a_string_literal_semver_like() {} + +#[cfg(version(false))] +//~^ ERROR expected a version literal +fn not_a_string_literal_other() {} + +#[cfg(version("1.43", "1.44", "1.45"))] +//~^ ERROR expected single version literal +fn multiple_version_literals() {} + +// The key-value form `cfg(version = "..")` is not considered a valid `cfg(version(..))` usage, but +// it will only trigger the `unexpected_cfgs` lint and not a hard error. + +#[cfg(version = "1.43")] +//~^ WARN unexpected `cfg` condition name: `version` +fn key_value_form() {} + +// Additional version string literal constraints +// ============================================= +// +// The `VERSION_STRING_LITERAL` ("version literal") has additional constraints on its syntactical +// well-formedness. + +// 1. A valid version literal can only constitute of numbers and periods (a "simple" semver version +// string). Non-semver strings or "complex" semver strings (such as build metadata) are not +// considered valid version literals, and will emit a non-lint warning "unknown version literal +// format". + +#[cfg(version("1.43.0"))] +fn valid_major_minor_patch() {} + +#[cfg(version("0.0.0"))] +fn valid_zero_zero_zero_major_minor_patch() {} + +#[cfg(version("foo"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn not_numbers_or_periods() {} + +#[cfg(version("1.20.0-stable"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn complex_semver_with_metadata() {} + +// 2. "Shortened" version strings are permitted but *only* for the omission of the patch number. + +#[cfg(version("1.0"))] +fn valid_major_minor_1() {} + +#[cfg(version("1.43"))] +fn valid_major_minor_2() {} + +#[cfg(not(version("1.44")))] +fn valid_major_minor_negated_smoke_test() {} + +#[cfg(version("0.0"))] +fn valid_zero_zero_major_minor() {} + +#[cfg(version("0.7"))] +fn valid_zero_major_minor() {} + +// 3. Major-only, or other non-Semver-like strings are not permitted. + +#[cfg(version("1"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn invalid_major_only() {} + +#[cfg(version("0"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn invalid_major_only_zero() {} + +#[cfg(version(".7"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn invalid_decimal_like() {} + +// Misc parsing overflow/underflow edge cases +// ========================================== +// +// Check that we report "unknown version literal format" user-facing warnings and not ICEs. + +#[cfg(version("-1"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn invalid_major_only_negative() {} + +// Implementation detail: we store rustc version as `{ major: u16, minor: u16, patch: u16 }`. + +#[cfg(version("65536"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn exceed_u16_major() {} + +#[cfg(version("1.65536.0"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn exceed_u16_minor() {} + +#[cfg(version("1.0.65536"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn exceed_u16_patch() {} + +#[cfg(version("65536.0.65536"))] +//~^ WARN unknown version literal format, assuming it refers to a future version +fn exceed_u16_mixed() {} + +// Usage as `cfg!()` +// ================= + +fn cfg_usage() { + assert!(cfg!(version("1.0"))); + assert!(cfg!(version("1.43"))); + assert!(cfg!(version("1.43.0"))); + + assert!(cfg!(version("foo"))); + //~^ WARN unknown version literal format, assuming it refers to a future version + assert!(cfg!(version("1.20.0-stable"))); + //~^ WARN unknown version literal format, assuming it refers to a future version + + assert!(cfg!(version = "1.43")); + //~^ WARN unexpected `cfg` condition name: `version` +} + +fn main() { + cfg_usage(); + + // `cfg(version = "..")` is not a valid `cfg_version` form, but it only triggers + // `unexpected_cfgs` lint, and `cfg(version = "..")` eval to `false`. + key_value_form(); //~ ERROR cannot find function + + // Invalid version literal formats within valid `cfg(version(..))` form should also cause + // `cfg(version(..))` eval to `false`. + not_numbers_or_periods(); //~ ERROR cannot find function + complex_semver_with_metadata(); //~ ERROR cannot find function + invalid_major_only(); //~ ERROR cannot find function + invalid_major_only_zero(); //~ ERROR cannot find function + invalid_major_only_negative(); //~ ERROR cannot find function + exceed_u16_major(); //~ ERROR cannot find function + exceed_u16_minor(); //~ ERROR cannot find function + exceed_u16_patch(); //~ ERROR cannot find function + exceed_u16_mixed(); //~ ERROR cannot find function +} diff --git a/tests/ui/cfg/cfg-version/syntax.stderr b/tests/ui/cfg/cfg-version/syntax.stderr new file mode 100644 index 000000000000..2facd9607631 --- /dev/null +++ b/tests/ui/cfg/cfg-version/syntax.stderr @@ -0,0 +1,188 @@ +error: expected a version literal + --> $DIR/syntax.rs:11:15 + | +LL | #[cfg(version(42))] + | ^^ + +error: expected a version literal + --> $DIR/syntax.rs:15:15 + | +LL | #[cfg(version(1.20))] + | ^^^^ + +error: expected a version literal + --> $DIR/syntax.rs:19:15 + | +LL | #[cfg(version(false))] + | ^^^^^ + +error: expected single version literal + --> $DIR/syntax.rs:23:7 + | +LL | #[cfg(version("1.43", "1.44", "1.45"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:51:15 + | +LL | #[cfg(version("foo"))] + | ^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:55:15 + | +LL | #[cfg(version("1.20.0-stable"))] + | ^^^^^^^^^^^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:78:15 + | +LL | #[cfg(version("1"))] + | ^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:82:15 + | +LL | #[cfg(version("0"))] + | ^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:86:15 + | +LL | #[cfg(version(".7"))] + | ^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:95:15 + | +LL | #[cfg(version("-1"))] + | ^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:101:15 + | +LL | #[cfg(version("65536"))] + | ^^^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:105:15 + | +LL | #[cfg(version("1.65536.0"))] + | ^^^^^^^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:109:15 + | +LL | #[cfg(version("1.0.65536"))] + | ^^^^^^^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:113:15 + | +LL | #[cfg(version("65536.0.65536"))] + | ^^^^^^^^^^^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:125:26 + | +LL | assert!(cfg!(version("foo"))); + | ^^^^^ + +warning: unknown version literal format, assuming it refers to a future version + --> $DIR/syntax.rs:127:26 + | +LL | assert!(cfg!(version("1.20.0-stable"))); + | ^^^^^^^^^^^^^^^ + +warning: unexpected `cfg` condition name: `version` + --> $DIR/syntax.rs:30:7 + | +LL | #[cfg(version = "1.43")] + | ^^^^^^^^^^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(version, values("1.43"))` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default +help: there is a similar config predicate: `version("..")` + | +LL - #[cfg(version = "1.43")] +LL + #[cfg(version("1.43"))] + | + +warning: unexpected `cfg` condition name: `version` + --> $DIR/syntax.rs:130:18 + | +LL | assert!(cfg!(version = "1.43")); + | ^^^^^^^^^^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(version, values("1.43"))` + = note: see for more information about checking conditional configuration +help: there is a similar config predicate: `version("..")` + | +LL - assert!(cfg!(version = "1.43")); +LL + assert!(cfg!(version("1.43"))); + | + +error[E0425]: cannot find function `key_value_form` in this scope + --> $DIR/syntax.rs:139:5 + | +LL | key_value_form(); + | ^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `not_numbers_or_periods` in this scope + --> $DIR/syntax.rs:143:5 + | +LL | not_numbers_or_periods(); + | ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `complex_semver_with_metadata` in this scope + --> $DIR/syntax.rs:144:5 + | +LL | complex_semver_with_metadata(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `invalid_major_only` in this scope + --> $DIR/syntax.rs:145:5 + | +LL | invalid_major_only(); + | ^^^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `invalid_major_only_zero` in this scope + --> $DIR/syntax.rs:146:5 + | +LL | invalid_major_only_zero(); + | ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `invalid_major_only_negative` in this scope + --> $DIR/syntax.rs:147:5 + | +LL | invalid_major_only_negative(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `exceed_u16_major` in this scope + --> $DIR/syntax.rs:148:5 + | +LL | exceed_u16_major(); + | ^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `exceed_u16_minor` in this scope + --> $DIR/syntax.rs:149:5 + | +LL | exceed_u16_minor(); + | ^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `exceed_u16_patch` in this scope + --> $DIR/syntax.rs:150:5 + | +LL | exceed_u16_patch(); + | ^^^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find function `exceed_u16_mixed` in this scope + --> $DIR/syntax.rs:151:5 + | +LL | exceed_u16_mixed(); + | ^^^^^^^^^^^^^^^^ not found in this scope + +error: aborting due to 14 previous errors; 14 warnings emitted + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-reexport-2.rs b/tests/ui/cfg/diagnostics-reexport-2.rs new file mode 100644 index 000000000000..f66b9ed99ee6 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport-2.rs @@ -0,0 +1,61 @@ +// issue#141256 + +mod original { + #[cfg(false)] + //~^ NOTE the item is gated here + //~| NOTE the item is gated here + //~| NOTE the item is gated here + //~| NOTE the item is gated here + //~| NOTE the item is gated here + pub mod gated { + //~^ NOTE found an item that was configured out + //~| NOTE found an item that was configured out + //~| NOTE found an item that was configured out + //~| NOTE found an item that was configured out + //~| NOTE found an item that was configured out + pub fn foo() {} + } +} + +mod reexport { + pub use super::original::*; +} + +mod reexport2 { + pub use super::reexport::*; +} + +mod reexport30 { + pub use super::original::*; + pub use super::reexport31::*; +} + +mod reexport31 { + pub use super::reexport30::*; +} + +mod reexport32 { + pub use super::reexport30::*; +} + +fn main() { + reexport::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport` + //~| NOTE could not find `gated` in `reexport` + + reexport2::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport2` + //~| NOTE could not find `gated` in `reexport2` + + reexport30::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport30` + //~| NOTE could not find `gated` in `reexport30` + + reexport31::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport31` + //~| NOTE could not find `gated` in `reexport31` + + reexport32::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport32` + //~| NOTE could not find `gated` in `reexport32` +} diff --git a/tests/ui/cfg/diagnostics-reexport-2.stderr b/tests/ui/cfg/diagnostics-reexport-2.stderr new file mode 100644 index 000000000000..95ac5a19b0b9 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport-2.stderr @@ -0,0 +1,88 @@ +error[E0433]: failed to resolve: could not find `gated` in `reexport` + --> $DIR/diagnostics-reexport-2.rs:42:15 + | +LL | reexport::gated::foo(); + | ^^^^^ could not find `gated` in `reexport` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport2` + --> $DIR/diagnostics-reexport-2.rs:46:16 + | +LL | reexport2::gated::foo(); + | ^^^^^ could not find `gated` in `reexport2` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport30` + --> $DIR/diagnostics-reexport-2.rs:50:17 + | +LL | reexport30::gated::foo(); + | ^^^^^ could not find `gated` in `reexport30` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport31` + --> $DIR/diagnostics-reexport-2.rs:54:17 + | +LL | reexport31::gated::foo(); + | ^^^^^ could not find `gated` in `reexport31` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport32` + --> $DIR/diagnostics-reexport-2.rs:58:17 + | +LL | reexport32::gated::foo(); + | ^^^^^ could not find `gated` in `reexport32` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/check-cfg/wrong-version-syntax.fixed b/tests/ui/check-cfg/wrong-version-syntax.fixed new file mode 100644 index 000000000000..efbe2ed1bd85 --- /dev/null +++ b/tests/ui/check-cfg/wrong-version-syntax.fixed @@ -0,0 +1,14 @@ +// Check warning for wrong `cfg(version("1.27"))` syntax +// +//@ check-pass +//@ no-auto-check-cfg +//@ compile-flags: --check-cfg=cfg() +//@ run-rustfix + +#![feature(cfg_version)] + +#[cfg(not(version("1.48.0")))] +//~^ WARNING unexpected `cfg` condition name: `version` +pub fn g() {} + +pub fn main() {} diff --git a/tests/ui/check-cfg/wrong-version-syntax.rs b/tests/ui/check-cfg/wrong-version-syntax.rs new file mode 100644 index 000000000000..221ecf4cae88 --- /dev/null +++ b/tests/ui/check-cfg/wrong-version-syntax.rs @@ -0,0 +1,14 @@ +// Check warning for wrong `cfg(version("1.27"))` syntax +// +//@ check-pass +//@ no-auto-check-cfg +//@ compile-flags: --check-cfg=cfg() +//@ run-rustfix + +#![feature(cfg_version)] + +#[cfg(not(version = "1.48.0"))] +//~^ WARNING unexpected `cfg` condition name: `version` +pub fn g() {} + +pub fn main() {} diff --git a/tests/ui/check-cfg/wrong-version-syntax.stderr b/tests/ui/check-cfg/wrong-version-syntax.stderr new file mode 100644 index 000000000000..97157a0c02b9 --- /dev/null +++ b/tests/ui/check-cfg/wrong-version-syntax.stderr @@ -0,0 +1,17 @@ +warning: unexpected `cfg` condition name: `version` + --> $DIR/wrong-version-syntax.rs:10:11 + | +LL | #[cfg(not(version = "1.48.0"))] + | ^^^^^^^^^^^^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(version, values("1.48.0"))` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default +help: there is a similar config predicate: `version("..")` + | +LL - #[cfg(not(version = "1.48.0"))] +LL + #[cfg(not(version("1.48.0")))] + | + +warning: 1 warning emitted + diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs index 7a4d7d9a81ec..afb16cf58e87 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs +++ b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs @@ -14,14 +14,14 @@ fn main() { map.insert("c", ()); { - let mut it = map.extract_if(|_, _| true); + let mut it = map.extract_if(.., |_, _| true); catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); let result = catch_unwind(AssertUnwindSafe(|| it.next())); assert!(matches!(result, Ok(None))); } { - let mut it = map.extract_if(|_, _| true); + let mut it = map.extract_if(.., |_, _| true); catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err(); let result = catch_unwind(AssertUnwindSafe(|| it.next())); assert!(matches!(result, Ok(None))); diff --git a/tests/ui/coercion/issue-73886.stderr b/tests/ui/coercion/issue-73886.stderr index 0d4c90017cf1..a287aa29e118 100644 --- a/tests/ui/coercion/issue-73886.stderr +++ b/tests/ui/coercion/issue-73886.stderr @@ -3,6 +3,8 @@ error[E0605]: non-primitive cast: `&&[i32; 1]` as `&[_]` | LL | let _ = &&[0] as &[_]; | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | + = note: casting reference expression `&&[0]` because `&` binds tighter than `as` error[E0605]: non-primitive cast: `u32` as `Option<_>` --> $DIR/issue-73886.rs:4:13 diff --git a/tests/ui/const-generics/associated_const_equality/equality_bound_with_infer.rs b/tests/ui/const-generics/associated_const_equality/equality_bound_with_infer.rs new file mode 100644 index 000000000000..f45b7c3268b1 --- /dev/null +++ b/tests/ui/const-generics/associated_const_equality/equality_bound_with_infer.rs @@ -0,0 +1,24 @@ +#![feature(generic_arg_infer, associated_const_equality, generic_const_items)] +#![expect(incomplete_features)] + +// Regression test for #133066 where we would try to evaluate `<() as Foo>::ASSOC<_>` even +// though it contained inference variables, which would cause ICEs. + +trait Foo { + const ASSOC: u32; +} + +impl Foo for () { + const ASSOC: u32 = N; +} + +fn bar = 10>>() {} + +fn main() { + bar::<_, ()>(); + //~^ ERROR: type mismatch resolving `<() as Foo>::ASSOC<_> == 10` + + // FIXME(mgca): + // FIXME(associated_const_equality): + // This ought to start compiling once const items are aliases rather than bodies +} diff --git a/tests/ui/const-generics/associated_const_equality/equality_bound_with_infer.stderr b/tests/ui/const-generics/associated_const_equality/equality_bound_with_infer.stderr new file mode 100644 index 000000000000..00741c901e4c --- /dev/null +++ b/tests/ui/const-generics/associated_const_equality/equality_bound_with_infer.stderr @@ -0,0 +1,17 @@ +error[E0271]: type mismatch resolving `<() as Foo>::ASSOC<_> == 10` + --> $DIR/equality_bound_with_infer.rs:18:14 + | +LL | bar::<_, ()>(); + | ^^ expected `10`, found `<() as Foo>::ASSOC::<_>` + | + = note: expected constant `10` + found constant `<() as Foo>::ASSOC::<_>` +note: required by a bound in `bar` + --> $DIR/equality_bound_with_infer.rs:15:29 + | +LL | fn bar = 10>>() {} + | ^^^^^^^^^^^^^ required by this bound in `bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/associated_const_equality/unconstrained_impl_param.rs b/tests/ui/const-generics/associated_const_equality/unconstrained_impl_param.rs new file mode 100644 index 000000000000..99318ef75984 --- /dev/null +++ b/tests/ui/const-generics/associated_const_equality/unconstrained_impl_param.rs @@ -0,0 +1,26 @@ +// regression test for #137813 where we would assume all constants in the type system +// cannot contain inference variables, even though associated const equality syntax +// was still lowered without the feature gate enabled. + +trait AssocConst { + const A: u8; +} + +impl AssocConst for (T,) { + const A: u8 = 0; +} + +trait Trait {} + +impl Trait for () where (U,): AssocConst {} +//~^ ERROR associated const equality is incomplete +//~| ERROR the type parameter `U` is not constrained by the impl trait + +fn foo() +where + (): Trait, + //~^ ERROR type mismatch resolving +{ +} + +fn main() {} diff --git a/tests/ui/const-generics/associated_const_equality/unconstrained_impl_param.stderr b/tests/ui/const-generics/associated_const_equality/unconstrained_impl_param.stderr new file mode 100644 index 000000000000..e6799ec5c3aa --- /dev/null +++ b/tests/ui/const-generics/associated_const_equality/unconstrained_impl_param.stderr @@ -0,0 +1,39 @@ +error[E0658]: associated const equality is incomplete + --> $DIR/unconstrained_impl_param.rs:15:45 + | +LL | impl Trait for () where (U,): AssocConst {} + | ^^^^^^^^^ + | + = note: see issue #92827 for more information + = help: add `#![feature(associated_const_equality)]` 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[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained_impl_param.rs:15:6 + | +LL | impl Trait for () where (U,): AssocConst {} + | ^ unconstrained type parameter + +error[E0271]: type mismatch resolving `<(_,) as AssocConst>::A == 0` + --> $DIR/unconstrained_impl_param.rs:21:5 + | +LL | (): Trait, + | ^^^^^^^^^ expected `0`, found `<(_,) as AssocConst>::A` + | + = note: expected constant `0` + found constant `<(_,) as AssocConst>::A` +note: required for `()` to implement `Trait` + --> $DIR/unconstrained_impl_param.rs:15:9 + | +LL | impl Trait for () where (U,): AssocConst {} + | ^^^^^ ^^ --------- unsatisfied trait bound introduced here + = help: see issue #48214 +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +LL + #![feature(trivial_bounds)] + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0207, E0271, E0658. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/crashes/auxiliary/aux133199.rs b/tests/ui/const-generics/generic_const_exprs/auxiliary/cross-crate-2.rs similarity index 67% rename from tests/crashes/auxiliary/aux133199.rs rename to tests/ui/const-generics/generic_const_exprs/auxiliary/cross-crate-2.rs index 40765d92fbfe..a8bda14f4bd8 100644 --- a/tests/crashes/auxiliary/aux133199.rs +++ b/tests/ui/const-generics/generic_const_exprs/auxiliary/cross-crate-2.rs @@ -1,9 +1,9 @@ #![allow(incomplete_features)] #![feature(generic_const_exprs)] -pub struct FixedBitSet; +pub struct Foo; -impl FixedBitSet +impl Foo where [u8; N.div_ceil(8)]: Sized, { diff --git a/tests/ui/const-generics/generic_const_exprs/cross-crate-2.rs b/tests/ui/const-generics/generic_const_exprs/cross-crate-2.rs new file mode 100644 index 000000000000..77998c7ec0a1 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/cross-crate-2.rs @@ -0,0 +1,10 @@ +//@ check-pass +//@ aux-build: cross-crate-2.rs + +extern crate cross_crate_2; + +use cross_crate_2::Foo; + +fn main() { + Foo::<7>::new(); +} diff --git a/tests/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr b/tests/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr index f454ff4e6c03..6b095f3818a1 100644 --- a/tests/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/dependence_lint.rs:14:32 + --> $DIR/dependence_lint.rs:15:32 | LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce | ^ cannot perform const operation using `T` @@ -8,7 +8,7 @@ LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/dependence_lint.rs:21:37 + --> $DIR/dependence_lint.rs:22:37 | LL | let _: [u8; if true { size_of::() } else { 3 }]; // error on stable, error with gce | ^ cannot perform const operation using `T` @@ -27,7 +27,7 @@ LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_ = note: `#[warn(const_evaluatable_unchecked)]` on by default warning: cannot use constants which depend on generic parameters in types - --> $DIR/dependence_lint.rs:17:9 + --> $DIR/dependence_lint.rs:18:9 | LL | [0; if false { size_of::() } else { 3 }]; // lint on stable, error with gce | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr b/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr index f6119c17bf47..12ac980c9754 100644 --- a/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr @@ -9,8 +9,19 @@ help: try adding a `where` bound LL | fn foo() where [(); size_of::<*mut T>()]: { | ++++++++++++++++++++++++++++++++ +error: unconstrained generic constant + --> $DIR/dependence_lint.rs:10:5 + | +LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try adding a `where` bound + | +LL | fn foo() where [(); size_of::<*mut T>()]: { + | ++++++++++++++++++++++++++++++++ + error: overly complex generic constant - --> $DIR/dependence_lint.rs:17:9 + --> $DIR/dependence_lint.rs:18:9 | LL | [0; if false { size_of::() } else { 3 }]; // lint on stable, error with gce | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants @@ -18,7 +29,7 @@ LL | [0; if false { size_of::() } else { 3 }]; // lint on stable, error w = help: consider moving this anonymous constant into a `const` function error: unconstrained generic constant - --> $DIR/dependence_lint.rs:14:12 + --> $DIR/dependence_lint.rs:15:12 | LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,12 +40,12 @@ LL | fn foo() where [(); size_of::<*mut T>()]: { | ++++++++++++++++++++++++++++++++ error: overly complex generic constant - --> $DIR/dependence_lint.rs:21:17 + --> $DIR/dependence_lint.rs:22:17 | LL | let _: [u8; if true { size_of::() } else { 3 }]; // error on stable, error with gce | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants | = help: consider moving this anonymous constant into a `const` function -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/dependence_lint.rs b/tests/ui/const-generics/generic_const_exprs/dependence_lint.rs index 107466cd1d9c..6b3c8f84be3d 100644 --- a/tests/ui/const-generics/generic_const_exprs/dependence_lint.rs +++ b/tests/ui/const-generics/generic_const_exprs/dependence_lint.rs @@ -9,7 +9,8 @@ use std::mem::size_of; fn foo() { [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` //[gce]~^ ERROR unconstrained - //[full]~^^ WARNING cannot use constants + //[gce]~| ERROR unconstrained generic constant + //[full]~^^^ WARNING cannot use constants //[full]~| WARNING this was previously accepted let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce //[full]~^ ERROR generic parameters may not be used diff --git a/tests/ui/const-generics/generic_const_exprs/different-fn.stderr b/tests/ui/const-generics/generic_const_exprs/different-fn.stderr index ac80463480db..52917df0da15 100644 --- a/tests/ui/const-generics/generic_const_exprs/different-fn.stderr +++ b/tests/ui/const-generics/generic_const_exprs/different-fn.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/different-fn.rs:10:5 | LL | [0; size_of::>()] - | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `size_of::()`, found `0` + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `size_of::()`, found `size_of::>()` | = note: expected constant `size_of::()` - found constant `0` + found constant `size_of::>()` error: unconstrained generic constant --> $DIR/different-fn.rs:10:9 diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs index 9ab715d01f7a..da9a75b50efa 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs @@ -15,7 +15,7 @@ impl Foo for () { } fn use_dyn(v: &dyn Foo) { //~ERROR the trait `Foo` is not dyn compatible - v.test(); //~ERROR the trait `Foo` is not dyn compatible + v.test(); } fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr index 8bc6ef093d04..120ee435e256 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr @@ -17,25 +17,6 @@ LL | fn test(&self) -> [u8; bar::()]; = help: consider moving `test` to another trait = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/dyn-compatibility-err-ret.rs:18:5 - | -LL | v.test(); - | ^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/dyn-compatibility-err-ret.rs:8:8 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | fn test(&self) -> [u8; bar::()]; - | ^^^^ ^^^^^^^^^^^^^^^^^^^ ...because method `test` references the `Self` type in its return type - | | - | ...because method `test` references the `Self` type in its `where` clause - = help: consider moving `test` to another trait - = help: only type `()` implements `Foo`; consider using it directly instead. - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs index a7b771cd4f84..8b735188f326 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs @@ -15,7 +15,6 @@ impl Foo for () { fn use_dyn(v: &dyn Foo) { //~^ ERROR the trait `Foo` is not dyn compatible v.test(); - //~^ ERROR the trait `Foo` is not dyn compatible } fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr index f5eaaa37916d..c2ad4d149884 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr @@ -15,23 +15,6 @@ LL | fn test(&self) where [u8; bar::()]: Sized; = help: consider moving `test` to another trait = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/dyn-compatibility-err-where-bounds.rs:17:5 - | -LL | v.test(); - | ^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/dyn-compatibility-err-where-bounds.rs:8:8 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | fn test(&self) where [u8; bar::()]: Sized; - | ^^^^ ...because method `test` references the `Self` type in its `where` clause - = help: consider moving `test` to another trait - = help: only type `()` implements `Foo`; consider using it directly instead. - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/generic_const_exprs/serializing_error_guaranteed.rs b/tests/ui/const-generics/generic_const_exprs/serializing_error_guaranteed.rs new file mode 100644 index 000000000000..83b73350f83b --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/serializing_error_guaranteed.rs @@ -0,0 +1,16 @@ +//@ check-pass + +// regression test for #136894. +// I (BoxyUwU) don't know what the underlying cause was here + +#![feature(generic_const_exprs)] +#![crate_type = "lib"] +#![allow(incomplete_features, dead_code)] + +struct X([(); f::()]) +where + [(); f::()]:; + +const fn f() -> usize { + panic!() +} diff --git a/tests/ui/const-generics/issues/issue-71202.rs b/tests/ui/const-generics/issues/issue-71202.rs index 0f955414d843..8ff49b55e6fd 100644 --- a/tests/ui/const-generics/issues/issue-71202.rs +++ b/tests/ui/const-generics/issues/issue-71202.rs @@ -25,7 +25,9 @@ impl DataHolder { } >::VALUE - } as usize] = []; //~ ERROR unconstrained generic constant + } as usize] = []; + //~^ ERROR unconstrained generic constant + //~^^ ERROR mismatched types } fn main() {} diff --git a/tests/ui/const-generics/issues/issue-71202.stderr b/tests/ui/const-generics/issues/issue-71202.stderr index cc3603d1145c..b7c3db494a57 100644 --- a/tests/ui/const-generics/issues/issue-71202.stderr +++ b/tests/ui/const-generics/issues/issue-71202.stderr @@ -59,5 +59,49 @@ LL + >::VALUE LL ~ } as usize]: = []; | -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/issue-71202.rs:28:19 + | +LL | } as usize] = []; + | ^^ expected `1 - { + trait NotCopy { + const VALUE: bool = false; + } + impl<__Type: ?Sized> NotCopy for __Type {} + + struct IsCopy<__Type: ?Sized>(PhantomData<__Type>); + + impl<__Type> IsCopy<__Type> + where + __Type: Sized + Copy, + { + const VALUE: bool = true; + } + + >::VALUE + } as usize`, found `0` + | + = note: expected constant `1 - { + trait NotCopy { + const VALUE: bool = false; + } + + impl<__Type: ?Sized> NotCopy for __Type {} + + struct IsCopy<__Type: ?Sized>(PhantomData<__Type>); + + impl<__Type> IsCopy<__Type> + where + __Type: Sized + Copy, + { + const VALUE: bool = true; + } + + >::VALUE + } as usize` + found constant `0` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/issues/issue-83765.rs b/tests/ui/const-generics/issues/issue-83765.rs index 0959f771c22e..f31c61408e9c 100644 --- a/tests/ui/const-generics/issues/issue-83765.rs +++ b/tests/ui/const-generics/issues/issue-83765.rs @@ -3,10 +3,6 @@ trait TensorDimension { const DIM: usize; - //~^ ERROR cycle detected when resolving instance - //~| ERROR cycle detected when resolving instance - // FIXME Given the current state of the compiler its expected that we cycle here, - // but the cycle is still wrong. const ISSCALAR: bool = Self::DIM == 0; fn is_scalar(&self) -> bool { Self::ISSCALAR @@ -49,6 +45,7 @@ impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> { fn size(&self) -> [usize; DIM] { + //~^ ERROR: method not compatible with trait self.size } } @@ -56,12 +53,17 @@ impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> { type Element = T::Element; fn bget(&self, index: [usize; DIM]) -> Option { + //~^ ERROR: method not compatible with trait assert!(DIM >= T::DIM); if !self.inbounds(index) { + //~^ ERROR: unconstrained generic constant + //~| ERROR: mismatched types return None; } let size = self.size(); + //~^ ERROR: unconstrained generic constant let newindex: [usize; T::DIM] = Default::default(); + //~^ ERROR: the trait bound self.reference.bget(newindex) } } @@ -82,6 +84,8 @@ impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSi fn size(&self) -> [usize; DIM] { //~^ ERROR: method not compatible with trait self.reference.size() + //~^ ERROR: unconstrained generic constant + //~| ERROR: mismatched types } } @@ -92,6 +96,8 @@ impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcas fn bget(&self, index: [usize; DIM]) -> Option { //~^ ERROR: method not compatible with trait self.reference.bget(index).map(&self.closure) + //~^ ERROR: unconstrained generic constant + //~| ERROR: mismatched types } } @@ -100,12 +106,14 @@ impl TensorDimension for Vec { } impl TensorSize for Vec { fn size(&self) -> [usize; 1] { + //~^ ERROR: method not compatible with trait [self.len()] } } impl Broadcastable for Vec { type Element = T; fn bget(&self, index: [usize; 1]) -> Option { + //~^ ERROR: method not compatible with trait self.get(index[0]).cloned() } } diff --git a/tests/ui/const-generics/issues/issue-83765.stderr b/tests/ui/const-generics/issues/issue-83765.stderr index 6b62012c14fc..5a06ee7ddbc0 100644 --- a/tests/ui/const-generics/issues/issue-83765.stderr +++ b/tests/ui/const-generics/issues/issue-83765.stderr @@ -1,43 +1,5 @@ -error[E0391]: cycle detected when resolving instance `::DIM, DIM> as TensorDimension>::DIM` - --> $DIR/issue-83765.rs:5:5 - | -LL | const DIM: usize; - | ^^^^^^^^^^^^^^^^ - | -note: ...which requires computing candidate for `::DIM, DIM> as TensorDimension>`... - --> $DIR/issue-83765.rs:4:1 - | -LL | trait TensorDimension { - | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires resolving instance `::DIM, DIM> as TensorDimension>::DIM`, completing the cycle -note: cycle used when checking assoc item `::size` is compatible with trait definition - --> $DIR/issue-83765.rs:51:5 - | -LL | fn size(&self) -> [usize; DIM] { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = 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[E0391]: cycle detected when resolving instance `::DIM, DIM> as TensorDimension>::DIM` - --> $DIR/issue-83765.rs:5:5 - | -LL | const DIM: usize; - | ^^^^^^^^^^^^^^^^ - | -note: ...which requires computing candidate for `::DIM, DIM> as TensorDimension>`... - --> $DIR/issue-83765.rs:4:1 - | -LL | trait TensorDimension { - | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires resolving instance `::DIM, DIM> as TensorDimension>::DIM`, completing the cycle -note: cycle used when checking assoc item `::bget` is compatible with trait definition - --> $DIR/issue-83765.rs:58:5 - | -LL | fn bget(&self, index: [usize; DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = 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[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:82:5 + --> $DIR/issue-83765.rs:47:5 | LL | fn size(&self) -> [usize; DIM] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` @@ -46,7 +8,7 @@ LL | fn size(&self) -> [usize; DIM] { found constant `DIM` error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:92:5 + --> $DIR/issue-83765.rs:55:5 | LL | fn bget(&self, index: [usize; DIM]) -> Option { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` @@ -54,7 +16,145 @@ LL | fn bget(&self, index: [usize; DIM]) -> Option { = note: expected constant `Self::DIM` found constant `DIM` -error: aborting due to 4 previous errors +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:84:5 + | +LL | fn size(&self) -> [usize; DIM] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` -Some errors have detailed explanations: E0308, E0391. -For more information about an error, try `rustc --explain E0308`. +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:96:5 + | +LL | fn bget(&self, index: [usize; DIM]) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` + +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:108:5 + | +LL | fn size(&self) -> [usize; 1] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `1` + | + = note: expected constant `Self::DIM` + found constant `1` + +error[E0308]: method not compatible with trait + --> $DIR/issue-83765.rs:115:5 + | +LL | fn bget(&self, index: [usize; 1]) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `1` + | + = note: expected constant `Self::DIM` + found constant `1` + +error: unconstrained generic constant + --> $DIR/issue-83765.rs:58:13 + | +LL | if !self.inbounds(index) { + | ^^^^ + | +note: required by a bound in `TensorSize::inbounds` + --> $DIR/issue-83765.rs:14:39 + | +LL | fn inbounds(&self, index: [usize; Self::DIM]) -> bool { + | ^^^^^^^^^ required by this bound in `TensorSize::inbounds` +help: try adding a `where` bound + | +LL | fn bget(&self, index: [usize; DIM]) -> Option where [(); Self::DIM]: { + | ++++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/issue-83765.rs:58:27 + | +LL | if !self.inbounds(index) { + | ^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` + +error: unconstrained generic constant + --> $DIR/issue-83765.rs:63:25 + | +LL | let size = self.size(); + | ^^^^ + | +note: required by a bound in `TensorSize::size` + --> $DIR/issue-83765.rs:13:31 + | +LL | fn size(&self) -> [usize; Self::DIM]; + | ^^^^^^^^^ required by this bound in `TensorSize::size` +help: try adding a `where` bound + | +LL | fn bget(&self, index: [usize; DIM]) -> Option where [(); Self::DIM]: { + | ++++++++++++++++++++++ + +error[E0277]: the trait bound `[usize; T::DIM]: Default` is not satisfied + --> $DIR/issue-83765.rs:65:41 + | +LL | let newindex: [usize; T::DIM] = Default::default(); + | ^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; T::DIM]` + | +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> where [usize; T::DIM]: Default { + | ++++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/issue-83765.rs:86:24 + | +LL | self.reference.size() + | ^^^^ + | +note: required by a bound in `TensorSize::size` + --> $DIR/issue-83765.rs:13:31 + | +LL | fn size(&self) -> [usize; Self::DIM]; + | ^^^^^^^^^ required by this bound in `TensorSize::size` +help: try adding a `where` bound + | +LL | fn size(&self) -> [usize; DIM] where [(); Self::DIM]: { + | ++++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/issue-83765.rs:86:9 + | +LL | self.reference.size() + | ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM` + | + = note: expected constant `DIM` + found constant `Self::DIM` + +error: unconstrained generic constant + --> $DIR/issue-83765.rs:98:9 + | +LL | self.reference.bget(index).map(&self.closure) + | ^^^^^^^^^^^^^^ + | +note: required by a bound in `Broadcastable::bget` + --> $DIR/issue-83765.rs:21:35 + | +LL | fn bget(&self, index: [usize; Self::DIM]) -> Option; + | ^^^^^^^^^ required by this bound in `Broadcastable::bget` +help: try adding a `where` bound + | +LL | fn bget(&self, index: [usize; DIM]) -> Option where [(); Self::DIM]: { + | ++++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/issue-83765.rs:98:29 + | +LL | self.reference.bget(index).map(&self.closure) + | ^^^^^ expected `Self::DIM`, found `DIM` + | + = note: expected constant `Self::DIM` + found constant `DIM` + +error: aborting due to 14 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/mgca/projection-error.rs b/tests/ui/const-generics/mgca/projection-error.rs new file mode 100644 index 000000000000..d1c4fa8a492d --- /dev/null +++ b/tests/ui/const-generics/mgca/projection-error.rs @@ -0,0 +1,17 @@ +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +// Regression test for #140642. Test that normalizing const aliases +// containing erroneous types normalizes to a const error instead of +// a type error. + + +pub trait Tr { + const SIZE: usize; +} + +fn mk_array(_x: T) -> [(); >::SIZE] {} +//~^ ERROR: cannot find type `T` in this scope +//~| ERROR: cannot find type `T` in this scope + +fn main() {} diff --git a/tests/ui/const-generics/mgca/projection-error.stderr b/tests/ui/const-generics/mgca/projection-error.stderr new file mode 100644 index 000000000000..e6888351da13 --- /dev/null +++ b/tests/ui/const-generics/mgca/projection-error.stderr @@ -0,0 +1,39 @@ +error[E0412]: cannot find type `T` in this scope + --> $DIR/projection-error.rs:13:17 + | +LL | pub trait Tr { + | --------------- similarly named trait `Tr` defined here +... +LL | fn mk_array(_x: T) -> [(); >::SIZE] {} + | ^ + | +help: a trait with a similar name exists + | +LL | fn mk_array(_x: Tr) -> [(); >::SIZE] {} + | + +help: you might be missing a type parameter + | +LL | fn mk_array(_x: T) -> [(); >::SIZE] {} + | +++ + +error[E0412]: cannot find type `T` in this scope + --> $DIR/projection-error.rs:13:29 + | +LL | pub trait Tr { + | --------------- similarly named trait `Tr` defined here +... +LL | fn mk_array(_x: T) -> [(); >::SIZE] {} + | ^ + | +help: a trait with a similar name exists + | +LL | fn mk_array(_x: T) -> [(); >::SIZE] {} + | + +help: you might be missing a type parameter + | +LL | fn mk_array(_x: T) -> [(); >::SIZE] {} + | +++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/const-ptr/allowed_slices.rs b/tests/ui/const-ptr/allowed_slices.rs index e5b9966c6093..23f63ff5febf 100644 --- a/tests/ui/const-ptr/allowed_slices.rs +++ b/tests/ui/const-ptr/allowed_slices.rs @@ -26,7 +26,7 @@ pub static S5: &[MaybeUninit] = unsafe { from_raw_parts((&D1) as *const _ as // is valid as [bool; 4], so this is not UB (it's basically a transmute) pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; -// Structs are considered single allocated objects, +// Structs are considered single allocations, // as long as you don't reinterpret padding as initialized // data everything is ok. pub static S7: &[u16] = unsafe { diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index c73d2ca938cb..e618fbf7e0fe 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -104,20 +104,12 @@ error[E0080]: could not evaluate static initializer | LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: assertion failed: 0 < pointee_size && pointee_size <= isize::MAX as usize - | -note: inside `from_ptr_range::<'_, ()>` - --> $SRC_DIR/core/src/slice/raw.rs:LL:COL -note: inside `std::ptr::const_ptr::::offset_from_unsigned` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: could not evaluate static initializer --> $DIR/forbidden_slices.rs:54:25 | LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore | ^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC10 which is only 4 bytes from the end of the allocation - | -note: inside `std::ptr::const_ptr::::add` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: it is undefined behavior to use this value --> $DIR/forbidden_slices.rs:57:1 @@ -170,31 +162,18 @@ error[E0080]: could not evaluate static initializer | LL | from_ptr_range(ptr..ptr.add(1)) | ^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC11+0x1 which is only 7 bytes from the end of the allocation - | -note: inside `std::ptr::const_ptr::::add` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: could not evaluate static initializer --> $DIR/forbidden_slices.rs:85:34 | LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation - | -note: inside `from_ptr_range::<'_, u32>` - --> $SRC_DIR/core/src/slice/raw.rs:LL:COL -note: inside `std::ptr::const_ptr::::offset_from_unsigned` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: could not evaluate static initializer --> $DIR/forbidden_slices.rs:87:35 | LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation - | -note: inside `from_ptr_range::<'_, u32>` - --> $SRC_DIR/core/src/slice/raw.rs:LL:COL -note: inside `std::ptr::const_ptr::::offset_from_unsigned` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error: aborting due to 18 previous errors diff --git a/tests/ui/const-ptr/out_of_bounds_read.stderr b/tests/ui/const-ptr/out_of_bounds_read.stderr index 1d625a26b78c..8f93793802ba 100644 --- a/tests/ui/const-ptr/out_of_bounds_read.stderr +++ b/tests/ui/const-ptr/out_of_bounds_read.stderr @@ -3,31 +3,18 @@ error[E0080]: evaluation of constant value failed | LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; | ^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes - | -note: inside `std::ptr::read::` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/out_of_bounds_read.rs:10:39 | LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; | ^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes - | -note: inside `std::ptr::const_ptr::::read` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `std::ptr::read::` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/out_of_bounds_read.rs:12:37 | LL | const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes - | -note: inside `std::ptr::mut_ptr::::read` - --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL -note: inside `std::ptr::read::` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error: aborting due to 3 previous errors diff --git a/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr b/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr index a506f2a282bb..b505b76a6abf 100644 --- a/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr +++ b/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr @@ -5,14 +5,9 @@ LL | const fn bar() -> u32 { foo() } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar() -> u32 { foo() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const fn bar() -> u32 { foo() } | diff --git a/tests/ui/consts/const-eval/float_methods.rs b/tests/ui/consts/const-eval/float_methods.rs deleted file mode 100644 index 853f75825ac2..000000000000 --- a/tests/ui/consts/const-eval/float_methods.rs +++ /dev/null @@ -1,46 +0,0 @@ -//@ run-pass -//! Tests the float intrinsics: min, max, abs, copysign - -#![feature(f16, f128)] - -const F16_MIN: f16 = 1.0_f16.min(0.5_f16); -const F16_MAX: f16 = 1.0_f16.max(0.5_f16); -const F16_ABS: f16 = (-1.0_f16).abs(); -const F16_COPYSIGN: f16 = 1.0_f16.copysign(-2.0_f16); - -const F32_MIN: f32 = 1.0_f32.min(0.5_f32); -const F32_MAX: f32 = 1.0_f32.max(0.5_f32); -const F32_ABS: f32 = (-1.0_f32).abs(); -const F32_COPYSIGN: f32 = 1.0_f32.copysign(-2.0_f32); - -const F64_MIN: f64 = 1.0_f64.min(0.5_f64); -const F64_MAX: f64 = 1.0_f64.max(0.5_f64); -const F64_ABS: f64 = (-1.0_f64).abs(); -const F64_COPYSIGN: f64 = 1.0_f64.copysign(-2.0_f64); - -const F128_MIN: f128 = 1.0_f128.min(0.5_f128); -const F128_MAX: f128 = 1.0_f128.max(0.5_f128); -const F128_ABS: f128 = (-1.0_f128).abs(); -const F128_COPYSIGN: f128 = 1.0_f128.copysign(-2.0_f128); - -fn main() { - assert_eq!(F16_MIN, 0.5); - assert_eq!(F16_MAX, 1.0); - assert_eq!(F16_ABS, 1.0); - assert_eq!(F16_COPYSIGN, -1.0); - - assert_eq!(F32_MIN, 0.5); - assert_eq!(F32_MAX, 1.0); - assert_eq!(F32_ABS, 1.0); - assert_eq!(F32_COPYSIGN, -1.0); - - assert_eq!(F64_MIN, 0.5); - assert_eq!(F64_MAX, 1.0); - assert_eq!(F64_ABS, 1.0); - assert_eq!(F64_COPYSIGN, -1.0); - - assert_eq!(F128_MIN, 0.5); - assert_eq!(F128_MAX, 1.0); - assert_eq!(F128_ABS, 1.0); - assert_eq!(F128_COPYSIGN, -1.0); -} 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 19c78f019aab..5fab075785aa 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,7 +1,9 @@ +#![deny(ptr_to_integer_transmute_in_consts)] + const fn foo(ptr: *const u8) -> usize { unsafe { std::mem::transmute(ptr) - //~^ WARN pointers cannot be transmuted to integers + //~^ ERROR pointers cannot be transmuted to integers } } @@ -11,7 +13,7 @@ trait Human { let ptr: *const usize = &value; unsafe { std::mem::transmute(ptr) - //~^ WARN pointers cannot be transmuted to integers + //~^ ERROR pointers cannot be transmuted to integers } }; @@ -28,7 +30,7 @@ impl Type { let ptr: *const usize = &value; unsafe { std::mem::transmute(ptr) - //~^ WARN pointers cannot be transmuted to integers + //~^ ERROR pointers cannot be transmuted to integers } }; @@ -38,9 +40,7 @@ impl Type { } fn control(ptr: *const u8) -> usize { - unsafe { - std::mem::transmute(ptr) - } + unsafe { std::mem::transmute(ptr) } } struct ControlStruct; @@ -49,22 +49,15 @@ impl ControlStruct { fn new() -> usize { let value = 10; let ptr: *const i32 = &value; - unsafe { - std::mem::transmute(ptr) - } + unsafe { std::mem::transmute(ptr) } } } - const fn zoom(ptr: *const u8) -> usize { unsafe { std::mem::transmute(ptr) - //~^ WARN pointers cannot be transmuted to integers + //~^ ERROR pointers cannot be transmuted to integers } } -fn main() { - const a: u8 = 10; - const value: usize = zoom(&a); - //~^ ERROR evaluation of constant value failed -} +fn main() {} 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 ca6ad9408ab9..2a9d9b5cb962 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,5 +1,5 @@ -warning: pointers cannot be transmuted to integers during const eval - --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:61:9 +error: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:5:9 | LL | std::mem::transmute(ptr) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,29 +7,14 @@ 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 +note: the lint level is defined here + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:1:9 + | +LL | #![deny(ptr_to_integer_transmute_in_consts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: evaluation of constant value failed - --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:68:26 - | -LL | const value: usize = zoom(&a); - | ^^^^^^^^ unable to turn pointer into integer - | - = 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 - -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 +error: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:15:13 | LL | std::mem::transmute(ptr) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,8 +23,8 @@ LL | std::mem::transmute(ptr) = 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 +error: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:32:13 | LL | std::mem::transmute(ptr) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,6 +33,15 @@ LL | std::mem::transmute(ptr) = 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 +error: pointers cannot be transmuted to integers during const eval + --> $DIR/ptr-to-int-transmute-in-consts-issue-87525.rs:58: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 + +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.rs b/tests/ui/consts/const-eval/raw-pointer-ub.rs index 13a95f9b78f9..1e76104d515e 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.rs +++ b/tests/ui/consts/const-eval/raw-pointer-ub.rs @@ -18,8 +18,7 @@ const MISALIGNED_COPY: () = unsafe { let mut z = 123; y.copy_to_nonoverlapping(&mut z, 1); //~^ ERROR evaluation of constant value failed - //~| NOTE inside `std::ptr::const_ptr - //~| NOTE inside `copy_nonoverlapping::` + //~| NOTE inside `std::ptr::copy_nonoverlapping::` //~| NOTE accessing memory with alignment 1, but alignment 4 is required // The actual error points into the implementation of `copy_to_nonoverlapping`. }; diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.stderr b/tests/ui/consts/const-eval/raw-pointer-ub.stderr index ed5793c84c57..01a8decc93b0 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.stderr +++ b/tests/ui/consts/const-eval/raw-pointer-ub.stderr @@ -16,19 +16,17 @@ error[E0080]: evaluation of constant value failed LL | y.copy_to_nonoverlapping(&mut z, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment 1, but alignment 4 is required | -note: inside `std::ptr::const_ptr::::copy_to_nonoverlapping` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `copy_nonoverlapping::` - --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL +note: inside `std::ptr::copy_nonoverlapping::` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error[E0080]: evaluation of constant value failed - --> $DIR/raw-pointer-ub.rs:34:16 + --> $DIR/raw-pointer-ub.rs:33:16 | LL | let _val = (*ptr).0; | ^^^^^^^^ accessing memory based on pointer with alignment 4, but alignment 16 is required error[E0080]: evaluation of constant value failed - --> $DIR/raw-pointer-ub.rs:41:16 + --> $DIR/raw-pointer-ub.rs:40:16 | LL | let _val = *ptr; | ^^^^ memory access failed: attempting to access 8 bytes, but got ALLOC0 which is only 4 bytes from the end of the allocation diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index de5e721c3f71..cfec1a42f28a 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -153,11 +153,6 @@ error[E0080]: evaluation of constant value failed | LL | ptr.read(); | ^^^^^^^^^^ accessing memory based on pointer with alignment 1, but alignment 4 is required - | -note: inside `std::ptr::const_ptr::::read` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `std::ptr::read::` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error: aborting due to 15 previous errors diff --git a/tests/ui/consts/const-unstable-intrinsic.stderr b/tests/ui/consts/const-unstable-intrinsic.stderr index 308b02386f5c..7e7ba966cee1 100644 --- a/tests/ui/consts/const-unstable-intrinsic.stderr +++ b/tests/ui/consts/const-unstable-intrinsic.stderr @@ -46,14 +46,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | size_of_val(&x); | ^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_main() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local)] LL | const fn const_main() { | @@ -63,14 +58,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | min_align_of_val(&x); | ^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_main() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local)] LL | const fn const_main() { | @@ -88,14 +78,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | super::size_of_val(src); | ^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn copy(src: *const T, _dst: *mut T, _count: usize) { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local)] LL | const unsafe fn copy(src: *const T, _dst: *mut T, _count: usize) { | diff --git a/tests/ui/consts/const_unsafe_unreachable_ub.rs b/tests/ui/consts/const_unsafe_unreachable_ub.rs index a3f7fd46a759..76c6c56d7c50 100644 --- a/tests/ui/consts/const_unsafe_unreachable_ub.rs +++ b/tests/ui/consts/const_unsafe_unreachable_ub.rs @@ -1,14 +1,15 @@ const unsafe fn foo(x: bool) -> bool { match x { true => true, - false => std::hint::unreachable_unchecked(), //~ NOTE inside `foo` + false => std::hint::unreachable_unchecked(), + //~^ NOTE inside `foo` + //~| NOTE the failure occurred here } } const BAR: bool = unsafe { foo(false) }; //~^ ERROR evaluation of constant value failed //~| NOTE entering unreachable code -//~| NOTE inside `unreachable_unchecked` fn main() { assert_eq!(BAR, true); diff --git a/tests/ui/consts/const_unsafe_unreachable_ub.stderr b/tests/ui/consts/const_unsafe_unreachable_ub.stderr index 079ed77b219b..42bf69aded02 100644 --- a/tests/ui/consts/const_unsafe_unreachable_ub.stderr +++ b/tests/ui/consts/const_unsafe_unreachable_ub.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const_unsafe_unreachable_ub.rs:8:28 + --> $DIR/const_unsafe_unreachable_ub.rs:10:28 | LL | const BAR: bool = unsafe { foo(false) }; | ^^^^^^^^^^ entering unreachable code @@ -8,9 +8,7 @@ note: inside `foo` --> $DIR/const_unsafe_unreachable_ub.rs:4:18 | LL | false => std::hint::unreachable_unchecked(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: inside `unreachable_unchecked` - --> $SRC_DIR/core/src/hint.rs:LL:COL + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here error: aborting due to 1 previous error diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs index da483d671f97..d0242f285444 100644 --- a/tests/ui/consts/copy-intrinsic.rs +++ b/tests/ui/consts/copy-intrinsic.rs @@ -1,22 +1,8 @@ -#![stable(feature = "dummy", since = "1.0.0")] - // ignore-tidy-linelength -#![feature(intrinsics, staged_api, rustc_attrs)] +#![feature(core_intrinsics)] + use std::mem; - -#[stable(feature = "dummy", since = "1.0.0")] -#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")] -#[rustc_intrinsic] -const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - unimplemented!() -} - -#[stable(feature = "dummy", since = "1.0.0")] -#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")] -#[rustc_intrinsic] -const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - unimplemented!() -} +use std::intrinsics::{copy, copy_nonoverlapping}; const COPY_ZERO: () = unsafe { // Since we are not copying anything, this should be allowed. diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr index 13321b5703a3..8d586428e56f 100644 --- a/tests/ui/consts/copy-intrinsic.stderr +++ b/tests/ui/consts/copy-intrinsic.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:34:5 + --> $DIR/copy-intrinsic.rs:20:5 | LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got 0x100[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:43:5 + --> $DIR/copy-intrinsic.rs:29:5 | LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x28 which is at or beyond the end of the allocation of size 4 bytes error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:50:5 + --> $DIR/copy-intrinsic.rs:36:5 | LL | copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy` error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:56:5 + --> $DIR/copy-intrinsic.rs:42:5 | LL | copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping` diff --git a/tests/ui/consts/issue-miri-1910.stderr b/tests/ui/consts/issue-miri-1910.stderr index 59cbccc13a72..52edad0c389d 100644 --- a/tests/ui/consts/issue-miri-1910.stderr +++ b/tests/ui/consts/issue-miri-1910.stderr @@ -4,10 +4,6 @@ error[E0080]: evaluation of constant value failed LL | (&foo as *const _ as *const u8).add(one_and_a_half_pointers).read(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer | -note: inside `std::ptr::const_ptr::::read` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `std::ptr::read::` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL = 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 diff --git a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr index 26dedc49a392..9efc252ce6b0 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr +++ b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr @@ -5,14 +5,9 @@ LL | const fn bar() -> u32 { foo() } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar() -> u32 { foo() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const fn bar() -> u32 { foo() } | @@ -23,14 +18,9 @@ LL | const fn bar2() -> u32 { foo2() } | ^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar2() -> u32 { foo2() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const fn bar2() -> u32 { foo2() } | @@ -40,14 +30,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | let x = async { 13 }; | ^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar3() -> u32 { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_async_blocks)] LL | const fn bar3() -> u32 { | @@ -58,14 +43,9 @@ LL | foo() | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar3() -> u32 { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const fn bar3() -> u32 { | @@ -76,14 +56,9 @@ LL | const fn bar2_gated() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn bar2_gated() -> u32 { foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const fn bar2_gated() -> u32 { foo2_gated() } | @@ -94,14 +69,9 @@ LL | pub(crate) const fn bar2_gated_stable_indirect() -> u32 { super::foo2_g | ^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub(crate) const fn bar2_gated_stable_indirect() -> u32 { super::foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | pub(crate) const fn bar2_gated_stable_indirect() -> u32 { super::foo2_gated() } | @@ -112,14 +82,9 @@ LL | const fn stable_indirect() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_indirect() -> u32 { foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const fn stable_indirect() -> u32 { foo2_gated() } | diff --git a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr index b61f7db6f43b..0712a790955a 100644 --- a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr +++ b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr @@ -5,14 +5,9 @@ LL | const unsafe fn bar() -> u32 { unsafe { foo() } } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar() -> u32 { unsafe { foo() } } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const unsafe fn bar() -> u32 { unsafe { foo() } } | @@ -23,14 +18,9 @@ LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } | ^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } | @@ -41,14 +31,9 @@ LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } | diff --git a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr index fad8e396e9ab..618b9a16dd46 100644 --- a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr +++ b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr @@ -5,14 +5,9 @@ LL | const unsafe fn bar() -> u32 { foo() } | ^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar() -> u32 { foo() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo)] LL | const unsafe fn bar() -> u32 { foo() } | @@ -23,14 +18,9 @@ LL | const unsafe fn bar2() -> u32 { foo2() } | ^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2() -> u32 { foo2() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2() -> u32 { foo2() } | @@ -41,14 +31,9 @@ LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(foo2)] LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } | diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr index bbe749f59589..04804cb6d339 100644 --- a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr @@ -5,14 +5,9 @@ LL | unstable_if_unmarked_const_fn_crate::not_stably_const(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_fn() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(rustc_private)] LL | const fn stable_fn() { | diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr index 9d7b81c822bd..14940ae93f89 100644 --- a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr @@ -5,14 +5,9 @@ LL | not_stably_const(); | ^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub const fn expose_on_stable() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(rustc_private)] LL | pub const fn expose_on_stable() { | @@ -22,14 +17,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | let _x = async { 15 }; | ^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub const fn expose_on_stable() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_async_blocks)] LL | pub const fn expose_on_stable() { | diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 88b3e37bb84f..f802138c6131 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -8,17 +8,14 @@ error[E0080]: evaluation of constant value failed 18 | | ); | |_________^ unable to copy parts of a pointer from memory at ALLOC0 | -note: inside `swap_nonoverlapping::>` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `swap_nonoverlapping::compiletime::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `std::ptr::swap_nonoverlapping_const::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `copy_nonoverlapping::>` - --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL +note: inside `std::ptr::copy_nonoverlapping::>` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL = 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 - = note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index 699b63dfd66d..31a2a36a6690 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -3,99 +3,66 @@ error[E0080]: evaluation of constant value failed | LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC0 which is at the beginning of the allocation - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:9:43 | LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC1 which is only 1 byte from the end of the allocation - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:10:45 | LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC2 which is only $BYTES bytes from the end of the allocation - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:12:43 | LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:13:44 | LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:14:56 | LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:15:57 | LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:16:49 | LL | pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC3-0x2 which is only $BYTES bytes from the beginning of the allocation - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:18:50 | LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:19:42 | LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::mut_ptr::::offset` - --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:22:47 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error: aborting due to 11 previous errors diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr index f70e262ac4c5..1375ac751f22 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr @@ -8,9 +8,7 @@ note: inside `ub` --> $DIR/interpret-in-promoted.rs:9:5 | LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: inside `unreachable_unchecked` - --> $SRC_DIR/core/src/hint.rs:LL:COL + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here note: erroneous constant encountered --> $DIR/interpret-in-promoted.rs:15:27 diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr index f70e262ac4c5..1375ac751f22 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr @@ -8,9 +8,7 @@ note: inside `ub` --> $DIR/interpret-in-promoted.rs:9:5 | LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: inside `unreachable_unchecked` - --> $SRC_DIR/core/src/hint.rs:LL:COL + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here note: erroneous constant encountered --> $DIR/interpret-in-promoted.rs:15:27 diff --git a/tests/ui/delegation/bad-resolve.stderr b/tests/ui/delegation/bad-resolve.stderr index 966387e1d616..fc6811292a6f 100644 --- a/tests/ui/delegation/bad-resolve.stderr +++ b/tests/ui/delegation/bad-resolve.stderr @@ -44,9 +44,7 @@ error[E0575]: expected method or associated constant, found associated type `Tra --> $DIR/bad-resolve.rs:27:11 | LL | reuse ::Type; - | ^^^^^^^^^^^^^^^^^^ - | - = note: can't use a type alias as a constructor + | ^^^^^^^^^^^^^^^^^^ not a method or associated constant error[E0576]: cannot find method or associated constant `baz` in trait `Trait` --> $DIR/bad-resolve.rs:30:25 diff --git a/tests/ui/delegation/glob-non-fn.stderr b/tests/ui/delegation/glob-non-fn.stderr index 4b918c53b848..f63c8e88c6fa 100644 --- a/tests/ui/delegation/glob-non-fn.stderr +++ b/tests/ui/delegation/glob-non-fn.stderr @@ -38,9 +38,7 @@ error[E0423]: expected function, found associated type `Trait::Type` --> $DIR/glob-non-fn.rs:30:11 | LL | reuse Trait::* { &self.0 } - | ^^^^^ - | - = note: can't use a type alias as a constructor + | ^^^^^ not a function error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method` --> $DIR/glob-non-fn.rs:29:1 diff --git a/tests/ui/derives/clone-debug-dead-code.stderr b/tests/ui/derives/clone-debug-dead-code.stderr index 38be486e3320..34b7f929ec5e 100644 --- a/tests/ui/derives/clone-debug-dead-code.stderr +++ b/tests/ui/derives/clone-debug-dead-code.stderr @@ -40,7 +40,7 @@ LL | struct D { f: () } | | | field in this struct | - = note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis + = note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis error: field `f` is never read --> $DIR/clone-debug-dead-code.rs:21:12 diff --git a/tests/ui/deriving/deriving-in-macro.rs b/tests/ui/deriving/deriving-in-macro.rs index 493c1415c7fa..739d9b306822 100644 --- a/tests/ui/deriving/deriving-in-macro.rs +++ b/tests/ui/deriving/deriving-in-macro.rs @@ -1,5 +1,6 @@ -//@ run-pass +//@ check-pass #![allow(non_camel_case_types)] +#![allow(dead_code)] macro_rules! define_vec { () => ( diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs index b76b550fcb24..a0e497fa045b 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs @@ -3,7 +3,7 @@ //@ reference: attributes.diagnostic.on_unimplemented.syntax //@ reference: attributes.diagnostic.on_unimplemented.invalid-formats #[diagnostic::on_unimplemented( - on(_Self = "&str"), + on(Self = "&str"), //~^WARN malformed `on_unimplemented` attribute //~|WARN malformed `on_unimplemented` attribute message = "trait has `{Self}` and `{T}` as params", @@ -41,7 +41,7 @@ impl Bar for i32 {} //~|WARN there is no parameter `integral` on trait `Baz` //~|WARN there is no parameter `integer` on trait `Baz` //~|WARN there is no parameter `integer` on trait `Baz` - label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" //~^WARN there is no parameter `float` on trait `Baz` //~|WARN there is no parameter `float` on trait `Baz` //~|WARN there is no parameter `_Self` on trait `Baz` @@ -52,6 +52,8 @@ impl Bar for i32 {} //~|WARN there is no parameter `Trait` on trait `Baz` //~|WARN there is no parameter `ItemContext` on trait `Baz` //~|WARN there is no parameter `ItemContext` on trait `Baz` + //~|WARN there is no parameter `This` on trait `Baz` + //~|WARN there is no parameter `This` on trait `Baz` )] trait Baz {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr index 88816a98dcf0..8dace7d90522 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -9,8 +9,8 @@ LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl warning: malformed `on_unimplemented` attribute --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:6:5 | -LL | on(_Self = "&str"), - | ^^^^^^^^^^^^^^^^^^ invalid option found here +LL | on(Self = "&str"), + | ^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options @@ -81,7 +81,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", warning: there is no parameter `float` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -89,7 +89,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `_Self` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -97,7 +97,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `crate_local` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -105,7 +105,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `Trait` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -113,16 +113,24 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `ItemContext` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument +warning: there is no parameter `This` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" + | ^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + warning: malformed `on_unimplemented` attribute --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:6:5 | -LL | on(_Self = "&str"), - | ^^^^^^^^^^^^^^^^^^ invalid option found here +LL | on(Self = "&str"), + | ^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` @@ -146,7 +154,7 @@ LL | append_const_msg = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: trait has `()` and `i32` as params - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:63:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15 | LL | takes_foo(()); | --------- ^^ trait has `()` and `i32` as params @@ -161,7 +169,7 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:60:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^^^^^^ required by this bound in `takes_foo` @@ -176,7 +184,7 @@ LL | #[diagnostic::on_unimplemented = "Message"] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:67:15 | LL | takes_bar(()); | --------- ^^ the trait `Bar` is not implemented for `()` @@ -185,7 +193,7 @@ LL | takes_bar(()); | = help: the trait `Bar` is implemented for `i32` note: required by a bound in `takes_bar` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:59:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:61:22 | LL | fn takes_bar(_: impl Bar) {} | ^^^ required by this bound in `takes_bar` @@ -238,7 +246,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", warning: there is no parameter `float` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -247,7 +255,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `_Self` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -256,7 +264,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `crate_local` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -265,7 +273,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `Trait` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -274,32 +282,41 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" warning: there is no parameter `ItemContext` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +warning: there is no parameter `This` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" + | ^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer} - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:67:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:69:15 | LL | takes_baz(()); - | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext} + | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This} | | | required by a bound introduced by this call | = help: the trait `Baz` is not implemented for `()` help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:56:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `takes_baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:60:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:62:22 | LL | fn takes_baz(_: impl Baz) {} | ^^^ required by this bound in `takes_baz` -error: aborting due to 3 previous errors; 29 warnings emitted +error: aborting due to 3 previous errors; 31 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs index 8328c10d2a07..08eb5707e909 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs @@ -14,11 +14,15 @@ struct Bar {} //~|WARN malformed `on_unimplemented` attribute trait Baz {} -#[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] +#[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] //~^WARN malformed `on_unimplemented` attribute //~|WARN malformed `on_unimplemented` attribute trait Boom {} +#[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] +//~^WARN malformed `on_unimplemented` attribute +trait _Self {} + #[diagnostic::on_unimplemented = "boom"] //~^WARN malformed `on_unimplemented` attribute trait Doom {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr index 4dd8c1afca02..80790dc3f792 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -25,13 +25,21 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 | +LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:50 + | LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:32 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:32 | LL | #[diagnostic::on_unimplemented = "boom"] | ^^^^^^^^ invalid option found here @@ -39,7 +47,7 @@ LL | #[diagnostic::on_unimplemented = "boom"] = help: only `message`, `note` and `label` are allowed as options warning: missing options for `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1 | LL | #[diagnostic::on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +55,7 @@ LL | #[diagnostic::on_unimplemented] = help: at least one of the `message`, `note` and `label` options are expected warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:44 | LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] | ^^^^^^^^^^^^ @@ -64,7 +72,7 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `i32: Foo` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:47:14 | LL | take_foo(1_i32); | -------- ^^^^^ the trait `Foo` is not implemented for `i32` @@ -77,7 +85,7 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `take_foo` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:36:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:40:21 | LL | fn take_foo(_: impl Foo) {} | ^^^ required by this bound in `take_foo` @@ -92,7 +100,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:45:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:49:14 | LL | take_baz(1_i32); | -------- ^^^^^ the trait `Baz` is not implemented for `i32` @@ -105,7 +113,7 @@ help: this trait has no implementations, consider adding one LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `take_baz` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:41:21 | LL | fn take_baz(_: impl Baz) {} | ^^^ required by this bound in `take_baz` @@ -113,14 +121,14 @@ LL | fn take_baz(_: impl Baz) {} warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 | -LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here +LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:47:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:51:15 | LL | take_boom(1_i32); | --------- ^^^^^ the trait `Boom` is not implemented for `i32` @@ -133,13 +141,13 @@ help: this trait has no implementations, consider adding one LL | trait Boom {} | ^^^^^^^^^^ note: required by a bound in `take_boom` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:42:22 | LL | fn take_boom(_: impl Boom) {} | ^^^^ required by this bound in `take_boom` warning: missing options for `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1 | LL | #[diagnostic::on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,7 +156,7 @@ LL | #[diagnostic::on_unimplemented] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `i32: Whatever` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:49:19 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:53:19 | LL | take_whatever(1_i32); | ------------- ^^^^^ the trait `Whatever` is not implemented for `i32` @@ -156,18 +164,18 @@ LL | take_whatever(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:29:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:1 | LL | trait Whatever {} | ^^^^^^^^^^^^^^ note: required by a bound in `take_whatever` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:26 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:26 | LL | fn take_whatever(_: impl Whatever) {} | ^^^^^^^^ required by this bound in `take_whatever` warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:44 | LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] | ^^^^^^^^^^^^ @@ -176,7 +184,7 @@ LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: {DoesNotExist} - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:51:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:55:15 | LL | take_test(()); | --------- ^^ the trait `Test` is not implemented for `()` @@ -184,16 +192,16 @@ LL | take_test(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:34:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:1 | LL | trait Test {} | ^^^^^^^^^^ note: required by a bound in `take_test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:40:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:44:22 | LL | fn take_test(_: impl Test) {} | ^^^^ required by this bound in `take_test` -error: aborting due to 5 previous errors; 12 warnings emitted +error: aborting due to 5 previous errors; 13 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs index 9cd32ffeb6d0..b7470864a30d 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs @@ -4,4 +4,5 @@ fn main() { let _: &Copy + 'static; //~ ERROR expected a path //~^ ERROR is not dyn compatible let _: &'static Copy + 'static; //~ ERROR expected a path + //~^ ERROR is not dyn compatible } diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr index 762b37b9e9d6..57dbc79a0fda 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr @@ -21,16 +21,26 @@ LL | let _: &'static (Copy + 'static); | + + error[E0038]: the trait `Copy` is not dyn compatible - --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:12 + --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:13 | LL | let _: &Copy + 'static; - | ^^^^^ `Copy` is not dyn compatible + | ^^^^ `Copy` is not dyn compatible | = note: the trait is not dyn compatible because it requires `Self: Sized` = note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit -error: aborting due to 3 previous errors +error[E0038]: the trait `Copy` is not dyn compatible + --> $DIR/trait-object-reference-without-parens-suggestion.rs:6:21 + | +LL | let _: &'static Copy + 'static; + | ^^^^ `Copy` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0038, E0178. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/drop/drop-order-comparisons.e2021.fixed b/tests/ui/drop/drop-order-comparisons.e2021.fixed index 71158cb80621..6c8d2d3fa9c1 100644 --- a/tests/ui/drop/drop-order-comparisons.e2021.fixed +++ b/tests/ui/drop/drop-order-comparisons.e2021.fixed @@ -24,6 +24,7 @@ //@ [e2024] edition: 2024 //@ run-pass +#![feature(if_let_guard)] #![cfg_attr(e2021, feature(let_chains))] #![cfg_attr(e2021, warn(rust_2024_compatibility))] @@ -344,6 +345,25 @@ fn t_if_let_chains_then() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() => { + e.mark(3); + } + _ => {} + }; + e.assert(9); +} + #[cfg(e2021)] #[rustfmt::skip] fn t_if_let_nested_else() { @@ -484,6 +504,25 @@ fn t_if_let_chains_then_else() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then_else() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) => {} + _ => { + e.mark(9); + } + }; + e.assert(9); +} + fn main() { t_bindings(); t_tuples(); @@ -502,10 +541,12 @@ fn main() { t_if_let_nested_then(); t_let_else_chained_then(); t_if_let_chains_then(); + t_guard_if_let_chains_then(); t_if_let_nested_else(); t_if_let_nested_then_else(); t_let_else_chained_then_else(); t_if_let_chains_then_else(); + t_guard_if_let_chains_then_else(); } // # Test scaffolding diff --git a/tests/ui/drop/drop-order-comparisons.e2021.stderr b/tests/ui/drop/drop-order-comparisons.e2021.stderr index 0717a8c1b9b9..8b93376cc0d0 100644 --- a/tests/ui/drop/drop-order-comparisons.e2021.stderr +++ b/tests/ui/drop/drop-order-comparisons.e2021.stderr @@ -1,5 +1,5 @@ warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:76:9 + --> $DIR/drop-order-comparisons.rs:77:9 | LL | _ = ({ | _________- @@ -29,35 +29,35 @@ LL | | }, e.mark(3), e.ok(4)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#3` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `_v` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages note: the lint level is defined here - --> $DIR/drop-order-comparisons.rs:28:25 + --> $DIR/drop-order-comparisons.rs:29:25 | LL | #![cfg_attr(e2021, warn(rust_2024_compatibility))] | ^^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(tail_expr_drop_order)]` implied by `#[warn(rust_2024_compatibility)]` warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:100:45 + --> $DIR/drop-order-comparisons.rs:101:45 | LL | _ = ({ | _________- @@ -77,19 +77,19 @@ LL | | }, e.mark(1), e.ok(4)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:100:19 + --> $DIR/drop-order-comparisons.rs:101:19 | LL | _ = ({ | _________- @@ -109,19 +109,19 @@ LL | | }, e.mark(1), e.ok(4)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:221:24 + --> $DIR/drop-order-comparisons.rs:222:24 | LL | _ = ({ | _________- @@ -141,19 +141,19 @@ LL | | }, e.mark(2), e.ok(3)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:247:24 + --> $DIR/drop-order-comparisons.rs:248:24 | LL | _ = ({ | _________- @@ -173,19 +173,19 @@ LL | | }, e.mark(2), e.ok(3)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:123:13 + --> $DIR/drop-order-comparisons.rs:124:13 | LL | _ = (if let Ok(_) = e.ok(4).as_ref() { | ^^^^^^^^^^^^-------^^^^^^^^^ @@ -195,12 +195,12 @@ LL | _ = (if let Ok(_) = e.ok(4).as_ref() { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:127:5 + --> $DIR/drop-order-comparisons.rs:128:5 | LL | }, e.mark(2), e.ok(3)); | ^ @@ -215,7 +215,7 @@ LL ~ } _ => {}}, e.mark(2), e.ok(3)); | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:145:13 + --> $DIR/drop-order-comparisons.rs:146:13 | LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -225,12 +225,12 @@ LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:145:44 + --> $DIR/drop-order-comparisons.rs:146:44 | LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { | ^ @@ -244,7 +244,7 @@ LL ~ }}, e.mark(2), e.ok(3)); | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:247:12 + --> $DIR/drop-order-comparisons.rs:248:12 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -254,12 +254,12 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:247:43 + --> $DIR/drop-order-comparisons.rs:248:43 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^ @@ -273,7 +273,7 @@ LL ~ }} | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:352:12 + --> $DIR/drop-order-comparisons.rs:372:12 | LL | if let true = e.err(9).is_ok() {} else { | ^^^^^^^^^^^--------^^^^^^^^ @@ -283,12 +283,12 @@ LL | if let true = e.err(9).is_ok() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:352:41 + --> $DIR/drop-order-comparisons.rs:372:41 | LL | if let true = e.err(9).is_ok() {} else { | ^ @@ -302,7 +302,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:355:12 + --> $DIR/drop-order-comparisons.rs:375:12 | LL | if let Ok(_v) = e.err(8) {} else { | ^^^^^^^^^^^^^-------- @@ -312,12 +312,12 @@ LL | if let Ok(_v) = e.err(8) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:355:35 + --> $DIR/drop-order-comparisons.rs:375:35 | LL | if let Ok(_v) = e.err(8) {} else { | ^ @@ -331,7 +331,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:358:12 + --> $DIR/drop-order-comparisons.rs:378:12 | LL | if let Ok(_) = e.err(7) {} else { | ^^^^^^^^^^^^-------- @@ -341,12 +341,12 @@ LL | if let Ok(_) = e.err(7) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:358:34 + --> $DIR/drop-order-comparisons.rs:378:34 | LL | if let Ok(_) = e.err(7) {} else { | ^ @@ -360,7 +360,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:361:12 + --> $DIR/drop-order-comparisons.rs:381:12 | LL | if let Ok(_) = e.err(6).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -370,12 +370,12 @@ LL | if let Ok(_) = e.err(6).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:361:43 + --> $DIR/drop-order-comparisons.rs:381:43 | LL | if let Ok(_) = e.err(6).as_ref() {} else { | ^ @@ -389,7 +389,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:365:12 + --> $DIR/drop-order-comparisons.rs:385:12 | LL | if let Ok(_v) = e.err(5) {} else { | ^^^^^^^^^^^^^-------- @@ -399,12 +399,12 @@ LL | if let Ok(_v) = e.err(5) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:365:35 + --> $DIR/drop-order-comparisons.rs:385:35 | LL | if let Ok(_v) = e.err(5) {} else { | ^ @@ -418,7 +418,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:368:12 + --> $DIR/drop-order-comparisons.rs:388:12 | LL | if let Ok(_) = e.err(4) {} else { | ^^^^^^^^^^^^-------- @@ -428,12 +428,12 @@ LL | if let Ok(_) = e.err(4) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:368:34 + --> $DIR/drop-order-comparisons.rs:388:34 | LL | if let Ok(_) = e.err(4) {} else { | ^ @@ -447,7 +447,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:404:12 + --> $DIR/drop-order-comparisons.rs:424:12 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -457,12 +457,12 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 + --> $DIR/drop-order-comparisons.rs:612:1 | LL | impl<'b> Drop for LogDrop<'b> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:404:43 + --> $DIR/drop-order-comparisons.rs:424:43 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^ diff --git a/tests/ui/drop/drop-order-comparisons.rs b/tests/ui/drop/drop-order-comparisons.rs index 0492b3a4db7f..9a10a08a3ff7 100644 --- a/tests/ui/drop/drop-order-comparisons.rs +++ b/tests/ui/drop/drop-order-comparisons.rs @@ -24,6 +24,7 @@ //@ [e2024] edition: 2024 //@ run-pass +#![feature(if_let_guard)] #![cfg_attr(e2021, feature(let_chains))] #![cfg_attr(e2021, warn(rust_2024_compatibility))] @@ -344,6 +345,25 @@ fn t_if_let_chains_then() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() => { + e.mark(3); + } + _ => {} + }; + e.assert(9); +} + #[cfg(e2021)] #[rustfmt::skip] fn t_if_let_nested_else() { @@ -484,6 +504,25 @@ fn t_if_let_chains_then_else() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then_else() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) => {} + _ => { + e.mark(9); + } + }; + e.assert(9); +} + fn main() { t_bindings(); t_tuples(); @@ -502,10 +541,12 @@ fn main() { t_if_let_nested_then(); t_let_else_chained_then(); t_if_let_chains_then(); + t_guard_if_let_chains_then(); t_if_let_nested_else(); t_if_let_nested_then_else(); t_let_else_chained_then_else(); t_if_let_chains_then_else(); + t_guard_if_let_chains_then_else(); } // # Test scaffolding diff --git a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs index 1b1b8bcf03dc..d73b67dc0809 100644 --- a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs +++ b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs @@ -6,7 +6,6 @@ use std::marker::PhantomData; fn transmute(t: T) -> U { (&PhantomData:: as &dyn Foo).transmute(t) //~^ ERROR the trait `Foo` is not dyn compatible - //~| ERROR the trait `Foo` is not dyn compatible } struct ActuallySuper; diff --git a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr index a384697ee083..acd6dbe7b2c2 100644 --- a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr +++ b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr @@ -1,12 +1,12 @@ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/almost-supertrait-associated-type.rs:21:20 + --> $DIR/almost-supertrait-associated-type.rs:20:20 | LL | impl Dyn for dyn Foo + '_ { | ^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/almost-supertrait-associated-type.rs:33:34 + --> $DIR/almost-supertrait-associated-type.rs:32:34 | LL | trait Foo: Super | --- this trait is not dyn compatible... @@ -16,14 +16,14 @@ LL | fn transmute(&self, t: T) -> >::Assoc; = help: consider moving `transmute` to another trait error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/almost-supertrait-associated-type.rs:7:27 + --> $DIR/almost-supertrait-associated-type.rs:7:32 | LL | (&PhantomData:: as &dyn Foo).transmute(t) - | ^^^^^^^^^^^^^^ `Foo` is not dyn compatible + | ^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/almost-supertrait-associated-type.rs:33:34 + --> $DIR/almost-supertrait-associated-type.rs:32:34 | LL | trait Foo: Super | --- this trait is not dyn compatible... @@ -32,24 +32,6 @@ LL | fn transmute(&self, t: T) -> >::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type = help: consider moving `transmute` to another trait -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/almost-supertrait-associated-type.rs:7:6 - | -LL | (&PhantomData:: as &dyn Foo).transmute(t) - | ^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/almost-supertrait-associated-type.rs:33:34 - | -LL | trait Foo: Super - | --- this trait is not dyn compatible... -... -LL | fn transmute(&self, t: T) -> >::Assoc; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type - = help: consider moving `transmute` to another trait - = note: required for the cast from `&PhantomData` to `&dyn Foo` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/associated-consts.rs b/tests/ui/dyn-compatibility/associated-consts.rs index 10d151d9a8b5..69fff81b2814 100644 --- a/tests/ui/dyn-compatibility/associated-consts.rs +++ b/tests/ui/dyn-compatibility/associated-consts.rs @@ -8,7 +8,6 @@ trait Bar { fn make_bar(t: &T) -> &dyn Bar { //~^ ERROR E0038 t - //~^ ERROR E0038 } fn main() { diff --git a/tests/ui/dyn-compatibility/associated-consts.stderr b/tests/ui/dyn-compatibility/associated-consts.stderr index beaf263af07e..dc64c93a577e 100644 --- a/tests/ui/dyn-compatibility/associated-consts.stderr +++ b/tests/ui/dyn-compatibility/associated-consts.stderr @@ -14,23 +14,6 @@ LL | const X: usize; | ^ ...because it contains this associated `const` = help: consider moving `X` to another trait -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/associated-consts.rs:10:5 - | -LL | t - | ^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/associated-consts.rs:5:11 - | -LL | trait Bar { - | --- this trait is not dyn compatible... -LL | const X: usize; - | ^ ...because it contains this associated `const` - = help: consider moving `X` to another trait - = note: required for the cast from `&T` to `&dyn Bar` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/generics.rs b/tests/ui/dyn-compatibility/generics.rs index dcce17f925bc..c25bdab361ba 100644 --- a/tests/ui/dyn-compatibility/generics.rs +++ b/tests/ui/dyn-compatibility/generics.rs @@ -15,14 +15,12 @@ trait Quux { fn make_bar(t: &T) -> &dyn Bar { //~^ ERROR E0038 t - //~^ ERROR E0038 } fn make_bar_explicit(t: &T) -> &dyn Bar { //~^ ERROR E0038 t as &dyn Bar //~^ ERROR E0038 - //~| ERROR E0038 } fn make_quux(t: &T) -> &dyn Quux { diff --git a/tests/ui/dyn-compatibility/generics.stderr b/tests/ui/dyn-compatibility/generics.stderr index c01930105419..79dccc422448 100644 --- a/tests/ui/dyn-compatibility/generics.stderr +++ b/tests/ui/dyn-compatibility/generics.stderr @@ -15,7 +15,7 @@ LL | fn bar(&self, t: T); = help: consider moving `bar` to another trait error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/generics.rs:21:40 + --> $DIR/generics.rs:20:40 | LL | fn make_bar_explicit(t: &T) -> &dyn Bar { | ^^^^^^^ `Bar` is not dyn compatible @@ -31,27 +31,10 @@ LL | fn bar(&self, t: T); = help: consider moving `bar` to another trait error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/generics.rs:17:5 - | -LL | t - | ^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/generics.rs:7:8 - | -LL | trait Bar { - | --- this trait is not dyn compatible... -LL | fn bar(&self, t: T); - | ^^^ ...because method `bar` has generic type parameters - = help: consider moving `bar` to another trait - = note: required for the cast from `&T` to `&dyn Bar` - -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/generics.rs:23:10 + --> $DIR/generics.rs:22:15 | LL | t as &dyn Bar - | ^^^^^^^^ `Bar` is not dyn compatible + | ^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -63,23 +46,6 @@ LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/generics.rs:23:5 - | -LL | t as &dyn Bar - | ^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/generics.rs:7:8 - | -LL | trait Bar { - | --- this trait is not dyn compatible... -LL | fn bar(&self, t: T); - | ^^^ ...because method `bar` has generic type parameters - = help: consider moving `bar` to another trait - = note: required for the cast from `&T` to `&dyn Bar` - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.rs b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.rs index 1289d2d7874a..d8b1bc5b7177 100644 --- a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.rs +++ b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.rs @@ -18,5 +18,4 @@ fn main() { let mut thing = Thing; let test: &mut dyn Bar = &mut thing; //~^ ERROR E0038 - //~| ERROR E0038 } diff --git a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr index c1e93ccb83ca..dd7b31a70c55 100644 --- a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr +++ b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr @@ -1,27 +1,8 @@ error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:30 + --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:24 | LL | let test: &mut dyn Bar = &mut thing; - | ^^^^^^^^^^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/mention-correct-dyn-incompatible-trait.rs:4:8 - | -LL | fn foo(&self, val: T); - | ^^^ ...because method `foo` has generic type parameters -... -LL | trait Bar: Foo { } - | --- this trait is not dyn compatible... - = help: consider moving `foo` to another trait - = help: only type `Thing` implements `Bar`; consider using it directly instead. - = note: required for the cast from `&mut Thing` to `&mut dyn Bar` - -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:15 - | -LL | let test: &mut dyn Bar = &mut thing; - | ^^^^^^^^^^^^ `Bar` is not dyn compatible + | ^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -35,6 +16,6 @@ LL | trait Bar: Foo { } = help: consider moving `foo` to another trait = help: only type `Thing` implements `Bar`; consider using it directly instead. -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/mentions-Self.rs b/tests/ui/dyn-compatibility/mentions-Self.rs index ce210f4776f7..8b0d5ec6604a 100644 --- a/tests/ui/dyn-compatibility/mentions-Self.rs +++ b/tests/ui/dyn-compatibility/mentions-Self.rs @@ -18,13 +18,11 @@ trait Quux { fn make_bar(t: &T) -> &dyn Bar { //~^ ERROR E0038 t - //~^ ERROR E0038 } fn make_baz(t: &T) -> &dyn Baz { //~^ ERROR E0038 t - //~^ ERROR E0038 } fn make_quux(t: &T) -> &dyn Quux { diff --git a/tests/ui/dyn-compatibility/mentions-Self.stderr b/tests/ui/dyn-compatibility/mentions-Self.stderr index 6d1ae90152e6..9d41e1d92ddf 100644 --- a/tests/ui/dyn-compatibility/mentions-Self.stderr +++ b/tests/ui/dyn-compatibility/mentions-Self.stderr @@ -15,7 +15,7 @@ LL | fn bar(&self, x: &Self); = help: consider moving `bar` to another trait error[E0038]: the trait `Baz` is not dyn compatible - --> $DIR/mentions-Self.rs:24:31 + --> $DIR/mentions-Self.rs:23:31 | LL | fn make_baz(t: &T) -> &dyn Baz { | ^^^^^^^ `Baz` is not dyn compatible @@ -30,40 +30,6 @@ LL | fn baz(&self) -> Self; | ^^^^ ...because method `baz` references the `Self` type in its return type = help: consider moving `baz` to another trait -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/mentions-Self.rs:20:5 - | -LL | t - | ^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/mentions-Self.rs:7:22 - | -LL | trait Bar { - | --- this trait is not dyn compatible... -LL | fn bar(&self, x: &Self); - | ^^^^^ ...because method `bar` references the `Self` type in this parameter - = help: consider moving `bar` to another trait - = note: required for the cast from `&T` to `&dyn Bar` - -error[E0038]: the trait `Baz` is not dyn compatible - --> $DIR/mentions-Self.rs:26:5 - | -LL | t - | ^ `Baz` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/mentions-Self.rs:11:22 - | -LL | trait Baz { - | --- this trait is not dyn compatible... -LL | fn baz(&self) -> Self; - | ^^^^ ...because method `baz` references the `Self` type in its return type - = help: consider moving `baz` to another trait - = note: required for the cast from `&T` to `&dyn Baz` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/no-static.rs b/tests/ui/dyn-compatibility/no-static.rs index 9bd871619728..2d5954afffda 100644 --- a/tests/ui/dyn-compatibility/no-static.rs +++ b/tests/ui/dyn-compatibility/no-static.rs @@ -17,5 +17,4 @@ impl Foo for Bar {} fn main() { let b: Box = Box::new(Bar); //~^ ERROR E0038 - //~| ERROR E0038 } diff --git a/tests/ui/dyn-compatibility/no-static.stderr b/tests/ui/dyn-compatibility/no-static.stderr index 814ab0d53c3f..c1d5dd6f562f 100644 --- a/tests/ui/dyn-compatibility/no-static.stderr +++ b/tests/ui/dyn-compatibility/no-static.stderr @@ -23,10 +23,10 @@ LL | fn foo() where Self: Sized {} | +++++++++++++++++ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/no-static.rs:18:12 + --> $DIR/no-static.rs:18:20 | LL | let b: Box = Box::new(Bar); - | ^^^^^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -46,31 +46,6 @@ help: alternatively, consider constraining `foo` so it does not apply to trait o LL | fn foo() where Self: Sized {} | +++++++++++++++++ -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/no-static.rs:18:27 - | -LL | let b: Box = Box::new(Bar); - | ^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/no-static.rs:5:8 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | fn foo() {} - | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `Bar` implements `Foo`; consider using it directly instead. - = note: required for the cast from `Box` to `Box` -help: consider turning `foo` into a method by giving it a `&self` argument - | -LL | fn foo(&self) {} - | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects - | -LL | fn foo() where Self: Sized {} - | +++++++++++++++++ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/sized-2.rs b/tests/ui/dyn-compatibility/sized-2.rs index f61d49ee8dff..c99dcce46b2c 100644 --- a/tests/ui/dyn-compatibility/sized-2.rs +++ b/tests/ui/dyn-compatibility/sized-2.rs @@ -10,7 +10,6 @@ trait Bar fn make_bar(t: &T) -> &dyn Bar { //~^ ERROR E0038 t - //~^ ERROR E0038 } fn main() { diff --git a/tests/ui/dyn-compatibility/sized-2.stderr b/tests/ui/dyn-compatibility/sized-2.stderr index 1834d906bb89..70bd5f6dd36c 100644 --- a/tests/ui/dyn-compatibility/sized-2.stderr +++ b/tests/ui/dyn-compatibility/sized-2.stderr @@ -13,22 +13,6 @@ LL | trait Bar LL | where Self : Sized | ^^^^^ ...because it requires `Self: Sized` -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/sized-2.rs:12:5 - | -LL | t - | ^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/sized-2.rs:5:18 - | -LL | trait Bar - | --- this trait is not dyn compatible... -LL | where Self : Sized - | ^^^^^ ...because it requires `Self: Sized` - = note: required for the cast from `&T` to `&dyn Bar` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/sized.rs b/tests/ui/dyn-compatibility/sized.rs index eb5279c17e62..b5a8a4be766a 100644 --- a/tests/ui/dyn-compatibility/sized.rs +++ b/tests/ui/dyn-compatibility/sized.rs @@ -8,7 +8,6 @@ trait Bar: Sized { fn make_bar(t: &T) -> &dyn Bar { //~^ ERROR E0038 t - //~^ ERROR E0038 } fn main() {} diff --git a/tests/ui/dyn-compatibility/sized.stderr b/tests/ui/dyn-compatibility/sized.stderr index c66e299cf6f5..0cc41179d9a8 100644 --- a/tests/ui/dyn-compatibility/sized.stderr +++ b/tests/ui/dyn-compatibility/sized.stderr @@ -13,22 +13,6 @@ LL | trait Bar: Sized { | | | this trait is not dyn compatible... -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/sized.rs:10:5 - | -LL | t - | ^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/sized.rs:4:12 - | -LL | trait Bar: Sized { - | --- ^^^^^ ...because it requires `Self: Sized` - | | - | this trait is not dyn compatible... - = note: required for the cast from `&T` to `&dyn Bar` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/taint-const-eval.rs b/tests/ui/dyn-compatibility/taint-const-eval.rs index 64c4df611e65..a5c01e1791e7 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.rs +++ b/tests/ui/dyn-compatibility/taint-const-eval.rs @@ -7,6 +7,5 @@ trait Qux { static FOO: &(dyn Qux + Sync) = "desc"; //~^ ERROR the trait `Qux` is not dyn compatible //~| ERROR the trait `Qux` is not dyn compatible -//~| ERROR the trait `Qux` is not dyn compatible fn main() {} diff --git a/tests/ui/dyn-compatibility/taint-const-eval.stderr b/tests/ui/dyn-compatibility/taint-const-eval.stderr index 942c20db6ce0..585c1f012c78 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.stderr +++ b/tests/ui/dyn-compatibility/taint-const-eval.stderr @@ -21,30 +21,6 @@ help: alternatively, consider constraining `bar` so it does not apply to trait o LL | fn bar() where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qux` is not dyn compatible - --> $DIR/taint-const-eval.rs:7:33 - | -LL | static FOO: &(dyn Qux + Sync) = "desc"; - | ^^^^^^ `Qux` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/taint-const-eval.rs:4:8 - | -LL | trait Qux { - | --- this trait is not dyn compatible... -LL | fn bar(); - | ^^^ ...because associated function `bar` has no `self` parameter - = note: required for the cast from `&'static str` to `&'static (dyn Qux + Sync + 'static)` -help: consider turning `bar` into a method by giving it a `&self` argument - | -LL | fn bar(&self); - | +++++ -help: alternatively, consider constraining `bar` so it does not apply to trait objects - | -LL | fn bar() where Self: Sized; - | +++++++++++++++++ - error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/taint-const-eval.rs:7:15 | @@ -69,6 +45,6 @@ help: alternatively, consider constraining `bar` so it does not apply to trait o LL | fn bar() where Self: Sized; | +++++++++++++++++ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs index ec32bec7785a..ac3c2aadf290 100644 --- a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs +++ b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs @@ -25,5 +25,4 @@ pub fn foo() { let fetcher = fetcher(); //~^ ERROR the trait `Fetcher` is not dyn compatible let _ = fetcher.get(); - //~^ ERROR the trait `Fetcher` is not dyn compatible } diff --git a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr index 1299167159e2..867a719e2ebf 100644 --- a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr +++ b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr @@ -34,24 +34,6 @@ LL | pub trait Fetcher: Send + Sync { LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> | ^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on -error[E0038]: the trait `Fetcher` is not dyn compatible - --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:27:13 - | -LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> - | ------------- help: consider changing method `get`'s `self` parameter to be `&self`: `&Self` -... -LL | let _ = fetcher.get(); - | ^^^^^^^^^^^^^ `Fetcher` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 - | -LL | pub trait Fetcher: Send + Sync { - | ------- this trait is not dyn compatible... -LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> - | ^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/editions/edition-extern-crate-allowed.stderr b/tests/ui/editions/edition-extern-crate-allowed.stderr index dde774c520d7..4444ab79b382 100644 --- a/tests/ui/editions/edition-extern-crate-allowed.stderr +++ b/tests/ui/editions/edition-extern-crate-allowed.stderr @@ -2,7 +2,7 @@ warning: unused extern crate --> $DIR/edition-extern-crate-allowed.rs:7:1 | LL | extern crate edition_extern_crate_allowed; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/edition-extern-crate-allowed.rs:5:9 @@ -10,6 +10,10 @@ note: the lint level is defined here LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate edition_extern_crate_allowed; + | warning: 1 warning emitted diff --git a/tests/ui/empty/empty-struct-braces-expr.stderr b/tests/ui/empty/empty-struct-braces-expr.stderr index 8ec8ecf46bf0..a176107a06e2 100644 --- a/tests/ui/empty/empty-struct-braces-expr.stderr +++ b/tests/ui/empty/empty-struct-braces-expr.stderr @@ -121,7 +121,7 @@ error[E0599]: no variant or associated item named `Empty3` found for enum `empty --> $DIR/empty-struct-braces-expr.rs:25:19 | LL | let xe3 = XE::Empty3; - | ^^^^^^ variant or associated item not found in `XE` + | ^^^^^^ variant or associated item not found in `empty_struct::XE` | help: there is a variant with a similar name | @@ -132,7 +132,7 @@ error[E0599]: no variant or associated item named `Empty3` found for enum `empty --> $DIR/empty-struct-braces-expr.rs:26:19 | LL | let xe3 = XE::Empty3(); - | ^^^^^^ variant or associated item not found in `XE` + | ^^^^^^ variant or associated item not found in `empty_struct::XE` | help: there is a variant with a similar name | diff --git a/tests/ui/enum-discriminant/discriminant_size.rs b/tests/ui/enum-discriminant/discriminant_size.rs index a3ec1b28e5c7..b1feff3c59e1 100644 --- a/tests/ui/enum-discriminant/discriminant_size.rs +++ b/tests/ui/enum-discriminant/discriminant_size.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(core_intrinsics, repr128)] -//~^ WARN the feature `repr128` is incomplete +#![feature(core_intrinsics)] use std::intrinsics::discriminant_value; diff --git a/tests/ui/enum-discriminant/discriminant_size.stderr b/tests/ui/enum-discriminant/discriminant_size.stderr deleted file mode 100644 index 9b1505b5c468..000000000000 --- a/tests/ui/enum-discriminant/discriminant_size.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `repr128` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/discriminant_size.rs:2:29 - | -LL | #![feature(core_intrinsics, repr128)] - | ^^^^^^^ - | - = note: see issue #56071 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/enum-discriminant/issue-43398.stderr b/tests/ui/enum-discriminant/issue-43398.stderr deleted file mode 100644 index fc7bbd062843..000000000000 --- a/tests/ui/enum-discriminant/issue-43398.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `repr128` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-43398.rs:4:12 - | -LL | #![feature(repr128)] - | ^^^^^^^ - | - = note: see issue #56071 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/enum-discriminant/issue-70509-partial_eq.rs b/tests/ui/enum-discriminant/issue-70509-partial_eq.rs index e98532c12079..5e71972c2808 100644 --- a/tests/ui/enum-discriminant/issue-70509-partial_eq.rs +++ b/tests/ui/enum-discriminant/issue-70509-partial_eq.rs @@ -1,6 +1,4 @@ //@ run-pass -#![feature(repr128)] -//~^ WARN the feature `repr128` is incomplete #[derive(PartialEq, Debug)] #[repr(i128)] diff --git a/tests/ui/enum-discriminant/issue-70509-partial_eq.stderr b/tests/ui/enum-discriminant/issue-70509-partial_eq.stderr deleted file mode 100644 index 2eef930c3943..000000000000 --- a/tests/ui/enum-discriminant/issue-70509-partial_eq.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `repr128` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-70509-partial_eq.rs:2:12 - | -LL | #![feature(repr128)] - | ^^^^^^^ - | - = note: see issue #56071 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/enum-discriminant/issue-43398.rs b/tests/ui/enum-discriminant/repr128-get-discriminant-issue-43398.rs similarity index 75% rename from tests/ui/enum-discriminant/issue-43398.rs rename to tests/ui/enum-discriminant/repr128-get-discriminant-issue-43398.rs index 574a4b3ad5a0..2bb9725fb77d 100644 --- a/tests/ui/enum-discriminant/issue-43398.rs +++ b/tests/ui/enum-discriminant/repr128-get-discriminant-issue-43398.rs @@ -1,8 +1,6 @@ //@ run-pass #![feature(core_intrinsics)] -#![feature(repr128)] -//~^ WARN the feature `repr128` is incomplete #[repr(i128)] enum Big { A, B } diff --git a/tests/ui/enum-discriminant/repr128.rs b/tests/ui/enum-discriminant/repr128.rs index 075ff7a76761..d59a5b3e256d 100644 --- a/tests/ui/enum-discriminant/repr128.rs +++ b/tests/ui/enum-discriminant/repr128.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(repr128, core_intrinsics, discriminant_kind)] -//~^ WARN the feature `repr128` is incomplete +#![feature(core_intrinsics, discriminant_kind)] use std::intrinsics::discriminant_value; use std::marker::DiscriminantKind; diff --git a/tests/ui/enum-discriminant/repr128.stderr b/tests/ui/enum-discriminant/repr128.stderr deleted file mode 100644 index da8d75c11aff..000000000000 --- a/tests/ui/enum-discriminant/repr128.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `repr128` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/repr128.rs:2:12 - | -LL | #![feature(repr128, core_intrinsics, discriminant_kind)] - | ^^^^^^^ - | - = note: see issue #56071 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/error-codes/E0038.rs b/tests/ui/error-codes/E0038.rs index a467767c3faa..9757e2ab10c7 100644 --- a/tests/ui/error-codes/E0038.rs +++ b/tests/ui/error-codes/E0038.rs @@ -5,8 +5,6 @@ trait Trait { fn call_foo(x: Box) { //~^ ERROR E0038 let y = x.foo(); - //~^ ERROR E0038 - //~| ERROR E0277 } fn main() { diff --git a/tests/ui/error-codes/E0038.stderr b/tests/ui/error-codes/E0038.stderr index 63a5249a3864..e09aefaa0dd1 100644 --- a/tests/ui/error-codes/E0038.stderr +++ b/tests/ui/error-codes/E0038.stderr @@ -14,33 +14,6 @@ LL | fn foo(&self) -> Self; | ^^^^ ...because method `foo` references the `Self` type in its return type = help: consider moving `foo` to another trait -error[E0038]: the trait `Trait` is not dyn compatible - --> $DIR/E0038.rs:7:13 - | -LL | let y = x.foo(); - | ^^^^^^^ `Trait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/E0038.rs:2:22 - | -LL | trait Trait { - | ----- this trait is not dyn compatible... -LL | fn foo(&self) -> Self; - | ^^^^ ...because method `foo` references the `Self` type in its return type - = help: consider moving `foo` to another trait +error: aborting due to 1 previous error -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/E0038.rs:7:9 - | -LL | let y = x.foo(); - | ^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - = note: all local variables must have a statically known size - = help: unsized locals are gated as an unstable feature - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0277. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/error-codes/E0223.stderr b/tests/ui/error-codes/E0223.stderr index e985a4c9bf0d..fbfdce5689a7 100644 --- a/tests/ui/error-codes/E0223.stderr +++ b/tests/ui/error-codes/E0223.stderr @@ -2,7 +2,13 @@ error[E0223]: ambiguous associated type --> $DIR/E0223.rs:8:14 | LL | let foo: MyTrait::X; - | ^^^^^^^^^^ help: use fully-qualified syntax: `::X` + | ^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let foo: MyTrait::X; +LL + let foo: ::X; + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0658.rs b/tests/ui/error-codes/E0658.rs index 9c9b95d70a7c..e51674cdf92e 100644 --- a/tests/ui/error-codes/E0658.rs +++ b/tests/ui/error-codes/E0658.rs @@ -1,6 +1,3 @@ -#[repr(u128)] -enum Foo { //~ ERROR E0658 - Bar(u64), -} +use std::intrinsics; //~ ERROR E0658 fn main() {} diff --git a/tests/ui/error-codes/E0658.stderr b/tests/ui/error-codes/E0658.stderr index e1e812940ec5..ae7ecbbc5cb0 100644 --- a/tests/ui/error-codes/E0658.stderr +++ b/tests/ui/error-codes/E0658.stderr @@ -1,11 +1,10 @@ -error[E0658]: repr with 128-bit type is unstable - --> $DIR/E0658.rs:2:1 +error[E0658]: use of unstable library feature `core_intrinsics`: intrinsics are unlikely to ever be stabilized, instead they should be used through stabilized interfaces in the rest of the standard library + --> $DIR/E0658.rs:1:5 | -LL | enum Foo { - | ^^^^^^^^ +LL | use std::intrinsics; + | ^^^^^^^^^^^^^^^ | - = note: see issue #56071 for more information - = help: add `#![feature(repr128)]` to the crate attributes to enable + = help: add `#![feature(core_intrinsics)]` 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 diff --git a/tests/ui/error-emitter/multiline-removal-suggestion.svg b/tests/ui/error-emitter/multiline-removal-suggestion.svg index 95c7740f6995..9c9bd163ecd4 100644 --- a/tests/ui/error-emitter/multiline-removal-suggestion.svg +++ b/tests/ui/error-emitter/multiline-removal-suggestion.svg @@ -1,4 +1,4 @@ - +

{} + +struct Container { + data: T, +} + +struct Desugared { + callback: Box MyFn<&'a Container<&'a u8>>>, +} + +fn test(callback: Box MyFn>>) -> Desugared { + Desugared { callback } + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/fn/coerce-suggestion-infer-region.stderr b/tests/ui/fn/coerce-suggestion-infer-region.stderr new file mode 100644 index 000000000000..9dd0fcf76ce1 --- /dev/null +++ b/tests/ui/fn/coerce-suggestion-infer-region.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/coerce-suggestion-infer-region.rs:22:17 + | +LL | Desugared { callback } + | ^^^^^^^^ expected `Box>>`, found `Box>>` + | + = note: expected struct `Box<(dyn for<'a> MyFn<&'a Container<&'a u8>> + 'static)>` + found struct `Box<(dyn for<'a> MyFn> + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/bare-fn-implements-fn-mut.rs b/tests/ui/functions-closures/bare-fn-implements-fn-mut.rs similarity index 76% rename from tests/ui/bare-fn-implements-fn-mut.rs rename to tests/ui/functions-closures/bare-fn-implements-fn-mut.rs index 49b31f28f8a0..52d5ad3d0f79 100644 --- a/tests/ui/bare-fn-implements-fn-mut.rs +++ b/tests/ui/functions-closures/bare-fn-implements-fn-mut.rs @@ -1,3 +1,7 @@ +//! Tests that bare functions implement the `FnMut` trait. +//! +//! See . + //@ run-pass fn call_f(mut f: F) { diff --git a/tests/ui/functions-closures/fn-help-with-err.stderr b/tests/ui/functions-closures/fn-help-with-err.stderr index e5fe4611434e..9dc56c3652ac 100644 --- a/tests/ui/functions-closures/fn-help-with-err.stderr +++ b/tests/ui/functions-closures/fn-help-with-err.stderr @@ -8,7 +8,7 @@ error[E0599]: no method named `bar` found for struct `Arc<{closure@$DIR/fn-help- --> $DIR/fn-help-with-err.rs:19:10 | LL | arc2.bar(); - | ^^^ method not found in `Arc<{closure@fn-help-with-err.rs:18:36}>` + | ^^^ method not found in `Arc<{closure@$DIR/fn-help-with-err.rs:18:36: 18:38}>` | = help: items from traits can only be used if the trait is implemented and in scope note: `Bar` defines an item `bar`, perhaps you need to implement it diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.rs b/tests/ui/generic-associated-types/gat-in-trait-path.rs index 7523803eacff..774e16d84c50 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path.rs +++ b/tests/ui/generic-associated-types/gat-in-trait-path.rs @@ -26,5 +26,4 @@ fn main() { let foo = Fooer(5); f(Box::new(foo)); //~^ ERROR the trait `Foo` is not dyn compatible - //~| ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.stderr index e57f6b48401f..d4ccd80f1465 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path.stderr +++ b/tests/ui/generic-associated-types/gat-in-trait-path.stderr @@ -30,23 +30,6 @@ LL | type A<'a> where Self: 'a; | ^ ...because it contains the generic associated type `A` = help: consider moving `A` to another trait -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/gat-in-trait-path.rs:27:5 - | -LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/gat-in-trait-path.rs:6:10 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = note: required for the cast from `Box>` to `Box<(dyn Foo = &'a ()> + 'static)>` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-71176.rs b/tests/ui/generic-associated-types/issue-71176.rs index d3a0caffec1e..8ecfa93750d4 100644 --- a/tests/ui/generic-associated-types/issue-71176.rs +++ b/tests/ui/generic-associated-types/issue-71176.rs @@ -18,6 +18,5 @@ fn main() { Holder { inner: Box::new(()), //~^ ERROR: the trait `Provider` is not dyn compatible - //~| ERROR: the trait `Provider` is not dyn compatible }; } diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index 56439f6dfea8..f231056a2eed 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -82,25 +82,7 @@ LL | type A<'a>; = help: consider moving `A` to another trait = help: only type `()` implements `Provider`; consider using it directly instead. -error[E0038]: the trait `Provider` is not dyn compatible - --> $DIR/issue-71176.rs:19:16 - | -LL | inner: Box::new(()), - | ^^^^^^^^^^^^ `Provider` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-71176.rs:2:10 - | -LL | trait Provider { - | -------- this trait is not dyn compatible... -LL | type A<'a>; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = help: only type `()` implements `Provider`; consider using it directly instead. - = note: required for the cast from `Box<()>` to `Box<(dyn Provider = _> + 'static), {type error}>` - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0038, E0107. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-76535.rs b/tests/ui/generic-associated-types/issue-76535.rs index 9e18c82c7f1c..dc697401a58c 100644 --- a/tests/ui/generic-associated-types/issue-76535.rs +++ b/tests/ui/generic-associated-types/issue-76535.rs @@ -33,6 +33,5 @@ impl SuperTrait for SuperStruct { fn main() { let sub: Box> = Box::new(SuperStruct::new(0)); //~^ ERROR missing generics for associated type - //~^^ ERROR the trait //~| ERROR the trait } diff --git a/tests/ui/generic-associated-types/issue-76535.stderr b/tests/ui/generic-associated-types/issue-76535.stderr index b828234afa12..2daf9d817bb3 100644 --- a/tests/ui/generic-associated-types/issue-76535.stderr +++ b/tests/ui/generic-associated-types/issue-76535.stderr @@ -15,10 +15,10 @@ LL | let sub: Box = SubStruct>> = Box::new(SuperS | ++++ error[E0038]: the trait `SuperTrait` is not dyn compatible - --> $DIR/issue-76535.rs:34:14 + --> $DIR/issue-76535.rs:34:22 | LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -32,26 +32,7 @@ LL | type SubType<'a>: SubTrait where Self: 'a; = help: only type `SuperStruct` implements `SuperTrait` within this crate; consider using it directly instead. = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type -error[E0038]: the trait `SuperTrait` is not dyn compatible - --> $DIR/issue-76535.rs:34:57 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-76535.rs:4:10 - | -LL | pub trait SuperTrait { - | ---------- this trait is not dyn compatible... -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` - = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` implements `SuperTrait` within this crate; consider using it directly instead. - = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type - = note: required for the cast from `Box` to `Box = SubStruct<'_>>>` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0038, E0107. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-78671.stderr b/tests/ui/generic-associated-types/issue-78671.stderr index c6da137672de..fff061a8ada7 100644 --- a/tests/ui/generic-associated-types/issue-78671.stderr +++ b/tests/ui/generic-associated-types/issue-78671.stderr @@ -15,10 +15,10 @@ LL | Box::new(Family) as &dyn CollectionFamily=usize> | +++ error[E0038]: the trait `CollectionFamily` is not dyn compatible - --> $DIR/issue-78671.rs:5:25 + --> $DIR/issue-78671.rs:5:30 | LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` is not dyn compatible + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit diff --git a/tests/ui/generic-associated-types/issue-79422.rs b/tests/ui/generic-associated-types/issue-79422.rs index fba7a86990ec..462614b36127 100644 --- a/tests/ui/generic-associated-types/issue-79422.rs +++ b/tests/ui/generic-associated-types/issue-79422.rs @@ -15,12 +15,17 @@ impl<'a, T> RefCont<'a, T> for Box { } trait MapLike { - type VRefCont<'a>: RefCont<'a, V> where Self: 'a; + type VRefCont<'a>: RefCont<'a, V> + where + Self: 'a; fn get<'a>(&'a self, key: &K) -> Option>; } impl MapLike for std::collections::BTreeMap { - type VRefCont<'a> = &'a V where Self: 'a; + type VRefCont<'a> + = &'a V + where + Self: 'a; fn get<'a>(&'a self, key: &K) -> Option<&'a V> { std::collections::BTreeMap::get(self, key) } @@ -37,8 +42,7 @@ impl MapLike for Source { fn main() { let m = Box::new(std::collections::BTreeMap::::new()) - //~^ ERROR the trait as Box>>; - //~^ ERROR missing generics for associated type - //~| ERROR the trait + //~^ ERROR the trait + //~| ERROR missing generics for associated type } diff --git a/tests/ui/generic-associated-types/issue-79422.stderr b/tests/ui/generic-associated-types/issue-79422.stderr index 6311e4de272d..dcf3a9008de5 100644 --- a/tests/ui/generic-associated-types/issue-79422.stderr +++ b/tests/ui/generic-associated-types/issue-79422.stderr @@ -1,5 +1,5 @@ error[E0107]: missing generics for associated type `MapLike::VRefCont` - --> $DIR/issue-79422.rs:41:36 + --> $DIR/issue-79422.rs:45:36 | LL | as Box>>; | ^^^^^^^^ expected 1 lifetime argument @@ -7,7 +7,7 @@ LL | as Box>>; note: associated type defined here, with 1 lifetime parameter: `'a` --> $DIR/issue-79422.rs:18:10 | -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; +LL | type VRefCont<'a>: RefCont<'a, V> | ^^^^^^^^ -- help: add missing lifetime argument | @@ -15,10 +15,10 @@ LL | as Box = dyn RefCont<'_, u8>>>; | ++++ error[E0038]: the trait `MapLike` is not dyn compatible - --> $DIR/issue-79422.rs:41:12 + --> $DIR/issue-79422.rs:45:20 | LL | as Box>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -26,28 +26,11 @@ note: for a trait to be dyn compatible it needs to allow building a vtable | LL | trait MapLike { | ------- this trait is not dyn compatible... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; +LL | type VRefCont<'a>: RefCont<'a, V> | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait -error[E0038]: the trait `MapLike` is not dyn compatible - --> $DIR/issue-79422.rs:39:13 - | -LL | let m = Box::new(std::collections::BTreeMap::::new()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-79422.rs:18:10 - | -LL | trait MapLike { - | ------- this trait is not dyn compatible... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` - = help: consider moving `VRefCont` to another trait - = note: required for the cast from `Box>` to `Box = (dyn RefCont<'_, u8> + 'static)>>` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0038, E0107. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/trait-objects.rs b/tests/ui/generic-associated-types/trait-objects.rs index 256cfee4c809..87817111b546 100644 --- a/tests/ui/generic-associated-types/trait-objects.rs +++ b/tests/ui/generic-associated-types/trait-objects.rs @@ -8,8 +8,6 @@ trait StreamingIterator { fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { //~^ ERROR the trait `StreamingIterator` is not dyn compatible x.size_hint().0 - //~^ ERROR the trait `StreamingIterator` is not dyn compatible - //~| ERROR the trait `StreamingIterator` is not dyn compatible } fn main() {} diff --git a/tests/ui/generic-associated-types/trait-objects.stderr b/tests/ui/generic-associated-types/trait-objects.stderr index 7d95718ec874..8c3af6b654ab 100644 --- a/tests/ui/generic-associated-types/trait-objects.stderr +++ b/tests/ui/generic-associated-types/trait-objects.stderr @@ -14,38 +14,6 @@ LL | type Item<'a> where Self: 'a; | ^^^^ ...because it contains the generic associated type `Item` = help: consider moving `Item` to another trait -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:10:7 - | -LL | x.size_hint().0 - | ^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:2:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:10:5 - | -LL | x.size_hint().0 - | ^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:2:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-const-items/def-site-eval.fail.stderr b/tests/ui/generic-const-items/def-site-eval.fail.stderr index fa07f3855224..4e7d9d8154a0 100644 --- a/tests/ui/generic-const-items/def-site-eval.fail.stderr +++ b/tests/ui/generic-const-items/def-site-eval.fail.stderr @@ -1,5 +1,5 @@ -error[E0080]: evaluation of `_::<'_>` failed - --> $DIR/def-site-eval.rs:14:20 +error[E0080]: evaluation of constant value failed + --> $DIR/def-site-eval.rs:13:20 | LL | const _<'_a>: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic diff --git a/tests/ui/generic-const-items/def-site-eval.rs b/tests/ui/generic-const-items/def-site-eval.rs index 3ed7f96aed02..b95e40c05d4d 100644 --- a/tests/ui/generic-const-items/def-site-eval.rs +++ b/tests/ui/generic-const-items/def-site-eval.rs @@ -1,16 +1,15 @@ //! Test that we only evaluate free const items (their def site to be clear) //! whose generics don't require monomorphization. #![feature(generic_const_items)] -#![allow(incomplete_features)] +#![expect(incomplete_features)] //@ revisions: fail pass -//@[fail] build-fail (we require monomorphization) -//@[pass] build-pass (we require monomorphization) +//@[pass] check-pass const _<_T>: () = panic!(); const _: () = panic!(); #[cfg(fail)] -const _<'_a>: () = panic!(); //[fail]~ ERROR evaluation of `_::<'_>` failed +const _<'_a>: () = panic!(); //[fail]~ ERROR evaluation of constant value failed fn main() {} diff --git a/tests/ui/generic-const-items/def-site-predicates-wf.rs b/tests/ui/generic-const-items/def-site-predicates-wf.rs new file mode 100644 index 000000000000..39cdcc304f3e --- /dev/null +++ b/tests/ui/generic-const-items/def-site-predicates-wf.rs @@ -0,0 +1,9 @@ +//! Ensure that we check the predicates for well-formedness at the definition site. +#![feature(generic_const_items)] +#![expect(incomplete_features)] + +const _: () = () +where + Vec: Sized; //~ ERROR the size for values of type `str` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/generic-const-items/def-site-predicates-wf.stderr b/tests/ui/generic-const-items/def-site-predicates-wf.stderr new file mode 100644 index 000000000000..62db089fd557 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-predicates-wf.stderr @@ -0,0 +1,13 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-predicates-wf.rs:7:15 + | +LL | Vec: Sized; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr index 240e1c3dde1a..21dc5c66adf2 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr @@ -1,4 +1,4 @@ -error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied +error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied --> $DIR/hrtb-doesnt-borrow-self-1.rs:114:22 | LL | pub struct Map { diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr index 13041ea2bb0b..23b979e2ef0b 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr @@ -1,4 +1,4 @@ -error[E0599]: the method `countx` exists for struct `Filter &u64 {identity::}>, {closure@hrtb-doesnt-borrow-self-2.rs:109:30}>`, but its trait bounds were not satisfied +error[E0599]: the method `countx` exists for struct `Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:109:30: 109:37}>`, but its trait bounds were not satisfied --> $DIR/hrtb-doesnt-borrow-self-2.rs:110:24 | LL | pub struct Filter { diff --git a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs index 5d039cd5dc65..cc8134304685 100644 --- a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs +++ b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs @@ -13,8 +13,6 @@ fn needs_bar(_: *mut Type2) {} fn main() { let x: &dyn Foo = &(); //~^ ERROR the trait `Foo` is not dyn compatible - //~| ERROR the trait `Foo` is not dyn compatible needs_bar(x); - //~^ ERROR mismatched types } diff --git a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr index 183ee678d7a4..d35394a4f669 100644 --- a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr +++ b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr @@ -1,24 +1,8 @@ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/span-bug-issue-121597.rs:14:23 + --> $DIR/span-bug-issue-121597.rs:14:17 | LL | let x: &dyn Foo = &(); - | ^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/span-bug-issue-121597.rs:4:12 - | -LL | trait Foo: for Bar {} - | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables - | | - | this trait is not dyn compatible... - = note: required for the cast from `&()` to `&dyn Foo` - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/span-bug-issue-121597.rs:14:12 - | -LL | let x: &dyn Foo = &(); - | ^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -29,23 +13,6 @@ LL | trait Foo: for Bar {} | | | this trait is not dyn compatible... -error[E0308]: mismatched types - --> $DIR/span-bug-issue-121597.rs:18:15 - | -LL | needs_bar(x); - | --------- ^ types differ in mutability - | | - | arguments to this function are incorrect - | - = note: expected raw pointer `*mut Type2` - found reference `&dyn Foo` -note: function defined here - --> $DIR/span-bug-issue-121597.rs:11:4 - | -LL | fn needs_bar(_: *mut Type2) {} - | ^^^^^^^^^ ------------- +error: aborting due to 1 previous error -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0308. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs index 901d4b39cf36..accd173ce235 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs @@ -21,6 +21,8 @@ impl DynIncompatible for B { fn car() -> dyn DynIncompatible { //~ ERROR the trait `DynIncompatible` is not dyn compatible //~^ ERROR return type cannot be a trait object without pointer indirection +//~| ERROR the trait `DynIncompatible` is not dyn compatible +//~| ERROR the trait `DynIncompatible` is not dyn compatible if true { return A; } @@ -29,9 +31,9 @@ fn car() -> dyn DynIncompatible { //~ ERROR the trait `DynIncompatible` is not d fn cat() -> Box { //~ ERROR the trait `DynIncompatible` is not dyn compatible if true { - return Box::new(A); //~ ERROR is not dyn compatible + return Box::new(A); } - Box::new(B) //~ ERROR is not dyn compatible + Box::new(B) } fn main() {} diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr index 2c314b07bcee..a8787a01a6f6 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr @@ -41,6 +41,7 @@ help: alternatively, box the return type, and wrap all of the returned values in | LL ~ fn car() -> Box { LL | +... LL | if true { LL ~ return Box::new(A); LL | } @@ -48,7 +49,7 @@ LL ~ Box::new(B) | error[E0038]: the trait `DynIncompatible` is not dyn compatible - --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:30:17 + --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:32:17 | LL | fn cat() -> Box { | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible @@ -76,10 +77,10 @@ LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ error[E0038]: the trait `DynIncompatible` is not dyn compatible - --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:32:16 + --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:22:17 | -LL | return Box::new(A); - | ^^^^^^^^^^^ `DynIncompatible` is not dyn compatible +LL | fn car() -> dyn DynIncompatible { + | ^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -94,7 +95,11 @@ LL | fn foo() -> Self; B consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead - = note: required for the cast from `Box` to `Box<(dyn DynIncompatible + 'static)>` +help: consider using an opaque type instead + | +LL - fn car() -> dyn DynIncompatible { +LL + fn car() -> impl DynIncompatible { + | help: consider turning `foo` into a method by giving it a `&self` argument | LL | fn foo(&self) -> Self; @@ -105,10 +110,10 @@ LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ error[E0038]: the trait `DynIncompatible` is not dyn compatible - --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:34:5 + --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:22:17 | -LL | Box::new(B) - | ^^^^^^^^^^^ `DynIncompatible` is not dyn compatible +LL | fn car() -> dyn DynIncompatible { + | ^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -123,7 +128,12 @@ LL | fn foo() -> Self; B consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead - = note: required for the cast from `Box` to `Box<(dyn DynIncompatible + 'static)>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider using an opaque type instead + | +LL - fn car() -> dyn DynIncompatible { +LL + fn car() -> impl DynIncompatible { + | help: consider turning `foo` into a method by giving it a `&self` argument | LL | fn foo(&self) -> Self; diff --git a/tests/ui/impl-trait/in-trait/dyn-compatibility.rs b/tests/ui/impl-trait/in-trait/dyn-compatibility.rs index 85b1ba269fc8..45b431f6d306 100644 --- a/tests/ui/impl-trait/in-trait/dyn-compatibility.rs +++ b/tests/ui/impl-trait/in-trait/dyn-compatibility.rs @@ -13,8 +13,5 @@ impl Foo for u32 { fn main() { let i = Box::new(42_u32) as Box; //~^ ERROR the trait `Foo` is not dyn compatible - //~| ERROR the trait `Foo` is not dyn compatible let s = i.baz(); - //~^ ERROR the trait `Foo` is not dyn compatible - //~| ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr b/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr index 840c27e183f0..8cdb38085331 100644 --- a/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr +++ b/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/dyn-compatibility.rs:14:33 + --> $DIR/dyn-compatibility.rs:14:41 | LL | let i = Box::new(42_u32) as Box; - | ^^^^^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -15,58 +15,6 @@ LL | fn baz(&self) -> impl Debug; = help: consider moving `baz` to another trait = help: only type `u32` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/dyn-compatibility.rs:17:15 - | -LL | let s = i.baz(); - | ^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/dyn-compatibility.rs:4:22 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | fn baz(&self) -> impl Debug; - | ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type - = help: consider moving `baz` to another trait - = help: only type `u32` implements `Foo`; consider using it directly instead. - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/dyn-compatibility.rs:17:13 - | -LL | let s = i.baz(); - | ^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/dyn-compatibility.rs:4:22 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | fn baz(&self) -> impl Debug; - | ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type - = help: consider moving `baz` to another trait - = help: only type `u32` implements `Foo`; consider using it directly instead. - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/dyn-compatibility.rs:14:13 - | -LL | let i = Box::new(42_u32) as Box; - | ^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/dyn-compatibility.rs:4:22 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | fn baz(&self) -> impl Debug; - | ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type - = help: consider moving `baz` to another trait - = help: only type `u32` implements `Foo`; consider using it directly instead. - = note: required for the cast from `Box` to `Box` - -error: aborting due to 4 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr b/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr index 29235ca78a50..68ac765a3c1e 100644 --- a/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr +++ b/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/foreign-dyn-error.rs:6:12 + --> $DIR/foreign-dyn-error.rs:6:17 | LL | let _: &dyn rpitit::Foo = todo!(); - | ^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible + | ^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit diff --git a/tests/ui/impl-trait/in-trait/not-inferred-generic.rs b/tests/ui/impl-trait/in-trait/not-inferred-generic.rs new file mode 100644 index 000000000000..3879ea0e6262 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/not-inferred-generic.rs @@ -0,0 +1,13 @@ +trait TypedClient { + fn publish_typed(&self) -> impl Sized + where + F: Clone; +} +impl TypedClient for () { + fn publish_typed(&self) -> impl Sized {} +} + +fn main() { + ().publish_typed(); + //~^ ERROR type annotations needed [E0283] +} diff --git a/tests/ui/impl-trait/in-trait/not-inferred-generic.stderr b/tests/ui/impl-trait/in-trait/not-inferred-generic.stderr new file mode 100644 index 000000000000..07f029d3bb7d --- /dev/null +++ b/tests/ui/impl-trait/in-trait/not-inferred-generic.stderr @@ -0,0 +1,21 @@ +error[E0283]: type annotations needed + --> $DIR/not-inferred-generic.rs:11:8 + | +LL | ().publish_typed(); + | ^^^^^^^^^^^^^ cannot infer type of the type parameter `F` declared on the method `publish_typed` + | + = note: cannot satisfy `_: Clone` + = note: associated types cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl` +note: required by a bound in `TypedClient::publish_typed::{anon_assoc#0}` + --> $DIR/not-inferred-generic.rs:4:12 + | +LL | F: Clone; + | ^^^^^ required by this bound in `TypedClient::publish_typed::{anon_assoc#0}` +help: consider specifying the generic argument + | +LL | ().publish_typed::(); + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/impl-trait/issues/issue-100075-2.stderr b/tests/ui/impl-trait/issues/issue-100075-2.stderr index b3b696775070..554c3ea34331 100644 --- a/tests/ui/impl-trait/issues/issue-100075-2.stderr +++ b/tests/ui/impl-trait/issues/issue-100075-2.stderr @@ -1,3 +1,9 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/issue-100075-2.rs:1:23 + | +LL | fn opaque(t: T) -> impl Sized { + | ^^^^^^^^^^ + warning: function cannot return without recursing --> $DIR/issue-100075-2.rs:1:1 | @@ -10,15 +16,6 @@ LL | opaque(Some(t)) = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0720]: cannot resolve opaque type - --> $DIR/issue-100075-2.rs:1:23 - | -LL | fn opaque(t: T) -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | opaque(Some(t)) - | --------------- returning here with type `impl Sized` - error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/issues/issue-100075.stderr b/tests/ui/impl-trait/issues/issue-100075.stderr index 759634892369..bca2874b2b4b 100644 --- a/tests/ui/impl-trait/issues/issue-100075.stderr +++ b/tests/ui/impl-trait/issues/issue-100075.stderr @@ -2,10 +2,7 @@ error[E0720]: cannot resolve opaque type --> $DIR/issue-100075.rs:13:37 | LL | fn _g(t: &'static T) -> &'static impl Marker { - | ^^^^^^^^^^^ recursive opaque type -... -LL | return _g(t); - | ----- returning here with type `&impl Marker` + | ^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-103599.rs b/tests/ui/impl-trait/issues/issue-103599.rs index 62741a7454ce..e674ce3cbdf8 100644 --- a/tests/ui/impl-trait/issues/issue-103599.rs +++ b/tests/ui/impl-trait/issues/issue-103599.rs @@ -1,9 +1,8 @@ -//@ check-pass - trait T {} fn wrap(x: impl T) -> impl T { - //~^ WARN function cannot return without recursing + //~^ ERROR cannot resolve opaque type + //~| WARN function cannot return without recursing wrap(wrap(x)) } diff --git a/tests/ui/impl-trait/issues/issue-103599.stderr b/tests/ui/impl-trait/issues/issue-103599.stderr index 82038c1dceb3..9878b12044f7 100644 --- a/tests/ui/impl-trait/issues/issue-103599.stderr +++ b/tests/ui/impl-trait/issues/issue-103599.stderr @@ -1,14 +1,21 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/issue-103599.rs:3:23 + | +LL | fn wrap(x: impl T) -> impl T { + | ^^^^^^ + warning: function cannot return without recursing - --> $DIR/issue-103599.rs:5:1 + --> $DIR/issue-103599.rs:3:1 | LL | fn wrap(x: impl T) -> impl T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing -LL | +... LL | wrap(wrap(x)) | ------- recursive call site | = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -warning: 1 warning emitted +error: aborting due to 1 previous error; 1 warning emitted +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/issues/issue-87450.rs b/tests/ui/impl-trait/issues/issue-87450.rs index 983ef7cfbe0a..5a7759af1119 100644 --- a/tests/ui/impl-trait/issues/issue-87450.rs +++ b/tests/ui/impl-trait/issues/issue-87450.rs @@ -3,8 +3,8 @@ fn bar() -> impl Fn() { } fn foo() -> impl Fn() { - //~^ WARNING 5:1: 5:22: function cannot return without recursing [unconditional_recursion] - //~| ERROR 5:13: 5:22: cannot resolve opaque type [E0720] + //~^ WARN function cannot return without recursing + //~| ERROR cannot resolve opaque type wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) } diff --git a/tests/ui/impl-trait/issues/issue-87450.stderr b/tests/ui/impl-trait/issues/issue-87450.stderr index 9567e09651d6..f0f8b5859c03 100644 --- a/tests/ui/impl-trait/issues/issue-87450.stderr +++ b/tests/ui/impl-trait/issues/issue-87450.stderr @@ -1,3 +1,9 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/issue-87450.rs:5:13 + | +LL | fn foo() -> impl Fn() { + | ^^^^^^^^^ + warning: function cannot return without recursing --> $DIR/issue-87450.rs:5:1 | @@ -10,18 +16,6 @@ LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0720]: cannot resolve opaque type - --> $DIR/issue-87450.rs:5:13 - | -LL | fn foo() -> impl Fn() { - | ^^^^^^^^^ recursive opaque type -... -LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) - | ----------------------------------------------- returning here with type `impl Fn()` -... -LL | fn wrap(f: impl Fn()) -> impl Fn() { - | --------- returning this opaque type `impl Fn()` - error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index 64202513ae2b..061c9bd8f350 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -173,7 +173,7 @@ error[E0599]: no method named `method2` found for struct `no_method_suggested_tr --> $DIR/no-method-suggested-traits.rs:52:37 | LL | no_method_suggested_traits::Foo.method2(); - | ^^^^^^^ method not found in `Foo` + | ^^^^^^^ method not found in `no_method_suggested_traits::Foo` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -186,7 +186,7 @@ error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method --> $DIR/no-method-suggested-traits.rs:54:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2(); - | ^^^^^^^ method not found in `Rc<&mut Box<&Foo>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Foo>>` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -199,7 +199,7 @@ error[E0599]: no method named `method2` found for enum `no_method_suggested_trai --> $DIR/no-method-suggested-traits.rs:56:40 | LL | no_method_suggested_traits::Bar::X.method2(); - | ^^^^^^^ method not found in `Bar` + | ^^^^^^^ method not found in `no_method_suggested_traits::Bar` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -212,7 +212,7 @@ error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method --> $DIR/no-method-suggested-traits.rs:58:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2(); - | ^^^^^^^ method not found in `Rc<&mut Box<&Bar>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Bar>>` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -283,25 +283,25 @@ error[E0599]: no method named `method3` found for struct `no_method_suggested_tr --> $DIR/no-method-suggested-traits.rs:73:37 | LL | no_method_suggested_traits::Foo.method3(); - | ^^^^^^^ method not found in `Foo` + | ^^^^^^^ method not found in `no_method_suggested_traits::Foo` error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:74:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3(); - | ^^^^^^^ method not found in `Rc<&mut Box<&Foo>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Foo>>` error[E0599]: no method named `method3` found for enum `no_method_suggested_traits::Bar` in the current scope --> $DIR/no-method-suggested-traits.rs:76:40 | LL | no_method_suggested_traits::Bar::X.method3(); - | ^^^^^^^ method not found in `Bar` + | ^^^^^^^ method not found in `no_method_suggested_traits::Bar` error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:77:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3(); - | ^^^^^^^ method not found in `Rc<&mut Box<&Bar>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Bar>>` error: aborting due to 24 previous errors diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 2d2731e4368f..af84375c7476 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -2,112 +2,67 @@ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:6:22 | LL | fn option(i: i32) -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | if i < 0 { None } else { Some((option(i - 1), i)) } - | ---- ------------------------ returning here with type `Option<(impl Sized, i32)>` - | | - | returning here with type `Option<(impl Sized, i32)>` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:11:15 | LL | fn tuple() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | (tuple(),) - | ---------- returning here with type `(impl Sized,)` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:16:15 | LL | fn array() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | [array()] - | --------- returning here with type `[impl Sized; 1]` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:21:13 | LL | fn ptr() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | &ptr() as *const _ - | ------------------ returning here with type `*const impl Sized` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:26:16 | LL | fn fn_ptr() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | fn_ptr as fn() -> _ - | ------------------- returning here with type `fn() -> impl Sized` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:31:25 | -LL | fn closure_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | x; - | | - closure captures itself here -LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:34:5: 34:12}` +LL | fn closure_capture() -> impl Sized { + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:39:29 | -LL | fn closure_ref_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | &x; - | | - closure captures itself here -LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:42:5: 42:12}` +LL | fn closure_ref_capture() -> impl Sized { + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:47:21 | LL | fn closure_sig() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | || closure_sig() - | ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:49:5: 49:7}` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:52:23 | LL | fn coroutine_sig() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | || coroutine_sig() - | ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7}` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:57:27 | -LL | fn coroutine_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | yield; -LL | | x; - | | - coroutine captures itself here -LL | | } - | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:62:5: 62:12}` +LL | fn coroutine_capture() -> impl Sized { + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:68:35 | LL | fn substs_change() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | (substs_change::<&T>(),) - | ------------------------ returning here with type `(impl Sized,)` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:78:26 diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr index 42dbc7c91607..080c32846415 100644 --- a/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr @@ -1,56 +1,21 @@ -warning: function cannot return without recursing - --> $DIR/recursive-in-exhaustiveness.rs:17:1 +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-in-exhaustiveness.rs:17:22 | LL | fn build(x: T) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing -LL | -LL | let (x,) = (build(x),); - | -------- recursive call site - | - = help: a `loop` may express intention better if this is on purpose - = note: `#[warn(unconditional_recursion)]` on by default - -warning: function cannot return without recursing - --> $DIR/recursive-in-exhaustiveness.rs:27:1 - | -LL | fn build2(x: T) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing -... -LL | let (x,) = (build2(x),); - | --------- recursive call site - | - = help: a `loop` may express intention better if this is on purpose + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-in-exhaustiveness.rs:27:23 | LL | fn build2(x: T) -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | (build2(x),) - | ------------ returning here with type `(impl Sized,)` + | ^^^^^^^^^^ -warning: function cannot return without recursing - --> $DIR/recursive-in-exhaustiveness.rs:40:1 +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-in-exhaustiveness.rs:39:23 | LL | fn build3(x: T) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing -LL | -LL | let (x,) = (build3((x,)),); - | ------------ recursive call site - | - = help: a `loop` may express intention better if this is on purpose + | ^^^^^^^^^^ -error[E0792]: expected generic type parameter, found `(T,)` - --> $DIR/recursive-in-exhaustiveness.rs:49:5 - | -LL | fn build3(x: T) -> impl Sized { - | - this generic parameter must be used with a generic type parameter -... -LL | build3(x) - | ^^^^^^^^^ +error: aborting due to 3 previous errors -error: aborting due to 2 previous errors; 3 warnings emitted - -Some errors have detailed explanations: E0720, E0792. -For more information about an error, try `rustc --explain E0720`. +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr index 4c3d5aa8fb8f..a3609b93cb3a 100644 --- a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr @@ -5,19 +5,19 @@ LL | let (x,) = (build(x),); | ^^^^^^^^ cannot satisfy `impl Sized == _` error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _` - --> $DIR/recursive-in-exhaustiveness.rs:31:6 + --> $DIR/recursive-in-exhaustiveness.rs:30:6 | LL | (build2(x),) | ^^^^^^^^^ types differ error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _` - --> $DIR/recursive-in-exhaustiveness.rs:31:5 + --> $DIR/recursive-in-exhaustiveness.rs:30:5 | LL | (build2(x),) | ^^^^^^^^^^^^ types differ error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time - --> $DIR/recursive-in-exhaustiveness.rs:31:5 + --> $DIR/recursive-in-exhaustiveness.rs:30:5 | LL | (build2(x),) | ^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -26,13 +26,13 @@ LL | (build2(x),) = note: tuples must have a statically known size to be initialized error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` - --> $DIR/recursive-in-exhaustiveness.rs:42:17 + --> $DIR/recursive-in-exhaustiveness.rs:41:17 | LL | let (x,) = (build3((x,)),); | ^^^^^^^^^^^^ types differ error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time - --> $DIR/recursive-in-exhaustiveness.rs:42:16 + --> $DIR/recursive-in-exhaustiveness.rs:41:16 | LL | let (x,) = (build3((x,)),); | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -41,7 +41,7 @@ LL | let (x,) = (build3((x,)),); = note: tuples must have a statically known size to be initialized error[E0308]: mismatched types - --> $DIR/recursive-in-exhaustiveness.rs:42:16 + --> $DIR/recursive-in-exhaustiveness.rs:41:16 | LL | fn build3(x: T) -> impl Sized { | ---------- the found opaque type @@ -53,7 +53,7 @@ LL | let (x,) = (build3((x,)),); found tuple `(impl Sized,)` error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` - --> $DIR/recursive-in-exhaustiveness.rs:42:17 + --> $DIR/recursive-in-exhaustiveness.rs:41:17 | LL | let (x,) = (build3((x,)),); | ^^^^^^^^^^^^ types differ @@ -61,13 +61,13 @@ LL | let (x,) = (build3((x,)),); = note: the return type of a function must have a statically known size error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` - --> $DIR/recursive-in-exhaustiveness.rs:42:16 + --> $DIR/recursive-in-exhaustiveness.rs:41:16 | LL | let (x,) = (build3((x,)),); | ^^^^^^^^^^^^^^^ types differ error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` - --> $DIR/recursive-in-exhaustiveness.rs:42:17 + --> $DIR/recursive-in-exhaustiveness.rs:41:17 | LL | let (x,) = (build3((x,)),); | ^^^^^^^^^^^^ types differ diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.rs b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs index 58944533686c..fa8fa0e81743 100644 --- a/tests/ui/impl-trait/recursive-in-exhaustiveness.rs +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs @@ -15,7 +15,7 @@ // We unfortunately accept this today, and due to how opaque type relating is implemented // in the NLL type relation, this defines `Opaque = T`. fn build(x: T) -> impl Sized { - //[current]~^ WARN function cannot return without recursing + //[current]~^ ERROR cannot resolve opaque type let (x,) = (build(x),); //[next]~^ ERROR type annotations needed build(x) @@ -26,7 +26,6 @@ fn build(x: T) -> impl Sized { // Not allowed today. Detected as recursive. fn build2(x: T) -> impl Sized { //[current]~^ ERROR cannot resolve opaque type - //[current]~| WARN function cannot return without recursing let (x,) = (build2(x),); (build2(x),) //[next]~^ ERROR type mismatch resolving @@ -38,7 +37,7 @@ fn build2(x: T) -> impl Sized { // // Not allowed today. Detected as not defining. fn build3(x: T) -> impl Sized { - //[current]~^ WARN function cannot return without recursing + //[current]~^ ERROR cannot resolve opaque type let (x,) = (build3((x,)),); //[next]~^ ERROR type mismatch resolving //[next]~| ERROR type mismatch resolving @@ -47,7 +46,6 @@ fn build3(x: T) -> impl Sized { //[next]~| ERROR the size for values of type //[next]~| ERROR mismatched types build3(x) - //[current]~^ ERROR expected generic type parameter, found `(T,)` } fn main() {} diff --git a/tests/ui/imports/extern-crate-used.stderr b/tests/ui/imports/extern-crate-used.stderr index 982da0c913ed..08bee3914147 100644 --- a/tests/ui/imports/extern-crate-used.stderr +++ b/tests/ui/imports/extern-crate-used.stderr @@ -2,13 +2,18 @@ error: unused extern crate --> $DIR/extern-crate-used.rs:18:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/extern-crate-used.rs:6:9 | LL | #![deny(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | error: aborting due to 1 previous error diff --git a/tests/ui/instrument-xray/platform-support.rs b/tests/ui/instrument-xray/platform-support.rs new file mode 100644 index 000000000000..238018b348de --- /dev/null +++ b/tests/ui/instrument-xray/platform-support.rs @@ -0,0 +1,27 @@ +//@ only-nightly (flag is still unstable) +//@ needs-xray + +//@ revisions: unsupported +//@[unsupported] needs-llvm-components: x86 +//@[unsupported] compile-flags: -Z instrument-xray --target=x86_64-pc-windows-msvc + +//@ revisions: x86_64-linux +//@[x86_64-linux] needs-llvm-components: x86 +//@[x86_64-linux] compile-flags: -Z instrument-xray --target=x86_64-unknown-linux-gnu +//@[x86_64-linux] check-pass + +//@ revisions: x86_64-darwin +//@[x86_64-darwin] needs-llvm-components: x86 +//@[x86_64-darwin] compile-flags: -Z instrument-xray --target=x86_64-apple-darwin +//@[x86_64-darwin] check-pass + +//@ revisions: aarch64-darwin +//@[aarch64-darwin] needs-llvm-components: aarch64 +//@[aarch64-darwin] compile-flags: -Z instrument-xray --target=aarch64-apple-darwin +//@[aarch64-darwin] check-pass + +#![feature(no_core)] +#![no_core] +#![no_main] + +//[unsupported]~? ERROR XRay instrumentation is not supported for this target diff --git a/tests/ui/instrument-xray/target-not-supported.stderr b/tests/ui/instrument-xray/platform-support.unsupported.stderr similarity index 100% rename from tests/ui/instrument-xray/target-not-supported.stderr rename to tests/ui/instrument-xray/platform-support.unsupported.stderr diff --git a/tests/ui/instrument-xray/target-not-supported.rs b/tests/ui/instrument-xray/target-not-supported.rs deleted file mode 100644 index 2045913b186c..000000000000 --- a/tests/ui/instrument-xray/target-not-supported.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Verifies that `-Z instrument-xray` cannot be used with unsupported targets, -// -//@ needs-llvm-components: x86 -//@ compile-flags: -Z instrument-xray --target x86_64-apple-darwin - -#![feature(no_core)] -#![no_core] -#![no_main] - -//~? ERROR XRay instrumentation is not supported for this target diff --git a/tests/ui/intrinsics/const-eval-select-stability.stderr b/tests/ui/intrinsics/const-eval-select-stability.stderr index 5f443b1d4ff7..513c19cbb5b7 100644 --- a/tests/ui/intrinsics/const-eval-select-stability.stderr +++ b/tests/ui/intrinsics/const-eval-select-stability.stderr @@ -4,14 +4,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const_eval_select((), nothing, log); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | pub const fn hey() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_eval_select)] LL | pub const fn hey() { | diff --git a/tests/ui/intrinsics/intrinsic-atomics.rs b/tests/ui/intrinsics/intrinsic-atomics.rs index 9127cc649e66..f96c6dc832ee 100644 --- a/tests/ui/intrinsics/intrinsic-atomics.rs +++ b/tests/ui/intrinsics/intrinsic-atomics.rs @@ -1,14 +1,14 @@ //@ run-pass #![feature(core_intrinsics)] -use std::intrinsics as rusti; +use std::intrinsics::{self as rusti, AtomicOrdering}; pub fn main() { unsafe { let mut x: Box<_> = Box::new(1); - assert_eq!(rusti::atomic_load_seqcst(&*x), 1); + assert_eq!(rusti::atomic_load::<_, { AtomicOrdering::SeqCst }>(&*x), 1); *x = 5; - assert_eq!(rusti::atomic_load_acquire(&*x), 5); + assert_eq!(rusti::atomic_load::<_, { AtomicOrdering::Acquire }>(&*x), 5); rusti::atomic_store_seqcst(&mut *x, 3); assert_eq!(*x, 3); diff --git a/tests/ui/intrinsics/non-integer-atomic.rs b/tests/ui/intrinsics/non-integer-atomic.rs index 2d1d08820849..dd129e559451 100644 --- a/tests/ui/intrinsics/non-integer-atomic.rs +++ b/tests/ui/intrinsics/non-integer-atomic.rs @@ -4,7 +4,7 @@ #![allow(warnings)] #![crate_type = "rlib"] -use std::intrinsics; +use std::intrinsics::{self, AtomicOrdering}; #[derive(Copy, Clone)] pub struct Foo(i64); @@ -12,8 +12,8 @@ pub type Bar = &'static Fn(); pub type Quux = [u8; 100]; pub unsafe fn test_bool_load(p: &mut bool, v: bool) { - intrinsics::atomic_load_seqcst(p); - //~^ ERROR `atomic_load_seqcst` intrinsic: expected basic integer type, found `bool` + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `bool` } pub unsafe fn test_bool_store(p: &mut bool, v: bool) { @@ -32,8 +32,8 @@ pub unsafe fn test_bool_cxchg(p: &mut bool, v: bool) { } pub unsafe fn test_Foo_load(p: &mut Foo, v: Foo) { - intrinsics::atomic_load_seqcst(p); - //~^ ERROR `atomic_load_seqcst` intrinsic: expected basic integer type, found `Foo` + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `Foo` } pub unsafe fn test_Foo_store(p: &mut Foo, v: Foo) { @@ -52,7 +52,7 @@ pub unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) { } pub unsafe fn test_Bar_load(p: &mut Bar, v: Bar) { - intrinsics::atomic_load_seqcst(p); + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); //~^ ERROR expected basic integer type, found `&dyn Fn()` } @@ -72,8 +72,8 @@ pub unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) { } pub unsafe fn test_Quux_load(p: &mut Quux, v: Quux) { - intrinsics::atomic_load_seqcst(p); - //~^ ERROR `atomic_load_seqcst` intrinsic: expected basic integer type, found `[u8; 100]` + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `[u8; 100]` } pub unsafe fn test_Quux_store(p: &mut Quux, v: Quux) { diff --git a/tests/ui/intrinsics/non-integer-atomic.stderr b/tests/ui/intrinsics/non-integer-atomic.stderr index 32791a8e8b7f..58c2dc00c66d 100644 --- a/tests/ui/intrinsics/non-integer-atomic.stderr +++ b/tests/ui/intrinsics/non-integer-atomic.stderr @@ -1,8 +1,8 @@ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `bool` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `bool` --> $DIR/non-integer-atomic.rs:15:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `bool` --> $DIR/non-integer-atomic.rs:20:5 @@ -22,11 +22,11 @@ error[E0511]: invalid monomorphization of `atomic_cxchg_seqcst_seqcst` intrinsic LL | intrinsics::atomic_cxchg_seqcst_seqcst(p, v, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `Foo` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `Foo` --> $DIR/non-integer-atomic.rs:35:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `Foo` --> $DIR/non-integer-atomic.rs:40:5 @@ -46,11 +46,11 @@ error[E0511]: invalid monomorphization of `atomic_cxchg_seqcst_seqcst` intrinsic LL | intrinsics::atomic_cxchg_seqcst_seqcst(p, v, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `&dyn Fn()` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:55:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:60:5 @@ -70,11 +70,11 @@ error[E0511]: invalid monomorphization of `atomic_cxchg_seqcst_seqcst` intrinsic LL | intrinsics::atomic_cxchg_seqcst_seqcst(p, v, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `[u8; 100]` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `[u8; 100]` --> $DIR/non-integer-atomic.rs:75:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `[u8; 100]` --> $DIR/non-integer-atomic.rs:80:5 diff --git a/tests/ui/issues/issue-18959.rs b/tests/ui/issues/issue-18959.rs index 6aeb34879ea1..415fe818f530 100644 --- a/tests/ui/issues/issue-18959.rs +++ b/tests/ui/issues/issue-18959.rs @@ -11,13 +11,12 @@ impl Foo for Thing { fn foo(b: &dyn Bar) { //~^ ERROR E0038 b.foo(&0) - //~^ ERROR E0038 } fn main() { let mut thing = Thing; let test: &dyn Bar = &mut thing; //~^ ERROR E0038 - //~| ERROR E0038 foo(test); + //~^ ERROR E0038 } diff --git a/tests/ui/issues/issue-18959.stderr b/tests/ui/issues/issue-18959.stderr index 1e050b115e57..df47d50a0197 100644 --- a/tests/ui/issues/issue-18959.stderr +++ b/tests/ui/issues/issue-18959.stderr @@ -15,10 +15,10 @@ LL | pub trait Bar: Foo { } = help: consider moving `foo` to another trait error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:13:5 + --> $DIR/issue-18959.rs:18:20 | -LL | b.foo(&0) - | ^^^^^^^^^ `Bar` is not dyn compatible +LL | let test: &dyn Bar = &mut thing; + | ^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -31,27 +31,10 @@ LL | pub trait Bar: Foo { } = help: consider moving `foo` to another trait error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:19:26 + --> $DIR/issue-18959.rs:20:9 | -LL | let test: &dyn Bar = &mut thing; - | ^^^^^^^^^^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-18959.rs:1:20 - | -LL | pub trait Foo { fn foo(&self, ext_thing: &T); } - | ^^^ ...because method `foo` has generic type parameters -LL | pub trait Bar: Foo { } - | --- this trait is not dyn compatible... - = help: consider moving `foo` to another trait - = note: required for the cast from `&mut Thing` to `&dyn Bar` - -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:19:15 - | -LL | let test: &dyn Bar = &mut thing; - | ^^^^^^^^ `Bar` is not dyn compatible +LL | foo(test); + | ^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -63,6 +46,6 @@ LL | pub trait Bar: Foo { } | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/issues/issue-19380.rs b/tests/ui/issues/issue-19380.rs index 8b3fe4d2b099..fce737cba18d 100644 --- a/tests/ui/issues/issue-19380.rs +++ b/tests/ui/issues/issue-19380.rs @@ -15,6 +15,5 @@ struct Bar { const FOO : Foo = Foo; const BAR : Bar = Bar { foos: &[&FOO]}; //~^ ERROR E0038 -//~| ERROR E0038 fn main() { } diff --git a/tests/ui/issues/issue-19380.stderr b/tests/ui/issues/issue-19380.stderr index f8509891d3ab..4c41d41ae379 100644 --- a/tests/ui/issues/issue-19380.stderr +++ b/tests/ui/issues/issue-19380.stderr @@ -22,31 +22,6 @@ help: alternatively, consider constraining `qiz` so it does not apply to trait o LL | fn qiz() where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qiz` is not dyn compatible - --> $DIR/issue-19380.rs:16:33 - | -LL | const BAR : Bar = Bar { foos: &[&FOO]}; - | ^^^^ `Qiz` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-19380.rs:2:6 - | -LL | trait Qiz { - | --- this trait is not dyn compatible... -LL | fn qiz(); - | ^^^ ...because associated function `qiz` has no `self` parameter - = help: only type `Foo` implements `Qiz`; consider using it directly instead. - = note: required for the cast from `&Foo` to `&'static (dyn Qiz + 'static)` -help: consider turning `qiz` into a method by giving it a `&self` argument - | -LL | fn qiz(&self); - | +++++ -help: alternatively, consider constraining `qiz` so it does not apply to trait objects - | -LL | fn qiz() where Self: Sized; - | +++++++++++++++++ - error[E0038]: the trait `Qiz` is not dyn compatible --> $DIR/issue-19380.rs:16:31 | @@ -71,6 +46,6 @@ help: alternatively, consider constraining `qiz` so it does not apply to trait o LL | fn qiz() where Self: Sized; | +++++++++++++++++ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/issues/issue-30123.stderr b/tests/ui/issues/issue-30123.stderr index c086b45ac9bc..3ed1f34c44aa 100644 --- a/tests/ui/issues/issue-30123.stderr +++ b/tests/ui/issues/issue-30123.stderr @@ -2,7 +2,7 @@ error[E0599]: no function or associated item named `new_undirected` found for st --> $DIR/issue-30123.rs:7:33 | LL | let ug = Graph::::new_undirected(); - | ^^^^^^^^^^^^^^ function or associated item not found in `Graph` + | ^^^^^^^^^^^^^^ function or associated item not found in `issue_30123_aux::Graph` | note: if you're trying to build a new `issue_30123_aux::Graph`, consider using `issue_30123_aux::Graph::::new` which returns `issue_30123_aux::Graph<_, _>` --> $DIR/auxiliary/issue-30123-aux.rs:14:5 diff --git a/tests/ui/issues/issue-41880.stderr b/tests/ui/issues/issue-41880.stderr index 9d09be66305b..1936c0aebd40 100644 --- a/tests/ui/issues/issue-41880.stderr +++ b/tests/ui/issues/issue-41880.stderr @@ -5,7 +5,7 @@ LL | pub struct Iterate { | ------------------------ method `iter` not found for this struct ... LL | println!("{:?}", a.iter().take(10).collect::>()); - | ^^^^ method not found in `Iterate<{integer}, {closure@issue-41880.rs:26:24}>` + | ^^^^ method not found in `Iterate<{integer}, {closure@$DIR/issue-41880.rs:26:24: 26:27}>` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-50781.rs b/tests/ui/issues/issue-50781.rs index ab90db1cadcf..d837b848591f 100644 --- a/tests/ui/issues/issue-50781.rs +++ b/tests/ui/issues/issue-50781.rs @@ -15,5 +15,4 @@ pub fn main() { // Check that this does not segfault. ::foo(&()); //~^ ERROR the trait `X` is not dyn compatible - //~| ERROR the trait `X` is not dyn compatible } diff --git a/tests/ui/issues/issue-50781.stderr b/tests/ui/issues/issue-50781.stderr index 88b83a83e0cf..4ba3166b6c58 100644 --- a/tests/ui/issues/issue-50781.stderr +++ b/tests/ui/issues/issue-50781.stderr @@ -16,28 +16,10 @@ LL | fn foo(&self) where Self: Trait; = help: only type `()` implements `X`; consider using it directly instead. error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:16:23 + --> $DIR/issue-50781.rs:16:10 | LL | ::foo(&()); - | ^^^ `X` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-50781.rs:4:8 - | -LL | trait X { - | - this trait is not dyn compatible... -LL | fn foo(&self) where Self: Trait; - | ^^^ ...because method `foo` references the `Self` type in its `where` clause - = help: consider moving `foo` to another trait - = help: only type `()` implements `X`; consider using it directly instead. - = note: required for the cast from `&()` to `&dyn X` - -error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:16:6 - | -LL | ::foo(&()); - | ^^^^^ `X` is not dyn compatible + | ^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -50,6 +32,6 @@ LL | fn foo(&self) where Self: Trait; = help: consider moving `foo` to another trait = help: only type `()` implements `X`; consider using it directly instead. -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/issues/issue-58734.rs b/tests/ui/issues/issue-58734.rs index ee23be87b6b7..e5b371f5530d 100644 --- a/tests/ui/issues/issue-58734.rs +++ b/tests/ui/issues/issue-58734.rs @@ -18,8 +18,7 @@ fn main() { Trait::exists(()); // no dyn-compatibility error Trait::nonexistent(()); - //~^ ERROR no function or associated item named `nonexistent` found - //~| WARN trait objects without an explicit `dyn` are deprecated + //~^ WARN trait objects without an explicit `dyn` are deprecated //~| WARN this is accepted in the current edition //~| ERROR the trait `Trait` is not dyn compatible } diff --git a/tests/ui/issues/issue-58734.stderr b/tests/ui/issues/issue-58734.stderr index c4624cecc621..e5dad000b510 100644 --- a/tests/ui/issues/issue-58734.stderr +++ b/tests/ui/issues/issue-58734.stderr @@ -37,13 +37,6 @@ help: alternatively, consider constraining `dyn_incompatible` so it does not app LL | fn dyn_incompatible() -> Self where Self: Sized; | +++++++++++++++++ -error[E0599]: no function or associated item named `nonexistent` found for trait object `dyn Trait` in the current scope - --> $DIR/issue-58734.rs:20:12 - | -LL | Trait::nonexistent(()); - | ^^^^^^^^^^^ function or associated item not found in `dyn Trait` +error: aborting due to 1 previous error; 1 warning emitted -error: aborting due to 2 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0038, E0599. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.rs b/tests/ui/kindck/kindck-inherited-copy-bound.rs index 20d54a3fb106..92c2b273c2c1 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.rs +++ b/tests/ui/kindck/kindck-inherited-copy-bound.rs @@ -22,7 +22,6 @@ fn b() { let y = &x; let z = &x as &dyn Foo; //~^ ERROR E0038 - //~| ERROR E0038 } fn main() { } diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.stderr b/tests/ui/kindck/kindck-inherited-copy-bound.stderr index edfa7ae7769d..c15aabacddd1 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.stderr +++ b/tests/ui/kindck/kindck-inherited-copy-bound.stderr @@ -20,10 +20,10 @@ LL | fn take_param(foo: &T) { } | ^^^ required by this bound in `take_param` error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/kindck-inherited-copy-bound.rs:23:19 + --> $DIR/kindck-inherited-copy-bound.rs:23:24 | LL | let z = &x as &dyn Foo; - | ^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -34,23 +34,7 @@ LL | trait Foo : Copy { | | | this trait is not dyn compatible... -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/kindck-inherited-copy-bound.rs:23:13 - | -LL | let z = &x as &dyn Foo; - | ^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/kindck-inherited-copy-bound.rs:6:13 - | -LL | trait Foo : Copy { - | --- ^^^^ ...because it requires `Self: Sized` - | | - | this trait is not dyn compatible... - = note: required for the cast from `&Box<{integer}>` to `&dyn Foo` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0038, E0277. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/label/continue-pointing-to-block-ice-113379.rs b/tests/ui/label/continue-pointing-to-block-ice-113379.rs new file mode 100644 index 000000000000..8a6a9cc84098 --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-113379.rs @@ -0,0 +1,12 @@ +//! Regression test for ICE #113379. Liveness linting assumes that `continue`s all point to loops. +//! This tests that if a `continue` points to a block, we don't run liveness lints. + +async fn f999() -> Vec { + //~^ ERROR `async fn` is not permitted in Rust 2015 + 'b: { + //~^ ERROR mismatched types + continue 'b; + //~^ ERROR `continue` pointing to a labeled block + } +} +//~^ ERROR `main` function not found diff --git a/tests/ui/label/continue-pointing-to-block-ice-113379.stderr b/tests/ui/label/continue-pointing-to-block-ice-113379.stderr new file mode 100644 index 000000000000..ada6305ec999 --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-113379.stderr @@ -0,0 +1,43 @@ +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/continue-pointing-to-block-ice-113379.rs:4:1 + | +LL | async fn f999() -> Vec { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0601]: `main` function not found in crate `continue_pointing_to_block_ice_113379` + --> $DIR/continue-pointing-to-block-ice-113379.rs:11:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/continue-pointing-to-block-ice-113379.rs` + +error[E0696]: `continue` pointing to a labeled block + --> $DIR/continue-pointing-to-block-ice-113379.rs:8:9 + | +LL | / 'b: { +LL | | +LL | | continue 'b; + | | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d +LL | | +LL | | } + | |_____- labeled block the `continue` points to + +error[E0308]: mismatched types + --> $DIR/continue-pointing-to-block-ice-113379.rs:6:5 + | +LL | / 'b: { +LL | | +LL | | continue 'b; +LL | | +LL | | } + | |_____^ expected `Vec`, found `()` + | + = note: expected struct `Vec` + found unit type `()` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0601, E0670, E0696. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/label/continue-pointing-to-block-ice-121623.rs b/tests/ui/label/continue-pointing-to-block-ice-121623.rs new file mode 100644 index 000000000000..7047f7a309aa --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-121623.rs @@ -0,0 +1,11 @@ +//! Regression test for ICE #121623. Liveness linting assumes that `continue`s all point to loops. +//! This tests that if a `continue` points to a block, we don't run liveness lints. + +fn main() { + match () { + _ => 'b: { + continue 'b; + //~^ ERROR `continue` pointing to a labeled block + } + } +} diff --git a/tests/ui/label/continue-pointing-to-block-ice-121623.stderr b/tests/ui/label/continue-pointing-to-block-ice-121623.stderr new file mode 100644 index 000000000000..a484fb629d18 --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-121623.stderr @@ -0,0 +1,14 @@ +error[E0696]: `continue` pointing to a labeled block + --> $DIR/continue-pointing-to-block-ice-121623.rs:7:13 + | +LL | _ => 'b: { + | ______________- +LL | | continue 'b; + | | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d +LL | | +LL | | } + | |_________- labeled block the `continue` points to + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0696`. diff --git a/tests/ui/lang-items/lang-item-generic-requirements.rs b/tests/ui/lang-items/lang-item-generic-requirements.rs index 90ed5f3f0efd..7676b5557d22 100644 --- a/tests/ui/lang-items/lang-item-generic-requirements.rs +++ b/tests/ui/lang-items/lang-item-generic-requirements.rs @@ -49,12 +49,14 @@ fn ice() { // Use index let arr = [0; 5]; let _ = arr[2]; + //~^ ERROR cannot index into a value of type `[{integer}; 5]` // Use phantomdata let _ = MyPhantomData::<(), i32>; // Use Foo let _: () = Foo; + //~^ ERROR mismatched types } // use `start` diff --git a/tests/ui/lang-items/lang-item-generic-requirements.stderr b/tests/ui/lang-items/lang-item-generic-requirements.stderr index 3de67d659403..409fa05d6371 100644 --- a/tests/ui/lang-items/lang-item-generic-requirements.stderr +++ b/tests/ui/lang-items/lang-item-generic-requirements.stderr @@ -76,9 +76,23 @@ LL | r + a; | | | {integer} +error[E0608]: cannot index into a value of type `[{integer}; 5]` + --> $DIR/lang-item-generic-requirements.rs:51:16 + | +LL | let _ = arr[2]; + | ^^^ + +error[E0308]: mismatched types + --> $DIR/lang-item-generic-requirements.rs:58:17 + | +LL | let _: () = Foo; + | -- ^^^ expected `()`, found `Foo` + | | + | expected due to this + error: requires `copy` lang_item -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors -Some errors have detailed explanations: E0369, E0392, E0718. -For more information about an error, try `rustc --explain E0369`. +Some errors have detailed explanations: E0308, E0369, E0392, E0608, E0718. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/lint/bare-trait-objects-path.stderr b/tests/ui/lint/bare-trait-objects-path.stderr index fbb647c37c5a..e611abd31f3b 100644 --- a/tests/ui/lint/bare-trait-objects-path.stderr +++ b/tests/ui/lint/bare-trait-objects-path.stderr @@ -55,7 +55,13 @@ error[E0223]: ambiguous associated type --> $DIR/bare-trait-objects-path.rs:23:12 | LL | let _: Dyn::Ty; - | ^^^^^^^ help: use fully-qualified syntax: `::Ty` + | ^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: Dyn::Ty; +LL + let _: ::Ty; + | error: aborting due to 1 previous error; 4 warnings emitted diff --git a/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs b/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs new file mode 100644 index 000000000000..4857ef6a9b8b --- /dev/null +++ b/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs @@ -0,0 +1,19 @@ +//@ check-pass + +#![deny(dead_code)] + +struct T(X); + +type A = T; + +trait Tr { + fn foo(); +} + +impl Tr for T> { + fn foo() {} +} + +fn main() { + T::>::foo(); +} diff --git a/tests/ui/lint/dead-code/issue-41883.stderr b/tests/ui/lint/dead-code/issue-41883.stderr index 47ccef9a5306..cf079e4dda33 100644 --- a/tests/ui/lint/dead-code/issue-41883.stderr +++ b/tests/ui/lint/dead-code/issue-41883.stderr @@ -29,6 +29,8 @@ error: struct `UnusedStruct` is never constructed | LL | struct UnusedStruct; | ^^^^^^^^^^^^ + | + = note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 4 previous errors diff --git a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr index 25a7d96cb897..b992005318f2 100644 --- a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr +++ b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr @@ -56,6 +56,8 @@ warning: struct `Foo` is never constructed | LL | struct Foo(usize, #[allow(unused)] usize); | ^^^ + | + = note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 2 previous errors; 2 warnings emitted diff --git a/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs b/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs new file mode 100644 index 000000000000..fb994653e1b1 --- /dev/null +++ b/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs @@ -0,0 +1,31 @@ +//@ check-pass + +#![deny(dead_code)] + +trait UInt: Copy + From {} + +impl UInt for u16 {} + +trait Int: Copy { + type Unsigned: UInt; + + fn as_unsigned(self) -> Self::Unsigned; +} + +impl Int for i16 { + type Unsigned = u16; + + fn as_unsigned(self) -> u16 { + self as _ + } +} + +fn priv_func(x: u8, y: T) -> (T::Unsigned, T::Unsigned) { + (T::Unsigned::from(x), y.as_unsigned()) +} + +pub fn pub_func(x: u8, y: i16) -> (u16, u16) { + priv_func(x, y) +} + +fn main() {} diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index b2ef27b833bd..612da86c9568 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -2,8 +2,6 @@ #![deny(improper_ctypes)] #![feature(ptr_internals)] #![feature(transparent_unions)] -#![feature(repr128)] -#![allow(incomplete_features)] use std::num; diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index d5fc844f7560..50a6f526f26d 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:84:14 + --> $DIR/lint-ctypes-enum.rs:82:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,7 +7,7 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:11:1 + --> $DIR/lint-ctypes-enum.rs:9:1 | LL | enum U { | ^^^^^^ @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:85:14 + --> $DIR/lint-ctypes-enum.rs:83:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:14:1 + --> $DIR/lint-ctypes-enum.rs:12:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:86:14 + --> $DIR/lint-ctypes-enum.rs:84:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,39 +40,39 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:18:1 + --> $DIR/lint-ctypes-enum.rs:16:1 | LL | enum T { | ^^^^^^ error: `extern` block uses type `U128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:90:21 + --> $DIR/lint-ctypes-enum.rs:88:21 | LL | fn repr_u128(x: U128); | ^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:46:1 + --> $DIR/lint-ctypes-enum.rs:44:1 | LL | enum U128 { | ^^^^^^^^^ error: `extern` block uses type `I128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:91:21 + --> $DIR/lint-ctypes-enum.rs:89:21 | LL | fn repr_i128(x: I128); | ^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:53:1 + --> $DIR/lint-ctypes-enum.rs:51:1 | LL | enum I128 { | ^^^^^^^^^ error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:100:31 + --> $DIR/lint-ctypes-enum.rs:98:31 | LL | fn option_nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +80,7 @@ LL | fn option_nonzero_u128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:107:31 + --> $DIR/lint-ctypes-enum.rs:105:31 | LL | fn option_nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -88,7 +88,7 @@ LL | fn option_nonzero_i128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:112:36 + --> $DIR/lint-ctypes-enum.rs:110:36 | LL | fn option_transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -97,7 +97,7 @@ LL | fn option_transparent_union(x: Option = note: enum has no representation hint error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:114:28 + --> $DIR/lint-ctypes-enum.rs:112:28 | LL | fn option_repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -106,7 +106,7 @@ LL | fn option_repr_rust(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:115:21 + --> $DIR/lint-ctypes-enum.rs:113:21 | LL | fn option_u8(x: Option); | ^^^^^^^^^^ not FFI-safe @@ -115,7 +115,7 @@ LL | fn option_u8(x: Option); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:125:33 + --> $DIR/lint-ctypes-enum.rs:123:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +123,7 @@ LL | fn result_nonzero_u128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:132:33 + --> $DIR/lint-ctypes-enum.rs:130:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -131,7 +131,7 @@ LL | fn result_nonzero_i128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:137:38 + --> $DIR/lint-ctypes-enum.rs:135:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -140,7 +140,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:139:30 + --> $DIR/lint-ctypes-enum.rs:137:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -149,7 +149,7 @@ LL | fn result_repr_rust_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:143:51 + --> $DIR/lint-ctypes-enum.rs:141:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -158,7 +158,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:145:53 + --> $DIR/lint-ctypes-enum.rs:143:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -167,7 +167,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result = note: enum has no representation hint error: `extern` block uses type `Result, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:147:51 + --> $DIR/lint-ctypes-enum.rs:145:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +176,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:150:49 + --> $DIR/lint-ctypes-enum.rs:148:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -185,7 +185,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result, Fi = note: enum has no representation hint error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:152:30 + --> $DIR/lint-ctypes-enum.rs:150:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -194,7 +194,7 @@ LL | fn result_cascading_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:163:33 + --> $DIR/lint-ctypes-enum.rs:161:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +202,7 @@ LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:170:33 + --> $DIR/lint-ctypes-enum.rs:168:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -210,7 +210,7 @@ LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:175:38 + --> $DIR/lint-ctypes-enum.rs:173:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -219,7 +219,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:177:30 + --> $DIR/lint-ctypes-enum.rs:175:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -228,7 +228,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust>>); = note: enum has no representation hint error: `extern` block uses type `Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:181:51 + --> $DIR/lint-ctypes-enum.rs:179:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -237,7 +237,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:183:53 + --> $DIR/lint-ctypes-enum.rs:181:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -246,7 +246,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:185:51 + --> $DIR/lint-ctypes-enum.rs:183:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -255,7 +255,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:188:49 + --> $DIR/lint-ctypes-enum.rs:186:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -264,7 +264,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:190:30 + --> $DIR/lint-ctypes-enum.rs:188:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -273,7 +273,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:192:27 + --> $DIR/lint-ctypes-enum.rs:190:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/big-literals.rs b/tests/ui/lint/overflowing-literals-valid.rs similarity index 84% rename from tests/ui/big-literals.rs rename to tests/ui/lint/overflowing-literals-valid.rs index d2f447a595c9..08aa092ee71e 100644 --- a/tests/ui/big-literals.rs +++ b/tests/ui/lint/overflowing-literals-valid.rs @@ -1,5 +1,7 @@ +//! Test that valid large numeric literals do not trigger the `overflowing_literals` lint. + //@ run-pass -// Catch mistakes in the overflowing literals lint. + #![deny(overflowing_literals)] pub fn main() { diff --git a/tests/ui/lint/unnecessary-extern-crate.stderr b/tests/ui/lint/unnecessary-extern-crate.stderr index 1fa4aa9c9a9c..db5406bc567d 100644 --- a/tests/ui/lint/unnecessary-extern-crate.stderr +++ b/tests/ui/lint/unnecessary-extern-crate.stderr @@ -2,43 +2,72 @@ error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:6:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/unnecessary-extern-crate.rs:3:9 | LL | #![deny(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate core; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:9:1 | LL | extern crate core as x; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core as x; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:31:5 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:35:5 | LL | extern crate core as x; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core as x; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:44:9 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:48:9 | LL | extern crate core as x; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core as x; + | error: aborting due to 6 previous errors diff --git a/tests/ui/lint/unused/lint-unused-extern-crate.stderr b/tests/ui/lint/unused/lint-unused-extern-crate.stderr index 46d8f3beeab4..7fcbdd813ce0 100644 --- a/tests/ui/lint/unused/lint-unused-extern-crate.stderr +++ b/tests/ui/lint/unused/lint-unused-extern-crate.stderr @@ -2,19 +2,30 @@ error: unused extern crate --> $DIR/lint-unused-extern-crate.rs:11:1 | LL | extern crate lint_unused_extern_crate5; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/lint-unused-extern-crate.rs:7:9 | LL | #![deny(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate lint_unused_extern_crate5; +LL + + | error: unused extern crate --> $DIR/lint-unused-extern-crate.rs:29:5 | LL | extern crate lint_unused_extern_crate2; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate lint_unused_extern_crate2; +LL + + | error: aborting due to 2 previous errors diff --git a/tests/ui/lint/unused/unused-braces-attrs-issue-141549.fixed b/tests/ui/lint/unused/unused-braces-attrs-issue-141549.fixed new file mode 100644 index 000000000000..6129da306766 --- /dev/null +++ b/tests/ui/lint/unused/unused-braces-attrs-issue-141549.fixed @@ -0,0 +1,15 @@ +//@ check-pass +//@ run-rustfix + +#![allow(dead_code)] +#![warn(unused_braces)] + +use std::cmp::Ordering; + +#[rustfmt::skip] +fn ptr_cmp(p1: *const T, p2: *const T) -> Ordering { + #[expect(ambiguous_wide_pointer_comparisons)] p1.cmp(&p2) + //~^ WARN unnecessary braces around block return value +} + +fn main() {} diff --git a/tests/ui/lint/unused/unused-braces-attrs-issue-141549.rs b/tests/ui/lint/unused/unused-braces-attrs-issue-141549.rs new file mode 100644 index 000000000000..a550ebc49737 --- /dev/null +++ b/tests/ui/lint/unused/unused-braces-attrs-issue-141549.rs @@ -0,0 +1,15 @@ +//@ check-pass +//@ run-rustfix + +#![allow(dead_code)] +#![warn(unused_braces)] + +use std::cmp::Ordering; + +#[rustfmt::skip] +fn ptr_cmp(p1: *const T, p2: *const T) -> Ordering { + { #[expect(ambiguous_wide_pointer_comparisons)] p1.cmp(&p2) } + //~^ WARN unnecessary braces around block return value +} + +fn main() {} diff --git a/tests/ui/lint/unused/unused-braces-attrs-issue-141549.stderr b/tests/ui/lint/unused/unused-braces-attrs-issue-141549.stderr new file mode 100644 index 000000000000..0b2b6211ab94 --- /dev/null +++ b/tests/ui/lint/unused/unused-braces-attrs-issue-141549.stderr @@ -0,0 +1,19 @@ +warning: unnecessary braces around block return value + --> $DIR/unused-braces-attrs-issue-141549.rs:11:5 + | +LL | { #[expect(ambiguous_wide_pointer_comparisons)] p1.cmp(&p2) } + | ^^ ^^ + | +note: the lint level is defined here + --> $DIR/unused-braces-attrs-issue-141549.rs:5:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ +help: remove these braces + | +LL - { #[expect(ambiguous_wide_pointer_comparisons)] p1.cmp(&p2) } +LL + #[expect(ambiguous_wide_pointer_comparisons)] p1.cmp(&p2) + | + +warning: 1 warning emitted + diff --git a/tests/ui/lint/wide_pointer_comparisons.stderr b/tests/ui/lint/wide_pointer_comparisons.stderr index 5a0b914d8320..4f5238e8252f 100644 --- a/tests/ui/lint/wide_pointer_comparisons.stderr +++ b/tests/ui/lint/wide_pointer_comparisons.stderr @@ -29,10 +29,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a < b; | ^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() < b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a < b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:26:13 @@ -40,10 +44,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a <= b; | ^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() <= b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a <= b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:28:13 @@ -51,10 +59,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a > b; | ^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() > b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a > b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:30:13 @@ -62,10 +74,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a >= b; | ^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() >= b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a >= b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:33:13 @@ -121,10 +137,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.cmp(&b); | ^^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>().cmp(&b.cast::<()>()); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.cmp(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:43:13 @@ -132,10 +152,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.partial_cmp(&b); | ^^^^^^^^^^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>().partial_cmp(&b.cast::<()>()); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.partial_cmp(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:45:13 @@ -143,10 +167,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.le(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>().le(&b.cast::<()>()); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.le(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:47:13 @@ -154,10 +182,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.lt(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>().lt(&b.cast::<()>()); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.lt(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:49:13 @@ -165,10 +197,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.ge(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>().ge(&b.cast::<()>()); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.ge(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:51:13 @@ -176,10 +212,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.gt(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>().gt(&b.cast::<()>()); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.gt(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:57:17 @@ -199,10 +239,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a >= b; | ^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.as_ptr().cast::<()>() >= b.as_ptr().cast::<()>(); | ++++++++++++++++++++++ ++++++++++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a >= b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:61:17 @@ -246,10 +290,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a < b; | ^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>() < (*b).cast::<()>(); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a < b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:76:17 @@ -257,10 +305,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a <= b; | ^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>() <= (*b).cast::<()>(); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a <= b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:78:17 @@ -268,10 +320,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a > b; | ^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>() > (*b).cast::<()>(); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a > b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:80:17 @@ -279,10 +335,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a >= b; | ^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>() >= (*b).cast::<()>(); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a >= b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:83:17 @@ -362,10 +422,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.cmp(&b); | ^^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>().cmp(&(*b).cast::<()>()); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.cmp(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:97:17 @@ -373,10 +437,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.partial_cmp(&b); | ^^^^^^^^^^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>().partial_cmp(&(*b).cast::<()>()); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.partial_cmp(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:99:17 @@ -384,10 +452,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.le(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>().le(&(*b).cast::<()>()); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.le(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:101:17 @@ -395,10 +467,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.lt(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>().lt(&(*b).cast::<()>()); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.lt(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:103:17 @@ -406,10 +482,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.ge(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>().ge(&(*b).cast::<()>()); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.ge(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:105:17 @@ -417,10 +497,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a.gt(&b); | ^^^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = (*a).cast::<()>().gt(&(*b).cast::<()>()); | ++ ++++++++++++++ ++ ++++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] a.gt(&b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:110:13 @@ -496,10 +580,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a < b; | ^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() < b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a < b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:124:17 @@ -507,10 +595,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a <= b; | ^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() <= b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a <= b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:126:17 @@ -518,10 +610,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a > b; | ^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() > b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a > b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:128:17 @@ -529,10 +625,14 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata whi LL | let _ = a >= b; | ^^^^^^ | -help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses +help: use untyped pointers to only compare their addresses | LL | let _ = a.cast::<()>() >= b.cast::<()>(); | +++++++++++++ +++++++++++++ +help: or expect the lint to compare the pointers metadata and addresses + | +LL | let _ = { #[expect(ambiguous_wide_pointer_comparisons, reason = "...")] (a >= b) }; + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:131:17 diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs new file mode 100644 index 000000000000..68346a00ae1a --- /dev/null +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs @@ -0,0 +1,13 @@ +#![feature(contracts)] +#![allow(incomplete_features)] + +struct T; + +impl T { + #[core::contracts::ensures] //~ ERROR expected a `Fn(&_)` closure, found `()` + fn b() {(loop)} + //~^ ERROR expected `{`, found `)` + //~| ERROR expected `{`, found `)` +} + +fn main() {} diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr new file mode 100644 index 000000000000..f1ffda2a9bee --- /dev/null +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr @@ -0,0 +1,34 @@ +error: expected `{`, found `)` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:8:18 + | +LL | fn b() {(loop)} + | ----^ expected `{` + | | + | while parsing this `loop` expression + +error: expected `{`, found `)` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:8:18 + | +LL | fn b() {(loop)} + | ----^ expected `{` + | | + | while parsing this `loop` expression + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: expected a `Fn(&_)` closure, found `()` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected an `Fn(&_)` closure, found `()` + | required by a bound introduced by this call + | + = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/macros/missing-writer.stderr b/tests/ui/macros/missing-writer.stderr index 86dfe7d65ea6..a3430cfc0eea 100644 --- a/tests/ui/macros/missing-writer.stderr +++ b/tests/ui/macros/missing-writer.stderr @@ -24,7 +24,7 @@ error[E0599]: cannot write into `&'static str` --> $DIR/missing-writer.rs:5:12 | LL | write!("{}_{}", x, y); - | -------^^^^^^^------- method not found in `&str` + | -------^^^^^^^------- method not found in `&'static str` | note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method --> $DIR/missing-writer.rs:5:12 @@ -41,7 +41,7 @@ error[E0599]: cannot write into `&'static str` --> $DIR/missing-writer.rs:11:14 | LL | writeln!("{}_{}", x, y); - | ---------^^^^^^^------- method not found in `&str` + | ---------^^^^^^^------- method not found in `&'static str` | note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method --> $DIR/missing-writer.rs:11:14 diff --git a/tests/ui/macros/no-close-delim-issue-139248.rs b/tests/ui/macros/no-close-delim-issue-139248.rs index 86583b2724ed..f15234eaff1a 100644 --- a/tests/ui/macros/no-close-delim-issue-139248.rs +++ b/tests/ui/macros/no-close-delim-issue-139248.rs @@ -2,9 +2,8 @@ macro_rules! m { (static a : () = $e:expr) => { - static a : () = $e; - //~^ ERROR macro expansion ends with an incomplete expression: expected expression - } + static a: () = $e; + }; } m! { static a : () = (if b) } diff --git a/tests/ui/macros/no-close-delim-issue-139248.stderr b/tests/ui/macros/no-close-delim-issue-139248.stderr index 6ed41ae9b46a..8aa39851b4b0 100644 --- a/tests/ui/macros/no-close-delim-issue-139248.stderr +++ b/tests/ui/macros/no-close-delim-issue-139248.stderr @@ -1,33 +1,27 @@ error: expected `{`, found `)` - --> $DIR/no-close-delim-issue-139248.rs:10:27 + --> $DIR/no-close-delim-issue-139248.rs:9:27 | LL | m! { static a : () = (if b) } | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/no-close-delim-issue-139248.rs:10:26 + --> $DIR/no-close-delim-issue-139248.rs:9:26 | LL | m! { static a : () = (if b) } | ^ error: expected `{`, found `)` - --> $DIR/no-close-delim-issue-139248.rs:10:27 + --> $DIR/no-close-delim-issue-139248.rs:9:27 | LL | m! { static a : () = (if b) } | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/no-close-delim-issue-139248.rs:10:26 + --> $DIR/no-close-delim-issue-139248.rs:9:26 | LL | m! { static a : () = (if b) } | ^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: macro expansion ends with an incomplete expression: expected expression - --> $DIR/no-close-delim-issue-139248.rs:5:28 - | -LL | static a : () = $e; - | ^ expected expression - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/methods/issue-19521.stderr b/tests/ui/methods/issue-19521.stderr index f451dc36d45f..2ef83a4792a7 100644 --- a/tests/ui/methods/issue-19521.stderr +++ b/tests/ui/methods/issue-19521.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `homura` found for reference `&'static str` in the --> $DIR/issue-19521.rs:2:8 | LL | "".homura()(); - | ^^^^^^ method not found in `&str` + | ^^^^^^ method not found in `&'static str` error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-not-found-generic-arg-elision.stderr b/tests/ui/methods/method-not-found-generic-arg-elision.stderr index a665500fd9e1..8429c3aebac2 100644 --- a/tests/ui/methods/method-not-found-generic-arg-elision.stderr +++ b/tests/ui/methods/method-not-found-generic-arg-elision.stderr @@ -23,7 +23,7 @@ error[E0599]: no method named `extend` found for struct `Map` in the current sco --> $DIR/method-not-found-generic-arg-elision.rs:87:67 | LL | v.iter().map(Box::new(|x| x * x) as Box i32>).extend(std::iter::once(100)); - | ^^^^^^ method not found in `Map, Box i32>>` + | ^^^^^^ method not found in `Map, Box Fn(&'a i32) -> i32>>` error[E0599]: no method named `method` found for struct `Wrapper` in the current scope --> $DIR/method-not-found-generic-arg-elision.rs:90:13 diff --git a/tests/ui/methods/receiver-equality.stderr b/tests/ui/methods/receiver-equality.stderr index cea3340e386d..bf149cc2eb4d 100644 --- a/tests/ui/methods/receiver-equality.stderr +++ b/tests/ui/methods/receiver-equality.stderr @@ -5,7 +5,7 @@ LL | struct B(T); | ----------- function or associated item `method` not found for this struct ... LL | B:: fn(&'a ())>::method(y); - | ^^^^^^ function or associated item not found in `B` + | ^^^^^^ function or associated item not found in `B fn(&'a ())>` error: aborting due to 1 previous error diff --git a/tests/ui/methods/untrimmed-path-type.rs b/tests/ui/methods/untrimmed-path-type.rs new file mode 100644 index 000000000000..e6e3ad79185d --- /dev/null +++ b/tests/ui/methods/untrimmed-path-type.rs @@ -0,0 +1,11 @@ +// Ensures that the path of the `Error` type is not trimmed +// to make it clear which Error type is meant. + +fn main() { + meow().unknown(); //~ ERROR no method named `unknown` found + //~^ NOTE method not found in `Result<(), std::io::Error>` +} + +fn meow() -> Result<(), std::io::Error> { + Ok(()) +} diff --git a/tests/ui/methods/untrimmed-path-type.stderr b/tests/ui/methods/untrimmed-path-type.stderr new file mode 100644 index 000000000000..1f3101ff4e78 --- /dev/null +++ b/tests/ui/methods/untrimmed-path-type.stderr @@ -0,0 +1,9 @@ +error[E0599]: no method named `unknown` found for enum `Result` in the current scope + --> $DIR/untrimmed-path-type.rs:5:11 + | +LL | meow().unknown(); + | ^^^^^^^ method not found in `Result<(), std::io::Error>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/auto-ref-slice-plus-ref.rs b/tests/ui/methods/vec-autoderef-autoref.rs similarity index 79% rename from tests/ui/auto-ref-slice-plus-ref.rs rename to tests/ui/methods/vec-autoderef-autoref.rs index 00b279d3226c..38c0ba8574b0 100644 --- a/tests/ui/auto-ref-slice-plus-ref.rs +++ b/tests/ui/methods/vec-autoderef-autoref.rs @@ -1,8 +1,7 @@ +//! Test that method resolution does not autoderef `Vec` +//! into a slice or perform additional autorefs. + fn main() { - - // Testing that method lookup does not automatically borrow - // vectors to slices then automatically create a self reference. - let mut a = vec![0]; a.test_mut(); //~ ERROR no method named `test_mut` found a.test(); //~ ERROR no method named `test` found diff --git a/tests/ui/auto-ref-slice-plus-ref.stderr b/tests/ui/methods/vec-autoderef-autoref.stderr similarity index 83% rename from tests/ui/auto-ref-slice-plus-ref.stderr rename to tests/ui/methods/vec-autoderef-autoref.stderr index 806c1ee064ff..61c3bcc5b3b3 100644 --- a/tests/ui/auto-ref-slice-plus-ref.stderr +++ b/tests/ui/methods/vec-autoderef-autoref.stderr @@ -1,12 +1,12 @@ error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in the current scope - --> $DIR/auto-ref-slice-plus-ref.rs:7:7 + --> $DIR/vec-autoderef-autoref.rs:6:7 | LL | a.test_mut(); | ^^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test_mut`, perhaps you need to implement it - --> $DIR/auto-ref-slice-plus-ref.rs:14:1 + --> $DIR/vec-autoderef-autoref.rs:13:1 | LL | trait MyIter { | ^^^^^^^^^^^^ @@ -14,40 +14,40 @@ help: there is a method `get_mut` with a similar name, but with different argume --> $SRC_DIR/core/src/slice/mod.rs:LL:COL error[E0599]: no method named `test` found for struct `Vec<{integer}>` in the current scope - --> $DIR/auto-ref-slice-plus-ref.rs:8:7 + --> $DIR/vec-autoderef-autoref.rs:7:7 | LL | a.test(); | ^^^^ method not found in `Vec<{integer}>` | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test`, perhaps you need to implement it - --> $DIR/auto-ref-slice-plus-ref.rs:14:1 + --> $DIR/vec-autoderef-autoref.rs:13:1 | LL | trait MyIter { | ^^^^^^^^^^^^ error[E0599]: no method named `test` found for array `[{integer}; 1]` in the current scope - --> $DIR/auto-ref-slice-plus-ref.rs:10:11 + --> $DIR/vec-autoderef-autoref.rs:9:11 | LL | ([1]).test(); | ^^^^ method not found in `[{integer}; 1]` | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test`, perhaps you need to implement it - --> $DIR/auto-ref-slice-plus-ref.rs:14:1 + --> $DIR/vec-autoderef-autoref.rs:13:1 | LL | trait MyIter { | ^^^^^^^^^^^^ error[E0599]: no method named `test` found for reference `&[{integer}; 1]` in the current scope - --> $DIR/auto-ref-slice-plus-ref.rs:11:12 + --> $DIR/vec-autoderef-autoref.rs:10:12 | LL | (&[1]).test(); | ^^^^ method not found in `&[{integer}; 1]` | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test`, perhaps you need to implement it - --> $DIR/auto-ref-slice-plus-ref.rs:14:1 + --> $DIR/vec-autoderef-autoref.rs:13:1 | LL | trait MyIter { | ^^^^^^^^^^^^ diff --git a/tests/ui/mir/mir_match_guard_let_chains_drop_order.rs b/tests/ui/mir/mir_match_guard_let_chains_drop_order.rs new file mode 100644 index 000000000000..e98d57d1154c --- /dev/null +++ b/tests/ui/mir/mir_match_guard_let_chains_drop_order.rs @@ -0,0 +1,110 @@ +//@ run-pass +//@ needs-unwind +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 + +// See `mir_drop_order.rs` for more information + +#![feature(if_let_guard)] +#![allow(irrefutable_let_patterns)] + +use std::cell::RefCell; +use std::panic; + +pub struct DropLogger<'a, T> { + extra: T, + id: usize, + log: &'a panic::AssertUnwindSafe>>, +} + +impl<'a, T> Drop for DropLogger<'a, T> { + fn drop(&mut self) { + self.log.0.borrow_mut().push(self.id); + } +} + +struct InjectedFailure; + +#[allow(unreachable_code)] +fn main() { + let log = panic::AssertUnwindSafe(RefCell::new(vec![])); + let d = |id, extra| DropLogger { extra, id: id, log: &log }; + let get = || -> Vec<_> { + let mut m = log.0.borrow_mut(); + let n = m.drain(..); + n.collect() + }; + + { + let _x = ( + d( + 0, + d( + 1, + match () { () if let Some(_) = d(2, Some(true)).extra + && let DropLogger { .. } = d(3, None) => { + None + } + _ => { + Some(true) + } + } + ) + .extra, + ), + d(4, None), + &d(5, None), + d(6, None), + match () { + () if let DropLogger { .. } = d(7, None) + && let DropLogger { .. } = d(8, None) => { + d(9, None) + } + _ => { + // 10 is not constructed + d(10, None) + } + }, + ); + assert_eq!(get(), vec![3, 2, 8, 7, 1]); + } + assert_eq!(get(), vec![0, 4, 6, 9, 5]); + + let _ = std::panic::catch_unwind(|| { + ( + d( + 11, + d( + 12, + match () { + () if let Some(_) = d(13, Some(true)).extra + && let DropLogger { .. } = d(14, None) => { + None + } + _ => { + Some(true) + } + } + ) + .extra, + ), + d(15, None), + &d(16, None), + d(17, None), + match () { + () if let DropLogger { .. } = d(18, None) + && let DropLogger { .. } = d(19, None) + => { + d(20, None) + } + _ => { + // 10 is not constructed + d(21, None) + } + }, + panic::panic_any(InjectedFailure), + ); + }); + assert_eq!(get(), vec![14, 13, 19, 18, 20, 17, 15, 11, 16, 12]); +} diff --git a/tests/ui/mismatched_types/hr-projection-mismatch.current.stderr b/tests/ui/mismatched_types/hr-projection-mismatch.current.stderr new file mode 100644 index 000000000000..a2cec972e4a9 --- /dev/null +++ b/tests/ui/mismatched_types/hr-projection-mismatch.current.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/hr-projection-mismatch.rs:20:5 + | +LL | wrap::<_, Thing>(); + | ^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected reference `&'a _` + found reference `&_` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/mismatched_types/hr-projection-mismatch.next.stderr b/tests/ui/mismatched_types/hr-projection-mismatch.next.stderr new file mode 100644 index 000000000000..6ea0d43e1530 --- /dev/null +++ b/tests/ui/mismatched_types/hr-projection-mismatch.next.stderr @@ -0,0 +1,20 @@ +error[E0271]: type mismatch resolving `>::Assoc == &i32` + --> $DIR/hr-projection-mismatch.rs:20:15 + | +LL | wrap::<_, Thing>(); + | ^^^^^ type mismatch resolving `>::Assoc == &i32` + | +note: types differ + --> $DIR/hr-projection-mismatch.rs:14:18 + | +LL | type Assoc = &'a i32; + | ^^^^^^^ +note: required by a bound in `wrap` + --> $DIR/hr-projection-mismatch.rs:17:33 + | +LL | fn wrap Trait<'a, Assoc = T>>() {} + | ^^^^^^^^^ required by this bound in `wrap` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/mismatched_types/hr-projection-mismatch.rs b/tests/ui/mismatched_types/hr-projection-mismatch.rs new file mode 100644 index 000000000000..f96314a3fca0 --- /dev/null +++ b/tests/ui/mismatched_types/hr-projection-mismatch.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . + +trait Trait<'a> { + type Assoc; +} + +struct Thing; + +impl<'a> Trait<'a> for Thing { + type Assoc = &'a i32; +} + +fn wrap Trait<'a, Assoc = T>>() {} + +fn foo() { + wrap::<_, Thing>(); + //[next]~^ ERROR type mismatch resolving `>::Assoc == &i32 + //[current]~^^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/mismatched_types/issue-36053-2.stderr b/tests/ui/mismatched_types/issue-36053-2.stderr index ffaa276b62e4..8827be73a574 100644 --- a/tests/ui/mismatched_types/issue-36053-2.stderr +++ b/tests/ui/mismatched_types/issue-36053-2.stderr @@ -15,7 +15,7 @@ help: consider adjusting the signature so it borrows its argument LL | once::<&str>("str").fuse().filter(|a: &&str| true).count(); | + -error[E0599]: the method `count` exists for struct `Filter>, {closure@issue-36053-2.rs:7:39}>`, but its trait bounds were not satisfied +error[E0599]: the method `count` exists for struct `Filter>, {closure@$DIR/issue-36053-2.rs:7:39: 7:48}>`, but its trait bounds were not satisfied --> $DIR/issue-36053-2.rs:7:55 | LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); diff --git a/tests/ui/namespace/namespace-mix.stderr b/tests/ui/namespace/namespace-mix.stderr index 412ea4aba30b..200d31cc7101 100644 --- a/tests/ui/namespace/namespace-mix.stderr +++ b/tests/ui/namespace/namespace-mix.stderr @@ -7,7 +7,6 @@ LL | pub struct TS(); LL | check(m1::S); | ^^^^^ | - = note: can't use a type alias as a constructor help: a tuple struct with a similar name exists | LL | check(m1::TS); @@ -35,7 +34,6 @@ LL | check(xm1::S); LL | pub struct TS(); | ------------- similarly named tuple struct `TS` defined here | - = note: can't use a type alias as a constructor help: a tuple struct with a similar name exists | LL | check(xm1::TS); @@ -61,7 +59,6 @@ LL | TV(), LL | check(m7::V); | ^^^^^ | - = note: can't use a type alias as a constructor help: a tuple variant with a similar name exists | LL | check(m7::TV); @@ -89,7 +86,6 @@ LL | check(xm7::V); LL | TV(), | -- similarly named tuple variant `TV` defined here | - = note: can't use a type alias as a constructor help: a tuple variant with a similar name exists | LL | check(xm7::TV); diff --git a/tests/ui/nll/issue-57362-2.stderr b/tests/ui/nll/issue-57362-2.stderr index 24787b990e31..8a1a4d6b22c1 100644 --- a/tests/ui/nll/issue-57362-2.stderr +++ b/tests/ui/nll/issue-57362-2.stderr @@ -2,7 +2,7 @@ error[E0599]: no function or associated item named `make_g` found for fn pointer --> $DIR/issue-57362-2.rs:23:25 | LL | let x = ::make_g(); - | ^^^^^^ function or associated item not found in `fn(&())` + | ^^^^^^ function or associated item not found in `for<'a> fn(&'a ())` | = help: items from traits can only be used if the trait is implemented and in scope note: `X` defines an item `make_g`, perhaps you need to implement it diff --git a/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr b/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr index 998d06b77065..27a887e86004 100644 --- a/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr +++ b/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr @@ -2,7 +2,7 @@ error[E0599]: no function or associated item named `make_g` found for fn pointer --> $DIR/issue-57642-higher-ranked-subtype.rs:31:25 | LL | let x = ::make_g(); - | ^^^^^^ function or associated item not found in `fn(&())` + | ^^^^^^ function or associated item not found in `for<'a> fn(&'a ())` | = help: items from traits can only be used if the trait is implemented and in scope note: `X` defines an item `make_g`, perhaps you need to implement it @@ -15,7 +15,7 @@ error[E0599]: no function or associated item named `make_f` found for fn pointer --> $DIR/issue-57642-higher-ranked-subtype.rs:36:25 | LL | let x = ::make_f(); - | ^^^^^^ function or associated item not found in `fn(&())` + | ^^^^^^ function or associated item not found in `for<'a> fn(&'a ())` | = help: items from traits can only be used if the trait is implemented and in scope note: `Y` defines an item `make_f`, perhaps you need to implement it diff --git a/tests/ui/nll/user-annotations/normalizing-user-annotation.rs b/tests/ui/nll/user-annotations/normalizing-user-annotation.rs new file mode 100644 index 000000000000..fa8b3bfd577d --- /dev/null +++ b/tests/ui/nll/user-annotations/normalizing-user-annotation.rs @@ -0,0 +1,31 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . + +// See description in there; this has to do with fundamental limitations +// to the old trait solver surrounding higher-ranked aliases with infer +// vars. This always worked in the new trait solver, but I added a revision +// just for good measure. + +trait Foo<'a> { + type Assoc; +} + +impl Foo<'_> for i32 { + type Assoc = u32; +} + +impl Foo<'_> for u32 { + type Assoc = u32; +} + +fn foo<'b: 'b, T: for<'a> Foo<'a>, F: for<'a> Fn(>::Assoc)>(_: F) -> (T, F) { + todo!() +} + +fn main() { + let (x, c): (i32, _) = foo::<'static, _, _>(|_| {}); +} diff --git a/tests/ui/not-enough-arguments.rs b/tests/ui/not-enough-arguments.rs index 4a2ea5e44c71..ec660a1de81b 100644 --- a/tests/ui/not-enough-arguments.rs +++ b/tests/ui/not-enough-arguments.rs @@ -1,20 +1,16 @@ +//@ aux-build: delegate_macro.rs +extern crate delegate_macro; +use delegate_macro::delegate; + // Check that the only error msg we report is the // mismatch between the # of params, and not other // unrelated errors. - -fn foo(a: isize, b: isize, c: isize, d:isize) { - panic!(); +fn foo(a: isize, b: isize, c: isize, d: isize) { + panic!(); } // Check that all arguments are shown in the error message, even if they're across multiple lines. -fn bar( - a: i32, - b: i32, - c: i32, - d: i32, - e: i32, - f: i32, -) { +fn bar(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) { println!("{}", a); println!("{}", b); println!("{}", c); @@ -23,9 +19,35 @@ fn bar( println!("{}", f); } -fn main() { - foo(1, 2, 3); - //~^ ERROR function takes 4 arguments but 3 - bar(1, 2, 3); - //~^ ERROR function takes 6 arguments but 3 +macro_rules! delegate_local { + ($method:ident) => { + ::$method(8) + //~^ ERROR function takes 2 arguments but 1 + }; +} + +macro_rules! delegate_from { + ($from:ident, $method:ident) => { + <$from>::$method(8) + //~^ ERROR function takes 2 arguments but 1 + }; +} + +struct Bar; + +impl Bar { + fn foo(a: u8, b: u8) {} + fn bar() { + delegate_local!(foo); + delegate!(foo); + //~^ ERROR function takes 2 arguments but 1 + delegate_from!(Bar, foo); + } +} + +fn main() { + foo(1, 2, 3); + //~^ ERROR function takes 4 arguments but 3 + bar(1, 2, 3); + //~^ ERROR function takes 6 arguments but 3 } diff --git a/tests/ui/not-enough-arguments.stderr b/tests/ui/not-enough-arguments.stderr index 099d82eb9355..908d0273bbec 100644 --- a/tests/ui/not-enough-arguments.stderr +++ b/tests/ui/not-enough-arguments.stderr @@ -1,42 +1,88 @@ -error[E0061]: this function takes 4 arguments but 3 arguments were supplied - --> $DIR/not-enough-arguments.rs:27:3 +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/not-enough-arguments.rs:24:9 | -LL | foo(1, 2, 3); - | ^^^--------- argument #4 of type `isize` is missing +LL | ::$method(8) + | ^^^^^^^^^^^^^^^--- argument #2 of type `u8` is missing +... +LL | delegate_local!(foo); + | -------------------- in this macro invocation | -note: function defined here - --> $DIR/not-enough-arguments.rs:5:4 +note: associated function defined here + --> $DIR/not-enough-arguments.rs:39:8 | -LL | fn foo(a: isize, b: isize, c: isize, d:isize) { - | ^^^ ------- +LL | fn foo(a: u8, b: u8) {} + | ^^^ ----- + = note: this error originates in the macro `delegate_local` (in Nightly builds, run with -Z macro-backtrace for more info) help: provide the argument | -LL | foo(1, 2, 3, /* isize */); - | +++++++++++++ +LL | ::$method(8, /* u8 */) + | ++++++++++ -error[E0061]: this function takes 6 arguments but 3 arguments were supplied - --> $DIR/not-enough-arguments.rs:29:3 +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/not-enough-arguments.rs:42:9 | -LL | bar(1, 2, 3); - | ^^^--------- three arguments of type `i32`, `i32`, and `i32` are missing +LL | delegate!(foo); + | ^^^^^^^^^^^^^^ argument #2 of type `u8` is missing + | +note: associated function defined here + --> $DIR/not-enough-arguments.rs:39:8 + | +LL | fn foo(a: u8, b: u8) {} + | ^^^ ----- + = note: this error originates in the macro `delegate` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/not-enough-arguments.rs:31:9 + | +LL | <$from>::$method(8) + | ^^^^^^^^^^^^^^^^--- argument #2 of type `u8` is missing +... +LL | delegate_from!(Bar, foo); + | ------------------------ in this macro invocation + | +note: associated function defined here + --> $DIR/not-enough-arguments.rs:39:8 + | +LL | fn foo(a: u8, b: u8) {} + | ^^^ ----- + = note: this error originates in the macro `delegate_from` (in Nightly builds, run with -Z macro-backtrace for more info) +help: provide the argument + | +LL | <$from>::$method(8, /* u8 */) + | ++++++++++ + +error[E0061]: this function takes 4 arguments but 3 arguments were supplied + --> $DIR/not-enough-arguments.rs:49:5 + | +LL | foo(1, 2, 3); + | ^^^--------- argument #4 of type `isize` is missing | note: function defined here - --> $DIR/not-enough-arguments.rs:10:4 + --> $DIR/not-enough-arguments.rs:8:4 | -LL | fn bar( - | ^^^ -... -LL | d: i32, - | ------ -LL | e: i32, - | ------ -LL | f: i32, - | ------ +LL | fn foo(a: isize, b: isize, c: isize, d: isize) { + | ^^^ -------- +help: provide the argument + | +LL | foo(1, 2, 3, /* isize */); + | +++++++++++++ + +error[E0061]: this function takes 6 arguments but 3 arguments were supplied + --> $DIR/not-enough-arguments.rs:51:5 + | +LL | bar(1, 2, 3); + | ^^^--------- three arguments of type `i32`, `i32`, and `i32` are missing + | +note: function defined here + --> $DIR/not-enough-arguments.rs:13:4 + | +LL | fn bar(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) { + | ^^^ ------ ------ ------ help: provide the arguments | -LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); - | +++++++++++++++++++++++++++++++++ +LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); + | +++++++++++++++++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0061`. diff --git a/tests/ui/object-pointer-types.stderr b/tests/ui/object-pointer-types.stderr index ac8e069cfd2c..72b290f2ad9f 100644 --- a/tests/ui/object-pointer-types.stderr +++ b/tests/ui/object-pointer-types.stderr @@ -25,7 +25,7 @@ error[E0599]: no method named `managed` found for struct `Box<(dyn Foo + 'static --> $DIR/object-pointer-types.rs:23:7 | LL | x.managed(); - | ^^^^^^^ method not found in `Box` + | ^^^^^^^ method not found in `Box<(dyn Foo + 'static)>` error: aborting due to 3 previous errors diff --git a/tests/ui/on-unimplemented/bad-annotation.rs b/tests/ui/on-unimplemented/bad-annotation.rs index 25de59781102..937e5b82da50 100644 --- a/tests/ui/on-unimplemented/bad-annotation.rs +++ b/tests/ui/on-unimplemented/bad-annotation.rs @@ -59,7 +59,7 @@ trait EmptyOn {} //~^^^ NOTE expected value here trait ExpectedPredicateInOn {} -#[rustc_on_unimplemented(on(x = "y"), message = "y")] +#[rustc_on_unimplemented(on(Self = "y"), message = "y")] trait OnWithoutDirectives {} #[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = "x")), message = "y")] @@ -107,3 +107,13 @@ trait InvalidPredicate {} //~^ ERROR invalid flag in `on`-clause //~^^ NOTE expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `something` trait InvalidFlag {} + +#[rustc_on_unimplemented(on(_Self = "y", message = "y"))] +//~^ ERROR invalid name in `on`-clause +//~^^ NOTE expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `_Self` +trait InvalidName {} + +#[rustc_on_unimplemented(on(abc = "y", message = "y"))] +//~^ ERROR invalid name in `on`-clause +//~^^ NOTE expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `abc` +trait InvalidName2 {} diff --git a/tests/ui/on-unimplemented/bad-annotation.stderr b/tests/ui/on-unimplemented/bad-annotation.stderr index 35b919c7b785..3fc545327740 100644 --- a/tests/ui/on-unimplemented/bad-annotation.stderr +++ b/tests/ui/on-unimplemented/bad-annotation.stderr @@ -125,7 +125,19 @@ error[E0232]: invalid flag in `on`-clause LL | #[rustc_on_unimplemented(on(something, message = "y"))] | ^^^^^^^^^ expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `something` -error: aborting due to 18 previous errors +error[E0232]: invalid name in `on`-clause + --> $DIR/bad-annotation.rs:111:29 + | +LL | #[rustc_on_unimplemented(on(_Self = "y", message = "y"))] + | ^^^^^ expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `_Self` + +error[E0232]: invalid name in `on`-clause + --> $DIR/bad-annotation.rs:116:29 + | +LL | #[rustc_on_unimplemented(on(abc = "y", message = "y"))] + | ^^^ expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `abc` + +error: aborting due to 20 previous errors Some errors have detailed explanations: E0230, E0231, E0232. For more information about an error, try `rustc --explain E0230`. diff --git a/tests/ui/on-unimplemented/on-trait.rs b/tests/ui/on-unimplemented/on-trait.rs index 556813cd4795..91630af17e92 100644 --- a/tests/ui/on-unimplemented/on-trait.rs +++ b/tests/ui/on-unimplemented/on-trait.rs @@ -3,7 +3,7 @@ #![feature(rustc_attrs)] pub mod Bar { - #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}` in `{Foo}`"] + #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}` in `{This}`"] pub trait Foo {} } diff --git a/tests/ui/panics/location-detail-unwrap-multiline.rs b/tests/ui/panics/location-detail-unwrap-multiline.rs new file mode 100644 index 000000000000..56e1760d851b --- /dev/null +++ b/tests/ui/panics/location-detail-unwrap-multiline.rs @@ -0,0 +1,12 @@ +//@ run-fail +//@ compile-flags: -Cstrip=none -Cdebuginfo=line-tables-only -Copt-level=0 +//@ exec-env:RUST_BACKTRACE=1 +//@ regex-error-pattern: location-detail-unwrap-multiline\.rs:11(:10)?\n +//@ needs-unwind +//@ ignore-android FIXME #17520 + +fn main() { + let opt: Option = None; + opt + .unwrap(); +} diff --git a/tests/ui/parser/macro/auxiliary/unicode-control.rs b/tests/ui/parser/macro/auxiliary/unicode-control.rs new file mode 100644 index 000000000000..8e73e3985ce9 --- /dev/null +++ b/tests/ui/parser/macro/auxiliary/unicode-control.rs @@ -0,0 +1,19 @@ +#![allow(text_direction_codepoint_in_literal)] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub fn create_rtl_in_string(_: TokenStream) -> TokenStream { + r#""‮test⁦ RTL in string literal""#.parse().unwrap() +} + +#[proc_macro] +pub fn forward_stream(s: TokenStream) -> TokenStream { + s +} + +#[proc_macro] +pub fn recollect_stream(s: TokenStream) -> TokenStream { + s.into_iter().collect() +} diff --git a/tests/ui/parser/macro/unicode-control-codepoints-macros.rs b/tests/ui/parser/macro/unicode-control-codepoints-macros.rs new file mode 100644 index 000000000000..775c50779760 --- /dev/null +++ b/tests/ui/parser/macro/unicode-control-codepoints-macros.rs @@ -0,0 +1,49 @@ +// Regression test for #140281 +//@ edition: 2021 +//@ proc-macro: unicode-control.rs + +extern crate unicode_control; +use unicode_control::*; + +macro_rules! foo { + ($x:expr) => { + $x + }; +} + +macro_rules! empty { + ($x:expr) => {}; +} + +fn main() { + let t = vec![ + /// ‮test⁦ RTL in doc in vec + //~^ ERROR unicode codepoint changing visible direction of text present in doc comment + 1 + ]; + foo!( + /** + * ‮test⁦ RTL in doc in macro + */ + //~^^^ ERROR unicode codepoint changing visible direction of text present in doc comment + 1 + ); + empty!( + /** + * ‮test⁦ RTL in doc in macro + */ + //~^^^ ERROR unicode codepoint changing visible direction of text present in doc comment + 1 + ); + let x = create_rtl_in_string!(); // OK + forward_stream!( + /// ‮test⁦ RTL in doc in proc macro + //~^ ERROR unicode codepoint changing visible direction of text present in doc comment + mod a {} + ); + recollect_stream!( + /// ‮test⁦ RTL in doc in proc macro + //~^ ERROR unicode codepoint changing visible direction of text present in doc comment + mod b {} + ); +} diff --git a/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr new file mode 100644 index 000000000000..ca813399eac2 --- /dev/null +++ b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr @@ -0,0 +1,57 @@ +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:20:9 + | +LL | /// �test� RTL in doc in vec + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + = note: `#[deny(text_direction_codepoint_in_literal)]` on by default + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:25:9 + | +LL | / /** +LL | | * �test� RTL in doc in macro +LL | | */ + | |___________^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:32:9 + | +LL | / /** +LL | | * �test� RTL in doc in macro +LL | | */ + | |___________^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:40:9 + | +LL | /// �test� RTL in doc in proc macro + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:45:9 + | +LL | /// �test� RTL in doc in proc macro + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: aborting due to 5 previous errors + diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed b/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed new file mode 100644 index 000000000000..4d99e4bdc1c3 --- /dev/null +++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.fixed @@ -0,0 +1,24 @@ +//@ run-rustfix + +pub enum Foo { + X, Y +} + +pub fn typo1(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y, + Y => X, //~ ERROR expected one of + } +} + +pub fn typo2(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y, + Y => X, //~ ERROR expected one of + } +} + + +fn main() { } diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.rs b/tests/ui/parser/match-arm-comma-typo-issue-140991.rs new file mode 100644 index 000000000000..3baf1ff3fa1d --- /dev/null +++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.rs @@ -0,0 +1,24 @@ +//@ run-rustfix + +pub enum Foo { + X, Y +} + +pub fn typo1(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y. + Y => X, //~ ERROR expected one of + } +} + +pub fn typo2(foo: Foo) -> Foo { + use Foo::*; + match foo { + X => Y/ + Y => X, //~ ERROR expected one of + } +} + + +fn main() { } diff --git a/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr b/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr new file mode 100644 index 000000000000..19532d14245d --- /dev/null +++ b/tests/ui/parser/match-arm-comma-typo-issue-140991.stderr @@ -0,0 +1,26 @@ +error: expected one of `(`, `,`, `.`, `::`, `?`, `}`, or an operator, found `=>` + --> $DIR/match-arm-comma-typo-issue-140991.rs:11:11 + | +LL | Y => X, + | ^^ expected one of 7 possible tokens + | +help: you might have meant to write a `,` to end this `match` arm + | +LL - X => Y. +LL + X => Y, + | + +error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>` + --> $DIR/match-arm-comma-typo-issue-140991.rs:19:11 + | +LL | Y => X, + | ^^ expected one of 8 possible tokens + | +help: you might have meant to write a `,` to end this `match` arm + | +LL - X => Y/ +LL + X => Y, + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/unicode-control-codepoints.rs b/tests/ui/parser/unicode-control-codepoints.rs index 14e1cfe59d39..e3c906063c47 100644 --- a/tests/ui/parser/unicode-control-codepoints.rs +++ b/tests/ui/parser/unicode-control-codepoints.rs @@ -34,7 +34,7 @@ fn main() { //~^ ERROR unicode codepoint changing visible direction of text present in literal println!("{{‮}}"); - //~^ ERROR unicode codepoint changing visible direction of text present in format string + //~^ ERROR unicode codepoint changing visible direction of text present in literal } //"/*‮ } ⁦if isAdmin⁩ ⁦ begin admins only */" diff --git a/tests/ui/parser/unicode-control-codepoints.stderr b/tests/ui/parser/unicode-control-codepoints.stderr index 27b95f9ac611..7978c1435f60 100644 --- a/tests/ui/parser/unicode-control-codepoints.stderr +++ b/tests/ui/parser/unicode-control-codepoints.stderr @@ -100,21 +100,6 @@ LL | // if access_level != "us�e�r" { // Check if admin = note: `#[deny(text_direction_codepoint_in_comment)]` on by default = help: if their presence wasn't intentional, you can remove them -error: unicode codepoint changing visible direction of text present in comment - --> $DIR/unicode-control-codepoints.rs:40:1 - | -LL | //"/*� } �if isAdmin� � begin admins only */" - | ^^^^^-^^^-^^^^^^^^^^-^-^^^^^^^^^^^^^^^^^^^^^^ - | | | | | | - | | | | | '\u{2066}' - | | | | '\u{2069}' - | | | '\u{2066}' - | | '\u{202e}' - | this comment contains invisible unicode text flow control codepoints - | - = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen - = help: if their presence wasn't intentional, you can remove them - error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:13:22 | @@ -207,14 +192,14 @@ LL - let _ = cr#"�"#; LL + let _ = cr#"\u{202e}"#; | -error: unicode codepoint changing visible direction of text present in format string +error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:36:14 | LL | println!("{{�}}"); | ^^^-^^^ | | | | | '\u{202e}' - | this format string contains an invisible unicode text flow control codepoint + | this literal contains an invisible unicode text flow control codepoint | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = help: if their presence wasn't intentional, you can remove them @@ -224,6 +209,21 @@ LL - println!("{{�}}"); LL + println!("{{\u{202e}}}"); | +error: unicode codepoint changing visible direction of text present in comment + --> $DIR/unicode-control-codepoints.rs:40:1 + | +LL | //"/*� } �if isAdmin� � begin admins only */" + | ^^^^^-^^^-^^^^^^^^^^-^-^^^^^^^^^^^^^^^^^^^^^^ + | | | | | | + | | | | | '\u{2066}' + | | | | '\u{2069}' + | | | '\u{2066}' + | | '\u{202e}' + | this comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = help: if their presence wasn't intentional, you can remove them + error: unicode codepoint changing visible direction of text present in doc comment --> $DIR/unicode-control-codepoints.rs:43:1 | diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs new file mode 100644 index 000000000000..7bb39ca7bb98 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs @@ -0,0 +1,15 @@ +//@ check-pass +//! Test that `GatherLocalsVisitor` only visits expressions in guard patterns when checking the +//! expressions, and not a second time when visiting the pattern. If locals are declared inside the +//! the guard expression, it would ICE if visited twice ("evaluated expression more than once"). + +#![feature(guard_patterns)] +#![expect(incomplete_features)] + +fn main() { + match (0,) { + // FIXME(guard_patterns): liveness lints don't work yet; this will ICE without the `_`. + (_ if { let _x = false; _x },) => {} + _ => {} + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.rs b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.rs new file mode 100644 index 000000000000..7dc9ef7161d1 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.rs @@ -0,0 +1,12 @@ +//! Regression test for . +//! Make sure expressions in top-level guard patterns are only resolved once. + +fn main() { + for + else if b 0 {} + //~^ ERROR expected identifier, found keyword `else` + //~| ERROR missing `in` in `for` loop + //~| ERROR cannot find value `b` in this scope + //~| ERROR guard patterns are experimental + //~| ERROR `{integer}` is not an iterator +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.stderr new file mode 100644 index 000000000000..dae384137f52 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.stderr @@ -0,0 +1,48 @@ +error: expected identifier, found keyword `else` + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:5 + | +LL | else if b 0 {} + | ^^^^ expected identifier, found keyword + +error: missing `in` in `for` loop + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:14 + | +LL | else if b 0 {} + | ^ + | +help: try adding `in` here + | +LL | else if b in 0 {} + | ++ + +error[E0425]: cannot find value `b` in this scope + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:13 + | +LL | else if b 0 {} + | ^ not found in this scope + +error[E0658]: guard patterns are experimental + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:13 + | +LL | else if b 0 {} + | ^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +error[E0277]: `{integer}` is not an iterator + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:15 + | +LL | else if b 0 {} + | ^ `{integer}` is not an iterator + | + = help: the trait `Iterator` is not implemented for `{integer}` + = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` + = note: required for `{integer}` to implement `IntoIterator` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0425, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/pattern/unused-parameters-const-pattern.rs b/tests/ui/pattern/unused-parameters-const-pattern.rs new file mode 100644 index 000000000000..107c65ddfd3a --- /dev/null +++ b/tests/ui/pattern/unused-parameters-const-pattern.rs @@ -0,0 +1,19 @@ +//@ check-pass + +// Tests that const patterns that use generic parameters are +// allowed if we are still able to evaluate them. + +trait Trait { const ASSOC: usize; } + +impl Trait for T { + const ASSOC: usize = 10; +} + +fn foo(a: usize) { + match a { + ::ASSOC => (), + _ => (), + } +} + +fn main() {} diff --git a/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.rs b/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.rs new file mode 100644 index 000000000000..b100c062bba4 --- /dev/null +++ b/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --print native-static-libs +//@ check-pass +//~? WARN cannot output linkage information without staticlib crate-type + +fn main() {} diff --git a/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.stderr b/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.stderr new file mode 100644 index 000000000000..ceff08baa13a --- /dev/null +++ b/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.stderr @@ -0,0 +1,6 @@ +warning: cannot output linkage information without staticlib crate-type + +note: consider `--crate-type staticlib` to print linkage information + +warning: 1 warning emitted + diff --git a/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.rs b/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.rs new file mode 100644 index 000000000000..3e9ca457a9c9 --- /dev/null +++ b/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.rs @@ -0,0 +1,3 @@ +//@ compile-flags: --print native-static-libs --crate-type staticlib --emit metadata +//@ check-pass +//~? WARN cannot output linkage information when --emit link is not passed diff --git a/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.stderr b/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.stderr new file mode 100644 index 000000000000..b32e1437d6b5 --- /dev/null +++ b/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.stderr @@ -0,0 +1,4 @@ +warning: cannot output linkage information when --emit link is not passed + +warning: 1 warning emitted + diff --git a/tests/ui/print-request/stability.rs b/tests/ui/print-request/stability.rs index 54142ce78cef..fbcdf916cc7c 100644 --- a/tests/ui/print-request/stability.rs +++ b/tests/ui/print-request/stability.rs @@ -110,3 +110,4 @@ fn main() {} //[check_cfg]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `check-cfg` print option //[supported_crate_types]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `supported-crate-types` print option //[target_spec_json]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `target-spec-json` print option +//[native_static_libs]~? WARNING cannot output linkage information without staticlib crate-type diff --git a/tests/ui/print_type_sizes/niche-filling.rs b/tests/ui/print_type_sizes/niche-filling.rs index 36739e3fc04c..719bc2a07dc9 100644 --- a/tests/ui/print_type_sizes/niche-filling.rs +++ b/tests/ui/print_type_sizes/niche-filling.rs @@ -55,7 +55,14 @@ pub struct NestedNonZero { impl Default for NestedNonZero { fn default() -> Self { - NestedNonZero { pre: 0, val: unsafe { NonZero::new_unchecked(1) }, post: 0 } + // Ideally we'd call NonZero::new_unchecked, but this test is supposed + // to be target-independent and NonZero::new_unchecked is #[track_caller] + // (see #129658) so mentioning that function pulls in std::panic::Location + // which contains a &str, whose layout is target-dependent. + const ONE: NonZero = const { + unsafe { std::mem::transmute(1u32) } + }; + NestedNonZero { pre: 0, val: ONE, post: 0 } } } diff --git a/tests/ui/print_type_sizes/niche-filling.stdout b/tests/ui/print_type_sizes/niche-filling.stdout index 70612490a471..3342f68dd70b 100644 --- a/tests/ui/print_type_sizes/niche-filling.stdout +++ b/tests/ui/print_type_sizes/niche-filling.stdout @@ -72,10 +72,6 @@ print-type-size type: `core::num::niche_types::NonZeroU32Inner`: 4 bytes, alignm print-type-size field `.0`: 4 bytes print-type-size type: `std::num::NonZero`: 4 bytes, alignment: 4 bytes print-type-size field `.0`: 4 bytes -print-type-size type: `std::option::Option>`: 4 bytes, alignment: 4 bytes -print-type-size variant `Some`: 4 bytes -print-type-size field `.0`: 4 bytes -print-type-size variant `None`: 0 bytes print-type-size type: `Enum4<(), (), (), MyOption>`: 2 bytes, alignment: 1 bytes print-type-size variant `Four`: 2 bytes print-type-size field `.0`: 2 bytes diff --git a/tests/ui/proc-macro/no-macro-use-attr.stderr b/tests/ui/proc-macro/no-macro-use-attr.stderr index 4913672450ab..0bef563fbb9e 100644 --- a/tests/ui/proc-macro/no-macro-use-attr.stderr +++ b/tests/ui/proc-macro/no-macro-use-attr.stderr @@ -2,13 +2,17 @@ warning: unused extern crate --> $DIR/no-macro-use-attr.rs:6:1 | LL | extern crate test_macros; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/no-macro-use-attr.rs:4:9 | LL | #![warn(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate test_macros; + | warning: 1 warning emitted diff --git a/tests/ui/qualified/qualified-path-params-2.stderr b/tests/ui/qualified/qualified-path-params-2.stderr index 6641e81013f0..e70cdbdc3f49 100644 --- a/tests/ui/qualified/qualified-path-params-2.stderr +++ b/tests/ui/qualified/qualified-path-params-2.stderr @@ -7,7 +7,7 @@ LL | type A = ::A::f; help: if there were a trait named `Example` with associated type `f` implemented for `::A`, you could use the fully-qualified path | LL - type A = ::A::f; -LL + type A = <::A as Example>::f; +LL + type A = <::A as Example>::f; | error: aborting due to 1 previous error diff --git a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs new file mode 100644 index 000000000000..6115146539c1 --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs @@ -0,0 +1,72 @@ +#![feature(generic_arg_infer)] + +// Test when deferring repeat expr copy checks to end of typechecking whether elements +// that are const items allow for repeat counts to go uninferred without an error being +// emitted if they would later wind up inferred by integer fallback. +// +// This test should be updated if we wind up deferring repeat expr checks until *after* +// integer fallback as the point of the test is not *specifically* about integer fallback +// but rather about the behaviour of `const` element exprs. + +trait Trait {} + +// We impl `Trait` for both `i32` and `u32` to avoid being able +// to prove `?int: Trait` from there only being one impl. +impl Trait<2> for i32 {} +impl Trait<2> for u32 {} + +fn tie_and_make_goal>(_: &T, _: &[String; N]) {} + +fn const_block() { + // Deferred repeat expr `String; ?n` + let a = [const { String::new() }; _]; + + // `?int: Trait` goal + tie_and_make_goal(&1, &a); + + // If repeat expr checks structurally resolve the `?n`s before checking if the + // element is a `const` then we would error here. Otherwise we avoid doing so, + // integer fallback occurs, allowing `?int: Trait` goals to make progress, + // inferring the repeat counts (to `2` but that doesn't matter as the element is `const`). +} + +fn const_item() { + const MY_CONST: String = String::new(); + + // Deferred repeat expr `String; ?n` + let a = [MY_CONST; _]; + + // `?int: Trait` goal + tie_and_make_goal(&1, &a); + + // ... same as `const_block` +} + +fn assoc_const() { + trait Dummy { + const ASSOC: String; + } + impl Dummy for () { + const ASSOC: String = String::new(); + } + + // Deferred repeat expr `String; ?n` + let a = [<() as Dummy>::ASSOC; _]; + + // `?int: Trait` goal + tie_and_make_goal(&1, &a); + + // ... same as `const_block` +} + +fn const_block_but_uninferred() { + // Deferred repeat expr `String; ?n` + let a = [const { String::new() }; _]; + //~^ ERROR: type annotations needed for `[String; _]` + + // Even if we don't structurally resolve the repeat count as part of repeat expr + // checks, we still error on the repeat count being uninferred as we require all + // types/consts to be inferred by the end of type checking. +} + +fn main() {} diff --git a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr new file mode 100644 index 000000000000..2f52537fa940 --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr @@ -0,0 +1,15 @@ +error[E0284]: type annotations needed for `[String; _]` + --> $DIR/copy-check-const-element-uninferred-count.rs:64:9 + | +LL | let a = [const { String::new() }; _]; + | ^ ---------------------------- type must be known at this point + | + = note: the length of array `[String; _]` must be type `usize` +help: consider giving `a` an explicit type, where the placeholders `_` are specified + | +LL | let a: [_; _] = [const { String::new() }; _]; + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs index d9ad93541ecf..3f310f07de0f 100644 --- a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs +++ b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs @@ -1,37 +1,53 @@ #![feature(generic_arg_infer)] -// Test that would start passing if we defer repeat expr copy checks to end of -// typechecking and they're checked after integer fallback occurs. We accomplish -// this by contriving a situation where integer fallback allows progress to be -// made on a trait goal that infers the length of a repeat expr. +// Test when deferring repeat expr copy checks to end of typechecking whether they're +// checked before integer fallback occurs or not. We accomplish this by having a repeat +// count that can only be inferred after integer fallback has occured. This test will +// pass if we were to check repeat exprs after integer fallback. use std::marker::PhantomData; +struct Foo(PhantomData); -struct NotCopy; +// We impl Copy/Clone for multiple (but not all) substitutions +// to ensure that `Foo: Copy` can't be proven on the basis +// of there only being one applying impl. +impl Clone for Foo { + fn clone(&self) -> Self { + Foo(PhantomData) + } +} +impl Clone for Foo { + fn clone(&self) -> Self { + Foo(PhantomData) + } +} +impl Copy for Foo {} +impl Copy for Foo {} trait Trait {} -impl Trait<2> for u32 {} +// We impl `Trait` for both `i32` and `u32` to avoid being able +// to prove `?int: Trait` from there only being one impl. impl Trait<1> for i32 {} +impl Trait<2> for u32 {} -fn make_goal, const N: usize>(_: &T, _: [NotCopy; N]) {} +fn tie_and_make_goal>(_: &T, _: &[Foo; N]) {} fn main() { let a = 1; - let b = [NotCopy; _]; - //~^ ERROR: type annotations needed + // Deferred repeat expr `Foo; ?n` + let b = [Foo(PhantomData); _]; + //~^ ERROR: type annotations needed for `[Foo<{integer}>; _]` - // a is of type `?y` - // b is of type `[NotCopy; ?x]` - // there is a goal ?y: Trait` with two candidates: - // - `i32: Trait<1>`, ?y=i32 ?x=1 which doesnt require `NotCopy: Copy` - // - `u32: Trait<2>` ?y=u32 ?x=2 which requires `NotCopy: Copy` - make_goal(&a, b); + // Introduces a `?int: Trait` goal + tie_and_make_goal(&a, &b); - // final repeat expr checks: - // - // `NotCopy; ?x` - // - succeeds if fallback happens before repeat exprs as `i32: Trait` infers `?x=1` - // - fails if repeat expr checks happen first as `?x` is unconstrained so cannot be - // structurally resolved + // If fallback doesn't occur: + // - `Foo; ?n`is ambig as repeat count is unknown -> error + + // If fallback occurs: + // - `?int` inferred to `i32` + // - `?int: Trait` becomes `i32: Trait` wihhc infers `?n=1` + // - Repeat expr check `Foo; ?n` is now `Foo; 1` + // - `Foo; 1` doesn't require `Foo: Copy` } diff --git a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.stderr b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.stderr index 2a0cb3fb7a39..103b074dda7c 100644 --- a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.stderr +++ b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.stderr @@ -1,13 +1,13 @@ -error[E0282]: type annotations needed for `[NotCopy; _]` - --> $DIR/copy-check-deferred-after-fallback.rs:21:9 +error[E0282]: type annotations needed for `[Foo<{integer}>; _]` + --> $DIR/copy-check-deferred-after-fallback.rs:39:9 | -LL | let b = [NotCopy; _]; - | ^ ------- type must be known at this point +LL | let b = [Foo(PhantomData); _]; + | ^ ---------------- type must be known at this point | help: consider giving `b` an explicit type, where the value of const parameter `N` is specified | -LL | let b: [_; N] = [NotCopy; _]; - | ++++++++ +LL | let b: [Foo<{integer}>; N] = [Foo(PhantomData); _]; + | +++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs index 4654d7483a64..4fbb8f0a00ca 100644 --- a/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs +++ b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs @@ -1,18 +1,13 @@ //@ check-pass - #![feature(generic_arg_infer)] -// Test that if we defer repeat expr copy checks to end of typechecking they're -// checked before integer fallback occurs. We accomplish this by contriving a -// situation where we have a goal that can be proven either via another repeat expr -// check or by integer fallback. In the integer fallback case an array length would -// be inferred to `2` requiring `NotCopy: Copy`, and in the repeat expr case it would -// be inferred to `1`. +// Test when deferring repeat expr checks to end of typechecking whether they're +// checked before integer fallback occurs. We accomplish this by having the repeat +// expr check allow inference progress on an ambiguous goal, where the ambiguous goal +// would fail if the inference variable was fallen back to `i32`. This test will +// pass if we check repeat exprs before integer fallback. use std::marker::PhantomData; - -struct NotCopy; - struct Foo(PhantomData); impl Clone for Foo { @@ -20,40 +15,34 @@ impl Clone for Foo { Foo(PhantomData) } } - impl Copy for Foo {} -fn tie(_: &T, _: [Foo; 2]) {} +trait Trait {} -trait Trait {} +// Two impls just to ensure that `?int: Trait` wont itself succeed by unifying with +// a self type on an impl here. It also ensures that integer fallback would actually +// be valid for all of the stalled goals incase that's ever something we take into account. +impl Trait for i32 {} +impl Trait for u32 {} -impl Trait<2> for i32 {} -impl Trait<1> for u32 {} - -fn make_goal, const N: usize>(_: &T, _: [NotCopy; N]) {} +fn make_goal(_: &T) {} +fn tie(_: &T, _: &[Foo; 2]) {} fn main() { let a = 1; + // `?int: Trait` + make_goal(&a); + + // Deferred `Foo: Copy` requirement let b: [Foo<_>; 2] = [Foo(PhantomData); _]; - tie(&a, b); - let c = [NotCopy; _]; + tie(&a, &b); - // a is of type `?y` - // b is of type `[Foo; 2]` - // c is of type `[NotCopy; ?x]` - // there is a goal ?y: Trait` with two candidates: - // - `i32: Trait<2>`, ?y=i32 ?x=2 which requires `NotCopy: Copy` when expr checks happen - // - `u32: Trait<1>` ?y=u32 ?x=1 which doesnt require `NotCopy: Copy` - make_goal(&a, c); + // If fallback doesn't occur: + // - `Foo; 2`is > 1, needs copy + // - `Foo: Copy` infers `?int=u32` + // - stalled goal `?int: Trait` can now make progress and succeed - // final repeat expr checks: - // - // `Foo; 2` - // - Foo: Copy - // - requires ?y=u32 - // - // `NotCopy; ?x` - // - fails if fallback happens before repeat exprs as `i32: Trait` infers `?x=2` - // - succeeds if repeat expr checks happen first as `?y=u32` means `u32: Trait` - // infers `?x=1` + // If fallback occurs: + // - `Foo; 2` is > 1, needs copy + // - `Foo: Copy` doesn't hold -> error } diff --git a/tests/ui/repeat-expr/copy-check-inference-side-effects.rs b/tests/ui/repeat-expr/copy-check-inference-side-effects.rs new file mode 100644 index 000000000000..4e3bfdead26b --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-inference-side-effects.rs @@ -0,0 +1,34 @@ +#![feature(generic_arg_infer)] + +struct Foo; + +impl Clone for Foo<1> { + fn clone(&self) -> Self { + Self + } +} +impl Copy for Foo<1> {} + +fn unify(_: &[Foo; 2], _: &[String; N]) {} + +fn works_if_inference_side_effects() { + // This will only pass if inference side effects from proving `Foo: Copy` are + // able to be relied upon by other repeat expressions. + let a /* : [Foo; 2] */ = [Foo::<_>; 2]; + //~^ ERROR: type annotations needed for `[Foo<_>; 2]` + let b /* : [String; ?x] */ = ["string".to_string(); _]; + + unify(&a, &b); +} + +fn works_if_fixed_point() { + // This will only pass if the *second* array repeat expr is checked first + // allowing `Foo: Copy` to infer the array length of the first repeat expr. + let b /* : [String; ?x] */ = ["string".to_string(); _]; + //~^ ERROR: type annotations needed for `[String; _]` + let a /* : [Foo; 2] */ = [Foo::<_>; 2]; + + unify(&a, &b); +} + +fn main() {} diff --git a/tests/ui/repeat-expr/copy-check-inference-side-effects.stderr b/tests/ui/repeat-expr/copy-check-inference-side-effects.stderr new file mode 100644 index 000000000000..505beff0f6b2 --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-inference-side-effects.stderr @@ -0,0 +1,28 @@ +error[E0282]: type annotations needed for `[Foo<_>; 2]` + --> $DIR/copy-check-inference-side-effects.rs:17:9 + | +LL | let a /* : [Foo; 2] */ = [Foo::<_>; 2]; + | ^ +LL | +LL | let b /* : [String; ?x] */ = ["string".to_string(); _]; + | -------------------- type must be known at this point + | +help: consider giving `a` an explicit type, where the value of const parameter `N` is specified + | +LL | let a: [Foo; 2] /* : [Foo; 2] */ = [Foo::<_>; 2]; + | +++++++++++++ + +error[E0282]: type annotations needed for `[String; _]` + --> $DIR/copy-check-inference-side-effects.rs:27:9 + | +LL | let b /* : [String; ?x] */ = ["string".to_string(); _]; + | ^ -------------------- type must be known at this point + | +help: consider giving `b` an explicit type, where the value of const parameter `N` is specified + | +LL | let b: [_; N] /* : [String; ?x] */ = ["string".to_string(); _]; + | ++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/repeat-expr/copy-check-when-count-inferred-later.rs b/tests/ui/repeat-expr/copy-check-when-count-inferred-later.rs new file mode 100644 index 000000000000..b9d123cbefae --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-when-count-inferred-later.rs @@ -0,0 +1,11 @@ +#![feature(generic_arg_infer)] + +// Test that we enforce repeat expr element types are `Copy` even +// when the repeat count is only inferred at a later point in type +// checking. + +fn main() { + let a = [String::new(); _]; + //~^ ERROR: the trait bound `String: Copy` is not satisfied + let b: [_; 2] = a; +} diff --git a/tests/ui/repeat-expr/copy-check-when-count-inferred-later.stderr b/tests/ui/repeat-expr/copy-check-when-count-inferred-later.stderr new file mode 100644 index 000000000000..d974f5add505 --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-when-count-inferred-later.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/copy-check-when-count-inferred-later.rs:8:14 + | +LL | let a = [String::new(); _]; + | ^^^^^^^^^^^^^ + | | + | the trait `Copy` is not implemented for `String` + | help: create an inline `const` block: `const { String::new() }` + | + = note: the `Copy` trait is required because this value will be copied for each element of the array + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs index 0b0672d9c2b5..d50466ac4bbd 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs @@ -1,8 +1,3 @@ -//@revisions: current gai -//@[current] check-pass - -#![cfg_attr(gai, feature(generic_arg_infer))] - use std::marker::PhantomData; struct Foo(PhantomData); @@ -20,6 +15,6 @@ fn extract(_: [Foo; N]) -> T { fn main() { let x = [Foo(PhantomData); 2]; - //[gai]~^ ERROR: type annotations needed - _ = extract(x).max(2); + //~^ ERROR: type annotations needed + extract(x).max(2); } diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr new file mode 100644 index 000000000000..ba44beb76dbb --- /dev/null +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed for `[Foo<_>; 2]` + --> $DIR/copy-inference-side-effects-are-lazy.rs:17:9 + | +LL | let x = [Foo(PhantomData); 2]; + | ^ +LL | +LL | extract(x).max(2); + | ---------- type must be known at this point + | +help: consider giving `x` an explicit type, where the type for type parameter `T` is specified + | +LL | let x: [Foo; 2] = [Foo(PhantomData); 2]; + | +++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs new file mode 100644 index 000000000000..d2aa61186bcd --- /dev/null +++ b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs @@ -0,0 +1,15 @@ +struct Foo { + x: i32 +} + +impl Foo { + fn foo(&self) { + let _ = format!("{x}"); //~ ERROR cannot find value `x` in this scope [E0425] + let _ = format!("{x }"); //~ ERROR cannot find value `x` in this scope [E0425] + let _ = format!("{ x}"); //~ ERROR invalid format string: expected `}`, found `x` + let _ = format!("{}", x); //~ ERROR cannot find value `x` in this scope [E0425] + println!("{x}"); //~ ERROR cannot find value `x` in this scope [E0425] + } +} + +fn main(){} diff --git a/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr new file mode 100644 index 000000000000..0a84848081d5 --- /dev/null +++ b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr @@ -0,0 +1,48 @@ +error: invalid format string: expected `}`, found `x` + --> $DIR/sugg-field-in-format-string-issue-141136.rs:9:28 + | +LL | let _ = format!("{ x}"); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:7:27 + | +LL | let _ = format!("{x}"); + | ^ + | + = help: you might have meant to use the available field in a format string: `"{}", self.x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:8:27 + | +LL | let _ = format!("{x }"); + | ^^ + | + = help: you might have meant to use the available field in a format string: `"{}", self.x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:10:31 + | +LL | let _ = format!("{}", x); + | ^ + | +help: you might have meant to use the available field + | +LL | let _ = format!("{}", self.x); + | +++++ + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:11:20 + | +LL | println!("{x}"); + | ^ + | + = help: you might have meant to use the available field in a format string: `"{}", self.x` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/resolve/tuple-struct-alias.stderr b/tests/ui/resolve/tuple-struct-alias.stderr index a739ea43eed4..bf026a499b8c 100644 --- a/tests/ui/resolve/tuple-struct-alias.stderr +++ b/tests/ui/resolve/tuple-struct-alias.stderr @@ -6,8 +6,6 @@ LL | struct S(u8, u16); ... LL | A(..) => {} | ^ help: a tuple struct with a similar name exists: `S` - | - = note: can't use a type alias as a constructor error[E0423]: expected function, tuple struct or tuple variant, found type alias `A` --> $DIR/tuple-struct-alias.rs:5:13 @@ -16,9 +14,18 @@ LL | struct S(u8, u16); | ------------------ similarly named tuple struct `S` defined here ... LL | let s = A(0, 1); - | ^ help: a tuple struct with a similar name exists: `S` + | ^ + | +help: a tuple struct with a similar name exists + | +LL - let s = A(0, 1); +LL + let s = S(0, 1); + | +help: you might have meant to use `:` for type annotation + | +LL - let s = A(0, 1); +LL + let s: A(0, 1); | - = note: can't use a type alias as a constructor error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr index 5832cb69a3dd..1ecbfee17bc7 100644 --- a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr +++ b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr @@ -20,17 +20,9 @@ error[E0425]: cannot find value `config` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:15:20 | LL | println!("{config}"); - | ^^^^^^ - | -help: you might have meant to use the available field - | -LL | println!("{self.config}"); - | +++++ -help: a local variable with a similar name exists - | -LL - println!("{config}"); -LL + println!("{cofig}"); + | ^^^^^^ help: a local variable with a similar name exists: `cofig` | + = help: you might have meant to use the available field in a format string: `"{}", self.config` error[E0425]: cannot find value `bah` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:33:9 diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs new file mode 100644 index 000000000000..9edbc3243c71 --- /dev/null +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs @@ -0,0 +1,29 @@ +// issue-103476 +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ check-pass + +#![feature(if_let_guard)] +#![allow(irrefutable_let_patterns)] + +struct Pd; + +impl Pd { + fn it(&self) -> It { + todo!() + } +} + +pub struct It<'a>(Box>); + +trait Tr<'a> {} + +fn f(m: Option) { + match () { + () if let Some(n) = m && let it = n.it() => {} + _ => {} + } +} + +fn main() {} diff --git a/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr b/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr index a68d99c14cea..248d42ba3f43 100644 --- a/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr +++ b/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr @@ -2,7 +2,7 @@ error: unused extern crate --> $DIR/extern-crate-idiomatic-in-2018.rs:12:1 | LL | extern crate edition_lint_paths; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/extern-crate-idiomatic-in-2018.rs:9:9 @@ -10,6 +10,10 @@ note: the lint level is defined here LL | #![deny(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(unused_extern_crates)]` implied by `#[deny(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate edition_lint_paths; + | error: aborting due to 1 previous error diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs index 573942bd0955..467914d6a5e8 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs @@ -8,7 +8,7 @@ // The suggestion span should include the attribute. -#[cfg(not(FALSE))] //~ HELP remove it +#[cfg(not(FALSE))] //~ HELP remove extern crate edition_lint_paths; //~^ ERROR unused extern crate diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr index 038a9dd967b7..9efc3493d348 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr @@ -1,11 +1,8 @@ error: unused extern crate --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:12:1 | -LL | / #[cfg(not(FALSE))] -LL | | extern crate edition_lint_paths; - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | |________________________________| - | help: remove it +LL | extern crate edition_lint_paths; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:6:9 @@ -13,6 +10,11 @@ note: the lint level is defined here LL | #![deny(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(unused_extern_crates)]` implied by `#[deny(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - #[cfg(not(FALSE))] +LL - extern crate edition_lint_paths; + | error: aborting due to 1 previous error diff --git a/tests/ui/rust-2018/remove-extern-crate.stderr b/tests/ui/rust-2018/remove-extern-crate.stderr index cb090c621e9f..a530d40188ba 100644 --- a/tests/ui/rust-2018/remove-extern-crate.stderr +++ b/tests/ui/rust-2018/remove-extern-crate.stderr @@ -2,7 +2,7 @@ warning: unused extern crate --> $DIR/remove-extern-crate.rs:11:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/remove-extern-crate.rs:7:9 @@ -10,6 +10,11 @@ note: the lint level is defined here LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | warning: `extern crate` is not idiomatic in the new edition --> $DIR/remove-extern-crate.rs:35:5 diff --git a/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.fixed b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.fixed new file mode 100644 index 000000000000..26c1c9015da8 --- /dev/null +++ b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.fixed @@ -0,0 +1,15 @@ +//@ edition:2018 +//@ aux-build:../removing-extern-crate.rs +//@ run-rustfix + +#![warn(rust_2018_idioms)] + + //~ WARNING unused extern crate + //~ WARNING unused extern crate + +mod another { + //~ WARNING unused extern crate + //~ WARNING unused extern crate +} + +fn main() {} diff --git a/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.rs b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.rs new file mode 100644 index 000000000000..c5b629fa90b7 --- /dev/null +++ b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.rs @@ -0,0 +1,17 @@ +//@ edition:2018 +//@ aux-build:../removing-extern-crate.rs +//@ run-rustfix + +#![warn(rust_2018_idioms)] + +#[cfg_attr(test, "macro_use")] //~ ERROR expected +extern crate removing_extern_crate as foo; //~ WARNING unused extern crate +extern crate core; //~ WARNING unused extern crate + +mod another { + #[cfg_attr(test)] //~ ERROR expected + extern crate removing_extern_crate as foo; //~ WARNING unused extern crate + extern crate core; //~ WARNING unused extern crate +} + +fn main() {} diff --git a/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.stderr b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.stderr new file mode 100644 index 000000000000..0e834707bf9b --- /dev/null +++ b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.stderr @@ -0,0 +1,76 @@ +error: expected identifier, found `"macro_use"` + --> $DIR/removing-extern-crate-malformed-cfg.rs:7:18 + | +LL | #[cfg_attr(test, "macro_use")] + | ^^^^^^^^^^^ expected identifier + | + = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]` + = note: for more information, visit + +error: expected one of `(`, `,`, `::`, or `=`, found `` + --> $DIR/removing-extern-crate-malformed-cfg.rs:12:16 + | +LL | #[cfg_attr(test)] + | ^^^^ expected one of `(`, `,`, `::`, or `=` + | + = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]` + = note: for more information, visit + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:8:1 + | +LL | extern crate removing_extern_crate as foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +note: the lint level is defined here + --> $DIR/removing-extern-crate-malformed-cfg.rs:5:9 + | +LL | #![warn(rust_2018_idioms)] + | ^^^^^^^^^^^^^^^^ + = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - #[cfg_attr(test, "macro_use")] +LL - extern crate removing_extern_crate as foo; +LL + + | + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:9:1 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:13:5 + | +LL | extern crate removing_extern_crate as foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - #[cfg_attr(test)] +LL - extern crate removing_extern_crate as foo; +LL + + | + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:14:5 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | + +error: aborting due to 2 previous errors; 4 warnings emitted + diff --git a/tests/ui/rust-2018/removing-extern-crate.stderr b/tests/ui/rust-2018/removing-extern-crate.stderr index 573125426404..b6f8314ce4b6 100644 --- a/tests/ui/rust-2018/removing-extern-crate.stderr +++ b/tests/ui/rust-2018/removing-extern-crate.stderr @@ -2,7 +2,7 @@ warning: unused extern crate --> $DIR/removing-extern-crate.rs:8:1 | LL | extern crate dummy_crate as foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/removing-extern-crate.rs:6:9 @@ -10,24 +10,47 @@ note: the lint level is defined here LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate dummy_crate as foo; +LL + + | warning: unused extern crate --> $DIR/removing-extern-crate.rs:9:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | warning: unused extern crate --> $DIR/removing-extern-crate.rs:12:5 | LL | extern crate dummy_crate as foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate dummy_crate as foo; +LL + + | warning: unused extern crate --> $DIR/removing-extern-crate.rs:13:5 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | warning: 4 warnings emitted diff --git a/tests/ui/self/arbitrary-self-types-dyn-incompatible.rs b/tests/ui/self/arbitrary-self-types-dyn-incompatible.rs index 0477d9d79c77..b223f18327b1 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-incompatible.rs +++ b/tests/ui/self/arbitrary-self-types-dyn-incompatible.rs @@ -28,7 +28,6 @@ impl Bar for usize { fn make_foo() { let x = Rc::new(5usize) as Rc; //~^ ERROR E0038 - //~| ERROR E0038 } fn make_bar() { diff --git a/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr b/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr index 9fb4c80329d5..fe4802c9b3a4 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr +++ b/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr @@ -1,11 +1,11 @@ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/arbitrary-self-types-dyn-incompatible.rs:29:32 + --> $DIR/arbitrary-self-types-dyn-incompatible.rs:29:39 | LL | fn foo(self: &Rc) -> usize; | --------- help: consider changing method `foo`'s `self` parameter to be `&self`: `&Self` ... LL | let x = Rc::new(5usize) as Rc; - | ^^^^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -17,26 +17,6 @@ LL | fn foo(self: &Rc) -> usize; | ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on = help: only type `usize` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/arbitrary-self-types-dyn-incompatible.rs:29:13 - | -LL | fn foo(self: &Rc) -> usize; - | --------- help: consider changing method `foo`'s `self` parameter to be `&self`: `&Self` -... -LL | let x = Rc::new(5usize) as Rc; - | ^^^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/arbitrary-self-types-dyn-incompatible.rs:4:18 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | fn foo(self: &Rc) -> usize; - | ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on - = help: only type `usize` implements `Foo`; consider using it directly instead. - = note: required for the cast from `Rc` to `Rc` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.rs b/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.rs new file mode 100644 index 000000000000..af35d1e0359d --- /dev/null +++ b/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.rs @@ -0,0 +1,18 @@ +// Regression test for . + +use std::ops::Deref; + +struct W; + +trait Foo: Deref { + fn method(self: &W) {} + //~^ ERROR invalid `self` parameter type: `&W` +} + +fn test(x: &dyn Foo) { + //~^ ERROR the trait `Foo` is not dyn compatible + x.method(); + //~^ ERROR the trait `Foo` is not dyn compatible +} + +fn main() {} diff --git a/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.stderr b/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.stderr new file mode 100644 index 000000000000..237bbc567151 --- /dev/null +++ b/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.stderr @@ -0,0 +1,49 @@ +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:12:13 + | +LL | fn method(self: &W) {} + | -- help: consider changing method `method`'s `self` parameter to be `&self`: `&Self` +... +LL | fn test(x: &dyn Foo) { + | ^^^^^^^ `Foo` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:8:21 + | +LL | trait Foo: Deref { + | --- this trait is not dyn compatible... +LL | fn method(self: &W) {} + | ^^ ...because method `method`'s `self` parameter cannot be dispatched on + +error[E0307]: invalid `self` parameter type: `&W` + --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:8:21 + | +LL | fn method(self: &W) {} + | ^^ + | + = 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` is not dyn compatible + --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:14:5 + | +LL | fn method(self: &W) {} + | -- help: consider changing method `method`'s `self` parameter to be `&self`: `&Self` +... +LL | x.method(); + | ^^^^^^^^^^ `Foo` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:8:21 + | +LL | trait Foo: Deref { + | --- this trait is not dyn compatible... +LL | fn method(self: &W) {} + | ^^ ...because method `method`'s `self` parameter cannot be dispatched on + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0038, E0307. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/self/self-impl.stderr b/tests/ui/self/self-impl.stderr index 18ffd15427f8..1bda307d0eb3 100644 --- a/tests/ui/self/self-impl.stderr +++ b/tests/ui/self/self-impl.stderr @@ -2,13 +2,25 @@ error[E0223]: ambiguous associated type --> $DIR/self-impl.rs:23:16 | LL | let _: ::Baz = true; - | ^^^^^^^^^^^ help: use fully-qualified syntax: `::Baz` + | ^^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: ::Baz = true; +LL + let _: ::Baz = true; + | error[E0223]: ambiguous associated type --> $DIR/self-impl.rs:25:16 | LL | let _: Self::Baz = true; - | ^^^^^^^^^ help: use fully-qualified syntax: `::Baz` + | ^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: Self::Baz = true; +LL + let _: ::Baz = true; + | error: aborting due to 2 previous errors diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.rs b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs index fd07937d90f2..14ba38d75356 100644 --- a/tests/ui/statics/unsizing-wfcheck-issue-127299.rs +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs @@ -12,6 +12,5 @@ pub struct Lint { static FOO: &Lint = &Lint { desc: "desc" }; //~^ ERROR cannot be shared between threads safely //~| ERROR is not dyn compatible -//~| ERROR is not dyn compatible fn main() {} diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr index 28427161e870..e401277a0209 100644 --- a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr @@ -51,7 +51,6 @@ LL | trait Qux { | --- this trait is not dyn compatible... LL | fn bar() -> i32; | ^^^ ...because associated function `bar` has no `self` parameter - = note: required for the cast from `&'static str` to `&'static (dyn Qux + 'static)` help: consider turning `bar` into a method by giving it a `&self` argument | LL | fn bar(&self) -> i32; @@ -61,30 +60,7 @@ help: alternatively, consider constraining `bar` so it does not apply to trait o LL | fn bar() -> i32 where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qux` is not dyn compatible - --> $DIR/unsizing-wfcheck-issue-127299.rs:12:35 - | -LL | static FOO: &Lint = &Lint { desc: "desc" }; - | ^^^^^^ `Qux` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 - | -LL | trait Qux { - | --- this trait is not dyn compatible... -LL | fn bar() -> i32; - | ^^^ ...because associated function `bar` has no `self` parameter -help: consider turning `bar` into a method by giving it a `&self` argument - | -LL | fn bar(&self) -> i32; - | +++++ -help: alternatively, consider constraining `bar` so it does not apply to trait objects - | -LL | fn bar() -> i32 where Self: Sized; - | +++++++++++++++++ - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0038, E0277. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/stats/input-stats.rs b/tests/ui/stats/input-stats.rs index f19a53cc610b..e760e2894e31 100644 --- a/tests/ui/stats/input-stats.rs +++ b/tests/ui/stats/input-stats.rs @@ -3,6 +3,10 @@ //@ only-64bit // layout randomization affects the hir stat output //@ needs-deterministic-layouts +// +// Filter out the percentages because a change to a single count can affect +// many or all percentages, which makes the diffs hard to read. +//@ normalize-stderr: "\([0-9 ][0-9]\.[0-9]%\)" -> "(NN.N%)" // Type layouts sometimes change. When that happens, until the next bootstrap // bump occurs, stage1 and stage2 will give different outputs for this test. diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index b369af62f876..58ab1a72556f 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -1,178 +1,178 @@ ast-stats-1 PRE EXPANSION AST STATS ast-stats-1 Name Accumulated Size Count Item Size ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Crate 40 ( 0.6%) 1 40 -ast-stats-1 GenericArgs 40 ( 0.6%) 1 40 -ast-stats-1 - AngleBracketed 40 ( 0.6%) 1 -ast-stats-1 ExprField 48 ( 0.7%) 1 48 -ast-stats-1 Attribute 64 ( 0.9%) 2 32 -ast-stats-1 - DocComment 32 ( 0.5%) 1 -ast-stats-1 - Normal 32 ( 0.5%) 1 -ast-stats-1 WherePredicate 72 ( 1.1%) 1 72 -ast-stats-1 - BoundPredicate 72 ( 1.1%) 1 -ast-stats-1 ForeignItem 80 ( 1.2%) 1 80 -ast-stats-1 - Fn 80 ( 1.2%) 1 -ast-stats-1 Arm 96 ( 1.4%) 2 48 -ast-stats-1 Local 96 ( 1.4%) 1 96 -ast-stats-1 FnDecl 120 ( 1.8%) 5 24 -ast-stats-1 Param 160 ( 2.4%) 4 40 -ast-stats-1 Stmt 160 ( 2.4%) 5 32 -ast-stats-1 - Let 32 ( 0.5%) 1 -ast-stats-1 - MacCall 32 ( 0.5%) 1 -ast-stats-1 - Expr 96 ( 1.4%) 3 -ast-stats-1 Block 192 ( 2.8%) 6 32 -ast-stats-1 FieldDef 208 ( 3.1%) 2 104 -ast-stats-1 Variant 208 ( 3.1%) 2 104 -ast-stats-1 AssocItem 320 ( 4.7%) 4 80 -ast-stats-1 - Fn 160 ( 2.4%) 2 -ast-stats-1 - Type 160 ( 2.4%) 2 -ast-stats-1 GenericBound 352 ( 5.2%) 4 88 -ast-stats-1 - Trait 352 ( 5.2%) 4 -ast-stats-1 GenericParam 480 ( 7.1%) 5 96 -ast-stats-1 Pat 504 ( 7.5%) 7 72 -ast-stats-1 - Struct 72 ( 1.1%) 1 -ast-stats-1 - Wild 72 ( 1.1%) 1 -ast-stats-1 - Ident 360 ( 5.3%) 5 -ast-stats-1 Expr 576 ( 8.5%) 8 72 -ast-stats-1 - Match 72 ( 1.1%) 1 -ast-stats-1 - Path 72 ( 1.1%) 1 -ast-stats-1 - Struct 72 ( 1.1%) 1 -ast-stats-1 - Lit 144 ( 2.1%) 2 -ast-stats-1 - Block 216 ( 3.2%) 3 -ast-stats-1 PathSegment 744 (11.0%) 31 24 -ast-stats-1 Ty 896 (13.3%) 14 64 -ast-stats-1 - Ptr 64 ( 0.9%) 1 -ast-stats-1 - Ref 64 ( 0.9%) 1 -ast-stats-1 - ImplicitSelf 128 ( 1.9%) 2 -ast-stats-1 - Path 640 ( 9.5%) 10 -ast-stats-1 Item 1_296 (19.2%) 9 144 -ast-stats-1 - Enum 144 ( 2.1%) 1 -ast-stats-1 - ForeignMod 144 ( 2.1%) 1 -ast-stats-1 - Impl 144 ( 2.1%) 1 -ast-stats-1 - Trait 144 ( 2.1%) 1 -ast-stats-1 - Fn 288 ( 4.3%) 2 -ast-stats-1 - Use 432 ( 6.4%) 3 +ast-stats-1 Crate 40 (NN.N%) 1 40 +ast-stats-1 GenericArgs 40 (NN.N%) 1 40 +ast-stats-1 - AngleBracketed 40 (NN.N%) 1 +ast-stats-1 ExprField 48 (NN.N%) 1 48 +ast-stats-1 Attribute 64 (NN.N%) 2 32 +ast-stats-1 - DocComment 32 (NN.N%) 1 +ast-stats-1 - Normal 32 (NN.N%) 1 +ast-stats-1 WherePredicate 72 (NN.N%) 1 72 +ast-stats-1 - BoundPredicate 72 (NN.N%) 1 +ast-stats-1 ForeignItem 80 (NN.N%) 1 80 +ast-stats-1 - Fn 80 (NN.N%) 1 +ast-stats-1 Arm 96 (NN.N%) 2 48 +ast-stats-1 Local 96 (NN.N%) 1 96 +ast-stats-1 FnDecl 120 (NN.N%) 5 24 +ast-stats-1 Param 160 (NN.N%) 4 40 +ast-stats-1 Stmt 160 (NN.N%) 5 32 +ast-stats-1 - Let 32 (NN.N%) 1 +ast-stats-1 - MacCall 32 (NN.N%) 1 +ast-stats-1 - Expr 96 (NN.N%) 3 +ast-stats-1 Block 192 (NN.N%) 6 32 +ast-stats-1 FieldDef 208 (NN.N%) 2 104 +ast-stats-1 Variant 208 (NN.N%) 2 104 +ast-stats-1 AssocItem 320 (NN.N%) 4 80 +ast-stats-1 - Fn 160 (NN.N%) 2 +ast-stats-1 - Type 160 (NN.N%) 2 +ast-stats-1 GenericBound 352 (NN.N%) 4 88 +ast-stats-1 - Trait 352 (NN.N%) 4 +ast-stats-1 GenericParam 480 (NN.N%) 5 96 +ast-stats-1 Pat 504 (NN.N%) 7 72 +ast-stats-1 - Struct 72 (NN.N%) 1 +ast-stats-1 - Wild 72 (NN.N%) 1 +ast-stats-1 - Ident 360 (NN.N%) 5 +ast-stats-1 Expr 576 (NN.N%) 8 72 +ast-stats-1 - Match 72 (NN.N%) 1 +ast-stats-1 - Path 72 (NN.N%) 1 +ast-stats-1 - Struct 72 (NN.N%) 1 +ast-stats-1 - Lit 144 (NN.N%) 2 +ast-stats-1 - Block 216 (NN.N%) 3 +ast-stats-1 PathSegment 744 (NN.N%) 31 24 +ast-stats-1 Ty 896 (NN.N%) 14 64 +ast-stats-1 - Ptr 64 (NN.N%) 1 +ast-stats-1 - Ref 64 (NN.N%) 1 +ast-stats-1 - ImplicitSelf 128 (NN.N%) 2 +ast-stats-1 - Path 640 (NN.N%) 10 +ast-stats-1 Item 1_296 (NN.N%) 9 144 +ast-stats-1 - Enum 144 (NN.N%) 1 +ast-stats-1 - ForeignMod 144 (NN.N%) 1 +ast-stats-1 - Impl 144 (NN.N%) 1 +ast-stats-1 - Trait 144 (NN.N%) 1 +ast-stats-1 - Fn 288 (NN.N%) 2 +ast-stats-1 - Use 432 (NN.N%) 3 ast-stats-1 ---------------------------------------------------------------- ast-stats-1 Total 6_752 116 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Crate 40 ( 0.5%) 1 40 -ast-stats-2 GenericArgs 40 ( 0.5%) 1 40 -ast-stats-2 - AngleBracketed 40 ( 0.5%) 1 -ast-stats-2 ExprField 48 ( 0.6%) 1 48 -ast-stats-2 WherePredicate 72 ( 1.0%) 1 72 -ast-stats-2 - BoundPredicate 72 ( 1.0%) 1 -ast-stats-2 ForeignItem 80 ( 1.1%) 1 80 -ast-stats-2 - Fn 80 ( 1.1%) 1 -ast-stats-2 Arm 96 ( 1.3%) 2 48 -ast-stats-2 Local 96 ( 1.3%) 1 96 -ast-stats-2 FnDecl 120 ( 1.6%) 5 24 -ast-stats-2 InlineAsm 120 ( 1.6%) 1 120 -ast-stats-2 Attribute 128 ( 1.7%) 4 32 -ast-stats-2 - DocComment 32 ( 0.4%) 1 -ast-stats-2 - Normal 96 ( 1.3%) 3 -ast-stats-2 Param 160 ( 2.2%) 4 40 -ast-stats-2 Stmt 160 ( 2.2%) 5 32 -ast-stats-2 - Let 32 ( 0.4%) 1 -ast-stats-2 - Semi 32 ( 0.4%) 1 -ast-stats-2 - Expr 96 ( 1.3%) 3 -ast-stats-2 Block 192 ( 2.6%) 6 32 -ast-stats-2 FieldDef 208 ( 2.8%) 2 104 -ast-stats-2 Variant 208 ( 2.8%) 2 104 -ast-stats-2 AssocItem 320 ( 4.3%) 4 80 -ast-stats-2 - Fn 160 ( 2.2%) 2 -ast-stats-2 - Type 160 ( 2.2%) 2 -ast-stats-2 GenericBound 352 ( 4.7%) 4 88 -ast-stats-2 - Trait 352 ( 4.7%) 4 -ast-stats-2 GenericParam 480 ( 6.5%) 5 96 -ast-stats-2 Pat 504 ( 6.8%) 7 72 -ast-stats-2 - Struct 72 ( 1.0%) 1 -ast-stats-2 - Wild 72 ( 1.0%) 1 -ast-stats-2 - Ident 360 ( 4.9%) 5 -ast-stats-2 Expr 648 ( 8.7%) 9 72 -ast-stats-2 - InlineAsm 72 ( 1.0%) 1 -ast-stats-2 - Match 72 ( 1.0%) 1 -ast-stats-2 - Path 72 ( 1.0%) 1 -ast-stats-2 - Struct 72 ( 1.0%) 1 -ast-stats-2 - Lit 144 ( 1.9%) 2 -ast-stats-2 - Block 216 ( 2.9%) 3 -ast-stats-2 PathSegment 864 (11.7%) 36 24 -ast-stats-2 Ty 896 (12.1%) 14 64 -ast-stats-2 - Ptr 64 ( 0.9%) 1 -ast-stats-2 - Ref 64 ( 0.9%) 1 -ast-stats-2 - ImplicitSelf 128 ( 1.7%) 2 -ast-stats-2 - Path 640 ( 8.6%) 10 -ast-stats-2 Item 1_584 (21.4%) 11 144 -ast-stats-2 - Enum 144 ( 1.9%) 1 -ast-stats-2 - ExternCrate 144 ( 1.9%) 1 -ast-stats-2 - ForeignMod 144 ( 1.9%) 1 -ast-stats-2 - Impl 144 ( 1.9%) 1 -ast-stats-2 - Trait 144 ( 1.9%) 1 -ast-stats-2 - Fn 288 ( 3.9%) 2 -ast-stats-2 - Use 576 ( 7.8%) 4 +ast-stats-2 Crate 40 (NN.N%) 1 40 +ast-stats-2 GenericArgs 40 (NN.N%) 1 40 +ast-stats-2 - AngleBracketed 40 (NN.N%) 1 +ast-stats-2 ExprField 48 (NN.N%) 1 48 +ast-stats-2 WherePredicate 72 (NN.N%) 1 72 +ast-stats-2 - BoundPredicate 72 (NN.N%) 1 +ast-stats-2 ForeignItem 80 (NN.N%) 1 80 +ast-stats-2 - Fn 80 (NN.N%) 1 +ast-stats-2 Arm 96 (NN.N%) 2 48 +ast-stats-2 Local 96 (NN.N%) 1 96 +ast-stats-2 FnDecl 120 (NN.N%) 5 24 +ast-stats-2 InlineAsm 120 (NN.N%) 1 120 +ast-stats-2 Attribute 128 (NN.N%) 4 32 +ast-stats-2 - DocComment 32 (NN.N%) 1 +ast-stats-2 - Normal 96 (NN.N%) 3 +ast-stats-2 Param 160 (NN.N%) 4 40 +ast-stats-2 Stmt 160 (NN.N%) 5 32 +ast-stats-2 - Let 32 (NN.N%) 1 +ast-stats-2 - Semi 32 (NN.N%) 1 +ast-stats-2 - Expr 96 (NN.N%) 3 +ast-stats-2 Block 192 (NN.N%) 6 32 +ast-stats-2 FieldDef 208 (NN.N%) 2 104 +ast-stats-2 Variant 208 (NN.N%) 2 104 +ast-stats-2 AssocItem 320 (NN.N%) 4 80 +ast-stats-2 - Fn 160 (NN.N%) 2 +ast-stats-2 - Type 160 (NN.N%) 2 +ast-stats-2 GenericBound 352 (NN.N%) 4 88 +ast-stats-2 - Trait 352 (NN.N%) 4 +ast-stats-2 GenericParam 480 (NN.N%) 5 96 +ast-stats-2 Pat 504 (NN.N%) 7 72 +ast-stats-2 - Struct 72 (NN.N%) 1 +ast-stats-2 - Wild 72 (NN.N%) 1 +ast-stats-2 - Ident 360 (NN.N%) 5 +ast-stats-2 Expr 648 (NN.N%) 9 72 +ast-stats-2 - InlineAsm 72 (NN.N%) 1 +ast-stats-2 - Match 72 (NN.N%) 1 +ast-stats-2 - Path 72 (NN.N%) 1 +ast-stats-2 - Struct 72 (NN.N%) 1 +ast-stats-2 - Lit 144 (NN.N%) 2 +ast-stats-2 - Block 216 (NN.N%) 3 +ast-stats-2 PathSegment 864 (NN.N%) 36 24 +ast-stats-2 Ty 896 (NN.N%) 14 64 +ast-stats-2 - Ptr 64 (NN.N%) 1 +ast-stats-2 - Ref 64 (NN.N%) 1 +ast-stats-2 - ImplicitSelf 128 (NN.N%) 2 +ast-stats-2 - Path 640 (NN.N%) 10 +ast-stats-2 Item 1_584 (NN.N%) 11 144 +ast-stats-2 - Enum 144 (NN.N%) 1 +ast-stats-2 - ExternCrate 144 (NN.N%) 1 +ast-stats-2 - ForeignMod 144 (NN.N%) 1 +ast-stats-2 - Impl 144 (NN.N%) 1 +ast-stats-2 - Trait 144 (NN.N%) 1 +ast-stats-2 - Fn 288 (NN.N%) 2 +ast-stats-2 - Use 576 (NN.N%) 4 ast-stats-2 ---------------------------------------------------------------- ast-stats-2 Total 7_416 127 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size hir-stats ---------------------------------------------------------------- -hir-stats ForeignItemRef 24 ( 0.3%) 1 24 -hir-stats Lifetime 28 ( 0.3%) 1 28 -hir-stats Mod 32 ( 0.4%) 1 32 -hir-stats ExprField 40 ( 0.4%) 1 40 -hir-stats TraitItemRef 56 ( 0.6%) 2 28 -hir-stats GenericArg 64 ( 0.7%) 4 16 -hir-stats - Type 16 ( 0.2%) 1 -hir-stats - Lifetime 48 ( 0.5%) 3 -hir-stats Param 64 ( 0.7%) 2 32 -hir-stats Body 72 ( 0.8%) 3 24 -hir-stats ImplItemRef 72 ( 0.8%) 2 36 -hir-stats InlineAsm 72 ( 0.8%) 1 72 -hir-stats Local 72 ( 0.8%) 1 72 -hir-stats WherePredicate 72 ( 0.8%) 3 24 -hir-stats - BoundPredicate 72 ( 0.8%) 3 -hir-stats Arm 80 ( 0.9%) 2 40 -hir-stats Stmt 96 ( 1.1%) 3 32 -hir-stats - Expr 32 ( 0.4%) 1 -hir-stats - Let 32 ( 0.4%) 1 -hir-stats - Semi 32 ( 0.4%) 1 -hir-stats FnDecl 120 ( 1.3%) 3 40 -hir-stats Attribute 128 ( 1.4%) 4 32 -hir-stats FieldDef 128 ( 1.4%) 2 64 -hir-stats GenericArgs 144 ( 1.6%) 3 48 -hir-stats Variant 144 ( 1.6%) 2 72 -hir-stats GenericBound 256 ( 2.8%) 4 64 -hir-stats - Trait 256 ( 2.8%) 4 -hir-stats Block 288 ( 3.2%) 6 48 -hir-stats Pat 360 ( 4.0%) 5 72 -hir-stats - Struct 72 ( 0.8%) 1 -hir-stats - Wild 72 ( 0.8%) 1 -hir-stats - Binding 216 ( 2.4%) 3 -hir-stats GenericParam 400 ( 4.5%) 5 80 -hir-stats Generics 560 ( 6.2%) 10 56 -hir-stats Ty 720 ( 8.0%) 15 48 -hir-stats - Ptr 48 ( 0.5%) 1 -hir-stats - Ref 48 ( 0.5%) 1 -hir-stats - Path 624 ( 6.9%) 13 -hir-stats Expr 768 ( 8.5%) 12 64 -hir-stats - InlineAsm 64 ( 0.7%) 1 -hir-stats - Match 64 ( 0.7%) 1 -hir-stats - Path 64 ( 0.7%) 1 -hir-stats - Struct 64 ( 0.7%) 1 -hir-stats - Lit 128 ( 1.4%) 2 -hir-stats - Block 384 ( 4.3%) 6 -hir-stats Item 968 (10.8%) 11 88 -hir-stats - Enum 88 ( 1.0%) 1 -hir-stats - ExternCrate 88 ( 1.0%) 1 -hir-stats - ForeignMod 88 ( 1.0%) 1 -hir-stats - Impl 88 ( 1.0%) 1 -hir-stats - Trait 88 ( 1.0%) 1 -hir-stats - Fn 176 ( 2.0%) 2 -hir-stats - Use 352 ( 3.9%) 4 -hir-stats Path 1_240 (13.8%) 31 40 -hir-stats PathSegment 1_920 (21.4%) 40 48 +hir-stats ForeignItemRef 24 (NN.N%) 1 24 +hir-stats Lifetime 28 (NN.N%) 1 28 +hir-stats Mod 32 (NN.N%) 1 32 +hir-stats ExprField 40 (NN.N%) 1 40 +hir-stats TraitItemRef 56 (NN.N%) 2 28 +hir-stats GenericArg 64 (NN.N%) 4 16 +hir-stats - Type 16 (NN.N%) 1 +hir-stats - Lifetime 48 (NN.N%) 3 +hir-stats Param 64 (NN.N%) 2 32 +hir-stats Body 72 (NN.N%) 3 24 +hir-stats ImplItemRef 72 (NN.N%) 2 36 +hir-stats InlineAsm 72 (NN.N%) 1 72 +hir-stats Local 72 (NN.N%) 1 72 +hir-stats WherePredicate 72 (NN.N%) 3 24 +hir-stats - BoundPredicate 72 (NN.N%) 3 +hir-stats Arm 80 (NN.N%) 2 40 +hir-stats Stmt 96 (NN.N%) 3 32 +hir-stats - Expr 32 (NN.N%) 1 +hir-stats - Let 32 (NN.N%) 1 +hir-stats - Semi 32 (NN.N%) 1 +hir-stats FnDecl 120 (NN.N%) 3 40 +hir-stats Attribute 128 (NN.N%) 4 32 +hir-stats FieldDef 128 (NN.N%) 2 64 +hir-stats GenericArgs 144 (NN.N%) 3 48 +hir-stats Variant 144 (NN.N%) 2 72 +hir-stats GenericBound 256 (NN.N%) 4 64 +hir-stats - Trait 256 (NN.N%) 4 +hir-stats Block 288 (NN.N%) 6 48 +hir-stats Pat 360 (NN.N%) 5 72 +hir-stats - Struct 72 (NN.N%) 1 +hir-stats - Wild 72 (NN.N%) 1 +hir-stats - Binding 216 (NN.N%) 3 +hir-stats GenericParam 400 (NN.N%) 5 80 +hir-stats Generics 560 (NN.N%) 10 56 +hir-stats Ty 720 (NN.N%) 15 48 +hir-stats - Ptr 48 (NN.N%) 1 +hir-stats - Ref 48 (NN.N%) 1 +hir-stats - Path 624 (NN.N%) 13 +hir-stats Expr 768 (NN.N%) 12 64 +hir-stats - InlineAsm 64 (NN.N%) 1 +hir-stats - Match 64 (NN.N%) 1 +hir-stats - Path 64 (NN.N%) 1 +hir-stats - Struct 64 (NN.N%) 1 +hir-stats - Lit 128 (NN.N%) 2 +hir-stats - Block 384 (NN.N%) 6 +hir-stats Item 968 (NN.N%) 11 88 +hir-stats - Enum 88 (NN.N%) 1 +hir-stats - ExternCrate 88 (NN.N%) 1 +hir-stats - ForeignMod 88 (NN.N%) 1 +hir-stats - Impl 88 (NN.N%) 1 +hir-stats - Trait 88 (NN.N%) 1 +hir-stats - Fn 176 (NN.N%) 2 +hir-stats - Use 352 (NN.N%) 4 +hir-stats Path 1_040 (NN.N%) 26 40 +hir-stats PathSegment 1_776 (NN.N%) 37 48 hir-stats ---------------------------------------------------------------- -hir-stats Total 8_988 180 +hir-stats Total 8_644 172 hir-stats diff --git a/tests/ui/bare-static-string.rs b/tests/ui/str/str-static-literal.rs similarity index 51% rename from tests/ui/bare-static-string.rs rename to tests/ui/str/str-static-literal.rs index b71cf38cfe81..61630f0f22bd 100644 --- a/tests/ui/bare-static-string.rs +++ b/tests/ui/str/str-static-literal.rs @@ -1,3 +1,5 @@ +//! Check that a bare string literal is typed as a `&'static str` and is usable. + //@ run-pass pub fn main() { diff --git a/tests/ui/structs/struct-path-associated-type.stderr b/tests/ui/structs/struct-path-associated-type.stderr index de396e875b0c..110362293ac3 100644 --- a/tests/ui/structs/struct-path-associated-type.stderr +++ b/tests/ui/structs/struct-path-associated-type.stderr @@ -48,19 +48,37 @@ error[E0223]: ambiguous associated type --> $DIR/struct-path-associated-type.rs:32:13 | LL | let s = S::A {}; - | ^^^^ help: use fully-qualified syntax: `::A` + | ^^^^ + | +help: use fully-qualified syntax + | +LL - let s = S::A {}; +LL + let s = ::A {}; + | error[E0223]: ambiguous associated type --> $DIR/struct-path-associated-type.rs:33:13 | LL | let z = S::A:: {}; - | ^^^^ help: use fully-qualified syntax: `::A` + | ^^^^ + | +help: use fully-qualified syntax + | +LL - let z = S::A:: {}; +LL + let z = ::A:: {}; + | error[E0223]: ambiguous associated type --> $DIR/struct-path-associated-type.rs:35:9 | LL | S::A {} => {} - | ^^^^ help: use fully-qualified syntax: `::A` + | ^^^^ + | +help: use fully-qualified syntax + | +LL - S::A {} => {} +LL + ::A {} => {} + | error: aborting due to 8 previous errors diff --git a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr index 09a9b1d3b348..eadf512a63b4 100644 --- a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -20,11 +20,11 @@ LL | let fp = BufWriter::new(fp); note: required by a bound in `BufWriter` --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL -error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn Write>`, but its trait bounds were not satisfied +error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn std::io::Write>`, but its trait bounds were not satisfied --> $DIR/mut-borrow-needed-by-trait.rs:21:14 | LL | writeln!(fp, "hello world").unwrap(); - | ---------^^---------------- method cannot be called on `BufWriter<&dyn Write>` due to unsatisfied trait bounds + | ---------^^---------------- method cannot be called on `BufWriter<&dyn std::io::Write>` due to unsatisfied trait bounds | note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method --> $DIR/mut-borrow-needed-by-trait.rs:21:14 diff --git a/tests/ui/suggestions/suggest-using-chars.stderr b/tests/ui/suggestions/suggest-using-chars.stderr index a197223beb05..20a162f8bfe8 100644 --- a/tests/ui/suggestions/suggest-using-chars.stderr +++ b/tests/ui/suggestions/suggest-using-chars.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `iter` found for reference `&'static str` in the c --> $DIR/suggest-using-chars.rs:2:19 | LL | let _ = "foo".iter(); - | ^^^^ method not found in `&str` + | ^^^^ method not found in `&'static str` | help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars` | @@ -14,7 +14,7 @@ error[E0599]: no method named `foo` found for reference `&'static str` in the cu --> $DIR/suggest-using-chars.rs:3:19 | LL | let _ = "foo".foo(); - | ^^^ method not found in `&str` + | ^^^ method not found in `&'static str` error[E0599]: no method named `iter` found for struct `String` in the current scope --> $DIR/suggest-using-chars.rs:4:33 diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.rs b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.rs new file mode 100644 index 000000000000..270874a9f581 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.rs @@ -0,0 +1,14 @@ +//@ compile-flags: --crate-type=lib +//@ compile-flags: --target=aarch64-unknown-none-softfloat +//@ needs-llvm-components: aarch64 +#![feature(no_core, lang_items)] +#![no_core] +#![deny(aarch64_softfloat_neon)] + +#[lang = "sized"] +pub trait Sized {} + +#[target_feature(enable = "neon")] +//~^ERROR: enabling the `neon` target feature on the current target is unsound +//~|WARN: previously accepted +pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.stderr new file mode 100644 index 000000000000..bf745291a5a8 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute-fcw.stderr @@ -0,0 +1,31 @@ +error: enabling the `neon` target feature on the current target is unsound due to ABI issues + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:18 + | +LL | #[target_feature(enable = "neon")] + | ^^^^^^^^^^^^^^^ + | + = 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 #134375 +note: the lint level is defined here + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:6:9 + | +LL | #![deny(aarch64_softfloat_neon)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +Future incompatibility report: Future breakage diagnostic: +error: enabling the `neon` target feature on the current target is unsound due to ABI issues + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:18 + | +LL | #[target_feature(enable = "neon")] + | ^^^^^^^^^^^^^^^ + | + = 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 #134375 +note: the lint level is defined here + --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:6:9 + | +LL | #![deny(aarch64_softfloat_neon)] + | ^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/ui/threads-sendsync/tls-dont-move-after-init.rs b/tests/ui/threads-sendsync/tls-dont-move-after-init.rs new file mode 100644 index 000000000000..54fcc32e9bd7 --- /dev/null +++ b/tests/ui/threads-sendsync/tls-dont-move-after-init.rs @@ -0,0 +1,37 @@ +//@ run-pass +//@ needs-threads + +use std::cell::Cell; +use std::thread; + +#[derive(Default)] +struct Foo { + ptr: Cell<*const Foo>, +} + +impl Foo { + fn touch(&self) { + if self.ptr.get().is_null() { + self.ptr.set(self); + } else { + assert!(self.ptr.get() == self); + } + } +} + +impl Drop for Foo { + fn drop(&mut self) { + self.touch(); + } +} + +thread_local!(static FOO: Foo = Foo::default()); + +fn main() { + thread::spawn(|| { + FOO.with(|foo| foo.touch()); + FOO.with(|foo| foo.touch()); + }) + .join() + .unwrap(); +} diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index cdf577287eec..4756c490cb10 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -71,14 +71,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -88,14 +83,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn const_context() { | @@ -105,14 +95,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -122,14 +107,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn const_context() { | @@ -139,14 +119,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -156,14 +131,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn const_context() { | @@ -173,14 +143,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | @@ -190,14 +155,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | @@ -207,14 +167,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn stable_const_context() { | @@ -224,14 +179,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | @@ -241,14 +191,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn stable_const_context() { | @@ -259,14 +204,9 @@ LL | const_context_not_const_stable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local_feature)] LL | const fn stable_const_context() { | @@ -276,14 +216,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | @@ -293,14 +228,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { | @@ -310,14 +240,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn implicitly_stable_const_context() { | @@ -327,14 +252,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { | @@ -344,14 +264,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | Foo::func(); | ^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(unstable)] LL | const fn implicitly_stable_const_context() { | @@ -362,14 +277,9 @@ LL | const_context_not_const_stable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(local_feature)] LL | const fn implicitly_stable_const_context() { | @@ -379,14 +289,9 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` | LL + #[rustc_const_unstable(feature = "...", issue = "...")] -LL | const fn implicitly_stable_const_context() { - | -help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) - | -LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { | diff --git a/tests/ui/traits/issue-20692.rs b/tests/ui/traits/issue-20692.rs index 10611a232f71..79edc389ec4a 100644 --- a/tests/ui/traits/issue-20692.rs +++ b/tests/ui/traits/issue-20692.rs @@ -2,7 +2,6 @@ trait Array: Sized + Copy {} fn f(x: &T) { let _ = x - //~^ ERROR `Array` is not dyn compatible as &dyn Array; //~^ ERROR `Array` is not dyn compatible diff --git a/tests/ui/traits/issue-20692.stderr b/tests/ui/traits/issue-20692.stderr index 32e29de49a11..d4a18899dcbb 100644 --- a/tests/ui/traits/issue-20692.stderr +++ b/tests/ui/traits/issue-20692.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Array` is not dyn compatible - --> $DIR/issue-20692.rs:7:5 + --> $DIR/issue-20692.rs:6:10 | LL | &dyn Array; - | ^^^^^^^^^^ `Array` is not dyn compatible + | ^^^^^ `Array` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -14,23 +14,6 @@ LL | trait Array: Sized + Copy {} | | ...because it requires `Self: Sized` | this trait is not dyn compatible... -error[E0038]: the trait `Array` is not dyn compatible - --> $DIR/issue-20692.rs:4:13 - | -LL | let _ = x - | ^ `Array` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-20692.rs:1:14 - | -LL | trait Array: Sized + Copy {} - | ----- ^^^^^ ^^^^ ...because it requires `Self: Sized` - | | | - | | ...because it requires `Self: Sized` - | this trait is not dyn compatible... - = note: required for the cast from `&T` to `&dyn Array` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/issue-38604.rs b/tests/ui/traits/issue-38604.rs index d90aa61ef9f3..70f0ef0cb9cb 100644 --- a/tests/ui/traits/issue-38604.rs +++ b/tests/ui/traits/issue-38604.rs @@ -12,5 +12,5 @@ impl Foo for () { fn main() { let _f: Box = //~ ERROR `Foo` is not dyn compatible - Box::new(()); //~ ERROR `Foo` is not dyn compatible + Box::new(()); } diff --git a/tests/ui/traits/issue-38604.stderr b/tests/ui/traits/issue-38604.stderr index e6a6b44e7304..0f39dc536eb8 100644 --- a/tests/ui/traits/issue-38604.stderr +++ b/tests/ui/traits/issue-38604.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/issue-38604.rs:14:13 + --> $DIR/issue-38604.rs:14:21 | LL | let _f: Box = - | ^^^^^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -14,23 +14,6 @@ LL | trait Foo where u32: Q { | this trait is not dyn compatible... = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/issue-38604.rs:15:9 - | -LL | Box::new(()); - | ^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-38604.rs:2:22 - | -LL | trait Foo where u32: Q { - | --- ^^^^^^^ ...because it uses `Self` as a type parameter - | | - | this trait is not dyn compatible... - = help: only type `()` implements `Foo`; consider using it directly instead. - = note: required for the cast from `Box<()>` to `Box` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/item-privacy.rs b/tests/ui/traits/item-privacy.rs index cdfd667a6f12..9f75e6e4c123 100644 --- a/tests/ui/traits/item-privacy.rs +++ b/tests/ui/traits/item-privacy.rs @@ -99,9 +99,7 @@ fn check_assoc_const() { S::C; // OK // A, B, C are resolved as inherent items, their traits don't need to be in scope ::A; - //~^ ERROR associated constant `A` is private - //~| ERROR the trait `assoc_const::C` is not dyn compatible - //~| ERROR the trait `assoc_const::C` is not dyn compatible + //~^ ERROR the trait `assoc_const::C` is not dyn compatible ::B; //~^ ERROR the trait `assoc_const::C` is not dyn compatible C::C; // OK diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index 4fd9ef911925..bf59cd079a5a 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -131,44 +131,10 @@ LL + use assoc_const::B; | error[E0038]: the trait `assoc_const::C` is not dyn compatible - --> $DIR/item-privacy.rs:101:6 + --> $DIR/item-privacy.rs:101:10 | LL | ::A; - | ^^^^^ `assoc_const::C` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/item-privacy.rs:25:15 - | -LL | const A: u8 = 0; - | ^ ...because it contains this associated `const` -... -LL | const B: u8 = 0; - | ^ ...because it contains this associated `const` -... -LL | pub trait C: A + B { - | - this trait is not dyn compatible... -LL | const C: u8 = 0; - | ^ ...because it contains this associated `const` - = help: consider moving `C` to another trait - = help: consider moving `A` to another trait - = help: consider moving `B` to another trait - = help: only type `S` implements `assoc_const::C`; consider using it directly instead. - -error[E0624]: associated constant `A` is private - --> $DIR/item-privacy.rs:101:14 - | -LL | const A: u8 = 0; - | ----------- private associated constant defined here -... -LL | ::A; - | ^ private associated constant - -error[E0038]: the trait `assoc_const::C` is not dyn compatible - --> $DIR/item-privacy.rs:101:5 - | -LL | ::A; - | ^^^^^^^^^^ `assoc_const::C` is not dyn compatible + | ^ `assoc_const::C` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -190,10 +156,10 @@ LL | const C: u8 = 0; = help: only type `S` implements `assoc_const::C`; consider using it directly instead. error[E0038]: the trait `assoc_const::C` is not dyn compatible - --> $DIR/item-privacy.rs:105:5 + --> $DIR/item-privacy.rs:103:10 | LL | ::B; - | ^^^^^^^^^^ `assoc_const::C` is not dyn compatible + | ^ `assoc_const::C` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -215,7 +181,7 @@ LL | const C: u8 = 0; = help: only type `S` implements `assoc_const::C`; consider using it directly instead. error[E0223]: ambiguous associated type - --> $DIR/item-privacy.rs:118:12 + --> $DIR/item-privacy.rs:116:12 | LL | let _: S::A; | ^^^^ @@ -227,19 +193,31 @@ LL + let _: ::A; | error[E0223]: ambiguous associated type - --> $DIR/item-privacy.rs:119:12 + --> $DIR/item-privacy.rs:117:12 | LL | let _: S::B; - | ^^^^ help: use fully-qualified syntax: `::B` + | ^^^^ + | +help: use fully-qualified syntax + | +LL - let _: S::B; +LL + let _: ::B; + | error[E0223]: ambiguous associated type - --> $DIR/item-privacy.rs:120:12 + --> $DIR/item-privacy.rs:118:12 | LL | let _: S::C; - | ^^^^ help: use fully-qualified syntax: `::C` + | ^^^^ + | +help: use fully-qualified syntax + | +LL - let _: S::C; +LL + let _: ::C; + | error[E0624]: associated type `A` is private - --> $DIR/item-privacy.rs:122:12 + --> $DIR/item-privacy.rs:120:12 | LL | type A = u8; | ------ the associated type is defined here @@ -248,7 +226,7 @@ LL | let _: T::A; | ^^^^ private associated type error[E0624]: associated type `A` is private - --> $DIR/item-privacy.rs:131:9 + --> $DIR/item-privacy.rs:129:9 | LL | type A = u8; | ------ the associated type is defined here @@ -256,7 +234,7 @@ LL | type A = u8; LL | A = u8, | ^^^^^^ private associated type -error: aborting due to 17 previous errors +error: aborting due to 15 previous errors Some errors have detailed explanations: E0038, E0223, E0599, E0624. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/next-solver/assembly/better_any-backcompat.rs b/tests/ui/traits/next-solver/assembly/better_any-backcompat.rs new file mode 100644 index 000000000000..8b153833e3a7 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/better_any-backcompat.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +// A regression test for trait-system-refactor-initiative#183. While +// this concrete instance is likely not practically unsound, the general +// pattern is, see #57893. + +use std::any::TypeId; + +unsafe trait TidAble<'a>: Tid<'a> {} +trait TidExt<'a>: Tid<'a> { + fn downcast_box(self: Box) { + loop {} + } +} + +impl<'a, X: ?Sized + Tid<'a>> TidExt<'a> for X {} + +unsafe trait Tid<'a>: 'a {} + +unsafe impl<'a, T: ?Sized + TidAble<'a>> Tid<'a> for T {} + +impl<'a> dyn Tid<'a> + 'a { + fn downcast_any_box(self: Box) { + self.downcast_box(); + } +} + +unsafe impl<'a> TidAble<'a> for dyn Tid<'a> + 'a {} + +fn main() {} diff --git a/tests/ui/traits/next-solver/coerce-ambig-alias-to-rigid-alias.rs b/tests/ui/traits/next-solver/coercion/coerce-ambig-alias-to-rigid-alias.rs similarity index 100% rename from tests/ui/traits/next-solver/coerce-ambig-alias-to-rigid-alias.rs rename to tests/ui/traits/next-solver/coercion/coerce-ambig-alias-to-rigid-alias.rs diff --git a/tests/ui/traits/next-solver/coerce-depth.rs b/tests/ui/traits/next-solver/coercion/coerce-depth.rs similarity index 100% rename from tests/ui/traits/next-solver/coerce-depth.rs rename to tests/ui/traits/next-solver/coercion/coerce-depth.rs diff --git a/tests/ui/traits/next-solver/coercion/fn-def-coerce-nested-obligations.rs b/tests/ui/traits/next-solver/coercion/fn-def-coerce-nested-obligations.rs new file mode 100644 index 000000000000..1b5abcb02f2f --- /dev/null +++ b/tests/ui/traits/next-solver/coercion/fn-def-coerce-nested-obligations.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Make sure that we consider nested obligations when checking whether +// we should coerce fn definitions to function pointers. + +fn foo() {} +fn bar() {} +fn main() { + let _ = if true { foo::<{ 0 + 0 }> } else { foo::<1> }; + let _ = if true { + bar:: fn( as IntoIterator>::Item)> + } else { + bar:: + }; +} diff --git a/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.rs b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs similarity index 100% rename from tests/ui/traits/next-solver/non-wf-in-coerce-pointers.rs rename to tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs diff --git a/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr similarity index 100% rename from tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr rename to tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr diff --git a/tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs b/tests/ui/traits/next-solver/coercion/trait-upcast-lhs-needs-normalization.rs similarity index 100% rename from tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs rename to tests/ui/traits/next-solver/coercion/trait-upcast-lhs-needs-normalization.rs diff --git a/tests/ui/traits/next-solver/upcast-right-substs.rs b/tests/ui/traits/next-solver/coercion/upcast-right-substs.rs similarity index 100% rename from tests/ui/traits/next-solver/upcast-right-substs.rs rename to tests/ui/traits/next-solver/coercion/upcast-right-substs.rs diff --git a/tests/ui/traits/next-solver/upcast-wrong-substs.rs b/tests/ui/traits/next-solver/coercion/upcast-wrong-substs.rs similarity index 100% rename from tests/ui/traits/next-solver/upcast-wrong-substs.rs rename to tests/ui/traits/next-solver/coercion/upcast-wrong-substs.rs diff --git a/tests/ui/traits/next-solver/upcast-wrong-substs.stderr b/tests/ui/traits/next-solver/coercion/upcast-wrong-substs.stderr similarity index 100% rename from tests/ui/traits/next-solver/upcast-wrong-substs.stderr rename to tests/ui/traits/next-solver/coercion/upcast-wrong-substs.stderr diff --git a/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env-2.rs b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env-2.rs new file mode 100644 index 000000000000..ffb99d6d6385 --- /dev/null +++ b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env-2.rs @@ -0,0 +1,29 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . +// See comment below. + +trait A { + fn hello(&self) {} +} + +trait B { + fn hello(&self) {} +} + +impl A for T {} +impl B for T {} + +fn test(q: F::Item) +where + F: Iterator, + // We want to prefer `A` for `R.hello()` + F::Item: A, +{ + q.hello(); +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env.rs b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env.rs new file mode 100644 index 000000000000..dde4f745879e --- /dev/null +++ b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env.rs @@ -0,0 +1,17 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for . + +fn execute(q: F::Item) -> R +where + F: Iterator, + // Both of the below bounds should be considered for `.into()`, and then be combined + // into a single `R: Into` bound which can be inferred to `?0 = R`. + F::Item: Into, + R: Into, +{ + q.into() +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/no-param-env-const-fold.rs b/tests/ui/traits/next-solver/no-param-env-const-fold.rs new file mode 100644 index 000000000000..4f47332dd23a --- /dev/null +++ b/tests/ui/traits/next-solver/no-param-env-const-fold.rs @@ -0,0 +1,10 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for . + +use std::ops::Deref; + +trait Trait: Deref {} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/normalize-place-elem.rs b/tests/ui/traits/next-solver/normalize/normalize-place-elem.rs new file mode 100644 index 000000000000..9a600875bb9a --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/normalize-place-elem.rs @@ -0,0 +1,32 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for . +// Ensure that we normalize after applying projection elems in MIR typeck. + +use std::marker::PhantomData; + +#[derive(Copy, Clone)] +struct Span; + +trait AstKind { + type Inner; +} + +struct WithSpan; +impl AstKind for WithSpan { + type Inner + = (i32,); +} + +struct Expr<'a> { f: &'a ::Inner } + +impl Expr<'_> { + fn span(self) { + match self { + Self { f: (n,) } => {}, + } + } +} + +fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs index 28785ae3dea1..96345732f0f6 100644 --- a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs +++ b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs @@ -18,7 +18,5 @@ impl Bar for () {} fn main() { let x: &dyn Foo = &(); //~^ ERROR the trait `Foo` is not dyn compatible - //~| ERROR the trait `Foo` is not dyn compatible needs_bar(x); - //~^ ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr index 43b69d0b50e4..aead19c45272 100644 --- a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr +++ b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr @@ -8,27 +8,10 @@ LL | #![feature(non_lifetime_binders)] = note: `#[warn(incomplete_features)]` on by default error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/supertrait-dyn-compatibility.rs:19:23 + --> $DIR/supertrait-dyn-compatibility.rs:19:17 | LL | let x: &dyn Foo = &(); - | ^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/supertrait-dyn-compatibility.rs:4:12 - | -LL | trait Foo: for Bar {} - | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables - | | - | this trait is not dyn compatible... - = help: only type `()` implements `Foo`; consider using it directly instead. - = note: required for the cast from `&()` to `&dyn Foo` - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/supertrait-dyn-compatibility.rs:19:12 - | -LL | let x: &dyn Foo = &(); - | ^^^^^^^^ `Foo` is not dyn compatible + | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -40,22 +23,6 @@ LL | trait Foo: for Bar {} | this trait is not dyn compatible... = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/supertrait-dyn-compatibility.rs:22:5 - | -LL | needs_bar(x); - | ^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/supertrait-dyn-compatibility.rs:4:12 - | -LL | trait Foo: for Bar {} - | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables - | | - | this trait is not dyn compatible... - = help: only type `()` implements `Foo`; consider using it directly instead. - -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs index 6fcd67b4950f..9ac3b84dc122 100644 --- a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs @@ -9,8 +9,6 @@ trait Try { fn w<'a, T: 'a, F: Fn(&'a T)>() { let b: &dyn FromResidual = &(); //~^ ERROR: the trait `FromResidual` is not dyn compatible - //~| ERROR: the trait `FromResidual` is not dyn compatible - //~| ERROR the type parameter `R` must be explicitly specified } fn main() {} diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr index b4bbd65b2f47..707aa9e9713d 100644 --- a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr @@ -1,47 +1,8 @@ -error[E0393]: the type parameter `R` must be explicitly specified +error[E0038]: the trait `FromResidual` is not dyn compatible --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:17 | -LL | trait FromResidual::Residual> { - | ----------------------------------------------- type parameter `R` must be specified for this -... LL | let b: &dyn FromResidual = &(); - | ^^^^^^^^^^^^ - | - = note: because the parameter default references `Self`, the parameter must be specified on the object type -help: set the type parameter to the desired type - | -LL | let b: &dyn FromResidual = &(); - | +++ - -error[E0038]: the trait `FromResidual` is not dyn compatible - --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:32 - | -LL | let b: &dyn FromResidual = &(); - | ^^^ `FromResidual` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 - | -LL | trait FromResidual::Residual> { - | ------------ this trait is not dyn compatible... -LL | fn from_residual(residual: R) -> Self; - | ^^^^^^^^^^^^^ ...because associated function `from_residual` has no `self` parameter - = note: required for the cast from `&()` to `&dyn FromResidual<{type error}>` -help: consider turning `from_residual` into a method by giving it a `&self` argument - | -LL | fn from_residual(&self, residual: R) -> Self; - | ++++++ -help: alternatively, consider constraining `from_residual` so it does not apply to trait objects - | -LL | fn from_residual(residual: R) -> Self where Self: Sized; - | +++++++++++++++++ - -error[E0038]: the trait `FromResidual` is not dyn compatible - --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:12 - | -LL | let b: &dyn FromResidual = &(); - | ^^^^^^^^^^^^^^^^^ `FromResidual` is not dyn compatible + | ^^^^^^^^^^^^ `FromResidual` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -60,7 +21,6 @@ help: alternatively, consider constraining `from_residual` so it does not apply LL | fn from_residual(residual: R) -> Self where Self: Sized; | +++++++++++++++++ -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0038, E0393. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/object/macro-matcher.stderr b/tests/ui/traits/object/macro-matcher.stderr index 3c668ce99c7f..94d5c189a436 100644 --- a/tests/ui/traits/object/macro-matcher.stderr +++ b/tests/ui/traits/object/macro-matcher.stderr @@ -1,19 +1,19 @@ +error[E0038]: the trait `Copy` is not dyn compatible + --> $DIR/macro-matcher.rs:8:12 + | +LL | m!(dyn Copy + Send + 'static); + | ^^^^ `Copy` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + error[E0224]: at least one trait is required for an object type --> $DIR/macro-matcher.rs:11:8 | LL | m!(dyn 'static +); | ^^^^^^^^^^^^^ -error[E0038]: the trait `Copy` is not dyn compatible - --> $DIR/macro-matcher.rs:8:8 - | -LL | m!(dyn Copy + Send + 'static); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ `Copy` is not dyn compatible - | - = note: the trait is not dyn compatible because it requires `Self: Sized` - = note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - error: aborting due to 2 previous errors Some errors have detailed explanations: E0038, E0224. diff --git a/tests/ui/traits/object/safety.rs b/tests/ui/traits/object/safety.rs index f4abcf8542e5..ec039557b635 100644 --- a/tests/ui/traits/object/safety.rs +++ b/tests/ui/traits/object/safety.rs @@ -13,5 +13,4 @@ impl Tr for St { fn main() { let _: &dyn Tr = &St; //~ ERROR E0038 - //~^ ERROR E0038 } diff --git a/tests/ui/traits/object/safety.stderr b/tests/ui/traits/object/safety.stderr index 593e42619f41..c5637b435262 100644 --- a/tests/ui/traits/object/safety.stderr +++ b/tests/ui/traits/object/safety.stderr @@ -1,33 +1,8 @@ error[E0038]: the trait `Tr` is not dyn compatible - --> $DIR/safety.rs:15:22 + --> $DIR/safety.rs:15:17 | LL | let _: &dyn Tr = &St; - | ^^^ `Tr` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/safety.rs:4:8 - | -LL | trait Tr { - | -- this trait is not dyn compatible... -LL | fn foo(); - | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `St` implements `Tr`; consider using it directly instead. - = note: required for the cast from `&St` to `&dyn Tr` -help: consider turning `foo` into a method by giving it a `&self` argument - | -LL | fn foo(&self); - | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects - | -LL | fn foo() where Self: Sized; - | +++++++++++++++++ - -error[E0038]: the trait `Tr` is not dyn compatible - --> $DIR/safety.rs:15:12 - | -LL | let _: &dyn Tr = &St; - | ^^^^^^^ `Tr` is not dyn compatible + | ^^ `Tr` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -47,6 +22,6 @@ help: alternatively, consider constraining `foo` so it does not apply to trait o LL | fn foo() where Self: Sized; | +++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/test-2.rs b/tests/ui/traits/test-2.rs index ffb778a01414..1b7fc55b99a8 100644 --- a/tests/ui/traits/test-2.rs +++ b/tests/ui/traits/test-2.rs @@ -12,6 +12,4 @@ fn main() { //~^ ERROR method takes 1 generic argument but 2 (Box::new(10) as Box).dup(); //~^ ERROR E0038 - //~| ERROR E0038 - //~| ERROR E0038 } diff --git a/tests/ui/traits/test-2.stderr b/tests/ui/traits/test-2.stderr index 6a6cb503aa4d..6a8109281a85 100644 --- a/tests/ui/traits/test-2.stderr +++ b/tests/ui/traits/test-2.stderr @@ -27,10 +27,10 @@ LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } | ^^^^ - error[E0038]: the trait `bar` is not dyn compatible - --> $DIR/test-2.rs:13:22 + --> $DIR/test-2.rs:13:30 | LL | (Box::new(10) as Box).dup(); - | ^^^^^^^^^^^^ `bar` is not dyn compatible + | ^^^ `bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -49,54 +49,7 @@ LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } consider defining an enum where each variant holds one of these types, implementing `bar` for this new enum and using it instead -error[E0038]: the trait `bar` is not dyn compatible - --> $DIR/test-2.rs:13:5 - | -LL | (Box::new(10) as Box).dup(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/test-2.rs:4:30 - | -LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } - | --- ^^^^ ^^^^ ...because method `blah` has generic type parameters - | | | - | | ...because method `dup` references the `Self` type in its return type - | this trait is not dyn compatible... - = help: consider moving `dup` to another trait - = help: consider moving `blah` to another trait - = help: the following types implement `bar`: - i32 - u32 - consider defining an enum where each variant holds one of these types, - implementing `bar` for this new enum and using it instead - -error[E0038]: the trait `bar` is not dyn compatible - --> $DIR/test-2.rs:13:6 - | -LL | (Box::new(10) as Box).dup(); - | ^^^^^^^^^^^^ `bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/test-2.rs:4:30 - | -LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } - | --- ^^^^ ^^^^ ...because method `blah` has generic type parameters - | | | - | | ...because method `dup` references the `Self` type in its return type - | this trait is not dyn compatible... - = help: consider moving `dup` to another trait - = help: consider moving `blah` to another trait - = help: the following types implement `bar`: - i32 - u32 - consider defining an enum where each variant holds one of these types, - implementing `bar` for this new enum and using it instead - = note: required for the cast from `Box<{integer}>` to `Box` - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0038, E0107. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/transmutability/enums/repr/should_handle_all.rs b/tests/ui/transmutability/enums/repr/should_handle_all.rs index dec0126f22d4..192b7cdcf726 100644 --- a/tests/ui/transmutability/enums/repr/should_handle_all.rs +++ b/tests/ui/transmutability/enums/repr/should_handle_all.rs @@ -1,8 +1,7 @@ //@ check-pass #![crate_type = "lib"] -#![feature(repr128)] #![feature(transmutability)] -#![allow(dead_code, incomplete_features, non_camel_case_types)] +#![allow(dead_code, non_camel_case_types)] mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/transmute/unnecessary-transmutation.fixed b/tests/ui/transmute/unnecessary-transmutation.fixed index bf7d769348aa..08010ec8b84e 100644 --- a/tests/ui/transmute/unnecessary-transmutation.fixed +++ b/tests/ui/transmute/unnecessary-transmutation.fixed @@ -8,7 +8,27 @@ pub fn bytes_at_home(x: u32) -> [u8; 4] { //~^ ERROR } +pub const fn intinator_const(from: bool) -> u8 { + unsafe { (from) as u8 } + //~^ ERROR +} + +pub static X: u8 = unsafe { (true) as u8 }; +//~^ ERROR +pub const Y: u8 = unsafe { (true) as u8 }; +//~^ ERROR + +pub struct Z {} +impl Z { + pub const fn intinator_assoc(x: bool) -> u8 { + unsafe { (x) as u8 } + //~^ ERROR + } +} + fn main() { + const { unsafe { (true) as u8 } }; + //~^ ERROR unsafe { let x: u16 = u16::from_ne_bytes(*b"01"); //~^ ERROR @@ -81,14 +101,14 @@ fn main() { let y: i64 = f64::to_bits(1f64).cast_signed(); //~^ ERROR - let z: bool = (1u8 == 1); - //~^ ERROR - let z: u8 = (z) as u8; + let z: bool = transmute(1u8); + // clippy + let z: u8 = u8::from(z); //~^ ERROR let z: bool = transmute(1i8); - // no error! - let z: i8 = (z) as i8; + // clippy + let z: i8 = i8::from(z); //~^ ERROR } } diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs index b9de529f1ccc..43eefb97dc28 100644 --- a/tests/ui/transmute/unnecessary-transmutation.rs +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -8,7 +8,27 @@ pub fn bytes_at_home(x: u32) -> [u8; 4] { //~^ ERROR } +pub const fn intinator_const(from: bool) -> u8 { + unsafe { transmute(from) } + //~^ ERROR +} + +pub static X: u8 = unsafe { transmute(true) }; +//~^ ERROR +pub const Y: u8 = unsafe { transmute(true) }; +//~^ ERROR + +pub struct Z {} +impl Z { + pub const fn intinator_assoc(x: bool) -> u8 { + unsafe { transmute(x) } + //~^ ERROR + } +} + fn main() { + const { unsafe { transmute::<_, u8>(true) } }; + //~^ ERROR unsafe { let x: u16 = transmute(*b"01"); //~^ ERROR @@ -82,12 +102,12 @@ fn main() { //~^ ERROR let z: bool = transmute(1u8); - //~^ ERROR + // clippy let z: u8 = transmute(z); //~^ ERROR let z: bool = transmute(1i8); - // no error! + // clippy let z: i8 = transmute(z); //~^ ERROR } diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.stderr index a19f1bebf16f..0132ac4776be 100644 --- a/tests/ui/transmute/unnecessary-transmutation.stderr +++ b/tests/ui/transmute/unnecessary-transmutation.stderr @@ -2,7 +2,9 @@ error: unnecessary transmute --> $DIR/unnecessary-transmutation.rs:7:14 | LL | unsafe { transmute(x) } - | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` + | ---------^^^ + | | + | help: replace this with: `u32::to_ne_bytes` | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order note: the lint level is defined here @@ -12,250 +14,387 @@ LL | #![deny(unnecessary_transmutes)] | ^^^^^^^^^^^^^^^^^^^^^^ error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:13:22 + --> $DIR/unnecessary-transmutation.rs:12:14 | -LL | let x: u16 = transmute(*b"01"); - | ^^^^^^^^^^^^^^^^^ help: replace this with: `u16::from_ne_bytes(*b"01")` +LL | unsafe { transmute(from) } + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - unsafe { transmute(from) } +LL + unsafe { (from) as u8 } | - = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:15:26 + --> $DIR/unnecessary-transmutation.rs:16:29 | -LL | let x: [u8; 2] = transmute(x); - | ^^^^^^^^^^^^ help: replace this with: `u16::to_ne_bytes(x)` +LL | pub static X: u8 = unsafe { transmute(true) }; + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - pub static X: u8 = unsafe { transmute(true) }; +LL + pub static X: u8 = unsafe { (true) as u8 }; | - = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:17:22 + --> $DIR/unnecessary-transmutation.rs:18:28 | -LL | let x: u32 = transmute(*b"0123"); - | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `u32::from_ne_bytes(*b"0123")` +LL | pub const Y: u8 = unsafe { transmute(true) }; + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - pub const Y: u8 = unsafe { transmute(true) }; +LL + pub const Y: u8 = unsafe { (true) as u8 }; | - = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:19:26 + --> $DIR/unnecessary-transmutation.rs:24:18 | -LL | let x: [u8; 4] = transmute(x); - | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ | - = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:21:22 +help: replace this with | -LL | let x: u64 = transmute(*b"feriscat"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `u64::from_ne_bytes(*b"feriscat")` +LL - unsafe { transmute(x) } +LL + unsafe { (x) as u8 } | - = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:23:26 - | -LL | let x: [u8; 8] = transmute(x); - | ^^^^^^^^^^^^ help: replace this with: `u64::to_ne_bytes(x)` - | - = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:26:22 - | -LL | let y: i16 = transmute(*b"01"); - | ^^^^^^^^^^^^^^^^^ help: replace this with: `i16::from_ne_bytes(*b"01")` - | - = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:28:26 - | -LL | let y: [u8; 2] = transmute(y); - | ^^^^^^^^^^^^ help: replace this with: `i16::to_ne_bytes(y)` - | - = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order error: unnecessary transmute --> $DIR/unnecessary-transmutation.rs:30:22 | -LL | let y: i32 = transmute(*b"0123"); - | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `i32::from_ne_bytes(*b"0123")` +LL | const { unsafe { transmute::<_, u8>(true) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - const { unsafe { transmute::<_, u8>(true) } }; +LL + const { unsafe { (true) as u8 } }; + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:33:22 + | +LL | let x: u16 = transmute(*b"01"); + | ---------^^^^^^^^ + | | + | help: replace this with: `u16::from_ne_bytes` | = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:32:26 + --> $DIR/unnecessary-transmutation.rs:35:26 + | +LL | let x: [u8; 2] = transmute(x); + | ---------^^^ + | | + | help: replace this with: `u16::to_ne_bytes` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:37:22 + | +LL | let x: u32 = transmute(*b"0123"); + | ---------^^^^^^^^^^ + | | + | help: replace this with: `u32::from_ne_bytes` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:39:26 + | +LL | let x: [u8; 4] = transmute(x); + | ---------^^^ + | | + | help: replace this with: `u32::to_ne_bytes` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:41:22 + | +LL | let x: u64 = transmute(*b"feriscat"); + | ---------^^^^^^^^^^^^^^ + | | + | help: replace this with: `u64::from_ne_bytes` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:43:26 + | +LL | let x: [u8; 8] = transmute(x); + | ---------^^^ + | | + | help: replace this with: `u64::to_ne_bytes` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:46:22 + | +LL | let y: i16 = transmute(*b"01"); + | ---------^^^^^^^^ + | | + | help: replace this with: `i16::from_ne_bytes` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:48:26 + | +LL | let y: [u8; 2] = transmute(y); + | ---------^^^ + | | + | help: replace this with: `i16::to_ne_bytes` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:50:22 + | +LL | let y: i32 = transmute(*b"0123"); + | ---------^^^^^^^^^^ + | | + | help: replace this with: `i32::from_ne_bytes` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:52:26 | LL | let y: [u8; 4] = transmute(y); - | ^^^^^^^^^^^^ help: replace this with: `i32::to_ne_bytes(y)` + | ---------^^^ + | | + | help: replace this with: `i32::to_ne_bytes` | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:34:22 + --> $DIR/unnecessary-transmutation.rs:54:22 | LL | let y: i64 = transmute(*b"feriscat"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `i64::from_ne_bytes(*b"feriscat")` + | ---------^^^^^^^^^^^^^^ + | | + | help: replace this with: `i64::from_ne_bytes` | = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:36:26 + --> $DIR/unnecessary-transmutation.rs:56:26 | LL | let y: [u8; 8] = transmute(y); - | ^^^^^^^^^^^^ help: replace this with: `i64::to_ne_bytes(y)` + | ---------^^^ + | | + | help: replace this with: `i64::to_ne_bytes` | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:39:22 - | -LL | let z: f32 = transmute(*b"0123"); - | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `f32::from_ne_bytes(*b"0123")` - | - = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:41:26 - | -LL | let z: [u8; 4] = transmute(z); - | ^^^^^^^^^^^^ help: replace this with: `f32::to_ne_bytes(z)` - | - = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:43:22 - | -LL | let z: f64 = transmute(*b"feriscat"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `f64::from_ne_bytes(*b"feriscat")` - | - = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:45:26 - | -LL | let z: [u8; 8] = transmute(z); - | ^^^^^^^^^^^^ help: replace this with: `f64::to_ne_bytes(z)` - | - = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:48:22 - | -LL | let y: u32 = transmute('🦀'); - | ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🦀')` - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:50:23 - | -LL | let y: char = transmute(y); - | ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(y)` - | - = help: consider `char::from_u32(…).unwrap()` - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:52:22 - | -LL | let y: i32 = transmute('🐱'); - | ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🐱').cast_signed()` - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:54:23 - | -LL | let y: char = transmute(y); - | ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(i32::cast_unsigned(y))` - | - = help: consider `char::from_u32(i32::cast_unsigned(…)).unwrap()` - -error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:57:22 - | -LL | let x: u16 = transmute(8i16); - | ^^^^^^^^^^^^^^^ help: replace this with: `i16::cast_unsigned(8i16)` - error: unnecessary transmute --> $DIR/unnecessary-transmutation.rs:59:22 | -LL | let x: i16 = transmute(x); - | ^^^^^^^^^^^^ help: replace this with: `u16::cast_signed(x)` +LL | let z: f32 = transmute(*b"0123"); + | ---------^^^^^^^^^^ + | | + | help: replace this with: `f32::from_ne_bytes` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:61:22 + --> $DIR/unnecessary-transmutation.rs:61:26 | -LL | let x: u32 = transmute(4i32); - | ^^^^^^^^^^^^^^^ help: replace this with: `i32::cast_unsigned(4i32)` +LL | let z: [u8; 4] = transmute(z); + | ---------^^^ + | | + | help: replace this with: `f32::to_ne_bytes` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order error: unnecessary transmute --> $DIR/unnecessary-transmutation.rs:63:22 | -LL | let x: i32 = transmute(x); - | ^^^^^^^^^^^^ help: replace this with: `u32::cast_signed(x)` +LL | let z: f64 = transmute(*b"feriscat"); + | ---------^^^^^^^^^^^^^^ + | | + | help: replace this with: `f64::from_ne_bytes` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:65:22 + --> $DIR/unnecessary-transmutation.rs:65:26 | -LL | let x: u64 = transmute(7i64); - | ^^^^^^^^^^^^^^^ help: replace this with: `i64::cast_unsigned(7i64)` +LL | let z: [u8; 8] = transmute(z); + | ---------^^^ + | | + | help: replace this with: `f64::to_ne_bytes` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:67:22 + --> $DIR/unnecessary-transmutation.rs:68:22 | -LL | let x: i64 = transmute(x); - | ^^^^^^^^^^^^ help: replace this with: `u64::cast_signed(x)` +LL | let y: u32 = transmute('🦀'); + | ---------^^^^^^ + | | + | help: replace this with: `u32::from` error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:70:22 + --> $DIR/unnecessary-transmutation.rs:70:23 | -LL | let y: f32 = transmute(1u32); - | ^^^^^^^^^^^^^^^ help: replace this with: `f32::from_bits(1u32)` +LL | let y: char = transmute(y); + | ---------^^^ + | | + | help: replace this with: `char::from_u32_unchecked` + | + = help: consider using `char::from_u32(…).unwrap()` error: unnecessary transmute --> $DIR/unnecessary-transmutation.rs:72:22 | -LL | let y: u32 = transmute(y); - | ^^^^^^^^^^^^ help: replace this with: `f32::to_bits(y)` +LL | let y: i32 = transmute('🐱'); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: i32 = transmute('🐱'); +LL + let y: i32 = u32::from('🐱').cast_signed(); + | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:74:22 + --> $DIR/unnecessary-transmutation.rs:74:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ + | + = help: consider using `char::from_u32(i32::cast_unsigned(…)).unwrap()` +help: replace this with + | +LL - let y: char = transmute(y); +LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); | -LL | let y: f64 = transmute(3u64); - | ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(3u64)` error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:76:22 + --> $DIR/unnecessary-transmutation.rs:77:22 | -LL | let y: u64 = transmute(2.0); - | ^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(2.0)` +LL | let x: u16 = transmute(8i16); + | ---------^^^^^^ + | | + | help: replace this with: `i16::cast_unsigned` error: unnecessary transmute --> $DIR/unnecessary-transmutation.rs:79:22 | -LL | let y: f64 = transmute(1i64); - | ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(i64::cast_unsigned(1i64))` +LL | let x: i16 = transmute(x); + | ---------^^^ + | | + | help: replace this with: `u16::cast_signed` error: unnecessary transmute --> $DIR/unnecessary-transmutation.rs:81:22 | -LL | let y: i64 = transmute(1f64); - | ^^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(1f64).cast_signed()` +LL | let x: u32 = transmute(4i32); + | ---------^^^^^^ + | | + | help: replace this with: `i32::cast_unsigned` error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:84:23 + --> $DIR/unnecessary-transmutation.rs:83:22 | -LL | let z: bool = transmute(1u8); - | ^^^^^^^^^^^^^^ help: replace this with: `(1u8 == 1)` +LL | let x: i32 = transmute(x); + | ---------^^^ + | | + | help: replace this with: `u32::cast_signed` error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:86:21 + --> $DIR/unnecessary-transmutation.rs:85:22 + | +LL | let x: u64 = transmute(7i64); + | ---------^^^^^^ + | | + | help: replace this with: `i64::cast_unsigned` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:87:22 + | +LL | let x: i64 = transmute(x); + | ---------^^^ + | | + | help: replace this with: `u64::cast_signed` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:90:22 + | +LL | let y: f32 = transmute(1u32); + | ---------^^^^^^ + | | + | help: replace this with: `f32::from_bits` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:92:22 + | +LL | let y: u32 = transmute(y); + | ---------^^^ + | | + | help: replace this with: `f32::to_bits` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:94:22 + | +LL | let y: f64 = transmute(3u64); + | ---------^^^^^^ + | | + | help: replace this with: `f64::from_bits` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:96:22 + | +LL | let y: u64 = transmute(2.0); + | ---------^^^^^ + | | + | help: replace this with: `f64::to_bits` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:99:22 + | +LL | let y: f64 = transmute(1i64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: f64 = transmute(1i64); +LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:101:22 + | +LL | let y: i64 = transmute(1f64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: i64 = transmute(1f64); +LL + let y: i64 = f64::to_bits(1f64).cast_signed(); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:106:21 | LL | let z: u8 = transmute(z); - | ^^^^^^^^^^^^ help: replace this with: `(z) as u8` + | ---------^^^ + | | + | help: replace this with: `u8::from` error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:91:21 + --> $DIR/unnecessary-transmutation.rs:111:21 | LL | let z: i8 = transmute(z); - | ^^^^^^^^^^^^ help: replace this with: `(z) as i8` + | ---------^^^ + | | + | help: replace this with: `i8::from` -error: aborting due to 36 previous errors +error: aborting due to 40 previous errors 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 9fcb26c20a68..81be8c8362e3 100644 --- a/tests/ui/type-alias-impl-trait/in-where-clause.stderr +++ b/tests/ui/type-alias-impl-trait/in-where-clause.stderr @@ -62,6 +62,11 @@ LL | / fn foo() -> Bar LL | | where LL | | Bar: Send, | |______________^ +note: ...which requires computing revealed normalized predicates of `foo::{constant#0}`... + --> $DIR/in-where-clause.rs:13:9 + | +LL | [0; 1 + 2] + | ^^^^^ = note: ...which requires revealing opaque types in `[Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] }]`... note: ...which requires computing type of `Bar::{opaque#0}`... --> $DIR/in-where-clause.rs:5:12 diff --git a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs index ef6871bec7c4..0aaee5d1764e 100644 --- a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs +++ b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs @@ -1,10 +1,10 @@ #![feature(type_alias_impl_trait)] type T = impl Copy; -//~^ ERROR cannot resolve opaque type #[define_opaque(T)] fn foo() -> T { + //~^ ERROR cannot resolve opaque type None::<&'static T> } diff --git a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr index d820df472f95..426a6c29fff6 100644 --- a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr +++ b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr @@ -1,8 +1,8 @@ error[E0720]: cannot resolve opaque type - --> $DIR/infinite-cycle-involving-weak.rs:3:10 + --> $DIR/infinite-cycle-involving-weak.rs:6:13 | -LL | type T = impl Copy; - | ^^^^^^^^^ cannot resolve opaque type +LL | fn foo() -> T { + | ^ error: aborting due to 1 previous error diff --git a/tests/crashes/139817.rs b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.rs similarity index 62% rename from tests/crashes/139817.rs rename to tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.rs index d439ed4cacbd..f4e2cb0c037b 100644 --- a/tests/crashes/139817.rs +++ b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.rs @@ -1,8 +1,12 @@ -//@ known-bug: #139817 +#![feature(type_alias_impl_trait)] + fn enum_upvar() { type T = impl Copy; let foo: T = Some((42, std::marker::PhantomData::)); let x = move || match foo { None => (), + //~^ ERROR cannot resolve opaque type }; } + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.stderr b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.stderr new file mode 100644 index 000000000000..2bfce2d63a8e --- /dev/null +++ b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.stderr @@ -0,0 +1,9 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/match-upvar-discriminant-of-opaque.rs:7:9 + | +LL | None => (), + | ^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs b/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs index 94597adfed04..fe354cc65452 100644 --- a/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs +++ b/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs @@ -13,7 +13,7 @@ pub fn add( n: Diff, m: Diff, ) -> Diff { - //~^ ERROR concrete type differs + //~^ ERROR cannot resolve opaque type move |x: usize| m(n(x)) } diff --git a/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr index 59ff99176124..847f1cc6c2eb 100644 --- a/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr @@ -1,14 +1,9 @@ -error: concrete type differs from previous defining opaque type use +error[E0720]: cannot resolve opaque type --> $DIR/recursive-fn-tait.rs:15:6 | LL | ) -> Diff { - | ^^^^ expected `{closure@$DIR/recursive-fn-tait.rs:8:5: 8:16}`, got `{closure@$DIR/recursive-fn-tait.rs:17:5: 17:20}` - | -note: previous use here - --> $DIR/recursive-fn-tait.rs:7:18 - | -LL | pub fn lift() -> Diff { - | ^^^^ + | ^^^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs index 858f2a2feb67..7803b3b78db8 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs @@ -13,7 +13,7 @@ fn transform() -> impl std::fmt::Display { } #[define_opaque(Op)] fn bad() -> Op { - //~^ ERROR concrete type differs from previous defining opaque type use + //~^ ERROR cannot resolve opaque type transform::() } diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr index e527b5bc7f8f..837df7f5d434 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr @@ -1,14 +1,9 @@ -error: concrete type differs from previous defining opaque type use +error[E0720]: cannot resolve opaque type --> $DIR/recursive-tait-conflicting-defn-2.rs:15:13 | LL | fn bad() -> Op { - | ^^ expected `&&str`, got `impl std::fmt::Display` - | -note: previous use here - --> $DIR/recursive-tait-conflicting-defn-2.rs:7:13 - | -LL | fn foo() -> Op { | ^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs index 90581a98a346..d66ceda52349 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs @@ -23,7 +23,7 @@ pub fn test() -> TestImpl { #[define_opaque(TestImpl)] fn make_option2() -> Option { - //~^ ERROR concrete type differs from previous defining opaque type use + //~^ ERROR cannot resolve opaque type let inner = make_option().unwrap(); Some(B { inner }) } diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr index 256f13b62217..c7a3381d6158 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr @@ -1,14 +1,9 @@ -error: concrete type differs from previous defining opaque type use +error[E0720]: cannot resolve opaque type --> $DIR/recursive-tait-conflicting-defn.rs:25:22 | LL | fn make_option2() -> Option { - | ^^^^^^^^^^^^^^^^ expected `A`, got `B` - | -note: previous use here - --> $DIR/recursive-tait-conflicting-defn.rs:20:18 - | -LL | pub fn test() -> TestImpl { - | ^^^^^^^^ + | ^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.rs b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.rs index 444453dc6943..b877ef569f6d 100644 --- a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.rs +++ b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.rs @@ -13,5 +13,4 @@ fn main() { let x: i32 = 5; let y = x as dyn MyAdd; //~^ ERROR E0038 - //~| ERROR cast to unsized type: `i32` as `dyn MyAdd` } diff --git a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr index eea2e75a2382..33478740d65c 100644 --- a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr +++ b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr @@ -1,20 +1,8 @@ -error[E0620]: cast to unsized type: `i32` as `dyn MyAdd` - --> $DIR/type-parameter-defaults-referencing-Self-ppaux.rs:14:13 - | -LL | let y = x as dyn MyAdd; - | ^^^^^^^^^^^^^^^^^^^ - | -help: consider using a box or reference as appropriate - --> $DIR/type-parameter-defaults-referencing-Self-ppaux.rs:14:13 - | -LL | let y = x as dyn MyAdd; - | ^ - error[E0038]: the trait `MyAdd` is not dyn compatible - --> $DIR/type-parameter-defaults-referencing-Self-ppaux.rs:14:18 + --> $DIR/type-parameter-defaults-referencing-Self-ppaux.rs:14:22 | LL | let y = x as dyn MyAdd; - | ^^^^^^^^^^^^^^ `MyAdd` is not dyn compatible + | ^^^^^^^^^^ `MyAdd` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -25,7 +13,6 @@ LL | trait MyAdd { fn add(&self, other: &Rhs) -> Self; } = help: consider moving `add` to another trait = help: only type `i32` implements `MyAdd`; consider using it directly instead. -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0038, E0620. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/typeck/closure-ty-mismatch-issue-128561.rs b/tests/ui/typeck/closure-ty-mismatch-issue-128561.rs new file mode 100644 index 000000000000..589a90e71d6e --- /dev/null +++ b/tests/ui/typeck/closure-ty-mismatch-issue-128561.rs @@ -0,0 +1,10 @@ +fn main() { + b"abc".iter().for_each(|x| x); //~ ERROR: mismatched types + + b"abc".iter().for_each(|x| dbg!(x)); //~ ERROR: mismatched types + + b"abc".iter().for_each(|x| { + println!("{}", x); + x //~ ERROR: mismatched types + }) +} diff --git a/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr new file mode 100644 index 000000000000..31acc5bb10ec --- /dev/null +++ b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr @@ -0,0 +1,28 @@ +error[E0308]: mismatched types + --> $DIR/closure-ty-mismatch-issue-128561.rs:2:32 + | +LL | b"abc".iter().for_each(|x| x); + | ^ expected `()`, found `&u8` + | +help: consider ignoring the value + | +LL | b"abc".iter().for_each(|x| _ = x); + | +++ + +error[E0308]: mismatched types + --> $DIR/closure-ty-mismatch-issue-128561.rs:4:32 + | +LL | b"abc".iter().for_each(|x| dbg!(x)); + | ^^^^^^^ expected `()`, found `&u8` + | + = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/closure-ty-mismatch-issue-128561.rs:8:9 + | +LL | x + | ^ expected `()`, found `&u8` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/consider-borrowing-141810-1.rs b/tests/ui/typeck/consider-borrowing-141810-1.rs new file mode 100644 index 000000000000..94c2d6909151 --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-1.rs @@ -0,0 +1,9 @@ +fn main() { + let x = if true { + &true + } else if false { //~ ERROR `if` and `else` have incompatible types [E0308] + true //~ HELP consider borrowing here + } else { + true + }; +} diff --git a/tests/ui/typeck/consider-borrowing-141810-1.stderr b/tests/ui/typeck/consider-borrowing-141810-1.stderr new file mode 100644 index 000000000000..9291721ac712 --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-1.stderr @@ -0,0 +1,28 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/consider-borrowing-141810-1.rs:4:12 + | +LL | let x = if true { + | ______________- +LL | | &true + | | ----- expected because of this +LL | | } else if false { + | | ____________^ +LL | || true +LL | || } else { +LL | || true +LL | || }; + | || ^ + | ||_____| + | |_____`if` and `else` have incompatible types + | expected `&bool`, found `bool` + | +help: consider borrowing here + | +LL ~ &true +LL | } else { +LL ~ &true + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/consider-borrowing-141810-2.rs b/tests/ui/typeck/consider-borrowing-141810-2.rs new file mode 100644 index 000000000000..e32e689efb7e --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-2.rs @@ -0,0 +1,8 @@ +fn main() { + let x = if true { + &() + } else if false { //~ ERROR `if` and `else` have incompatible types [E0308] + } else { + }; + +} diff --git a/tests/ui/typeck/consider-borrowing-141810-2.stderr b/tests/ui/typeck/consider-borrowing-141810-2.stderr new file mode 100644 index 000000000000..dd229897283b --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-2.stderr @@ -0,0 +1,19 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/consider-borrowing-141810-2.rs:4:12 + | +LL | let x = if true { + | ______________- +LL | | &() + | | --- expected because of this +LL | | } else if false { + | | ____________^ +LL | || } else { +LL | || }; + | || ^ + | ||_____| + | |_____`if` and `else` have incompatible types + | expected `&()`, found `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/consider-borrowing-141810-3.rs b/tests/ui/typeck/consider-borrowing-141810-3.rs new file mode 100644 index 000000000000..d38828de7c17 --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-3.rs @@ -0,0 +1,7 @@ +fn main() { + let x = if true { + &() + } else if false { //~ ERROR `if` and `else` have incompatible types [E0308] + + }; +} diff --git a/tests/ui/typeck/consider-borrowing-141810-3.stderr b/tests/ui/typeck/consider-borrowing-141810-3.stderr new file mode 100644 index 000000000000..0b0c5f191a0d --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-3.stderr @@ -0,0 +1,22 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/consider-borrowing-141810-3.rs:4:12 + | +LL | let x = if true { + | ______________- +LL | | &() + | | --- expected because of this +LL | | } else if false { + | | ____________^ +LL | || +LL | || }; + | || ^ + | ||_____| + | |_____`if` and `else` have incompatible types + | expected `&()`, found `()` + | + = note: `if` expressions without `else` evaluate to `()` + = note: consider adding an `else` block that evaluates to the expected type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/consider-borrowing-141810-4.rs b/tests/ui/typeck/consider-borrowing-141810-4.rs new file mode 100644 index 000000000000..754af7920a85 --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-4.rs @@ -0,0 +1,11 @@ +fn baz(x: &String) {} + +fn bar() { + baz({ + String::from("hi") //~ ERROR mismatched types + }); +} + +fn main() { + bar(); +} diff --git a/tests/ui/typeck/consider-borrowing-141810-4.stderr b/tests/ui/typeck/consider-borrowing-141810-4.stderr new file mode 100644 index 000000000000..80869d4a5d5c --- /dev/null +++ b/tests/ui/typeck/consider-borrowing-141810-4.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/consider-borrowing-141810-4.rs:5:9 + | +LL | String::from("hi") + | ^^^^^^^^^^^^^^^^^^ expected `&String`, found `String` + | +help: consider borrowing here + | +LL | &String::from("hi") + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/ice-unexpected-region-123863.stderr b/tests/ui/typeck/ice-unexpected-region-123863.stderr index 8a4d767c1435..e5050b4d3167 100644 --- a/tests/ui/typeck/ice-unexpected-region-123863.stderr +++ b/tests/ui/typeck/ice-unexpected-region-123863.stderr @@ -39,7 +39,7 @@ LL | Inner::concat_strs::<"a">::A help: if there were a trait named `Example` with associated type `concat_strs` implemented for `Inner<_>`, you could use the fully-qualified path | LL - Inner::concat_strs::<"a">::A -LL + as Example>::concat_strs::A +LL + as Example>::concat_strs::<"a">::A | error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-107087.stderr b/tests/ui/typeck/issue-107087.stderr index 289c8d161ae8..157ba5a16721 100644 --- a/tests/ui/typeck/issue-107087.stderr +++ b/tests/ui/typeck/issue-107087.stderr @@ -2,7 +2,13 @@ error[E0223]: ambiguous associated type --> $DIR/issue-107087.rs:16:5 | LL | A::B::<>::C - | ^^^^^^^^ help: use fully-qualified syntax: ` as Foo>::B` + | ^^^^^^^^ + | +help: use fully-qualified syntax + | +LL - A::B::<>::C +LL + as Foo>::B::<>::C + | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-31173.stderr b/tests/ui/typeck/issue-31173.stderr index 9598bc61352f..5815da4dea9c 100644 --- a/tests/ui/typeck/issue-31173.stderr +++ b/tests/ui/typeck/issue-31173.stderr @@ -21,7 +21,7 @@ LL | | }) note: required by a bound in `cloned` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0599]: the method `collect` exists for struct `Cloned, {closure@issue-31173.rs:7:21}>>`, but its trait bounds were not satisfied +error[E0599]: the method `collect` exists for struct `Cloned, {closure@$DIR/issue-31173.rs:7:21: 7:25}>>`, but its trait bounds were not satisfied --> $DIR/issue-31173.rs:12:10 | LL | let temp: Vec = it diff --git a/tests/ui/ufcs/ufcs-partially-resolved.stderr b/tests/ui/ufcs/ufcs-partially-resolved.stderr index 0a9c190cb356..69d6bd74a736 100644 --- a/tests/ui/ufcs/ufcs-partially-resolved.stderr +++ b/tests/ui/ufcs/ufcs-partially-resolved.stderr @@ -235,8 +235,6 @@ LL | ::X; | ^^^^^^^^^^^^- | | | help: an associated function with a similar name exists: `Z` - | - = note: can't use a type alias as a constructor error[E0575]: expected associated type, found associated function `Dr::Z` --> $DIR/ufcs-partially-resolved.rs:54:12 diff --git a/tests/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/tests/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr index 4d5e6f479650..db5a49d2b341 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `call` found for closure `{closure@$DIR/unboxed-cl --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10 | LL | mut_.call((0, )); - | ^^^^ method not found in `{closure@unboxed-closures-static-call-wrong-trait.rs:6:26}` + | ^^^^ method not found in `{closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29}` error: aborting due to 1 previous error diff --git a/tests/ui/unsafe-binders/cat-projection.rs b/tests/ui/unsafe-binders/cat-projection.rs new file mode 100644 index 000000000000..0ce8579a6882 --- /dev/null +++ b/tests/ui/unsafe-binders/cat-projection.rs @@ -0,0 +1,24 @@ +//@ revisions: e2015 e2021 +//@[e2015] edition: 2015 +//@[e2021] edition: 2021 +//@ check-pass + +#![feature(unsafe_binders)] +#![allow(incomplete_features)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(x: unsafe<'a> S) -> usize { + unsafe { (|| unwrap_binder!(x).0[0])() } +} + +// Regression test for . +pub fn by_ref(x: unsafe<'a> &'a S) -> usize { + unsafe { (|| unwrap_binder!(x).0[0])() } +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/non-strucutral-type-diag.rs b/tests/ui/unsafe-binders/non-strucutral-type-diag.rs new file mode 100644 index 000000000000..e021e9567e5c --- /dev/null +++ b/tests/ui/unsafe-binders/non-strucutral-type-diag.rs @@ -0,0 +1,17 @@ +// regression test for . + +#![feature(unsafe_binders)] +#![allow(incomplete_features)] + +#[derive(Copy, Clone)] +struct Adt<'a>(&'a ()); + +const C: Option<(unsafe<'a> Adt<'a>, Box)> = None; + +fn main() { + match None { + C => {} + //~^ ERROR constant of non-structural type + _ => {} + } +} diff --git a/tests/ui/unsafe-binders/non-strucutral-type-diag.stderr b/tests/ui/unsafe-binders/non-strucutral-type-diag.stderr new file mode 100644 index 000000000000..ab23210b2e57 --- /dev/null +++ b/tests/ui/unsafe-binders/non-strucutral-type-diag.stderr @@ -0,0 +1,13 @@ +error: constant of non-structural type `Option<(unsafe<'a> Adt<'a>, Box)>` in a pattern + --> $DIR/non-strucutral-type-diag.rs:13:9 + | +LL | const C: Option<(unsafe<'a> Adt<'a>, Box)> = None; + | ---------------------------------------------------- constant defined here +... +LL | C => {} + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error: aborting due to 1 previous error + diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.fixed b/tests/ui/unsafe-binders/unused-lifetimes-2.fixed new file mode 100644 index 000000000000..714a5fdaf031 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes-2.fixed @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: unsafe<'a> &'a S) -> usize { + //~^ WARN lifetime parameter `'b` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.rs b/tests/ui/unsafe-binders/unused-lifetimes-2.rs new file mode 100644 index 000000000000..5b34cf911637 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes-2.rs @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: unsafe<'a, 'b> &'a S) -> usize { + //~^ WARN lifetime parameter `'b` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes-2.stderr b/tests/ui/unsafe-binders/unused-lifetimes-2.stderr new file mode 100644 index 000000000000..bca8a15d56bd --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes-2.stderr @@ -0,0 +1,16 @@ +warning: lifetime parameter `'b` never used + --> $DIR/unused-lifetimes-2.rs:15:32 + | +LL | pub fn by_value(_x: unsafe<'a, 'b> &'a S) -> usize { + | --^^ + | | + | help: elide the unused lifetime + | +note: the lint level is defined here + --> $DIR/unused-lifetimes-2.rs:5:9 + | +LL | #![warn(unused_lifetimes)] + | ^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/unsafe-binders/unused-lifetimes.fixed b/tests/ui/unsafe-binders/unused-lifetimes.fixed new file mode 100644 index 000000000000..4295b6a848c3 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes.fixed @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: S) -> usize { + //~^ WARN lifetime parameter `'a` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes.rs b/tests/ui/unsafe-binders/unused-lifetimes.rs new file mode 100644 index 000000000000..b13823283183 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes.rs @@ -0,0 +1,20 @@ +// regression test for #141758 +//@ run-rustfix +//@ check-pass + +#![warn(unused_lifetimes)] +#![allow(incomplete_features, unused_imports, dead_code)] +#![feature(unsafe_binders)] + +use std::unsafe_binder::unwrap_binder; + +#[derive(Copy, Clone)] +pub struct S([usize; 8]); + +// Regression test for . +pub fn by_value(_x: unsafe<'a> S) -> usize { + //~^ WARN lifetime parameter `'a` never used + 0 +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/unused-lifetimes.stderr b/tests/ui/unsafe-binders/unused-lifetimes.stderr new file mode 100644 index 000000000000..d9a5216301f5 --- /dev/null +++ b/tests/ui/unsafe-binders/unused-lifetimes.stderr @@ -0,0 +1,14 @@ +warning: lifetime parameter `'a` never used + --> $DIR/unused-lifetimes.rs:15:28 + | +LL | pub fn by_value(_x: unsafe<'a> S) -> usize { + | -------^^-- help: elide the unused lifetime + | +note: the lint level is defined here + --> $DIR/unused-lifetimes.rs:5:9 + | +LL | #![warn(unused_lifetimes)] + | ^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/unsafe/move-out-of-non-copy.rs b/tests/ui/unsafe/move-out-of-non-copy.rs new file mode 100644 index 000000000000..ca6bf4277a15 --- /dev/null +++ b/tests/ui/unsafe/move-out-of-non-copy.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Zvalidate-mir + +// Regression test for . + +#![feature(unsafe_binders)] +#![allow(incomplete_features)] + +use std::unsafe_binder::unwrap_binder; + +fn id(x: unsafe<> T) -> T { + //~^ ERROR the trait bound `T: Copy` is not satisfied + unsafe { unwrap_binder!(x) } +} + +fn main() {} diff --git a/tests/ui/unsafe/move-out-of-non-copy.stderr b/tests/ui/unsafe/move-out-of-non-copy.stderr new file mode 100644 index 000000000000..4598742c92b4 --- /dev/null +++ b/tests/ui/unsafe/move-out-of-non-copy.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/move-out-of-non-copy.rs:10:13 + | +LL | fn id(x: unsafe<> T) -> T { + | ^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +help: consider restricting type parameter `T` with trait `Copy` + | +LL | fn id(x: unsafe<> T) -> T { + | +++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-dyn-incompatible.stderr b/tests/ui/wf/wf-dyn-incompatible.stderr index e61b37d92932..2ba17b7aaefc 100644 --- a/tests/ui/wf/wf-dyn-incompatible.stderr +++ b/tests/ui/wf/wf-dyn-incompatible.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `A` is not dyn compatible - --> $DIR/wf-dyn-incompatible.rs:9:13 + --> $DIR/wf-dyn-incompatible.rs:9:18 | LL | let _x: &dyn A; - | ^^^^^^ `A` is not dyn compatible + | ^ `A` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit diff --git a/triagebot.toml b/triagebot.toml index 3afa2d364109..a8d529cae77a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -44,8 +44,6 @@ remove_labels = ["S-waiting-on-author"] # Those labels are added when PR author requests a review from an assignee add_labels = ["S-waiting-on-review"] -[glacier] - [ping.icebreakers-llvm] message = """\ Hey LLVM ICE-breakers! This bug has been identified as a good @@ -595,6 +593,13 @@ trigger_files = [ "src/doc/rustc-dev-guide", ] +[autolabel."A-LLVM"] +trigger_files = [ + "src/llvm-project", + "compiler/rustc_llvm", + "compiler/rustc_codegen_llvm", +] + [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/prioritization/alerts topic = "#{number} {title}" @@ -887,6 +892,7 @@ message = "Some changes occurred in HTML/CSS/JS." cc = [ "@GuillaumeGomez", "@jsha", + "@lolbinarycat", ] [mentions."tests/rustdoc-gui/"] @@ -1171,8 +1177,6 @@ contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" users_on_vacation = [ "fmease", "jyn514", - "Noratrieb", - "spastorino", ] [[assign.warn_non_default_branch.exceptions]] @@ -1192,13 +1196,13 @@ compiler = [ "@BoxyUwU", "@compiler-errors", "@davidtwco", + "@eholk", "@fee1-dead", "@fmease", "@jieyouxu", "@lcnr", "@Nadrieril", "@nnethercote", - "@Noratrieb", "@oli-obk", "@petrochenkov", "@SparrowLii", @@ -1206,7 +1210,6 @@ compiler = [ ] libs = [ "@Mark-Simulacrum", - "@Noratrieb", "@workingjubilee", "@joboet", "@jhpratt", @@ -1413,8 +1416,10 @@ compiletest = [ "/src/tools/rustdoc-gui-test" = ["bootstrap", "@onur-ozkan"] "/src/tools/libcxx-version" = ["@onur-ozkan"] -# Enable tracking of PR review assignment -# Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment-tracking.html +# Enable review queue tracking +# Documentation at: https://forge.rust-lang.org/triagebot/review-queue-tracking.html +[assign.review_prefs] + [pr-tracking] # Enable issue transfers within the org @@ -1427,3 +1432,12 @@ compiletest = [ [behind-upstream] days-threshold = 14 + +# Canonicalize issue numbers to avoid closing the wrong issue +# when commits are included in subtrees, as well as warning links in commits. +# Documentation at: https://forge.rust-lang.org/triagebot/issue-links.html +[issue-links] + +# Prevents mentions in commits to avoid users being spammed +# Documentation at: https://forge.rust-lang.org/triagebot/no-mentions.html +[no-mentions]