From 9c4ead681480427806416cb09ea1f667bd6052b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 7 Jan 2026 16:44:51 +0100 Subject: [PATCH 01/32] Remove legacy homu `try` and `auto` branch mentions --- .github/workflows/ci.yml | 19 +++++++++---------- src/ci/citool/src/main.rs | 6 ++---- src/ci/scripts/verify-channel.sh | 3 +-- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb6bc325a3bb7..5d26d617ba88c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,9 @@ name: CI on: push: branches: - - auto - - try - - try-perf - - automation/bors/try - automation/bors/auto + - automation/bors/try + - try-perf pull_request: branches: - "**" @@ -33,9 +31,10 @@ defaults: 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.ref == 'refs/heads/automation/bors/try') && github.sha) || github.ref }} + # We add an exception for try builds (automation/bors/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-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" @@ -57,7 +56,7 @@ jobs: - name: Test citool # Only test citool on the auto branch, to reduce latency of the calculate matrix job # on PR/try builds. - if: ${{ github.ref == 'refs/heads/auto' || github.ref == 'refs/heads/automation/bors/auto' }} + if: ${{ github.ref == 'refs/heads/automation/bors/auto' }} run: | cd src/ci/citool CARGO_INCREMENTAL=0 cargo test @@ -80,7 +79,7 @@ jobs: # 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' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }} + environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }} env: CI_JOB_NAME: ${{ matrix.name }} CI_JOB_DOC_URL: ${{ matrix.doc_url }} @@ -314,7 +313,7 @@ jobs: needs: [ calculate_matrix, job ] # !cancelled() executes the job regardless of whether the previous jobs passed or failed if: ${{ !cancelled() && contains(fromJSON('["auto", "try"]'), needs.calculate_matrix.outputs.run_type) }} - 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' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }} + environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }} steps: - name: checkout the source code uses: actions/checkout@v5 diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index d7b491d38f41f..01c0650b3c98f 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -41,14 +41,12 @@ impl GitHubContext { match (self.event_name.as_str(), self.branch_ref.as_str()) { ("pull_request", _) => Some(RunType::PullRequest), ("push", "refs/heads/try-perf") => Some(RunType::TryJob { job_patterns: None }), - ("push", "refs/heads/try" | "refs/heads/automation/bors/try") => { + ("push", "refs/heads/automation/bors/try") => { let patterns = self.get_try_job_patterns(); let patterns = if !patterns.is_empty() { Some(patterns) } else { None }; Some(RunType::TryJob { job_patterns: patterns }) } - ("push", "refs/heads/auto" | "refs/heads/automation/bors/auto") => { - Some(RunType::AutoJob) - } + ("push", "refs/heads/automation/bors/auto") => Some(RunType::AutoJob), ("push", "refs/heads/main") => Some(RunType::MainJob), _ => None, } diff --git a/src/ci/scripts/verify-channel.sh b/src/ci/scripts/verify-channel.sh index cac8ba332ed19..286869e8a411b 100755 --- a/src/ci/scripts/verify-channel.sh +++ b/src/ci/scripts/verify-channel.sh @@ -8,8 +8,7 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" -if isCiBranch auto || isCiBranch try || isCiBranch try-perf || \ - isCiBranch automation/bors/try || isCiBranch automation/bors/auto; then +if isCiBranch try-perf || isCiBranch automation/bors/try || isCiBranch automation/bors/auto; then echo "channel verification is only executed on PR builds" exit fi From 98d3026c416a2a588bf271414a4695436cd7ef25 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 1 Jan 2026 20:19:21 -0800 Subject: [PATCH 02/32] Resolve intra-doc links to variant fields behind type aliases Previously, these failed to resolve, despite them working for struct fields or enum variants that were behind aliases. However, there is now an inconsistency where enum variant fields behind an alias resolve to the entry on the alias's page, while enum variants and struct fields resolve to the page of the backing ADT. --- .../passes/collect_intra_doc_links.rs | 35 ++++++++++--------- .../intra-doc/adt-through-alias.rs | 25 +++++++++++++ 2 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 tests/rustdoc-html/intra-doc/adt-through-alias.rs diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 3abf0fee3959a..44affba9a8c07 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -308,24 +308,27 @@ impl<'tcx> LinkCollector<'_, 'tcx> { let ty_res = self.resolve_path(path, TypeNS, item_id, module_id).ok_or_else(no_res)?; match ty_res { - Res::Def(DefKind::Enum, did) => match tcx.type_of(did).instantiate_identity().kind() { - ty::Adt(def, _) if def.is_enum() => { - if let Some(variant) = def.variants().iter().find(|v| v.name == variant_name) - && let Some(field) = - variant.fields.iter().find(|f| f.name == variant_field_name) - { - Ok((ty_res, field.did)) - } else { - Err(UnresolvedPath { - item_id, - module_id, - partial_res: Some(Res::Def(DefKind::Enum, def.did())), - unresolved: variant_field_name.to_string().into(), - }) + Res::Def(DefKind::Enum | DefKind::TyAlias, did) => { + match tcx.type_of(did).instantiate_identity().kind() { + ty::Adt(def, _) if def.is_enum() => { + if let Some(variant) = + def.variants().iter().find(|v| v.name == variant_name) + && let Some(field) = + variant.fields.iter().find(|f| f.name == variant_field_name) + { + Ok((ty_res, field.did)) + } else { + Err(UnresolvedPath { + item_id, + module_id, + partial_res: Some(Res::Def(DefKind::Enum, def.did())), + unresolved: variant_field_name.to_string().into(), + }) + } } + _ => unreachable!(), } - _ => unreachable!(), - }, + } _ => Err(UnresolvedPath { item_id, module_id, diff --git a/tests/rustdoc-html/intra-doc/adt-through-alias.rs b/tests/rustdoc-html/intra-doc/adt-through-alias.rs new file mode 100644 index 0000000000000..58e0f37edbabd --- /dev/null +++ b/tests/rustdoc-html/intra-doc/adt-through-alias.rs @@ -0,0 +1,25 @@ +#![crate_name = "foo"] + +//! [`TheStructAlias::the_field`] +//! [`TheEnumAlias::TheVariant`] +//! [`TheEnumAlias::TheVariant::the_field`] + +// FIXME: this should resolve to the alias's version +//@ has foo/index.html '//a[@href="struct.TheStruct.html#structfield.the_field"]' 'TheStructAlias::the_field' +// FIXME: this should resolve to the alias's version +//@ has foo/index.html '//a[@href="enum.TheEnum.html#variant.TheVariant"]' 'TheEnumAlias::TheVariant' +//@ has foo/index.html '//a[@href="type.TheEnumAlias.html#variant.TheVariant.field.the_field"]' 'TheEnumAlias::TheVariant::the_field' + +pub struct TheStruct { + pub the_field: i32, +} + +pub type TheStructAlias = TheStruct; + +pub enum TheEnum { + TheVariant { the_field: i32 }, +} + +pub type TheEnumAlias = TheEnum; + +fn main() {} From a90e0418c69e2f46000e918c404d4490cd5f2b4e Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 1 Jan 2026 20:29:43 -0800 Subject: [PATCH 03/32] rustdoc: Refactor `def_id_to_res` as `ty_to_res` The old name and API were confusing. In my opinion, looking up the type at the call site is clearer. --- src/librustdoc/passes/collect_intra_doc_links.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 44affba9a8c07..512f0f418702a 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -384,7 +384,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { }; match tcx.def_kind(self_id) { - DefKind::Impl { .. } => self.def_id_to_res(self_id), + DefKind::Impl { .. } => self.ty_to_res(tcx.type_of(self_id).instantiate_identity()), DefKind::Use => None, def_kind => Some(Res::Def(def_kind, self_id)), } @@ -506,12 +506,12 @@ impl<'tcx> LinkCollector<'_, 'tcx> { } } - /// Convert a DefId to a Res, where possible. + /// Convert a Ty to a Res, where possible. /// /// This is used for resolving type aliases. - fn def_id_to_res(&self, ty_id: DefId) -> Option { + fn ty_to_res(&self, ty: Ty<'tcx>) -> Option { use PrimitiveType::*; - Some(match *self.cx.tcx.type_of(ty_id).instantiate_identity().kind() { + Some(match *ty.kind() { ty::Bool => Res::Primitive(Bool), ty::Char => Res::Primitive(Char), ty::Int(ity) => Res::Primitive(ity.into()), @@ -614,7 +614,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { // Resolve the link on the type the alias points to. // FIXME: if the associated item is defined directly on the type alias, // it will show up on its documentation page, we should link there instead. - let Some(res) = self.def_id_to_res(did) else { return Vec::new() }; + let Some(res) = self.ty_to_res(tcx.type_of(did).instantiate_identity()) else { return Vec::new() }; self.resolve_associated_item(res, item_name, ns, disambiguator, module_id) } Res::Def( From 945f3f91712b2ccbfce0bde5c1d0f4ad36a69876 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 1 Jan 2026 23:46:46 -0800 Subject: [PATCH 04/32] rustdoc: Use trait as root `Res` instead of assoc item All the other parts of this function return the Res for the containing page, but for some reason, this returns the associated item itself. It doesn't seem to affect the behavior of rustdoc because e.g. the href functions use the parent if the DefId is for an assoc item. But it's clearer and simpler to be consistent. --- src/librustdoc/passes/collect_intra_doc_links.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 512f0f418702a..4233f0903b232 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -614,7 +614,9 @@ impl<'tcx> LinkCollector<'_, 'tcx> { // Resolve the link on the type the alias points to. // FIXME: if the associated item is defined directly on the type alias, // it will show up on its documentation page, we should link there instead. - let Some(res) = self.ty_to_res(tcx.type_of(did).instantiate_identity()) else { return Vec::new() }; + let Some(res) = self.ty_to_res(tcx.type_of(did).instantiate_identity()) else { + return Vec::new(); + }; self.resolve_associated_item(res, item_name, ns, disambiguator, module_id) } Res::Def( @@ -720,10 +722,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { Ident::with_dummy_span(item_name), ns, ) - .map(|item| { - let res = Res::Def(item.as_def_kind(), item.def_id); - (res, item.def_id) - }) + .map(|item| (root_res, item.def_id)) .collect::>(), _ => Vec::new(), } From f503b894e912532299fd7669844efbdebaedcfd7 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 1 Jan 2026 23:54:43 -0800 Subject: [PATCH 05/32] rustdoc: Extract helper function for resolving assoc items on ADTs --- .../passes/collect_intra_doc_links.rs | 200 ++++++++++-------- 1 file changed, 106 insertions(+), 94 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 4233f0903b232..cb3fd72bc1963 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -622,100 +622,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { Res::Def( def_kind @ (DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::ForeignTy), did, - ) => { - debug!("looking for associated item named {item_name} for item {did:?}"); - // Checks if item_name is a variant of the `SomeItem` enum - if ns == TypeNS && def_kind == DefKind::Enum { - match tcx.type_of(did).instantiate_identity().kind() { - ty::Adt(adt_def, _) => { - for variant in adt_def.variants() { - if variant.name == item_name { - return vec![(root_res, variant.def_id)]; - } - } - } - _ => unreachable!(), - } - } - - let search_for_field = || { - let (DefKind::Struct | DefKind::Union) = def_kind else { return vec![] }; - debug!("looking for fields named {item_name} for {did:?}"); - // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?) - // NOTE: it's different from variant_field because it only resolves struct fields, - // not variant fields (2 path segments, not 3). - // - // We need to handle struct (and union) fields in this code because - // syntactically their paths are identical to associated item paths: - // `module::Type::field` and `module::Type::Assoc`. - // - // On the other hand, variant fields can't be mistaken for associated - // items because they look like this: `module::Type::Variant::field`. - // - // Variants themselves don't need to be handled here, even though - // they also look like associated items (`module::Type::Variant`), - // because they are real Rust syntax (unlike the intra-doc links - // field syntax) and are handled by the compiler's resolver. - let ty::Adt(def, _) = tcx.type_of(did).instantiate_identity().kind() else { - unreachable!() - }; - def.non_enum_variant() - .fields - .iter() - .filter(|field| field.name == item_name) - .map(|field| (root_res, field.did)) - .collect::>() - }; - - if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator { - return search_for_field(); - } - - // Checks if item_name belongs to `impl SomeItem` - let mut assoc_items: Vec<_> = tcx - .inherent_impls(did) - .iter() - .flat_map(|&imp| { - filter_assoc_items_by_name_and_namespace( - tcx, - imp, - Ident::with_dummy_span(item_name), - ns, - ) - }) - .map(|item| (root_res, item.def_id)) - .collect(); - - if assoc_items.is_empty() { - // Check if item_name belongs to `impl SomeTrait for SomeItem` - // FIXME(#74563): This gives precedence to `impl SomeItem`: - // Although having both would be ambiguous, use impl version for compatibility's sake. - // To handle that properly resolve() would have to support - // something like [`ambi_fn`](::ambi_fn) - assoc_items = resolve_associated_trait_item( - tcx.type_of(did).instantiate_identity(), - module_id, - item_name, - ns, - self.cx, - ) - .into_iter() - .map(|item| (root_res, item.def_id)) - .collect::>(); - } - - debug!("got associated item {assoc_items:?}"); - - if !assoc_items.is_empty() { - return assoc_items; - } - - if ns != Namespace::ValueNS { - return Vec::new(); - } - - search_for_field() - } + ) => self.resolve_assoc_on_adt(), Res::Def(DefKind::Trait, did) => filter_assoc_items_by_name_and_namespace( tcx, did, @@ -727,6 +634,111 @@ impl<'tcx> LinkCollector<'_, 'tcx> { _ => Vec::new(), } } + + fn resolve_assoc_on_adt( + &mut self, + adt_def_kind: DefKind, + adt_def_id: DefId, + item_name: Symbol, + ns: Namespace, + disambiguator: Option, + module_id: DefId, + ) -> Vec<(Res, DefId)> { + let tcx = self.cx.tcx; + let root_res = Res::Def(adt_def_kind, adt_def_id); + debug!("looking for associated item named {item_name} for item {adt_def_id:?}"); + // Checks if item_name is a variant of the `SomeItem` enum + if ns == TypeNS && adt_def_kind == DefKind::Enum { + match tcx.type_of(adt_def_id).instantiate_identity().kind() { + ty::Adt(adt_def, _) => { + for variant in adt_def.variants() { + if variant.name == item_name { + return vec![(root_res, variant.def_id)]; + } + } + } + _ => unreachable!(), + } + } + + let search_for_field = || { + let (DefKind::Struct | DefKind::Union) = adt_def_kind else { return vec![] }; + debug!("looking for fields named {item_name} for {adt_def_id:?}"); + // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?) + // NOTE: it's different from variant_field because it only resolves struct fields, + // not variant fields (2 path segments, not 3). + // + // We need to handle struct (and union) fields in this code because + // syntactically their paths are identical to associated item paths: + // `module::Type::field` and `module::Type::Assoc`. + // + // On the other hand, variant fields can't be mistaken for associated + // items because they look like this: `module::Type::Variant::field`. + // + // Variants themselves don't need to be handled here, even though + // they also look like associated items (`module::Type::Variant`), + // because they are real Rust syntax (unlike the intra-doc links + // field syntax) and are handled by the compiler's resolver. + let ty::Adt(def, _) = tcx.type_of(adt_def_id).instantiate_identity().kind() else { + unreachable!() + }; + def.non_enum_variant() + .fields + .iter() + .filter(|field| field.name == item_name) + .map(|field| (root_res, field.did)) + .collect::>() + }; + + if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator { + return search_for_field(); + } + + // Checks if item_name belongs to `impl SomeItem` + let mut assoc_items: Vec<_> = tcx + .inherent_impls(adt_def_id) + .iter() + .flat_map(|&imp| { + filter_assoc_items_by_name_and_namespace( + tcx, + imp, + Ident::with_dummy_span(item_name), + ns, + ) + }) + .map(|item| (root_res, item.def_id)) + .collect(); + + if assoc_items.is_empty() { + // Check if item_name belongs to `impl SomeTrait for SomeItem` + // FIXME(#74563): This gives precedence to `impl SomeItem`: + // Although having both would be ambiguous, use impl version for compatibility's sake. + // To handle that properly resolve() would have to support + // something like [`ambi_fn`](::ambi_fn) + assoc_items = resolve_associated_trait_item( + tcx.type_of(adt_def_id).instantiate_identity(), + module_id, + item_name, + ns, + self.cx, + ) + .into_iter() + .map(|item| (root_res, item.def_id)) + .collect::>(); + } + + debug!("got associated item {assoc_items:?}"); + + if !assoc_items.is_empty() { + return assoc_items; + } + + if ns != Namespace::ValueNS { + return Vec::new(); + } + + search_for_field() + } } fn full_res(tcx: TyCtxt<'_>, (base, assoc_item): (Res, Option)) -> Res { From 2c98517faea2f0dc5b96282c2677cdb4628abd34 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 2 Jan 2026 00:36:30 -0800 Subject: [PATCH 06/32] Refactor out many free functions from intra-doc link code --- .../passes/collect_intra_doc_links.rs | 564 +++++++++--------- 1 file changed, 272 insertions(+), 292 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index cb3fd72bc1963..553d8636c85ba 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -338,58 +338,6 @@ impl<'tcx> LinkCollector<'_, 'tcx> { } } - /// Given a primitive type, try to resolve an associated item. - fn resolve_primitive_associated_item( - &self, - prim_ty: PrimitiveType, - ns: Namespace, - item_name: Symbol, - ) -> Vec<(Res, DefId)> { - let tcx = self.cx.tcx; - - prim_ty - .impls(tcx) - .flat_map(|impl_| { - filter_assoc_items_by_name_and_namespace( - tcx, - impl_, - Ident::with_dummy_span(item_name), - ns, - ) - .map(|item| (Res::Primitive(prim_ty), item.def_id)) - }) - .collect::>() - } - - fn resolve_self_ty(&self, path_str: &str, ns: Namespace, item_id: DefId) -> Option { - if ns != TypeNS || path_str != "Self" { - return None; - } - - let tcx = self.cx.tcx; - let self_id = match tcx.def_kind(item_id) { - def_kind @ (DefKind::AssocFn - | DefKind::AssocConst - | DefKind::AssocTy - | DefKind::Variant - | DefKind::Field) => { - let parent_def_id = tcx.parent(item_id); - if def_kind == DefKind::Field && tcx.def_kind(parent_def_id) == DefKind::Variant { - tcx.parent(parent_def_id) - } else { - parent_def_id - } - } - _ => item_id, - }; - - match tcx.def_kind(self_id) { - DefKind::Impl { .. } => self.ty_to_res(tcx.type_of(self_id).instantiate_identity()), - DefKind::Use => None, - def_kind => Some(Res::Def(def_kind, self_id)), - } - } - /// Convenience wrapper around `doc_link_resolutions`. /// /// This also handles resolving `true` and `false` as booleans. @@ -402,7 +350,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { item_id: DefId, module_id: DefId, ) -> Option { - if let res @ Some(..) = self.resolve_self_ty(path_str, ns, item_id) { + if let res @ Some(..) = resolve_self_ty(self.cx.tcx, path_str, ns, item_id) { return res; } @@ -432,13 +380,15 @@ impl<'tcx> LinkCollector<'_, 'tcx> { /// Resolves a string as a path within a particular namespace. Returns an /// optional URL fragment in the case of variants and methods. fn resolve<'path>( - &mut self, + &self, path_str: &'path str, ns: Namespace, disambiguator: Option, item_id: DefId, module_id: DefId, ) -> Result)>, UnresolvedPath<'path>> { + let tcx = self.cx.tcx; + if let Some(res) = self.resolve_path(path_str, ns, item_id, module_id) { return Ok(match res { Res::Def( @@ -484,7 +434,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { match resolve_primitive(path_root, TypeNS) .or_else(|| self.resolve_path(path_root, TypeNS, item_id, module_id)) .map(|ty_res| { - self.resolve_associated_item(ty_res, item_name, ns, disambiguator, module_id) + resolve_associated_item(tcx, ty_res, item_name, ns, disambiguator, module_id) .into_iter() .map(|(res, def_id)| (res, Some(def_id))) .collect::>() @@ -505,244 +455,280 @@ impl<'tcx> LinkCollector<'_, 'tcx> { } } } +} - /// Convert a Ty to a Res, where possible. - /// - /// This is used for resolving type aliases. - fn ty_to_res(&self, ty: Ty<'tcx>) -> Option { - use PrimitiveType::*; - Some(match *ty.kind() { - ty::Bool => Res::Primitive(Bool), - ty::Char => Res::Primitive(Char), - ty::Int(ity) => Res::Primitive(ity.into()), - ty::Uint(uty) => Res::Primitive(uty.into()), - ty::Float(fty) => Res::Primitive(fty.into()), - ty::Str => Res::Primitive(Str), - ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), - ty::Tuple(_) => Res::Primitive(Tuple), - ty::Pat(..) => Res::Primitive(Pat), - ty::Array(..) => Res::Primitive(Array), - ty::Slice(_) => Res::Primitive(Slice), - ty::RawPtr(_, _) => Res::Primitive(RawPointer), - ty::Ref(..) => Res::Primitive(Reference), - ty::FnDef(..) => panic!("type alias to a function definition"), - ty::FnPtr(..) => Res::Primitive(Fn), - ty::Never => Res::Primitive(Never), - ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => { - Res::from_def_id(self.cx.tcx, did) - } - ty::Alias(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Dynamic(..) - | ty::UnsafeBinder(_) - | ty::Param(_) - | ty::Bound(..) - | ty::Placeholder(_) - | ty::Infer(_) - | ty::Error(_) => return None, - }) - } - - /// Convert a PrimitiveType to a Ty, where possible. - /// - /// This is used for resolving trait impls for primitives - fn primitive_type_to_ty(&mut self, prim: PrimitiveType) -> Option> { - use PrimitiveType::*; - let tcx = self.cx.tcx; +fn full_res(tcx: TyCtxt<'_>, (base, assoc_item): (Res, Option)) -> Res { + assoc_item.map_or(base, |def_id| Res::from_def_id(tcx, def_id)) +} - // FIXME: Only simple types are supported here, see if we can support - // other types such as Tuple, Array, Slice, etc. - // See https://github.com/rust-lang/rust/issues/90703#issuecomment-1004263455 - Some(match prim { - Bool => tcx.types.bool, - Str => tcx.types.str_, - Char => tcx.types.char, - Never => tcx.types.never, - I8 => tcx.types.i8, - I16 => tcx.types.i16, - I32 => tcx.types.i32, - I64 => tcx.types.i64, - I128 => tcx.types.i128, - Isize => tcx.types.isize, - F16 => tcx.types.f16, - F32 => tcx.types.f32, - F64 => tcx.types.f64, - F128 => tcx.types.f128, - U8 => tcx.types.u8, - U16 => tcx.types.u16, - U32 => tcx.types.u32, - U64 => tcx.types.u64, - U128 => tcx.types.u128, - Usize => tcx.types.usize, - _ => return None, +/// Given a primitive type, try to resolve an associated item. +fn resolve_primitive_associated_item<'tcx>( + tcx: TyCtxt<'tcx>, + prim_ty: PrimitiveType, + ns: Namespace, + item_ident: Ident, +) -> Vec<(Res, DefId)> { + prim_ty + .impls(tcx) + .flat_map(|impl_| { + filter_assoc_items_by_name_and_namespace(tcx, impl_, item_ident, ns) + .map(|item| (Res::Primitive(prim_ty), item.def_id)) }) - } + .collect::>() +} - /// Resolve an associated item, returning its containing page's `Res` - /// and the fragment targeting the associated item on its page. - fn resolve_associated_item( - &mut self, - root_res: Res, - item_name: Symbol, - ns: Namespace, - disambiguator: Option, - module_id: DefId, - ) -> Vec<(Res, DefId)> { - let tcx = self.cx.tcx; +fn resolve_self_ty<'tcx>( + tcx: TyCtxt<'tcx>, + path_str: &str, + ns: Namespace, + item_id: DefId, +) -> Option { + if ns != TypeNS || path_str != "Self" { + return None; + } - match root_res { - Res::Primitive(prim) => { - let items = self.resolve_primitive_associated_item(prim, ns, item_name); - if !items.is_empty() { - items - // Inherent associated items take precedence over items that come from trait impls. - } else { - self.primitive_type_to_ty(prim) - .map(|ty| { - resolve_associated_trait_item(ty, module_id, item_name, ns, self.cx) - .iter() - .map(|item| (root_res, item.def_id)) - .collect::>() - }) - .unwrap_or_default() - } - } - Res::Def(DefKind::TyAlias, did) => { - // Resolve the link on the type the alias points to. - // FIXME: if the associated item is defined directly on the type alias, - // it will show up on its documentation page, we should link there instead. - let Some(res) = self.ty_to_res(tcx.type_of(did).instantiate_identity()) else { - return Vec::new(); - }; - self.resolve_associated_item(res, item_name, ns, disambiguator, module_id) + let self_id = match tcx.def_kind(item_id) { + def_kind @ (DefKind::AssocFn + | DefKind::AssocConst + | DefKind::AssocTy + | DefKind::Variant + | DefKind::Field) => { + let parent_def_id = tcx.parent(item_id); + if def_kind == DefKind::Field && tcx.def_kind(parent_def_id) == DefKind::Variant { + tcx.parent(parent_def_id) + } else { + parent_def_id } - Res::Def( - def_kind @ (DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::ForeignTy), - did, - ) => self.resolve_assoc_on_adt(), - Res::Def(DefKind::Trait, did) => filter_assoc_items_by_name_and_namespace( - tcx, - did, - Ident::with_dummy_span(item_name), - ns, - ) - .map(|item| (root_res, item.def_id)) - .collect::>(), - _ => Vec::new(), } + _ => item_id, + }; + + match tcx.def_kind(self_id) { + DefKind::Impl { .. } => ty_to_res(tcx, tcx.type_of(self_id).instantiate_identity()), + DefKind::Use => None, + def_kind => Some(Res::Def(def_kind, self_id)), } +} - fn resolve_assoc_on_adt( - &mut self, - adt_def_kind: DefKind, - adt_def_id: DefId, - item_name: Symbol, - ns: Namespace, - disambiguator: Option, - module_id: DefId, - ) -> Vec<(Res, DefId)> { - let tcx = self.cx.tcx; - let root_res = Res::Def(adt_def_kind, adt_def_id); - debug!("looking for associated item named {item_name} for item {adt_def_id:?}"); - // Checks if item_name is a variant of the `SomeItem` enum - if ns == TypeNS && adt_def_kind == DefKind::Enum { - match tcx.type_of(adt_def_id).instantiate_identity().kind() { - ty::Adt(adt_def, _) => { - for variant in adt_def.variants() { - if variant.name == item_name { - return vec![(root_res, variant.def_id)]; - } - } - } - _ => unreachable!(), - } +/// Convert a Ty to a Res, where possible. +/// +/// This is used for resolving type aliases. +fn ty_to_res<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + use PrimitiveType::*; + Some(match *ty.kind() { + ty::Bool => Res::Primitive(Bool), + ty::Char => Res::Primitive(Char), + ty::Int(ity) => Res::Primitive(ity.into()), + ty::Uint(uty) => Res::Primitive(uty.into()), + ty::Float(fty) => Res::Primitive(fty.into()), + ty::Str => Res::Primitive(Str), + ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), + ty::Tuple(_) => Res::Primitive(Tuple), + ty::Pat(..) => Res::Primitive(Pat), + ty::Array(..) => Res::Primitive(Array), + ty::Slice(_) => Res::Primitive(Slice), + ty::RawPtr(_, _) => Res::Primitive(RawPointer), + ty::Ref(..) => Res::Primitive(Reference), + ty::FnDef(..) => panic!("type alias to a function definition"), + ty::FnPtr(..) => Res::Primitive(Fn), + ty::Never => Res::Primitive(Never), + ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => { + Res::from_def_id(tcx, did) } + ty::Alias(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::UnsafeBinder(_) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(_) + | ty::Infer(_) + | ty::Error(_) => return None, + }) +} - let search_for_field = || { - let (DefKind::Struct | DefKind::Union) = adt_def_kind else { return vec![] }; - debug!("looking for fields named {item_name} for {adt_def_id:?}"); - // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?) - // NOTE: it's different from variant_field because it only resolves struct fields, - // not variant fields (2 path segments, not 3). - // - // We need to handle struct (and union) fields in this code because - // syntactically their paths are identical to associated item paths: - // `module::Type::field` and `module::Type::Assoc`. - // - // On the other hand, variant fields can't be mistaken for associated - // items because they look like this: `module::Type::Variant::field`. - // - // Variants themselves don't need to be handled here, even though - // they also look like associated items (`module::Type::Variant`), - // because they are real Rust syntax (unlike the intra-doc links - // field syntax) and are handled by the compiler's resolver. - let ty::Adt(def, _) = tcx.type_of(adt_def_id).instantiate_identity().kind() else { - unreachable!() - }; - def.non_enum_variant() - .fields - .iter() - .filter(|field| field.name == item_name) - .map(|field| (root_res, field.did)) - .collect::>() - }; +/// Convert a PrimitiveType to a Ty, where possible. +/// +/// This is used for resolving trait impls for primitives +fn primitive_type_to_ty<'tcx>(tcx: TyCtxt<'tcx>, prim: PrimitiveType) -> Option> { + use PrimitiveType::*; + + // FIXME: Only simple types are supported here, see if we can support + // other types such as Tuple, Array, Slice, etc. + // See https://github.com/rust-lang/rust/issues/90703#issuecomment-1004263455 + Some(match prim { + Bool => tcx.types.bool, + Str => tcx.types.str_, + Char => tcx.types.char, + Never => tcx.types.never, + I8 => tcx.types.i8, + I16 => tcx.types.i16, + I32 => tcx.types.i32, + I64 => tcx.types.i64, + I128 => tcx.types.i128, + Isize => tcx.types.isize, + F16 => tcx.types.f16, + F32 => tcx.types.f32, + F64 => tcx.types.f64, + F128 => tcx.types.f128, + U8 => tcx.types.u8, + U16 => tcx.types.u16, + U32 => tcx.types.u32, + U64 => tcx.types.u64, + U128 => tcx.types.u128, + Usize => tcx.types.usize, + _ => return None, + }) +} - if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator { - return search_for_field(); +/// Resolve an associated item, returning its containing page's `Res` +/// and the fragment targeting the associated item on its page. +fn resolve_associated_item<'tcx>( + tcx: TyCtxt<'tcx>, + root_res: Res, + item_name: Symbol, + ns: Namespace, + disambiguator: Option, + module_id: DefId, +) -> Vec<(Res, DefId)> { + let item_ident = Ident::with_dummy_span(item_name); + + match root_res { + Res::Primitive(prim) => { + let items = resolve_primitive_associated_item(tcx, prim, ns, item_ident); + if !items.is_empty() { + items + // Inherent associated items take precedence over items that come from trait impls. + } else { + primitive_type_to_ty(tcx, prim) + .map(|ty| { + resolve_associated_trait_item(ty, module_id, item_ident, ns, tcx) + .iter() + .map(|item| (root_res, item.def_id)) + .collect::>() + }) + .unwrap_or_default() + } + } + Res::Def(DefKind::TyAlias, did) => { + // Resolve the link on the type the alias points to. + // FIXME: if the associated item is defined directly on the type alias, + // it will show up on its documentation page, we should link there instead. + let Some(res) = ty_to_res(tcx, tcx.type_of(did).instantiate_identity()) else { + return Vec::new(); + }; + resolve_associated_item(tcx, res, item_name, ns, disambiguator, module_id) + } + Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, did) => { + resolve_assoc_on_adt(tcx, did, item_name, ns, disambiguator, module_id) } + Res::Def(DefKind::ForeignTy, did) => { + resolve_assoc_on_simple_type(tcx, did, item_ident, ns, module_id) + } + Res::Def(DefKind::Trait, did) => filter_assoc_items_by_name_and_namespace( + tcx, + did, + Ident::with_dummy_span(item_name), + ns, + ) + .map(|item| (root_res, item.def_id)) + .collect::>(), + _ => Vec::new(), + } +} - // Checks if item_name belongs to `impl SomeItem` - let mut assoc_items: Vec<_> = tcx - .inherent_impls(adt_def_id) - .iter() - .flat_map(|&imp| { - filter_assoc_items_by_name_and_namespace( - tcx, - imp, - Ident::with_dummy_span(item_name), - ns, - ) - }) - .map(|item| (root_res, item.def_id)) - .collect(); +fn resolve_assoc_on_adt<'tcx>( + tcx: TyCtxt<'tcx>, + adt_def_id: DefId, + item_name: Symbol, + ns: Namespace, + disambiguator: Option, + module_id: DefId, +) -> Vec<(Res, DefId)> { + debug!("looking for associated item named {item_name} for item {adt_def_id:?}"); + let root_res = Res::from_def_id(tcx, adt_def_id); + let adt_ty = tcx.type_of(adt_def_id).instantiate_identity(); + let adt_def = adt_ty.ty_adt_def().expect("must be ADT"); + let item_ident = Ident::with_dummy_span(item_name); + // Checks if item_name is a variant of the `SomeItem` enum + if ns == TypeNS && adt_def.is_enum() { + for variant in adt_def.variants() { + if variant.name == item_name { + return vec![(root_res, variant.def_id)]; + } + } + } - if assoc_items.is_empty() { - // Check if item_name belongs to `impl SomeTrait for SomeItem` - // FIXME(#74563): This gives precedence to `impl SomeItem`: - // Although having both would be ambiguous, use impl version for compatibility's sake. - // To handle that properly resolve() would have to support - // something like [`ambi_fn`](::ambi_fn) - assoc_items = resolve_associated_trait_item( - tcx.type_of(adt_def_id).instantiate_identity(), - module_id, - item_name, - ns, - self.cx, - ) + if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator + && (adt_def.is_struct() || adt_def.is_union()) + { + return resolve_structfield(adt_def, item_name) .into_iter() - .map(|item| (root_res, item.def_id)) - .collect::>(); - } + .map(|did| (root_res, did)) + .collect(); + } - debug!("got associated item {assoc_items:?}"); + let assoc_items = resolve_assoc_on_simple_type(tcx, adt_def_id, item_ident, ns, module_id); + if !assoc_items.is_empty() { + return assoc_items; + } - if !assoc_items.is_empty() { - return assoc_items; - } + if ns == Namespace::ValueNS && (adt_def.is_struct() || adt_def.is_union()) { + return resolve_structfield(adt_def, item_name) + .into_iter() + .map(|did| (root_res, did)) + .collect(); + } - if ns != Namespace::ValueNS { - return Vec::new(); - } + vec![] +} - search_for_field() +/// "Simple" i.e. an ADT, foreign type, etc. -- not a type alias, primitive type, or other trickier type. +fn resolve_assoc_on_simple_type<'tcx>( + tcx: TyCtxt<'tcx>, + ty_def_id: DefId, + item_ident: Ident, + ns: Namespace, + module_id: DefId, +) -> Vec<(Res, DefId)> { + let root_res = Res::from_def_id(tcx, ty_def_id); + // Checks if item_name belongs to `impl SomeItem` + let inherent_assoc_items: Vec<_> = tcx + .inherent_impls(ty_def_id) + .iter() + .flat_map(|&imp| filter_assoc_items_by_name_and_namespace(tcx, imp, item_ident, ns)) + .map(|item| (root_res, item.def_id)) + .collect(); + debug!("got inherent assoc items {inherent_assoc_items:?}"); + if !inherent_assoc_items.is_empty() { + return inherent_assoc_items; } + + // Check if item_name belongs to `impl SomeTrait for SomeItem` + // FIXME(#74563): This gives precedence to `impl SomeItem`: + // Although having both would be ambiguous, use impl version for compatibility's sake. + // To handle that properly resolve() would have to support + // something like [`ambi_fn`](::ambi_fn) + let ty = tcx.type_of(ty_def_id).instantiate_identity(); + let trait_assoc_items = resolve_associated_trait_item(ty, module_id, item_ident, ns, tcx) + .into_iter() + .map(|item| (root_res, item.def_id)) + .collect::>(); + debug!("got trait assoc items {trait_assoc_items:?}"); + trait_assoc_items } -fn full_res(tcx: TyCtxt<'_>, (base, assoc_item): (Res, Option)) -> Res { - assoc_item.map_or(base, |def_id| Res::from_def_id(tcx, def_id)) +fn resolve_structfield<'tcx>(adt_def: ty::AdtDef<'tcx>, item_name: Symbol) -> Option { + debug!("looking for fields named {item_name} for {adt_def:?}"); + adt_def + .non_enum_variant() + .fields + .iter() + .find(|field| field.name == item_name) + .map(|field| field.did) } /// Look to see if a resolved item has an associated item named `item_name`. @@ -750,12 +736,12 @@ fn full_res(tcx: TyCtxt<'_>, (base, assoc_item): (Res, Option)) -> Res { /// Given `[std::io::Error::source]`, where `source` is unresolved, this would /// find `std::error::Error::source` and return /// `::source`. -fn resolve_associated_trait_item<'a>( - ty: Ty<'a>, +fn resolve_associated_trait_item<'tcx>( + ty: Ty<'tcx>, module: DefId, - item_name: Symbol, + item_ident: Ident, ns: Namespace, - cx: &mut DocContext<'a>, + tcx: TyCtxt<'tcx>, ) -> Vec { // FIXME: this should also consider blanket impls (`impl X for T`). Unfortunately // `get_auto_trait_and_blanket_impls` is broken because the caching behavior is wrong. In the @@ -763,22 +749,17 @@ fn resolve_associated_trait_item<'a>( // Next consider explicit impls: `impl MyTrait for MyType` // Give precedence to inherent impls. - let traits = trait_impls_for(cx, ty, module); - let tcx = cx.tcx; + let traits = trait_impls_for(tcx, ty, module); debug!("considering traits {traits:?}"); let candidates = traits .iter() .flat_map(|&(impl_, trait_)| { - filter_assoc_items_by_name_and_namespace( - tcx, - trait_, - Ident::with_dummy_span(item_name), - ns, + filter_assoc_items_by_name_and_namespace(tcx, trait_, item_ident, ns).map( + move |trait_assoc| { + trait_assoc_to_impl_assoc_item(tcx, impl_, trait_assoc.def_id) + .unwrap_or(*trait_assoc) + }, ) - .map(move |trait_assoc| { - trait_assoc_to_impl_assoc_item(tcx, impl_, trait_assoc.def_id) - .unwrap_or(*trait_assoc) - }) }) .collect::>(); // FIXME(#74563): warn about ambiguity @@ -813,13 +794,12 @@ fn trait_assoc_to_impl_assoc_item<'tcx>( /// /// NOTE: this cannot be a query because more traits could be available when more crates are compiled! /// So it is not stable to serialize cross-crate. -#[instrument(level = "debug", skip(cx))] -fn trait_impls_for<'a>( - cx: &mut DocContext<'a>, - ty: Ty<'a>, +#[instrument(level = "debug", skip(tcx))] +fn trait_impls_for<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, module: DefId, ) -> FxIndexSet<(DefId, DefId)> { - let tcx = cx.tcx; let mut impls = FxIndexSet::default(); for &trait_ in tcx.doc_link_traits_in_scope(module) { @@ -1535,7 +1515,7 @@ impl LinkCollector<'_, '_> { } None => { // Try everything! - let mut candidate = |ns| { + let candidate = |ns| { self.resolve(path_str, ns, None, item_id, module_id) .map_err(ResolutionFailure::NotResolved) }; @@ -1921,7 +1901,7 @@ fn report_diagnostic( /// handled earlier. For example, if passed `Item::Crate(std)` and `path_str` /// `std::io::Error::x`, this will resolve `std::io::Error`. fn resolution_failure( - collector: &mut LinkCollector<'_, '_>, + collector: &LinkCollector<'_, '_>, diag_info: DiagnosticInfo<'_>, path_str: &str, disambiguator: Option, From 291d0a9a4b175b61b7c499551751ad5023b38bbc Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 6 Jan 2026 16:08:10 -0500 Subject: [PATCH 07/32] diagnostics: make implicit Sized bounds explicit in E0277 When a trait parameter depends upon Sized, the error message only referred to the full trait itself and didn't mention Sized. This makes the failure to implement Sized explicit. It also notes when the Sized trait bound is explicit or implicit. --- .../src/error_reporting/traits/suggestions.rs | 57 +++++++++++--- .../substs-ppaux.normal.stderr | 2 +- .../substs-ppaux.verbose.stderr | 2 +- tests/ui/error-codes/E0277-4.rs | 50 ++++++++++++ tests/ui/error-codes/E0277-4.stderr | 77 +++++++++++++++++++ tests/ui/recursion/issue-23122-2.stderr | 2 +- tests/ui/suggestions/slice-issue-87994.stderr | 10 +-- ...-iterator-deref-in-for-loop.current.stderr | 4 +- ...dyn-iterator-deref-in-for-loop.next.stderr | 4 +- tests/ui/traits/issue-18400.stderr | 2 +- 10 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 tests/ui/error-codes/E0277-4.rs create mode 100644 tests/ui/error-codes/E0277-4.stderr 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 511e9e85b5f60..08a3fd4a0bb30 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1391,25 +1391,45 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // return early in the caller. let mut label = || { + // Special case `Sized` as `old_pred` will be the trait itself instead of + // `Sized` when the trait bound is the source of the error. + let is_sized = match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => { + self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized) + } + _ => false, + }; + let msg = format!( "the trait bound `{}` is not satisfied", self.tcx.short_string(old_pred, err.long_ty_path()), ); - let self_ty_str = - self.tcx.short_string(old_pred.self_ty().skip_binder(), err.long_ty_path()); + let self_ty_str = self.tcx.short_string(old_pred.self_ty(), err.long_ty_path()); let trait_path = self .tcx .short_string(old_pred.print_modifiers_and_trait_path(), err.long_ty_path()); if has_custom_message { + let msg = if is_sized { + "the trait bound `Sized` is not satisfied".into() + } else { + msg + }; err.note(msg); } else { err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)]; } - err.span_label( - span, - format!("the trait `{trait_path}` is not implemented for `{self_ty_str}`"), - ); + if is_sized { + err.span_label( + span, + format!("the trait `Sized` is not implemented for `{self_ty_str}`"), + ); + } else { + err.span_label( + span, + format!("the trait `{trait_path}` is not implemented for `{self_ty_str}`"), + ); + } }; let mut sugg_prefixes = vec![]; @@ -3578,10 +3598,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "unsatisfied trait bound introduced in this `derive` macro", ); } else if !data.span.is_dummy() && !data.span.overlaps(self_ty.span) { - spans.push_span_label( - data.span, - "unsatisfied trait bound introduced here", - ); + // `Sized` may be an explicit or implicit trait bound. If it is + // implicit, mention it as such. + if let Some(pred) = predicate.as_trait_clause() + && self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) + && self + .tcx + .generics_of(data.impl_or_alias_def_id) + .own_params + .iter() + .any(|param| self.tcx.def_span(param.def_id) == data.span) + { + spans.push_span_label( + data.span, + "unsatisfied trait bound implicitly introduced here", + ); + } else { + spans.push_span_label( + data.span, + "unsatisfied trait bound introduced here", + ); + } } err.span_note(spans, msg); point_at_assoc_type_restriction( diff --git a/tests/ui/associated-types/substs-ppaux.normal.stderr b/tests/ui/associated-types/substs-ppaux.normal.stderr index aa2aca7426e1a..b1c9ce3f22b6a 100644 --- a/tests/ui/associated-types/substs-ppaux.normal.stderr +++ b/tests/ui/associated-types/substs-ppaux.normal.stderr @@ -88,7 +88,7 @@ note: required for `str` to implement `Foo<'_, '_, u8>` LL | impl<'a, 'b, T, S> Foo<'a, 'b, S> for T {} | - ^^^^^^^^^^^^^^ ^ | | - | unsatisfied trait bound introduced here + | unsatisfied trait bound implicitly introduced here error: aborting due to 5 previous errors diff --git a/tests/ui/associated-types/substs-ppaux.verbose.stderr b/tests/ui/associated-types/substs-ppaux.verbose.stderr index 23cf222a1d37e..46cae5db56fdd 100644 --- a/tests/ui/associated-types/substs-ppaux.verbose.stderr +++ b/tests/ui/associated-types/substs-ppaux.verbose.stderr @@ -88,7 +88,7 @@ note: required for `str` to implement `Foo<'?0, '?1, u8>` LL | impl<'a, 'b, T, S> Foo<'a, 'b, S> for T {} | - ^^^^^^^^^^^^^^ ^ | | - | unsatisfied trait bound introduced here + | unsatisfied trait bound implicitly introduced here error: aborting due to 5 previous errors diff --git a/tests/ui/error-codes/E0277-4.rs b/tests/ui/error-codes/E0277-4.rs new file mode 100644 index 0000000000000..ead57f00dde50 --- /dev/null +++ b/tests/ui/error-codes/E0277-4.rs @@ -0,0 +1,50 @@ +use std::fmt::Display; + +trait ImplicitTrait { + fn foo(&self); +} + +trait ExplicitTrait { + fn foo(&self); +} + +trait DisplayTrait { + fn foo(&self); +} + +trait UnimplementedTrait { + fn foo(&self); +} + +// Implicitly requires `T: Sized`. +impl ImplicitTrait for T { + fn foo(&self) {} +} + +// Explicitly requires `T: Sized`. +impl ExplicitTrait for T { + fn foo(&self) {} +} + +// Requires `T: Display`. +impl DisplayTrait for T { + fn foo(&self) {} +} + +fn main() { + // `[u8]` does not implement `Sized`. + let x: &[u8] = &[]; + ImplicitTrait::foo(x); + //~^ ERROR: the trait bound `[u8]: ImplicitTrait` is not satisfied [E0277] + ExplicitTrait::foo(x); + //~^ ERROR: the trait bound `[u8]: ExplicitTrait` is not satisfied [E0277] + + // `UnimplementedTrait` has no implementations. + UnimplementedTrait::foo(x); + //~^ ERROR: the trait bound `[u8]: UnimplementedTrait` is not satisfied [E0277] + + // `[u8; 0]` implements `Sized` but not `Display`. + let x: &[u8; 0] = &[]; + DisplayTrait::foo(x); + //~^ ERROR: the trait bound `[u8; 0]: DisplayTrait` is not satisfied [E0277] +} diff --git a/tests/ui/error-codes/E0277-4.stderr b/tests/ui/error-codes/E0277-4.stderr new file mode 100644 index 0000000000000..f38b8146a63d2 --- /dev/null +++ b/tests/ui/error-codes/E0277-4.stderr @@ -0,0 +1,77 @@ +error[E0277]: the trait bound `[u8]: ImplicitTrait` is not satisfied + --> $DIR/E0277-4.rs:37:24 + | +LL | ImplicitTrait::foo(x); + | ------------------ ^ the trait `Sized` is not implemented for `[u8]` + | | + | required by a bound introduced by this call + | +note: required for `[u8]` to implement `ImplicitTrait` + --> $DIR/E0277-4.rs:20:9 + | +LL | impl ImplicitTrait for T { + | - ^^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound implicitly introduced here +help: consider borrowing here + | +LL | ImplicitTrait::foo(&x); + | + +LL | ImplicitTrait::foo(&mut x); + | ++++ + +error[E0277]: the trait bound `[u8]: ExplicitTrait` is not satisfied + --> $DIR/E0277-4.rs:39:24 + | +LL | ExplicitTrait::foo(x); + | ------------------ ^ the trait `Sized` is not implemented for `[u8]` + | | + | required by a bound introduced by this call + | +note: required for `[u8]` to implement `ExplicitTrait` + --> $DIR/E0277-4.rs:25:16 + | +LL | impl ExplicitTrait for T { + | ----- ^^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +help: consider borrowing here + | +LL | ExplicitTrait::foo(&x); + | + +LL | ExplicitTrait::foo(&mut x); + | ++++ + +error[E0277]: the trait bound `[u8]: UnimplementedTrait` is not satisfied + --> $DIR/E0277-4.rs:43:29 + | +LL | UnimplementedTrait::foo(x); + | ----------------------- ^ the trait `UnimplementedTrait` is not implemented for `[u8]` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/E0277-4.rs:15:1 + | +LL | trait UnimplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `[u8; 0]: DisplayTrait` is not satisfied + --> $DIR/E0277-4.rs:48:23 + | +LL | DisplayTrait::foo(x); + | ----------------- ^ the trait `std::fmt::Display` is not implemented for `[u8; 0]` + | | + | required by a bound introduced by this call + | +note: required for `[u8; 0]` to implement `DisplayTrait` + --> $DIR/E0277-4.rs:30:18 + | +LL | impl DisplayTrait for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/recursion/issue-23122-2.stderr b/tests/ui/recursion/issue-23122-2.stderr index de402d65e6d6a..39cd0eb35a630 100644 --- a/tests/ui/recursion/issue-23122-2.stderr +++ b/tests/ui/recursion/issue-23122-2.stderr @@ -11,7 +11,7 @@ note: required for `GetNext<<<<... as Next>::Next as Next>::Next as Next>::Next> LL | impl Next for GetNext { | - ^^^^ ^^^^^^^^^^ | | - | unsatisfied trait bound introduced here + | unsatisfied trait bound implicitly introduced here = note: the full name for the type has been written to '$TEST_BUILD_DIR/issue-23122-2.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/suggestions/slice-issue-87994.stderr b/tests/ui/suggestions/slice-issue-87994.stderr index 22ad5d352120d..dd5c00af90b22 100644 --- a/tests/ui/suggestions/slice-issue-87994.stderr +++ b/tests/ui/suggestions/slice-issue-87994.stderr @@ -17,11 +17,10 @@ error[E0277]: `[i32]` is not an iterator --> $DIR/slice-issue-87994.rs:3:12 | LL | for _ in v[1..] { - | ^^^^^^ the trait `IntoIterator` is not implemented for `[i32]` + | ^^^^^^ the trait `Sized` is not implemented for `[i32]` | - = note: the trait bound `[i32]: IntoIterator` is not satisfied + = note: the trait bound `Sized` is not satisfied = note: required for `[i32]` to implement `IntoIterator` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider borrowing here | LL | for _ in &v[1..] { @@ -48,11 +47,10 @@ error[E0277]: `[K]` is not an iterator --> $DIR/slice-issue-87994.rs:11:13 | LL | for i2 in v2[1..] { - | ^^^^^^^ the trait `IntoIterator` is not implemented for `[K]` + | ^^^^^^^ the trait `Sized` is not implemented for `[K]` | - = note: the trait bound `[K]: IntoIterator` is not satisfied + = note: the trait bound `Sized` is not satisfied = note: required for `[K]` to implement `IntoIterator` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider borrowing here | LL | for i2 in &v2[1..] { diff --git a/tests/ui/traits/dyn-iterator-deref-in-for-loop.current.stderr b/tests/ui/traits/dyn-iterator-deref-in-for-loop.current.stderr index b5a9b82a93a3e..0dd2ffffdfeaf 100644 --- a/tests/ui/traits/dyn-iterator-deref-in-for-loop.current.stderr +++ b/tests/ui/traits/dyn-iterator-deref-in-for-loop.current.stderr @@ -2,9 +2,9 @@ error[E0277]: `dyn Iterator` is not an iterator --> $DIR/dyn-iterator-deref-in-for-loop.rs:9:17 | LL | for item in *things { - | ^^^^^^^ the trait `IntoIterator` is not implemented for `dyn Iterator` + | ^^^^^^^ the trait `Sized` is not implemented for `dyn Iterator` | - = note: the trait bound `dyn Iterator: IntoIterator` is not satisfied + = note: the trait bound `Sized` is not satisfied = note: required for `dyn Iterator` to implement `IntoIterator` help: consider mutably borrowing here | diff --git a/tests/ui/traits/dyn-iterator-deref-in-for-loop.next.stderr b/tests/ui/traits/dyn-iterator-deref-in-for-loop.next.stderr index b5a9b82a93a3e..0dd2ffffdfeaf 100644 --- a/tests/ui/traits/dyn-iterator-deref-in-for-loop.next.stderr +++ b/tests/ui/traits/dyn-iterator-deref-in-for-loop.next.stderr @@ -2,9 +2,9 @@ error[E0277]: `dyn Iterator` is not an iterator --> $DIR/dyn-iterator-deref-in-for-loop.rs:9:17 | LL | for item in *things { - | ^^^^^^^ the trait `IntoIterator` is not implemented for `dyn Iterator` + | ^^^^^^^ the trait `Sized` is not implemented for `dyn Iterator` | - = note: the trait bound `dyn Iterator: IntoIterator` is not satisfied + = note: the trait bound `Sized` is not satisfied = note: required for `dyn Iterator` to implement `IntoIterator` help: consider mutably borrowing here | diff --git a/tests/ui/traits/issue-18400.stderr b/tests/ui/traits/issue-18400.stderr index 146ba16397a20..af5519a15c23d 100644 --- a/tests/ui/traits/issue-18400.stderr +++ b/tests/ui/traits/issue-18400.stderr @@ -11,7 +11,7 @@ note: required for `{integer}` to implement `Set<&[_]>` LL | impl<'a, T, S> Set<&'a [T]> for S where | - ^^^^^^^^^^^^ ^ | | - | unsatisfied trait bound introduced here + | unsatisfied trait bound implicitly introduced here = note: 128 redundant requirements hidden = note: required for `{integer}` to implement `Set<&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[_]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>` From 2b618ed6da5f0e55b2c00e32c767ba3dec6d7951 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 2 Jan 2026 00:57:49 -0800 Subject: [PATCH 08/32] rustdoc: Correctly resolve variant and struct fields on alias --- .../passes/collect_intra_doc_links.rs | 86 +++++++++++++------ .../intra-doc/adt-through-alias.rs | 54 +++++++++++- .../intra-doc/associated-items.rs | 9 +- 3 files changed, 113 insertions(+), 36 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 553d8636c85ba..9e86781224d38 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -462,7 +462,7 @@ fn full_res(tcx: TyCtxt<'_>, (base, assoc_item): (Res, Option)) -> Res { } /// Given a primitive type, try to resolve an associated item. -fn resolve_primitive_associated_item<'tcx>( +fn resolve_primitive_inherent_assoc_item<'tcx>( tcx: TyCtxt<'tcx>, prim_ty: PrimitiveType, ns: Namespace, @@ -597,33 +597,30 @@ fn resolve_associated_item<'tcx>( let item_ident = Ident::with_dummy_span(item_name); match root_res { - Res::Primitive(prim) => { - let items = resolve_primitive_associated_item(tcx, prim, ns, item_ident); - if !items.is_empty() { - items - // Inherent associated items take precedence over items that come from trait impls. - } else { - primitive_type_to_ty(tcx, prim) - .map(|ty| { - resolve_associated_trait_item(ty, module_id, item_ident, ns, tcx) - .iter() - .map(|item| (root_res, item.def_id)) - .collect::>() - }) - .unwrap_or_default() - } - } - Res::Def(DefKind::TyAlias, did) => { + Res::Def(DefKind::TyAlias, alias_did) => { // Resolve the link on the type the alias points to. // FIXME: if the associated item is defined directly on the type alias, // it will show up on its documentation page, we should link there instead. - let Some(res) = ty_to_res(tcx, tcx.type_of(did).instantiate_identity()) else { - return Vec::new(); + let Some(aliased_res) = ty_to_res(tcx, tcx.type_of(alias_did).instantiate_identity()) + else { + return vec![]; }; - resolve_associated_item(tcx, res, item_name, ns, disambiguator, module_id) + let aliased_items = + resolve_associated_item(tcx, aliased_res, item_name, ns, disambiguator, module_id); + aliased_items + .into_iter() + .map(|(res, assoc_did)| { + if is_assoc_item_on_alias_page(tcx, assoc_did) { + (root_res, assoc_did) + } else { + (res, assoc_did) + } + }) + .collect() } + Res::Primitive(prim) => resolve_assoc_on_primitive(tcx, prim, ns, item_ident, module_id), Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, did) => { - resolve_assoc_on_adt(tcx, did, item_name, ns, disambiguator, module_id) + resolve_assoc_on_adt(tcx, did, item_ident, ns, disambiguator, module_id) } Res::Def(DefKind::ForeignTy, did) => { resolve_assoc_on_simple_type(tcx, did, item_ident, ns, module_id) @@ -640,23 +637,56 @@ fn resolve_associated_item<'tcx>( } } +// FIXME: make this fully complete by also including ALL inherent impls +// and trait impls BUT ONLY if on alias directly +fn is_assoc_item_on_alias_page<'tcx>(tcx: TyCtxt<'tcx>, assoc_did: DefId) -> bool { + match tcx.def_kind(assoc_did) { + // Variants and fields always have docs on the alias page. + DefKind::Variant | DefKind::Field => true, + _ => false, + } +} + +fn resolve_assoc_on_primitive<'tcx>( + tcx: TyCtxt<'tcx>, + prim: PrimitiveType, + ns: Namespace, + item_ident: Ident, + module_id: DefId, +) -> Vec<(Res, DefId)> { + let root_res = Res::Primitive(prim); + let items = resolve_primitive_inherent_assoc_item(tcx, prim, ns, item_ident); + if !items.is_empty() { + items + // Inherent associated items take precedence over items that come from trait impls. + } else { + primitive_type_to_ty(tcx, prim) + .map(|ty| { + resolve_associated_trait_item(ty, module_id, item_ident, ns, tcx) + .iter() + .map(|item| (root_res, item.def_id)) + .collect::>() + }) + .unwrap_or_default() + } +} + fn resolve_assoc_on_adt<'tcx>( tcx: TyCtxt<'tcx>, adt_def_id: DefId, - item_name: Symbol, + item_ident: Ident, ns: Namespace, disambiguator: Option, module_id: DefId, ) -> Vec<(Res, DefId)> { - debug!("looking for associated item named {item_name} for item {adt_def_id:?}"); + debug!("looking for associated item named {item_ident} for item {adt_def_id:?}"); let root_res = Res::from_def_id(tcx, adt_def_id); let adt_ty = tcx.type_of(adt_def_id).instantiate_identity(); let adt_def = adt_ty.ty_adt_def().expect("must be ADT"); - let item_ident = Ident::with_dummy_span(item_name); // Checks if item_name is a variant of the `SomeItem` enum if ns == TypeNS && adt_def.is_enum() { for variant in adt_def.variants() { - if variant.name == item_name { + if variant.name == item_ident.name { return vec![(root_res, variant.def_id)]; } } @@ -665,7 +695,7 @@ fn resolve_assoc_on_adt<'tcx>( if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator && (adt_def.is_struct() || adt_def.is_union()) { - return resolve_structfield(adt_def, item_name) + return resolve_structfield(adt_def, item_ident.name) .into_iter() .map(|did| (root_res, did)) .collect(); @@ -677,7 +707,7 @@ fn resolve_assoc_on_adt<'tcx>( } if ns == Namespace::ValueNS && (adt_def.is_struct() || adt_def.is_union()) { - return resolve_structfield(adt_def, item_name) + return resolve_structfield(adt_def, item_ident.name) .into_iter() .map(|did| (root_res, did)) .collect(); diff --git a/tests/rustdoc-html/intra-doc/adt-through-alias.rs b/tests/rustdoc-html/intra-doc/adt-through-alias.rs index 58e0f37edbabd..6d5454bbaf200 100644 --- a/tests/rustdoc-html/intra-doc/adt-through-alias.rs +++ b/tests/rustdoc-html/intra-doc/adt-through-alias.rs @@ -3,12 +3,35 @@ //! [`TheStructAlias::the_field`] //! [`TheEnumAlias::TheVariant`] //! [`TheEnumAlias::TheVariant::the_field`] +//! [`TheUnionAlias::f1`] +//! +//! [`TheStruct::trait_`] +//! [`TheStructAlias::trait_`] +//! [`TheEnum::trait_`] +//! [`TheEnumAlias::trait_`] +//! +//! [`TheStruct::inherent`] +//! [`TheStructAlias::inherent`] +//! [`TheEnum::inherent`] +//! [`TheEnumAlias::inherent`] -// FIXME: this should resolve to the alias's version -//@ has foo/index.html '//a[@href="struct.TheStruct.html#structfield.the_field"]' 'TheStructAlias::the_field' -// FIXME: this should resolve to the alias's version -//@ has foo/index.html '//a[@href="enum.TheEnum.html#variant.TheVariant"]' 'TheEnumAlias::TheVariant' +//@ has foo/index.html '//a[@href="type.TheStructAlias.html#structfield.the_field"]' 'TheStructAlias::the_field' +//@ has foo/index.html '//a[@href="type.TheEnumAlias.html#variant.TheVariant"]' 'TheEnumAlias::TheVariant' //@ has foo/index.html '//a[@href="type.TheEnumAlias.html#variant.TheVariant.field.the_field"]' 'TheEnumAlias::TheVariant::the_field' +//@ has foo/index.html '//a[@href="type.TheUnionAlias.html#structfield.f1"]' 'TheUnionAlias::f1' + +//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.trait_"]' 'TheStruct::trait_' +//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.trait_"]' 'TheStructAlias::trait_' +//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.trait_"]' 'TheEnum::trait_' +// FIXME: this one should resolve to alias since it's impl Trait for TheEnumAlias +//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.trait_"]' 'TheEnumAlias::trait_' + +//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.inherent"]' 'TheStruct::inherent' +// FIXME: this one should resolve to alias +//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.inherent"]' 'TheStructAlias::inherent' +//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.inherent"]' 'TheEnum::inherent' +// FIXME: this one should resolve to alias +//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.inherent"]' 'TheEnumAlias::inherent' pub struct TheStruct { pub the_field: i32, @@ -22,4 +45,27 @@ pub enum TheEnum { pub type TheEnumAlias = TheEnum; +pub trait Trait { + fn trait_() {} +} + +impl Trait for TheStruct {} + +impl Trait for TheEnumAlias {} + +impl TheStruct { + pub fn inherent() {} +} + +impl TheEnumAlias { + pub fn inherent() {} +} + +pub union TheUnion { + pub f1: usize, + pub f2: isize, +} + +pub type TheUnionAlias = TheUnion; + fn main() {} diff --git a/tests/rustdoc-html/intra-doc/associated-items.rs b/tests/rustdoc-html/intra-doc/associated-items.rs index 84cfd06111df7..b3bed196aded0 100644 --- a/tests/rustdoc-html/intra-doc/associated-items.rs +++ b/tests/rustdoc-html/intra-doc/associated-items.rs @@ -13,7 +13,9 @@ pub fn foo() {} //@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from struct' //@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.clone"]' 'MyStruct::clone' //@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' -pub struct MyStruct { foo: () } +pub struct MyStruct { + foo: (), +} impl Clone for MyStruct { fn clone(&self) -> Self { @@ -31,8 +33,7 @@ impl T for MyStruct { /// [link from method][MyStruct::method] on method //@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from method' - fn method(i: usize) { - } + fn method(i: usize) {} } /// Ambiguity between which trait to use @@ -57,7 +58,7 @@ impl T2 for S { fn ambiguous_method() {} } -//@ has associated_items/enum.MyEnum.html '//a/@href' 'enum.MyEnum.html#variant.MyVariant' +//@ has associated_items/enum.MyEnum.html '//a/@href' 'type.MyEnumAlias.html#variant.MyVariant' /// Link to [MyEnumAlias::MyVariant] pub enum MyEnum { MyVariant, From b57c2493339dd2d0dd4a8df6ed7ee81b81e84816 Mon Sep 17 00:00:00 2001 From: BD103 <59022059+BD103@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:42:00 -0500 Subject: [PATCH 09/32] fix: make `Type::of` supported unsized types --- library/core/src/mem/type_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 7938e2b52ed02..6b16bfadc233a 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -31,7 +31,7 @@ impl Type { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] // FIXME(reflection): don't require the 'static bound - pub const fn of() -> Self { + pub const fn of() -> Self { const { TypeId::of::().info() } } } From 1bde2f4705d351e24eb556604c0da5511a277e15 Mon Sep 17 00:00:00 2001 From: BD103 <59022059+BD103@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:43:21 -0500 Subject: [PATCH 10/32] chore: test `Type::of` on unsized types I chose to simply extend `dump.rs`, rather than create a new UI test. --- tests/ui/reflection/dump.rs | 2 ++ tests/ui/reflection/dump.run.stdout | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/ui/reflection/dump.rs b/tests/ui/reflection/dump.rs index 3bf4f32b6641d..efc3024a84e9f 100644 --- a/tests/ui/reflection/dump.rs +++ b/tests/ui/reflection/dump.rs @@ -27,4 +27,6 @@ fn main() { println!("{:#?}", const { Type::of::<&Unsized>() }.kind); println!("{:#?}", const { Type::of::<&str>() }.kind); println!("{:#?}", const { Type::of::<&[u8]>() }.kind); + println!("{:#?}", const { Type::of::() }.kind); + println!("{:#?}", const { Type::of::<[u8]>() }.kind); } diff --git a/tests/ui/reflection/dump.run.stdout b/tests/ui/reflection/dump.run.stdout index 71fd80b466580..e9db772e8ebac 100644 --- a/tests/ui/reflection/dump.run.stdout +++ b/tests/ui/reflection/dump.run.stdout @@ -21,3 +21,5 @@ Other Other Other Other +Other +Other From c98b90eaadc1a4ba4fca60572f3ce75f6f44b216 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Mon, 12 Jan 2026 17:26:23 -0500 Subject: [PATCH 11/32] std: Change UEFI env vars to volatile storage The UEFI variables set by the env vars should be volatile, otherwise they will persist after reboot and use up scarce non-volatile storage. --- library/std/src/sys/env/uefi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/env/uefi.rs b/library/std/src/sys/env/uefi.rs index 5fe29a47a2ea1..af16a02642a4c 100644 --- a/library/std/src/sys/env/uefi.rs +++ b/library/std/src/sys/env/uefi.rs @@ -95,8 +95,8 @@ mod uefi_env { val_ptr: *mut r_efi::efi::Char16, ) -> io::Result<()> { let shell = helpers::open_shell().ok_or(unsupported_err())?; - let r = - unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + let volatile = r_efi::efi::Boolean::TRUE; + let r = unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, volatile) }; if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } } From d697b4d7abf5573d63a9e3e5d7f84479eb554e67 Mon Sep 17 00:00:00 2001 From: KaiTomotake Date: Mon, 5 Jan 2026 01:32:34 +0900 Subject: [PATCH 12/32] Improve std::path::Path::join documentation Adapt `PathBuf::push` documentation for `Path::join` and add it to `join`'s docs Fix to comply with tidy Remove unnecessary whitespace --- library/std/src/path.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index e8eda3c5f76bb..25bd7005b9942 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2972,6 +2972,15 @@ impl Path { /// /// If `path` is absolute, it replaces the current path. /// + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g., `\windows`), it + /// replaces and returns everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, `self` is ignored and `path` is returned. + /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`) + /// and `path` is not empty, the new path is normalized: all references + /// to `.` and `..` are removed. + /// /// See [`PathBuf::push`] for more details on what it means to adjoin a path. /// /// # Examples From 37afa310c907250468ff8cf66d9916ab956a1dac Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sat, 10 Jan 2026 17:06:21 +0800 Subject: [PATCH 13/32] Remove all usage of FeedConstTy::No --- .../src/hir_ty_lowering/bounds.rs | 34 +++++----- .../src/hir_ty_lowering/mod.rs | 53 ++++++++++++--- compiler/rustc_hir_typeck/src/expr.rs | 5 +- src/librustdoc/clean/mod.rs | 68 ++++++++++++++++--- 4 files changed, 124 insertions(+), 36 deletions(-) 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 72d21371f66cc..0a874b881801d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -510,7 +510,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Create the generic arguments for the associated type or constant by joining the // parent arguments (the arguments of the trait) and the own arguments (the ones of // the associated item itself) and construct an alias type using them. - let alias_term = candidate.map_bound(|trait_ref| { + candidate.map_bound(|trait_ref| { let item_segment = hir::PathSegment { ident: constraint.ident, hir_id: constraint.hir_id, @@ -528,20 +528,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?alias_args); ty::AliasTerm::new_from_args(tcx, assoc_item.def_id, alias_args) - }); - - // Provide the resolved type of the associated constant to `type_of(AnonConst)`. - if let Some(const_arg) = constraint.ct() - && let hir::ConstArgKind::Anon(anon_const) = const_arg.kind - { - let ty = alias_term - .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); - let ty = - check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id); - tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); - } - - alias_term + }) }; match constraint.kind { @@ -555,7 +542,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::AssocItemConstraintKind::Equality { term } => { let term = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), - hir::Term::Const(ct) => self.lower_const_arg(ct, FeedConstTy::No).into(), + hir::Term::Const(ct) => { + // Provide the resolved type of the associated constant + let ty = projection_term.map_bound(|alias| { + tcx.type_of(alias.def_id).instantiate(tcx, alias.args) + }); + let ty = check_assoc_const_binding_type( + self, + constraint.ident, + ty, + constraint.hir_id, + ); + + self.lower_const_arg(ct, FeedConstTy::WithTy(ty)).into() + } }; // Find any late-bound regions declared in `ty` that are not @@ -871,7 +871,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// probably gate this behind another feature flag. /// /// [^1]: . -fn check_assoc_const_binding_type<'tcx>( +pub(crate) fn check_assoc_const_binding_type<'tcx>( cx: &dyn HirTyLowerer<'tcx>, assoc_const: Ident, ty: ty::Binder<'tcx, Ty<'tcx>>, 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 d2cbf89336d8b..be5ac21e2ca84 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1269,10 +1269,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut where_bounds = vec![]; for bound in [bound, bound2].into_iter().chain(matching_candidates) { let bound_id = bound.def_id(); - let bound_span = tcx - .associated_items(bound_id) - .find_by_ident_and_kind(tcx, assoc_ident, assoc_tag, bound_id) - .and_then(|item| tcx.hir_span_if_local(item.def_id)); + let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( + tcx, + assoc_ident, + assoc_tag, + bound_id, + ); + let bound_span = assoc_item.and_then(|item| tcx.hir_span_if_local(item.def_id)); if let Some(bound_span) = bound_span { err.span_label( @@ -1285,7 +1288,41 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let term: ty::Term<'_> = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => { - self.lower_const_arg(ct, FeedConstTy::No).into() + let assoc_item = + assoc_item.expect("assoc_item should be present"); + let projection_term = bound.map_bound(|trait_ref| { + let item_segment = hir::PathSegment { + ident: constraint.ident, + hir_id: constraint.hir_id, + res: Res::Err, + args: Some(constraint.gen_args), + infer_args: false, + }; + + let alias_args = self.lower_generic_args_of_assoc_item( + constraint.ident.span, + assoc_item.def_id, + &item_segment, + trait_ref.args, + ); + ty::AliasTerm::new_from_args( + tcx, + assoc_item.def_id, + alias_args, + ) + }); + + let ty = projection_term.map_bound(|alias| { + tcx.type_of(alias.def_id).instantiate(tcx, alias.args) + }); + let ty = bounds::check_assoc_const_binding_type( + self, + constraint.ident, + ty, + constraint.hir_id, + ); + + self.lower_const_arg(ct, FeedConstTy::WithTy(ty)).into() } }; if term.references_error() { @@ -2993,7 +3030,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } hir::TyKind::Array(ty, length) => { - let length = self.lower_const_arg(length, FeedConstTy::No); + let length = self.lower_const_arg(length, FeedConstTy::WithTy(tcx.types.usize)); Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Infer(()) => { @@ -3033,8 +3070,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Keep this list of types in sync with the list of types that // the `RangePattern` trait is implemented for. ty::Int(_) | ty::Uint(_) | ty::Char => { - let start = self.lower_const_arg(start, FeedConstTy::No); - let end = self.lower_const_arg(end, FeedConstTy::No); + let start = self.lower_const_arg(start, FeedConstTy::WithTy(ty)); + let end = self.lower_const_arg(end, FeedConstTy::WithTy(ty)); Ok(ty::PatternKind::Range { start, end }) } _ => Err(self diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 9d7e09b020a75..d7366887d85c9 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1749,7 +1749,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let count_span = count.span; let count = self.try_structurally_resolve_const( count_span, - self.normalize(count_span, self.lower_const_arg(count, FeedConstTy::No)), + self.normalize( + count_span, + self.lower_const_arg(count, FeedConstTy::WithTy(tcx.types.usize)), + ), ); if let Some(count) = count.try_to_target_usize(tcx) { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 597a85f397695..c93778ec8f0af 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -469,11 +469,23 @@ fn clean_middle_term<'tcx>( } } -fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { +fn clean_hir_term<'tcx>( + assoc_item: Option, + term: &hir::Term<'tcx>, + span: rustc_span::Span, + cx: &mut DocContext<'tcx>, +) -> Term { match term { hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), hir::Term::Const(c) => { - let ct = lower_const_arg_for_rustdoc(cx.tcx, c, FeedConstTy::No); + let ty = if let Some(assoc_item) = assoc_item { + // FIXME(generic_const_items): this should instantiate with the alias item's args + cx.tcx.type_of(assoc_item).instantiate_identity() + } else { + Ty::new_error_with_message(cx.tcx, span, "cannot find the associated constant") + }; + + let ct = lower_const_arg_for_rustdoc(cx.tcx, c, FeedConstTy::WithTy(ty)); Term::Constant(clean_middle_const(ty::Binder::dummy(ct), cx)) } } @@ -650,7 +662,14 @@ fn clean_generic_param<'tcx>( GenericParamDefKind::Const { ty: Box::new(clean_ty(ty, cx)), default: default.map(|ct| { - Box::new(lower_const_arg_for_rustdoc(cx.tcx, ct, FeedConstTy::No).to_string()) + Box::new( + lower_const_arg_for_rustdoc( + cx.tcx, + ct, + FeedConstTy::WithTy(lower_ty(cx.tcx, ty)), + ) + .to_string(), + ) }), }, ), @@ -1531,7 +1550,7 @@ fn first_non_private_clean_path<'tcx>( && path_last.args.is_some() { assert!(new_path_last.args.is_empty()); - new_path_last.args = clean_generic_args(path_last_args, cx); + new_path_last.args = clean_generic_args(None, path_last_args, cx); } new_clean_path } @@ -1812,7 +1831,11 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T let length = match const_arg.kind { hir::ConstArgKind::Infer(..) | hir::ConstArgKind::Error(..) => "_".to_string(), hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) => { - let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, FeedConstTy::No); + let ct = lower_const_arg_for_rustdoc( + cx.tcx, + const_arg, + FeedConstTy::WithTy(cx.tcx.types.usize), + ); let typing_env = ty::TypingEnv::post_analysis(cx.tcx, *def_id); let ct = cx.tcx.normalize_erasing_regions(typing_env, ct); print_const(cx, ct) @@ -1823,7 +1846,11 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T | hir::ConstArgKind::Tup(..) | hir::ConstArgKind::Array(..) | hir::ConstArgKind::Literal(..) => { - let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, FeedConstTy::No); + let ct = lower_const_arg_for_rustdoc( + cx.tcx, + const_arg, + FeedConstTy::WithTy(cx.tcx.types.usize), + ); print_const(cx, ct) } }; @@ -2516,6 +2543,7 @@ fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path { } fn clean_generic_args<'tcx>( + trait_did: Option, generic_args: &hir::GenericArgs<'tcx>, cx: &mut DocContext<'tcx>, ) -> GenericArgs { @@ -2539,7 +2567,13 @@ fn clean_generic_args<'tcx>( let constraints = generic_args .constraints .iter() - .map(|c| clean_assoc_item_constraint(c, cx)) + .map(|c| { + clean_assoc_item_constraint( + trait_did.expect("only trait ref has constraints"), + c, + cx, + ) + }) .collect::>(); GenericArgs::AngleBracketed { args, constraints } } @@ -2562,7 +2596,9 @@ fn clean_path_segment<'tcx>( path: &hir::PathSegment<'tcx>, cx: &mut DocContext<'tcx>, ) -> PathSegment { - PathSegment { name: path.ident.name, args: clean_generic_args(path.args(), cx) } + let trait_did = + if let hir::def::Res::Def(DefKind::Trait, did) = path.res { Some(did) } else { None }; + PathSegment { name: path.ident.name, args: clean_generic_args(trait_did, path.args(), cx) } } fn clean_bare_fn_ty<'tcx>( @@ -3126,17 +3162,29 @@ fn clean_maybe_renamed_foreign_item<'tcx>( } fn clean_assoc_item_constraint<'tcx>( + trait_did: DefId, constraint: &hir::AssocItemConstraint<'tcx>, cx: &mut DocContext<'tcx>, ) -> AssocItemConstraint { AssocItemConstraint { assoc: PathSegment { name: constraint.ident.name, - args: clean_generic_args(constraint.gen_args, cx), + args: clean_generic_args(None, constraint.gen_args, cx), }, kind: match constraint.kind { hir::AssocItemConstraintKind::Equality { ref term } => { - AssocItemConstraintKind::Equality { term: clean_hir_term(term, cx) } + let assoc_tag = match term { + hir::Term::Ty(_) => ty::AssocTag::Type, + hir::Term::Const(_) => ty::AssocTag::Const, + }; + let assoc_item = cx + .tcx + .associated_items(trait_did) + .find_by_ident_and_kind(cx.tcx, constraint.ident, assoc_tag, trait_did) + .map(|item| item.def_id); + AssocItemConstraintKind::Equality { + term: clean_hir_term(assoc_item, term, constraint.span, cx), + } } hir::AssocItemConstraintKind::Bound { bounds } => AssocItemConstraintKind::Bound { bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), From 6d837ae9c7660ce82d63af3b622c0fbb976b09cb Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sun, 11 Jan 2026 18:43:25 +0800 Subject: [PATCH 14/32] Remove FeedConstTy --- compiler/rustc_hir_analysis/src/collect.rs | 8 +- .../src/hir_ty_lowering/bounds.rs | 5 +- .../src/hir_ty_lowering/mod.rs | 89 +++++-------------- compiler/rustc_hir_analysis/src/lib.rs | 6 +- compiler/rustc_hir_typeck/src/expr.rs | 7 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 13 +-- .../rustc_hir_typeck/src/method/confirm.rs | 7 +- src/librustdoc/clean/mod.rs | 22 +---- 8 files changed, 48 insertions(+), 109 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index bacdf0049806e..2c02534093870 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -47,9 +47,7 @@ use rustc_trait_selection::traits::{ use tracing::{debug, instrument}; use crate::errors; -use crate::hir_ty_lowering::{ - FeedConstTy, HirTyLowerer, InherentAssocCandidate, RegionInferReason, -}; +use crate::hir_ty_lowering::{HirTyLowerer, InherentAssocCandidate, RegionInferReason}; pub(crate) mod dump; mod generics_of; @@ -1499,7 +1497,7 @@ fn const_param_default<'tcx>( let ct = icx .lowerer() - .lower_const_arg(default_ct, FeedConstTy::with_type_of(tcx, def_id, identity_args)); + .lower_const_arg(default_ct, tcx.type_of(def_id).instantiate(tcx, identity_args)); ty::EarlyBinder::bind(ct) } @@ -1557,7 +1555,7 @@ fn const_of_item<'tcx>( let identity_args = ty::GenericArgs::identity_for_item(tcx, def_id); let ct = icx .lowerer() - .lower_const_arg(ct_arg, FeedConstTy::with_type_of(tcx, def_id.to_def_id(), identity_args)); + .lower_const_arg(ct_arg, tcx.type_of(def_id.to_def_id()).instantiate(tcx, identity_args)); if let Err(e) = icx.check_tainted_by_errors() && !ct.references_error() { 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 0a874b881801d..d6441702b268c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -20,7 +20,7 @@ use tracing::{debug, instrument}; use crate::errors; use crate::hir_ty_lowering::{ - AssocItemQSelf, FeedConstTy, GenericsArgsErrExtend, HirTyLowerer, ImpliedBoundsContext, + AssocItemQSelf, GenericsArgsErrExtend, HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, }; @@ -543,7 +543,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let term = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => { - // Provide the resolved type of the associated constant let ty = projection_term.map_bound(|alias| { tcx.type_of(alias.def_id).instantiate(tcx, alias.args) }); @@ -554,7 +553,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { constraint.hir_id, ); - self.lower_const_arg(ct, FeedConstTy::WithTy(ty)).into() + self.lower_const_arg(ct, ty).into() } }; 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 be5ac21e2ca84..3dcc0232b301e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -253,35 +253,6 @@ impl AssocItemQSelf { } } -/// In some cases, [`hir::ConstArg`]s that are being used in the type system -/// through const generics need to have their type "fed" to them -/// using the query system. -/// -/// Use this enum with `::lower_const_arg` to instruct it with the -/// desired behavior. -#[derive(Debug, Clone, Copy)] -pub enum FeedConstTy<'tcx> { - /// Feed the type to the (anno) const arg. - WithTy(Ty<'tcx>), - /// Don't feed the type. - No, -} - -impl<'tcx> FeedConstTy<'tcx> { - /// The `DefId` belongs to the const param that we are supplying - /// this (anon) const arg to. - /// - /// The list of generic args is used to instantiate the parameters - /// used by the type of the const param specified by `DefId`. - pub fn with_type_of( - tcx: TyCtxt<'tcx>, - def_id: DefId, - generic_args: &[ty::GenericArg<'tcx>], - ) -> Self { - Self::WithTy(tcx.type_of(def_id).instantiate(tcx, generic_args)) - } -} - #[derive(Debug, Clone, Copy)] enum LowerTypeRelativePathMode { Type(PermitVariants), @@ -733,7 +704,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Ambig portions of `ConstArg` are handled in the match arm below .lower_const_arg( ct.as_unambig_ct(), - FeedConstTy::with_type_of(tcx, param.def_id, preceding_args), + tcx.type_of(param.def_id).instantiate(tcx, preceding_args), ) .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { @@ -1322,7 +1293,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { constraint.hir_id, ); - self.lower_const_arg(ct, FeedConstTy::WithTy(ty)).into() + self.lower_const_arg(ct, ty).into() } }; if term.references_error() { @@ -2347,16 +2318,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Lower a [`hir::ConstArg`] to a (type-level) [`ty::Const`](Const). #[instrument(skip(self), level = "debug")] - pub fn lower_const_arg( - &self, - const_arg: &hir::ConstArg<'tcx>, - feed: FeedConstTy<'tcx>, - ) -> Const<'tcx> { + pub fn lower_const_arg(&self, const_arg: &hir::ConstArg<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { let tcx = self.tcx(); - if let FeedConstTy::WithTy(anon_const_type) = feed - && let hir::ConstArgKind::Anon(anon) = &const_arg.kind - { + if let hir::ConstArgKind::Anon(anon) = &const_arg.kind { // FIXME(generic_const_parameter_types): Ideally we remove these errors below when // we have the ability to intermix typeck of anon const const args with the parent // bodies typeck. @@ -2366,7 +2331,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // hir typeck was using equality but mir borrowck wound up using subtyping as that could // result in a non-infer in hir typeck but a region variable in borrowck. if tcx.features().generic_const_parameter_types() - && (anon_const_type.has_free_regions() || anon_const_type.has_erased_regions()) + && (ty.has_free_regions() || ty.has_erased_regions()) { let e = self.dcx().span_err( const_arg.span, @@ -2378,7 +2343,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // We must error if the instantiated type has any inference variables as we will // 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() { + if ty.has_non_region_infer() { let e = self.dcx().span_err( const_arg.span, "anonymous constants with inferred types are not yet supported", @@ -2388,7 +2353,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() { + if ty.has_non_region_param() { let e = self.dcx().span_err( const_arg.span, "anonymous constants referencing generics are not yet supported", @@ -2397,12 +2362,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return ty::Const::new_error(tcx, e); } - tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(anon_const_type)); + tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(ty)); } let hir_id = const_arg.hir_id; match const_arg.kind { - hir::ConstArgKind::Tup(exprs) => self.lower_const_arg_tup(exprs, feed, const_arg.span), + hir::ConstArgKind::Tup(exprs) => self.lower_const_arg_tup(exprs, ty, const_arg.span), 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)); @@ -2426,16 +2391,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::ConstArgKind::TupleCall(qpath, args) => { self.lower_const_arg_tuple_call(hir_id, qpath, args, const_arg.span) } - hir::ConstArgKind::Array(array_expr) => self.lower_const_arg_array(array_expr, feed), + hir::ConstArgKind::Array(array_expr) => self.lower_const_arg_array(array_expr, ty), hir::ConstArgKind::Anon(anon) => self.lower_const_arg_anon(anon), hir::ConstArgKind::Infer(()) => self.ct_infer(None, const_arg.span), hir::ConstArgKind::Error(e) => ty::Const::new_error(tcx, e), - hir::ConstArgKind::Literal(kind) if let FeedConstTy::WithTy(anon_const_type) = feed => { - self.lower_const_arg_literal(&kind, anon_const_type, const_arg.span) - } - hir::ConstArgKind::Literal(..) => { - let e = self.dcx().span_err(const_arg.span, "literal of unknown type"); - ty::Const::new_error(tcx, e) + hir::ConstArgKind::Literal(kind) => { + self.lower_const_arg_literal(&kind, ty, const_arg.span) } } } @@ -2443,14 +2404,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn lower_const_arg_array( &self, array_expr: &'tcx hir::ConstArgArrayExpr<'tcx>, - feed: FeedConstTy<'tcx>, + ty: Ty<'tcx>, ) -> Const<'tcx> { let tcx = self.tcx(); - let FeedConstTy::WithTy(ty) = feed else { - return Const::new_error_with_message(tcx, array_expr.span, "unsupported const array"); - }; - let ty::Array(elem_ty, _) = ty.kind() else { return Const::new_error_with_message( tcx, @@ -2462,7 +2419,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let elems = array_expr .elems .iter() - .map(|elem| self.lower_const_arg(elem, FeedConstTy::WithTy(*elem_ty))) + .map(|elem| self.lower_const_arg(elem, *elem_ty)) .collect::>(); let valtree = ty::ValTree::from_branches(tcx, elems); @@ -2545,7 +2502,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .iter() .zip(args) .map(|(field_def, arg)| { - self.lower_const_arg(arg, FeedConstTy::with_type_of(tcx, field_def.did, adt_args)) + self.lower_const_arg(arg, tcx.type_of(field_def.did).instantiate(tcx, adt_args)) }) .collect::>(); @@ -2564,15 +2521,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn lower_const_arg_tup( &self, exprs: &'tcx [&'tcx hir::ConstArg<'tcx>], - feed: FeedConstTy<'tcx>, + ty: Ty<'tcx>, span: Span, ) -> Const<'tcx> { let tcx = self.tcx(); - let FeedConstTy::WithTy(ty) = feed else { - return Const::new_error_with_message(tcx, span, "const tuple lack type information"); - }; - let ty::Tuple(tys) = ty.kind() else { let e = tcx.dcx().span_err(span, format!("expected `{}`, found const tuple", ty)); return Const::new_error(tcx, e); @@ -2581,7 +2534,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let exprs = exprs .iter() .zip(tys.iter()) - .map(|(expr, ty)| self.lower_const_arg(expr, FeedConstTy::WithTy(ty))) + .map(|(expr, ty)| self.lower_const_arg(expr, ty)) .collect::>(); let valtree = ty::ValTree::from_branches(tcx, exprs); @@ -2668,7 +2621,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_const_arg( expr.expr, - FeedConstTy::with_type_of(tcx, field_def.did, adt_args), + tcx.type_of(field_def.did).instantiate(tcx, adt_args), ) } None => { @@ -3030,7 +2983,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } hir::TyKind::Array(ty, length) => { - let length = self.lower_const_arg(length, FeedConstTy::WithTy(tcx.types.usize)); + let length = self.lower_const_arg(length, tcx.types.usize); Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Infer(()) => { @@ -3070,8 +3023,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Keep this list of types in sync with the list of types that // the `RangePattern` trait is implemented for. ty::Int(_) | ty::Uint(_) | ty::Char => { - let start = self.lower_const_arg(start, FeedConstTy::WithTy(ty)); - let end = self.lower_const_arg(end, FeedConstTy::WithTy(ty)); + let start = self.lower_const_arg(start, ty); + let end = self.lower_const_arg(end, ty); Ok(ty::PatternKind::Range { start, end }) } _ => Err(self diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 7296ba6f964a4..a51355adf72fe 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -101,7 +101,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; pub use crate::collect::suggest_impl_trait; -use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer}; +use crate::hir_ty_lowering::HirTyLowerer; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -301,8 +301,8 @@ pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { pub fn lower_const_arg_for_rustdoc<'tcx>( tcx: TyCtxt<'tcx>, hir_ct: &hir::ConstArg<'tcx>, - feed: FeedConstTy<'tcx>, + ty: Ty<'tcx>, ) -> Const<'tcx> { let env_def_id = tcx.hir_get_parent_item(hir_ct.hir_id); - collect::ItemCtxt::new(tcx, env_def_id.def_id).lowerer().lower_const_arg(hir_ct, feed) + collect::ItemCtxt::new(tcx, env_def_id.def_id).lowerer().lower_const_arg(hir_ct, ty) } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d7366887d85c9..9f3ff0b2d03c2 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -21,7 +21,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; use rustc_hir_analysis::NoVariantNamed; -use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _}; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; @@ -1749,10 +1749,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let count_span = count.span; let count = self.try_structurally_resolve_const( count_span, - self.normalize( - count_span, - self.lower_const_arg(count, FeedConstTy::WithTy(tcx.types.usize)), - ), + self.normalize(count_span, self.lower_const_arg(count, tcx.types.usize)), ); if let Some(count) = count.try_to_target_usize(tcx) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index a66ff2a23c250..91f91d9114449 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -14,8 +14,8 @@ use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, }; use rustc_hir_analysis::hir_ty_lowering::{ - ExplicitLateBound, FeedConstTy, GenericArgCountMismatch, GenericArgCountResult, - GenericArgsLowerer, GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, + ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgsLowerer, + GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; @@ -525,9 +525,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn lower_const_arg( &self, const_arg: &'tcx hir::ConstArg<'tcx>, - feed: FeedConstTy<'tcx>, + ty: Ty<'tcx>, ) -> ty::Const<'tcx> { - let ct = self.lowerer().lower_const_arg(const_arg, feed); + let ct = self.lowerer().lower_const_arg(const_arg, ty); self.register_wf_obligation( ct.into(), self.tcx.hir_span(const_arg.hir_id), @@ -1228,7 +1228,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Ambiguous parts of `ConstArg` are handled in the match arms below .lower_const_arg( ct.as_unambig_ct(), - FeedConstTy::with_type_of(self.fcx.tcx, param.def_id, preceding_args), + self.fcx + .tcx + .type_of(param.def_id) + .instantiate(self.fcx.tcx, preceding_args), ) .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index aab4e39855557..270f011b2b15f 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -7,7 +7,7 @@ use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, }; use rustc_hir_analysis::hir_ty_lowering::{ - FeedConstTy, GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason, + GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::{ BoundRegionConversionTime, DefineOpaqueTypes, InferOk, RegionVariableOrigin, @@ -447,7 +447,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // We handle the ambig portions of `ConstArg` in the match arms below .lower_const_arg( ct.as_unambig_ct(), - FeedConstTy::with_type_of(self.cfcx.tcx, param.def_id, preceding_args), + self.cfcx + .tcx + .type_of(param.def_id) + .instantiate(self.cfcx.tcx, preceding_args), ) .into(), (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c93778ec8f0af..aa31cb1eae12f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -43,7 +43,6 @@ use rustc_hir::attrs::{AttributeKind, DocAttribute, DocInline}; use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId}; use rustc_hir::{LangItem, PredicateOrigin, find_attr}; -use rustc_hir_analysis::hir_ty_lowering::FeedConstTy; use rustc_hir_analysis::{lower_const_arg_for_rustdoc, lower_ty}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; @@ -485,7 +484,7 @@ fn clean_hir_term<'tcx>( Ty::new_error_with_message(cx.tcx, span, "cannot find the associated constant") }; - let ct = lower_const_arg_for_rustdoc(cx.tcx, c, FeedConstTy::WithTy(ty)); + let ct = lower_const_arg_for_rustdoc(cx.tcx, c, ty); Term::Constant(clean_middle_const(ty::Binder::dummy(ct), cx)) } } @@ -663,12 +662,7 @@ fn clean_generic_param<'tcx>( ty: Box::new(clean_ty(ty, cx)), default: default.map(|ct| { Box::new( - lower_const_arg_for_rustdoc( - cx.tcx, - ct, - FeedConstTy::WithTy(lower_ty(cx.tcx, ty)), - ) - .to_string(), + lower_const_arg_for_rustdoc(cx.tcx, ct, lower_ty(cx.tcx, ty)).to_string(), ) }), }, @@ -1831,11 +1825,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T let length = match const_arg.kind { hir::ConstArgKind::Infer(..) | hir::ConstArgKind::Error(..) => "_".to_string(), hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) => { - let ct = lower_const_arg_for_rustdoc( - cx.tcx, - const_arg, - FeedConstTy::WithTy(cx.tcx.types.usize), - ); + let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, cx.tcx.types.usize); let typing_env = ty::TypingEnv::post_analysis(cx.tcx, *def_id); let ct = cx.tcx.normalize_erasing_regions(typing_env, ct); print_const(cx, ct) @@ -1846,11 +1836,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T | hir::ConstArgKind::Tup(..) | hir::ConstArgKind::Array(..) | hir::ConstArgKind::Literal(..) => { - let ct = lower_const_arg_for_rustdoc( - cx.tcx, - const_arg, - FeedConstTy::WithTy(cx.tcx.types.usize), - ); + let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, cx.tcx.types.usize); print_const(cx, ct) } }; From bf423e70f897120c574a6bcbcc061fe99a4138b8 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sun, 11 Jan 2026 21:02:00 +0800 Subject: [PATCH 15/32] Bless tests --- .../associated-const-bindings/ambiguity.stderr | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/ui/const-generics/associated-const-bindings/ambiguity.stderr b/tests/ui/const-generics/associated-const-bindings/ambiguity.stderr index a14afe9d6923b..806708f18d654 100644 --- a/tests/ui/const-generics/associated-const-bindings/ambiguity.stderr +++ b/tests/ui/const-generics/associated-const-bindings/ambiguity.stderr @@ -27,6 +27,12 @@ LL | const C: &'static str; ... LL | fn take1(_: impl Trait1) {} | ^^^^^^^ ambiguous associated constant `C` + | + = help: consider introducing a new type parameter `T` and adding `where` constraints: + where + T: Trait1, + T: Parent2::C = "?", + T: Parent1::C = "?" error: aborting due to 2 previous errors From d95613a3771f96a45046f7d36ad5d656f54cca3e Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 12 Jan 2026 23:50:32 +0800 Subject: [PATCH 16/32] Fix review comments --- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 2 ++ src/librustdoc/clean/mod.rs | 14 +++----------- 2 files changed, 5 insertions(+), 11 deletions(-) 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 3dcc0232b301e..65d2d61e6fc45 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1283,6 +1283,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) }); + // FIXME(mgca): code duplication with other places we lower + // the rhs' of associated const bindings let ty = projection_term.map_bound(|alias| { tcx.type_of(alias.def_id).instantiate(tcx, alias.args) }); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index aa31cb1eae12f..b42ee3c0fb329 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -471,19 +471,13 @@ fn clean_middle_term<'tcx>( fn clean_hir_term<'tcx>( assoc_item: Option, term: &hir::Term<'tcx>, - span: rustc_span::Span, cx: &mut DocContext<'tcx>, ) -> Term { match term { hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), hir::Term::Const(c) => { - let ty = if let Some(assoc_item) = assoc_item { - // FIXME(generic_const_items): this should instantiate with the alias item's args - cx.tcx.type_of(assoc_item).instantiate_identity() - } else { - Ty::new_error_with_message(cx.tcx, span, "cannot find the associated constant") - }; - + // FIXME(generic_const_items): this should instantiate with the alias item's args + let ty = cx.tcx.type_of(assoc_item.unwrap()).instantiate_identity(); let ct = lower_const_arg_for_rustdoc(cx.tcx, c, ty); Term::Constant(clean_middle_const(ty::Binder::dummy(ct), cx)) } @@ -3168,9 +3162,7 @@ fn clean_assoc_item_constraint<'tcx>( .associated_items(trait_did) .find_by_ident_and_kind(cx.tcx, constraint.ident, assoc_tag, trait_did) .map(|item| item.def_id); - AssocItemConstraintKind::Equality { - term: clean_hir_term(assoc_item, term, constraint.span, cx), - } + AssocItemConstraintKind::Equality { term: clean_hir_term(assoc_item, term, cx) } } hir::AssocItemConstraintKind::Bound { bounds } => AssocItemConstraintKind::Bound { bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), From c263d36539d2859b9fd6f433e6a23a99a3d09d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 13 Jan 2026 07:35:24 +0100 Subject: [PATCH 17/32] Fix citool tests --- src/ci/citool/tests/jobs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index 5231d4616e049..6787f00d9af83 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -4,7 +4,7 @@ const TEST_JOBS_YML_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/tes #[test] fn auto_jobs() { - let stdout = get_matrix("push", "commit", "refs/heads/auto"); + let stdout = get_matrix("push", "commit", "refs/heads/automation/bors/auto"); insta::assert_snapshot!(stdout, @r#" jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-14","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","SELECT_XCODE":"/Applications/Xcode_15.4.app","TOOLSTATE_PUBLISH":1,"USE_XCODE_CLANG":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"pr-check-1","full_name":"auto - pr-check-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"pr-check-2","full_name":"auto - pr-check-2","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"tidy","full_name":"auto - tidy","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true,"doc_url":"https://foo.bar"}] run_type=auto @@ -13,7 +13,7 @@ fn auto_jobs() { #[test] fn try_jobs() { - let stdout = get_matrix("push", "commit", "refs/heads/try"); + let stdout = get_matrix("push", "commit", "refs/heads/automation/bors/try"); insta::assert_snapshot!(stdout, @r###" jobs=[{"name":"dist-x86_64-linux","full_name":"try - dist-x86_64-linux","os":"ubuntu-22.04-16core-64gb","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_TRY_BUILD":1,"TOOLSTATE_PUBLISH":1}}] run_type=try @@ -28,7 +28,7 @@ fn try_custom_jobs() { try-job: aarch64-gnu try-job: dist-i686-msvc"#, - "refs/heads/try", + "refs/heads/automation/bors/try", ); insta::assert_snapshot!(stdout, @r###" jobs=[{"name":"aarch64-gnu","full_name":"try - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"dist-i686-msvc","full_name":"try - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}}] From ef9cbadc433ede9334ae9a9461e65fed4e90f9c2 Mon Sep 17 00:00:00 2001 From: vsriram Date: Tue, 13 Jan 2026 13:31:54 +0530 Subject: [PATCH 18/32] ui: add regression test for macro resolution ICE --- tests/ui/resolve/decl-macro-use-no-ice.rs | 20 ++++++++ tests/ui/resolve/decl-macro-use-no-ice.stderr | 47 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 tests/ui/resolve/decl-macro-use-no-ice.rs create mode 100644 tests/ui/resolve/decl-macro-use-no-ice.stderr diff --git a/tests/ui/resolve/decl-macro-use-no-ice.rs b/tests/ui/resolve/decl-macro-use-no-ice.rs new file mode 100644 index 0000000000000..39b9cb03fea0d --- /dev/null +++ b/tests/ui/resolve/decl-macro-use-no-ice.rs @@ -0,0 +1,20 @@ +//@ edition: 2024 +#![feature(decl_macro)] + +// Regression test for issue +// The compiler previously ICE'd during identifier resolution +// involving `macro` items and `use` inside a public macro. + + +mod foo { + macro f() {} + + pub macro m() { + use f; //~ ERROR `f` is private, and cannot be re-exported + f!(); //~ ERROR macro import `f` is private + } +} + +fn main() { + foo::m!(); +} diff --git a/tests/ui/resolve/decl-macro-use-no-ice.stderr b/tests/ui/resolve/decl-macro-use-no-ice.stderr new file mode 100644 index 0000000000000..9fb75b48b428c --- /dev/null +++ b/tests/ui/resolve/decl-macro-use-no-ice.stderr @@ -0,0 +1,47 @@ +error[E0364]: `f` is private, and cannot be re-exported + --> $DIR/decl-macro-use-no-ice.rs:13:13 + | +LL | use f; + | ^ +... +LL | foo::m!(); + | --------- in this macro invocation + | +note: consider marking `f` as `pub` in the imported module + --> $DIR/decl-macro-use-no-ice.rs:13:13 + | +LL | use f; + | ^ +... +LL | foo::m!(); + | --------- in this macro invocation + = note: this error originates in the macro `foo::m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0603]: macro import `f` is private + --> $DIR/decl-macro-use-no-ice.rs:14:9 + | +LL | f!(); + | ^ private macro import +... +LL | foo::m!(); + | --------- in this macro invocation + | +note: the macro import `f` is defined here... + --> $DIR/decl-macro-use-no-ice.rs:13:13 + | +LL | use f; + | ^ +... +LL | foo::m!(); + | --------- in this macro invocation +note: ...and refers to the macro `f` which is defined here + --> $DIR/decl-macro-use-no-ice.rs:10:5 + | +LL | macro f() {} + | ^^^^^^^^^ + = note: this error originates in the macro `foo::m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0364, E0603. +For more information about an error, try `rustc --explain E0364`. From 62849e64412e3dd9f7fba08881a6fab92aadadc3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Jan 2026 10:22:34 +0100 Subject: [PATCH 19/32] Reduce flakyness for `tests/rustdoc-gui/notable-trait.goml` --- tests/rustdoc-gui/notable-trait.goml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml index 839988021fce5..8e4c180e0ef17 100644 --- a/tests/rustdoc-gui/notable-trait.goml +++ b/tests/rustdoc-gui/notable-trait.goml @@ -250,7 +250,7 @@ set-window-size: (1100, 600) reload: assert-count: ("//*[@class='tooltip popover']", 0) click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" -assert-count: ("//*[@class='tooltip popover']", 1) +wait-for-count: ("//*[@class='tooltip popover']", 1) call-function: ("open-settings-menu", {}) -assert-count: ("//*[@class='tooltip popover']", 0) +wait-for-count: ("//*[@class='tooltip popover']", 0) assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" From e259373ce9eca5a10201d74a016a4a2e7ab42ed7 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 6 Jan 2026 13:38:27 +0100 Subject: [PATCH 20/32] std: move `errno` and related functions into `sys::io` --- library/std/src/io/error.rs | 12 +- library/std/src/io/error/tests.rs | 3 +- library/std/src/sys/exit_guard.rs | 2 +- library/std/src/sys/fs/unix.rs | 4 +- library/std/src/sys/io/error/generic.rs | 15 ++ library/std/src/sys/io/error/hermit.rs | 35 ++++ library/std/src/sys/io/error/mod.rs | 55 ++++++ library/std/src/sys/io/error/motor.rs | 67 +++++++ library/std/src/sys/io/error/sgx.rs | 65 ++++++ library/std/src/sys/io/error/solid.rs | 19 ++ library/std/src/sys/io/error/teeos.rs | 64 ++++++ library/std/src/sys/io/error/uefi.rs | 104 ++++++++++ library/std/src/sys/io/error/unix.rs | 186 ++++++++++++++++++ library/std/src/sys/io/error/wasi.rs | 80 ++++++++ library/std/src/sys/io/error/windows.rs | 140 +++++++++++++ library/std/src/sys/io/error/xous.rs | 17 ++ library/std/src/sys/io/mod.rs | 20 +- .../std/src/sys/net/connection/socket/unix.rs | 2 +- library/std/src/sys/net/hostname/unix.rs | 2 +- library/std/src/sys/pal/hermit/mod.rs | 33 +--- library/std/src/sys/pal/hermit/os.rs | 8 - library/std/src/sys/pal/motor/mod.rs | 39 ---- library/std/src/sys/pal/motor/os.rs | 29 --- library/std/src/sys/pal/sgx/mod.rs | 50 ----- library/std/src/sys/pal/sgx/os.rs | 18 +- library/std/src/sys/pal/solid/mod.rs | 9 - library/std/src/sys/pal/solid/os.rs | 10 +- library/std/src/sys/pal/teeos/mod.rs | 55 ------ library/std/src/sys/pal/teeos/os.rs | 8 - library/std/src/sys/pal/uefi/mod.rs | 50 ----- library/std/src/sys/pal/uefi/os.rs | 53 ----- library/std/src/sys/pal/unix/futex.rs | 6 +- library/std/src/sys/pal/unix/mod.rs | 61 +----- library/std/src/sys/pal/unix/os.rs | 127 ------------ .../pal/unix/stack_overflow/thread_info.rs | 2 +- library/std/src/sys/pal/unsupported/common.rs | 8 - library/std/src/sys/pal/unsupported/os.rs | 8 - library/std/src/sys/pal/vexos/mod.rs | 4 +- library/std/src/sys/pal/vexos/os.rs | 4 +- library/std/src/sys/pal/wasi/helpers.rs | 56 +----- library/std/src/sys/pal/wasi/mod.rs | 2 +- library/std/src/sys/pal/wasi/os.rs | 30 +-- library/std/src/sys/pal/windows/mod.rs | 78 -------- library/std/src/sys/pal/windows/os.rs | 60 ------ library/std/src/sys/pal/xous/os.rs | 9 - library/std/src/sys/pal/zkvm/mod.rs | 8 - library/std/src/sys/pal/zkvm/os.rs | 8 - library/std/src/sys/process/uefi.rs | 2 +- library/std/src/sys/process/unix/common.rs | 2 +- library/std/src/sys/process/unix/unix.rs | 2 +- library/std/src/sys/random/linux.rs | 2 +- library/std/src/sys/sync/condvar/windows7.rs | 4 +- library/std/src/sys/thread/unix.rs | 9 +- 53 files changed, 900 insertions(+), 846 deletions(-) create mode 100644 library/std/src/sys/io/error/generic.rs create mode 100644 library/std/src/sys/io/error/hermit.rs create mode 100644 library/std/src/sys/io/error/mod.rs create mode 100644 library/std/src/sys/io/error/motor.rs create mode 100644 library/std/src/sys/io/error/sgx.rs create mode 100644 library/std/src/sys/io/error/solid.rs create mode 100644 library/std/src/sys/io/error/teeos.rs create mode 100644 library/std/src/sys/io/error/uefi.rs create mode 100644 library/std/src/sys/io/error/unix.rs create mode 100644 library/std/src/sys/io/error/wasi.rs create mode 100644 library/std/src/sys/io/error/windows.rs create mode 100644 library/std/src/sys/io/error/xous.rs diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 528eb185df088..b19087e7424a0 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -653,7 +653,7 @@ impl Error { #[must_use] #[inline] pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::os::errno()) + Error::from_raw_os_error(sys::io::errno()) } /// Creates a new instance of an [`Error`] from a particular OS error code. @@ -1004,7 +1004,7 @@ impl Error { #[inline] pub fn kind(&self) -> ErrorKind { match self.repr.data() { - ErrorData::Os(code) => sys::decode_error_kind(code), + ErrorData::Os(code) => sys::io::decode_error_kind(code), ErrorData::Custom(c) => c.kind, ErrorData::Simple(kind) => kind, ErrorData::SimpleMessage(m) => m.kind, @@ -1014,7 +1014,7 @@ impl Error { #[inline] pub(crate) fn is_interrupted(&self) -> bool { match self.repr.data() { - ErrorData::Os(code) => sys::is_interrupted(code), + ErrorData::Os(code) => sys::io::is_interrupted(code), ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, @@ -1028,8 +1028,8 @@ impl fmt::Debug for Repr { ErrorData::Os(code) => fmt .debug_struct("Os") .field("code", &code) - .field("kind", &sys::decode_error_kind(code)) - .field("message", &sys::os::error_string(code)) + .field("kind", &sys::io::decode_error_kind(code)) + .field("message", &sys::io::error_string(code)) .finish(), ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), @@ -1047,7 +1047,7 @@ impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.repr.data() { ErrorData::Os(code) => { - let detail = sys::os::error_string(code); + let detail = sys::io::error_string(code); write!(fmt, "{detail} (os error {code})") } ErrorData::Custom(ref c) => c.error.fmt(fmt), diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 3e4029768eb85..eef44c6ac3b6a 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,7 +1,6 @@ use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; use crate::assert_matches::assert_matches; -use crate::sys::decode_error_kind; -use crate::sys::os::error_string; +use crate::sys::io::{decode_error_kind, error_string}; use crate::{error, fmt}; #[test] diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index 00b91842e9dba..e7d7a478a5baa 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -34,7 +34,7 @@ cfg_select! { // lifetime of the thread. Additionally, accesses to `errno` are // async-signal-safe, so this function is available in all imaginable // circumstances. - let this_thread_id = crate::sys::os::errno_location(); + let this_thread_id = crate::sys::io::errno_location(); match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { Ok(_) => { // This is the first thread to call `unique_thread_exit`, diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 1cc2edd0cf474..716ca9783d1fb 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -726,7 +726,7 @@ impl Iterator for ReadDir { target_os = "wasi", ))] fn next(&mut self) -> Option> { - use crate::sys::os::{errno, set_errno}; + use crate::sys::io::{errno, set_errno}; if self.end_of_stream { return None; @@ -864,7 +864,7 @@ impl Iterator for ReadDir { /// The downside is that it costs an extra syscall, so we only do it for debug. #[inline] pub(crate) fn debug_assert_fd_is_open(fd: RawFd) { - use crate::sys::os::errno; + use crate::sys::io::errno; // this is similar to assert_unsafe_precondition!() but it doesn't require const if core::ub_checks::check_library_ub() { diff --git a/library/std/src/sys/io/error/generic.rs b/library/std/src/sys/io/error/generic.rs new file mode 100644 index 0000000000000..fc70fbaba7e8c --- /dev/null +++ b/library/std/src/sys/io/error/generic.rs @@ -0,0 +1,15 @@ +pub fn errno() -> i32 { + 0 +} + +pub fn is_interrupted(_code: i32) -> bool { + false +} + +pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { + crate::io::ErrorKind::Uncategorized +} + +pub fn error_string(_errno: i32) -> String { + "operation successful".to_string() +} diff --git a/library/std/src/sys/io/error/hermit.rs b/library/std/src/sys/io/error/hermit.rs new file mode 100644 index 0000000000000..5f42144bb7cfb --- /dev/null +++ b/library/std/src/sys/io/error/hermit.rs @@ -0,0 +1,35 @@ +use crate::io; + +pub fn errno() -> i32 { + unsafe { hermit_abi::get_errno() } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == hermit_abi::errno::EINTR +} + +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + match errno { + hermit_abi::errno::EACCES => io::ErrorKind::PermissionDenied, + hermit_abi::errno::EADDRINUSE => io::ErrorKind::AddrInUse, + hermit_abi::errno::EADDRNOTAVAIL => io::ErrorKind::AddrNotAvailable, + hermit_abi::errno::EAGAIN => io::ErrorKind::WouldBlock, + hermit_abi::errno::ECONNABORTED => io::ErrorKind::ConnectionAborted, + hermit_abi::errno::ECONNREFUSED => io::ErrorKind::ConnectionRefused, + hermit_abi::errno::ECONNRESET => io::ErrorKind::ConnectionReset, + hermit_abi::errno::EEXIST => io::ErrorKind::AlreadyExists, + hermit_abi::errno::EINTR => io::ErrorKind::Interrupted, + hermit_abi::errno::EINVAL => io::ErrorKind::InvalidInput, + hermit_abi::errno::ENOENT => io::ErrorKind::NotFound, + hermit_abi::errno::ENOTCONN => io::ErrorKind::NotConnected, + hermit_abi::errno::EPERM => io::ErrorKind::PermissionDenied, + hermit_abi::errno::EPIPE => io::ErrorKind::BrokenPipe, + hermit_abi::errno::ETIMEDOUT => io::ErrorKind::TimedOut, + _ => io::ErrorKind::Uncategorized, + } +} + +pub fn error_string(errno: i32) -> String { + hermit_abi::error_string(errno).to_string() +} diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs new file mode 100644 index 0000000000000..d7a0b9b4b301d --- /dev/null +++ b/library/std/src/sys/io/error/mod.rs @@ -0,0 +1,55 @@ +cfg_select! { + target_os = "hermit" => { + mod hermit; + pub use hermit::*; + } + target_os = "motor" => { + mod motor; + pub use motor::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::*; + } + target_os = "solid_asp3" => { + mod solid; + pub use solid::*; + } + target_os = "teeos" => { + mod teeos; + pub use teeos::*; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::*; + } + target_family = "unix" => { + mod unix; + pub use unix::*; + } + target_os = "wasi" => { + mod wasi; + pub use wasi::*; + } + target_os = "windows" => { + mod windows; + pub use windows::*; + } + target_os = "xous" => { + mod xous; + pub use xous::*; + } + any( + target_os = "vexos", + target_family = "wasm", + target_os = "zkvm", + ) => { + mod generic; + pub use generic::*; + } +} + +pub type RawOsError = cfg_select! { + target_os = "uefi" => usize, + _ => i32, +}; diff --git a/library/std/src/sys/io/error/motor.rs b/library/std/src/sys/io/error/motor.rs new file mode 100644 index 0000000000000..7d612d817cdd7 --- /dev/null +++ b/library/std/src/sys/io/error/motor.rs @@ -0,0 +1,67 @@ +use crate::io; +use crate::sys::io::RawOsError; + +pub fn errno() -> RawOsError { + // Not used in Motor OS because it is ambiguous: Motor OS + // is micro-kernel-based, and I/O happens via a shared-memory + // ring buffer, so an I/O operation that on a unix is a syscall + // may involve no sycalls on Motor OS at all, or a syscall + // that e.g. waits for a notification from the I/O driver + // (sys-io); and the wait syscall may succeed, but the + // driver may report an I/O error; or a bunch of results + // for several I/O operations, some successful and some + // not. + // + // Also I/O operations in a Motor OS process are handled by a + // separate runtime background/I/O thread, so it is really hard + // to define what "last system error in the current thread" + // actually means. + let error_code: moto_rt::ErrorCode = moto_rt::Error::Unknown.into(); + error_code.into() +} + +pub fn is_interrupted(_code: io::RawOsError) -> bool { + false // Motor OS doesn't have signals. +} + +pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { + if code < 0 || code > u16::MAX.into() { + return io::ErrorKind::Uncategorized; + } + + let error = moto_rt::Error::from(code as moto_rt::ErrorCode); + + match error { + moto_rt::Error::Unspecified => io::ErrorKind::Uncategorized, + moto_rt::Error::Unknown => io::ErrorKind::Uncategorized, + moto_rt::Error::NotReady => io::ErrorKind::WouldBlock, + moto_rt::Error::NotImplemented => io::ErrorKind::Unsupported, + moto_rt::Error::VersionTooHigh => io::ErrorKind::Unsupported, + moto_rt::Error::VersionTooLow => io::ErrorKind::Unsupported, + moto_rt::Error::InvalidArgument => io::ErrorKind::InvalidInput, + moto_rt::Error::OutOfMemory => io::ErrorKind::OutOfMemory, + moto_rt::Error::NotAllowed => io::ErrorKind::PermissionDenied, + moto_rt::Error::NotFound => io::ErrorKind::NotFound, + moto_rt::Error::InternalError => io::ErrorKind::Other, + moto_rt::Error::TimedOut => io::ErrorKind::TimedOut, + moto_rt::Error::AlreadyInUse => io::ErrorKind::AlreadyExists, + moto_rt::Error::UnexpectedEof => io::ErrorKind::UnexpectedEof, + moto_rt::Error::InvalidFilename => io::ErrorKind::InvalidFilename, + moto_rt::Error::NotADirectory => io::ErrorKind::NotADirectory, + moto_rt::Error::BadHandle => io::ErrorKind::InvalidInput, + moto_rt::Error::FileTooLarge => io::ErrorKind::FileTooLarge, + moto_rt::Error::NotConnected => io::ErrorKind::NotConnected, + moto_rt::Error::StorageFull => io::ErrorKind::StorageFull, + moto_rt::Error::InvalidData => io::ErrorKind::InvalidData, + _ => io::ErrorKind::Uncategorized, + } +} + +pub fn error_string(errno: RawOsError) -> String { + let error: moto_rt::Error = match errno { + x if x < 0 => moto_rt::Error::Unknown, + x if x > u16::MAX.into() => moto_rt::Error::Unknown, + x => (x as moto_rt::ErrorCode).into(), /* u16 */ + }; + format!("{}", error) +} diff --git a/library/std/src/sys/io/error/sgx.rs b/library/std/src/sys/io/error/sgx.rs new file mode 100644 index 0000000000000..8b3e08b0b661b --- /dev/null +++ b/library/std/src/sys/io/error/sgx.rs @@ -0,0 +1,65 @@ +use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; + +use crate::io; + +pub fn errno() -> i32 { + RESULT_SUCCESS +} + +#[inline] +pub fn is_interrupted(code: i32) -> bool { + code == fortanix_sgx_abi::Error::Interrupted as _ +} + +pub fn decode_error_kind(code: i32) -> io::ErrorKind { + // FIXME: not sure how to make sure all variants of Error are covered + if code == Error::NotFound as _ { + io::ErrorKind::NotFound + } else if code == Error::PermissionDenied as _ { + io::ErrorKind::PermissionDenied + } else if code == Error::ConnectionRefused as _ { + io::ErrorKind::ConnectionRefused + } else if code == Error::ConnectionReset as _ { + io::ErrorKind::ConnectionReset + } else if code == Error::ConnectionAborted as _ { + io::ErrorKind::ConnectionAborted + } else if code == Error::NotConnected as _ { + io::ErrorKind::NotConnected + } else if code == Error::AddrInUse as _ { + io::ErrorKind::AddrInUse + } else if code == Error::AddrNotAvailable as _ { + io::ErrorKind::AddrNotAvailable + } else if code == Error::BrokenPipe as _ { + io::ErrorKind::BrokenPipe + } else if code == Error::AlreadyExists as _ { + io::ErrorKind::AlreadyExists + } else if code == Error::WouldBlock as _ { + io::ErrorKind::WouldBlock + } else if code == Error::InvalidInput as _ { + io::ErrorKind::InvalidInput + } else if code == Error::InvalidData as _ { + io::ErrorKind::InvalidData + } else if code == Error::TimedOut as _ { + io::ErrorKind::TimedOut + } else if code == Error::WriteZero as _ { + io::ErrorKind::WriteZero + } else if code == Error::Interrupted as _ { + io::ErrorKind::Interrupted + } else if code == Error::Other as _ { + io::ErrorKind::Uncategorized + } else if code == Error::UnexpectedEof as _ { + io::ErrorKind::UnexpectedEof + } else { + io::ErrorKind::Uncategorized + } +} + +pub fn error_string(errno: i32) -> String { + if errno == RESULT_SUCCESS { + "operation successful".into() + } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) { + format!("user-specified error {errno:08x}") + } else { + decode_error_kind(errno).as_str().into() + } +} diff --git a/library/std/src/sys/io/error/solid.rs b/library/std/src/sys/io/error/solid.rs new file mode 100644 index 0000000000000..8e9503272abbc --- /dev/null +++ b/library/std/src/sys/io/error/solid.rs @@ -0,0 +1,19 @@ +use crate::io; +use crate::sys::pal::error; + +pub fn errno() -> i32 { + 0 +} + +#[inline] +pub fn is_interrupted(code: i32) -> bool { + crate::sys::net::is_interrupted(code) +} + +pub fn decode_error_kind(code: i32) -> io::ErrorKind { + error::decode_error_kind(code) +} + +pub fn error_string(errno: i32) -> String { + if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{errno}") } +} diff --git a/library/std/src/sys/io/error/teeos.rs b/library/std/src/sys/io/error/teeos.rs new file mode 100644 index 0000000000000..18826ffc3894f --- /dev/null +++ b/library/std/src/sys/io/error/teeos.rs @@ -0,0 +1,64 @@ +use crate::io; + +pub fn errno() -> i32 { + unsafe { (*libc::__errno_location()) as i32 } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +// Note: code below is 1:1 copied from unix/mod.rs +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + use io::ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => QuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + libc::ENOTEMPTY => DirectoryNotEmpty, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + + libc::EACCES | libc::EPERM => PermissionDenied, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +pub fn error_string(_errno: i32) -> String { + "error string unimplemented".to_string() +} diff --git a/library/std/src/sys/io/error/uefi.rs b/library/std/src/sys/io/error/uefi.rs new file mode 100644 index 0000000000000..bedea240d523b --- /dev/null +++ b/library/std/src/sys/io/error/uefi.rs @@ -0,0 +1,104 @@ +use r_efi::efi::Status; + +use crate::io; + +pub fn errno() -> io::RawOsError { + 0 +} + +pub fn is_interrupted(_code: io::RawOsError) -> bool { + false +} + +pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { + match Status::from_usize(code) { + Status::ALREADY_STARTED + | Status::COMPROMISED_DATA + | Status::CONNECTION_FIN + | Status::CRC_ERROR + | Status::DEVICE_ERROR + | Status::END_OF_MEDIA + | Status::HTTP_ERROR + | Status::ICMP_ERROR + | Status::INCOMPATIBLE_VERSION + | Status::LOAD_ERROR + | Status::MEDIA_CHANGED + | Status::NO_MAPPING + | Status::NO_MEDIA + | Status::NOT_STARTED + | Status::PROTOCOL_ERROR + | Status::PROTOCOL_UNREACHABLE + | Status::TFTP_ERROR + | Status::VOLUME_CORRUPTED => io::ErrorKind::Other, + Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => io::ErrorKind::InvalidData, + Status::ABORTED => io::ErrorKind::ConnectionAborted, + Status::ACCESS_DENIED => io::ErrorKind::PermissionDenied, + Status::BUFFER_TOO_SMALL => io::ErrorKind::FileTooLarge, + Status::CONNECTION_REFUSED => io::ErrorKind::ConnectionRefused, + Status::CONNECTION_RESET => io::ErrorKind::ConnectionReset, + Status::END_OF_FILE => io::ErrorKind::UnexpectedEof, + Status::HOST_UNREACHABLE => io::ErrorKind::HostUnreachable, + Status::INVALID_PARAMETER => io::ErrorKind::InvalidInput, + Status::IP_ADDRESS_CONFLICT => io::ErrorKind::AddrInUse, + Status::NETWORK_UNREACHABLE => io::ErrorKind::NetworkUnreachable, + Status::NO_RESPONSE => io::ErrorKind::HostUnreachable, + Status::NOT_FOUND => io::ErrorKind::NotFound, + Status::NOT_READY => io::ErrorKind::ResourceBusy, + Status::OUT_OF_RESOURCES => io::ErrorKind::OutOfMemory, + Status::SECURITY_VIOLATION => io::ErrorKind::PermissionDenied, + Status::TIMEOUT => io::ErrorKind::TimedOut, + Status::UNSUPPORTED => io::ErrorKind::Unsupported, + Status::VOLUME_FULL => io::ErrorKind::StorageFull, + Status::WRITE_PROTECTED => io::ErrorKind::ReadOnlyFilesystem, + _ => io::ErrorKind::Uncategorized, + } +} + +pub fn error_string(errno: io::RawOsError) -> String { + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + #[rustfmt::skip] + let msg = match Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.", + Status::ACCESS_DENIED => "Access was denied.", + Status::ALREADY_STARTED => "The protocol has already been started.", + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.", + Status::BUFFER_TOO_SMALL => "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.", + Status::COMPROMISED_DATA => "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.", + Status::CONNECTION_FIN => "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.", + Status::CONNECTION_REFUSED => "The receiving or transmission operation fails because this connection is refused.", + Status::CONNECTION_RESET => "The connect fails because the connection is reset either by instance itself or the communication peer.", + Status::CRC_ERROR => "A CRC error was detected.", + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.", + Status::END_OF_FILE => "The end of the file was reached.", + Status::END_OF_MEDIA => "Beginning or end of media was reached", + Status::HOST_UNREACHABLE => "The remote host is not reachable.", + Status::HTTP_ERROR => "A HTTP error occurred during the network operation.", + Status::ICMP_ERROR => "An ICMP error occurred during the network operation.", + Status::INCOMPATIBLE_VERSION => "The function encountered an internal version that was incompatible with a version requested by the caller.", + Status::INVALID_LANGUAGE => "The language specified was invalid.", + Status::INVALID_PARAMETER => "A parameter was incorrect.", + Status::IP_ADDRESS_CONFLICT => "There is an address conflict address allocation", + Status::LOAD_ERROR => "The image failed to load.", + Status::MEDIA_CHANGED => "The medium in the device has changed since the last access.", + Status::NETWORK_UNREACHABLE => "The network containing the remote host is not reachable.", + Status::NO_MAPPING => "A mapping to a device does not exist.", + Status::NO_MEDIA => "The device does not contain any medium to perform the operation.", + Status::NO_RESPONSE => "The server was not found or did not respond to the request.", + Status::NOT_FOUND => "The item was not found.", + Status::NOT_READY => "There is no data pending upon return.", + Status::NOT_STARTED => "The protocol has not been started.", + Status::OUT_OF_RESOURCES => "A resource has run out.", + Status::PROTOCOL_ERROR => "A protocol error occurred during the network operation.", + Status::PROTOCOL_UNREACHABLE => "An ICMP protocol unreachable error is received.", + Status::SECURITY_VIOLATION => "The function was not performed due to a security violation.", + Status::TFTP_ERROR => "A TFTP error occurred during the network operation.", + Status::TIMEOUT => "The timeout time expired.", + Status::UNSUPPORTED => "The operation is not supported.", + Status::VOLUME_FULL => "There is no more space on the file system.", + Status::VOLUME_CORRUPTED => "An inconstancy was detected on the file system causing the operating to fail.", + Status::WRITE_PROTECTED => "The device cannot be written to.", + _ => return format!("Status: {errno}"), + }; + msg.to_owned() +} diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs new file mode 100644 index 0000000000000..b10343b2752c2 --- /dev/null +++ b/library/std/src/sys/io/error/unix.rs @@ -0,0 +1,186 @@ +use crate::ffi::{CStr, c_char, c_int}; +use crate::io; + +unsafe extern "C" { + #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] + #[cfg_attr( + any( + target_os = "linux", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "hurd", + ), + link_name = "__errno_location" + )] + #[cfg_attr( + any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "android", + target_os = "redox", + target_os = "nuttx", + target_env = "newlib" + ), + link_name = "__errno" + )] + #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] + #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] + #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")] + #[cfg_attr(target_os = "haiku", link_name = "_errnop")] + #[cfg_attr(target_os = "aix", link_name = "_Errno")] + // SAFETY: this will always return the same pointer on a given thread. + #[unsafe(ffi_const)] + pub safe fn errno_location() -> *mut c_int; +} + +/// Returns the platform-specific value of errno +#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] +#[inline] +pub fn errno() -> i32 { + unsafe { (*errno_location()) as i32 } +} + +/// Sets the platform-specific value of errno +// needed for readdir and syscall! +#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] +#[allow(dead_code)] // but not all target cfgs actually end up using it +#[inline] +pub fn set_errno(e: i32) { + unsafe { *errno_location() = e as c_int } +} + +#[cfg(target_os = "vxworks")] +#[inline] +pub fn errno() -> i32 { + unsafe { libc::errnoGet() } +} + +#[cfg(target_os = "rtems")] +#[inline] +pub fn errno() -> i32 { + unsafe extern "C" { + #[thread_local] + static _tls_errno: c_int; + } + + unsafe { _tls_errno as i32 } +} + +#[cfg(target_os = "dragonfly")] +#[inline] +pub fn errno() -> i32 { + unsafe extern "C" { + #[thread_local] + static errno: c_int; + } + + unsafe { errno as i32 } +} + +#[cfg(target_os = "dragonfly")] +#[allow(dead_code)] +#[inline] +pub fn set_errno(e: i32) { + unsafe extern "C" { + #[thread_local] + static mut errno: c_int; + } + + unsafe { + errno = e; + } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + use io::ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => QuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + #[cfg(not(target_os = "aix"))] + libc::ENOTEMPTY => DirectoryNotEmpty, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + libc::EINPROGRESS => InProgress, + libc::EOPNOTSUPP => Unsupported, + + libc::EACCES | libc::EPERM => PermissionDenied, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(errno: i32) -> String { + const TMPBUF_SZ: usize = 128; + + unsafe extern "C" { + #[cfg_attr( + all( + any( + target_os = "linux", + target_os = "hurd", + target_env = "newlib", + target_os = "cygwin" + ), + not(target_env = "ohos") + ), + link_name = "__xpg_strerror_r" + )] + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; + } + + let mut buf = [0 as c_char; TMPBUF_SZ]; + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + + let p = p as *const _; + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() + } +} diff --git a/library/std/src/sys/io/error/wasi.rs b/library/std/src/sys/io/error/wasi.rs new file mode 100644 index 0000000000000..719fc0c900adc --- /dev/null +++ b/library/std/src/sys/io/error/wasi.rs @@ -0,0 +1,80 @@ +use crate::ffi::CStr; +use crate::io as std_io; + +unsafe extern "C" { + #[thread_local] + #[link_name = "errno"] + static mut libc_errno: libc::c_int; +} + +pub fn errno() -> i32 { + unsafe { libc_errno as i32 } +} + +pub fn set_errno(val: i32) { + unsafe { + libc_errno = val; + } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { + use std_io::ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => QuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + libc::EINPROGRESS => InProgress, + libc::EOPNOTSUPP => Unsupported, + libc::EACCES | libc::EPERM => PermissionDenied, + libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +pub fn error_string(errno: i32) -> String { + let mut buf = [0 as libc::c_char; 1024]; + + let p = buf.as_mut_ptr(); + unsafe { + if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } +} diff --git a/library/std/src/sys/io/error/windows.rs b/library/std/src/sys/io/error/windows.rs new file mode 100644 index 0000000000000..e02197ac67756 --- /dev/null +++ b/library/std/src/sys/io/error/windows.rs @@ -0,0 +1,140 @@ +use crate::sys::pal::{api, c}; +use crate::{io, ptr}; + +pub fn errno() -> i32 { + api::get_last_error().code as i32 +} + +#[inline] +pub fn is_interrupted(_errno: i32) -> bool { + false +} + +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + use io::ErrorKind::*; + + match errno as u32 { + c::ERROR_ACCESS_DENIED => return PermissionDenied, + c::ERROR_ALREADY_EXISTS => return AlreadyExists, + c::ERROR_FILE_EXISTS => return AlreadyExists, + c::ERROR_BROKEN_PIPE => return BrokenPipe, + c::ERROR_FILE_NOT_FOUND + | c::ERROR_PATH_NOT_FOUND + | c::ERROR_INVALID_DRIVE + | c::ERROR_BAD_NETPATH + | c::ERROR_BAD_NET_NAME => return NotFound, + c::ERROR_NO_DATA => return BrokenPipe, + c::ERROR_INVALID_NAME | c::ERROR_BAD_PATHNAME => return InvalidFilename, + c::ERROR_INVALID_PARAMETER => return InvalidInput, + c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory, + c::ERROR_SEM_TIMEOUT + | c::WAIT_TIMEOUT + | c::ERROR_DRIVER_CANCEL_TIMEOUT + | c::ERROR_OPERATION_ABORTED + | c::ERROR_SERVICE_REQUEST_TIMEOUT + | c::ERROR_COUNTER_TIMEOUT + | c::ERROR_TIMEOUT + | c::ERROR_RESOURCE_CALL_TIMED_OUT + | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT + | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT + | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT + | c::ERROR_DS_TIMELIMIT_EXCEEDED + | c::DNS_ERROR_RECORD_TIMED_OUT + | c::ERROR_IPSEC_IKE_TIMED_OUT + | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT + | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return TimedOut, + c::ERROR_CALL_NOT_IMPLEMENTED => return Unsupported, + c::ERROR_HOST_UNREACHABLE => return HostUnreachable, + c::ERROR_NETWORK_UNREACHABLE => return NetworkUnreachable, + c::ERROR_DIRECTORY => return NotADirectory, + c::ERROR_DIRECTORY_NOT_SUPPORTED => return IsADirectory, + c::ERROR_DIR_NOT_EMPTY => return DirectoryNotEmpty, + c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem, + c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull, + c::ERROR_SEEK_ON_DEVICE => return NotSeekable, + c::ERROR_DISK_QUOTA_EXCEEDED => return QuotaExceeded, + c::ERROR_FILE_TOO_LARGE => return FileTooLarge, + c::ERROR_BUSY => return ResourceBusy, + c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, + c::ERROR_NOT_SAME_DEVICE => return CrossesDevices, + c::ERROR_TOO_MANY_LINKS => return TooManyLinks, + c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename, + c::ERROR_CANT_RESOLVE_FILENAME => return FilesystemLoop, + _ => {} + } + + match errno { + c::WSAEACCES => PermissionDenied, + c::WSAEADDRINUSE => AddrInUse, + c::WSAEADDRNOTAVAIL => AddrNotAvailable, + c::WSAECONNABORTED => ConnectionAborted, + c::WSAECONNREFUSED => ConnectionRefused, + c::WSAECONNRESET => ConnectionReset, + c::WSAEINVAL => InvalidInput, + c::WSAENOTCONN => NotConnected, + c::WSAEWOULDBLOCK => WouldBlock, + c::WSAETIMEDOUT => TimedOut, + c::WSAEHOSTUNREACH => HostUnreachable, + c::WSAENETDOWN => NetworkDown, + c::WSAENETUNREACH => NetworkUnreachable, + c::WSAEDQUOT => QuotaExceeded, + + _ => Uncategorized, + } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(mut errnum: i32) -> String { + let mut buf = [0 as c::WCHAR; 2048]; + + unsafe { + let mut module = ptr::null_mut(); + let mut flags = 0; + + // NTSTATUS errors may be encoded as HRESULT, which may returned from + // GetLastError. For more information about Windows error codes, see + // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a + if (errnum & c::FACILITY_NT_BIT as i32) != 0 { + // format according to https://support.microsoft.com/en-us/help/259693 + const NTDLL_DLL: &[u16] = &[ + 'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _, + 'L' as _, 0, + ]; + module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); + + if !module.is_null() { + errnum ^= c::FACILITY_NT_BIT as i32; + flags = c::FORMAT_MESSAGE_FROM_HMODULE; + } + } + + let res = c::FormatMessageW( + flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, + module, + errnum as u32, + 0, + buf.as_mut_ptr(), + buf.len() as u32, + ptr::null(), + ) as usize; + if res == 0 { + // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId, + let fm_err = errno(); + return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})"); + } + + match String::from_utf16(&buf[..res]) { + Ok(mut msg) => { + // Trim trailing CRLF inserted by FormatMessageW + let len = msg.trim_end().len(); + msg.truncate(len); + msg + } + Err(..) => format!( + "OS Error {} (FormatMessageW() returned \ + invalid UTF-16)", + errnum + ), + } + } +} diff --git a/library/std/src/sys/io/error/xous.rs b/library/std/src/sys/io/error/xous.rs new file mode 100644 index 0000000000000..2e9ea8e4f0928 --- /dev/null +++ b/library/std/src/sys/io/error/xous.rs @@ -0,0 +1,17 @@ +use crate::os::xous::ffi::Error as XousError; + +pub fn errno() -> i32 { + 0 +} + +pub fn is_interrupted(_code: i32) -> bool { + false +} + +pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { + crate::io::ErrorKind::Uncategorized +} + +pub fn error_string(errno: i32) -> String { + Into::::into(errno).to_string() +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 4a5d6f8e27f25..b3587ab63696a 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -1,5 +1,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] +mod error; + mod io_slice { cfg_select! { any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { @@ -48,6 +50,19 @@ mod is_terminal { mod kernel_copy; +#[cfg_attr(not(target_os = "linux"), allow(unused_imports))] +#[cfg(all( + target_family = "unix", + not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")) +))] +pub use error::errno_location; +#[cfg_attr(not(target_os = "linux"), allow(unused_imports))] +#[cfg(any( + all(target_family = "unix", not(any(target_os = "vxworks", target_os = "rtems"))), + target_os = "wasi", +))] +pub use error::set_errno; +pub use error::{RawOsError, decode_error_kind, errno, error_string, is_interrupted}; pub use io_slice::{IoSlice, IoSliceMut}; pub use is_terminal::is_terminal; pub use kernel_copy::{CopyState, kernel_copy}; @@ -55,8 +70,3 @@ pub use kernel_copy::{CopyState, kernel_copy}; // Bare metal platforms usually have very small amounts of RAM // (in the order of hundreds of KB) pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; - -pub type RawOsError = cfg_select! { - target_os = "uefi" => usize, - _ => i32, -}; diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index d09ba97cfe012..5e20c0ffdfa6a 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -151,7 +151,7 @@ impl Socket { loop { let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; if result.is_minus_one() { - let err = crate::sys::os::errno(); + let err = crate::sys::io::errno(); match err { libc::EINTR => continue, libc::EISCONN => return Ok(()), diff --git a/library/std/src/sys/net/hostname/unix.rs b/library/std/src/sys/net/hostname/unix.rs index bc6fa82a38f0d..d444182f3fde6 100644 --- a/library/std/src/sys/net/hostname/unix.rs +++ b/library/std/src/sys/net/hostname/unix.rs @@ -1,7 +1,7 @@ use crate::ffi::OsString; use crate::io; use crate::os::unix::ffi::OsStringExt; -use crate::sys::pal::os::errno; +use crate::sys::io::errno; pub fn hostname() -> io::Result { // Query the system for the maximum host name length. diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 0e1328a7972f8..db64f8d882e24 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -75,32 +75,6 @@ pub unsafe extern "C" fn runtime_entry( } } -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == hermit_abi::errno::EINTR -} - -pub fn decode_error_kind(errno: i32) -> io::ErrorKind { - match errno { - hermit_abi::errno::EACCES => io::ErrorKind::PermissionDenied, - hermit_abi::errno::EADDRINUSE => io::ErrorKind::AddrInUse, - hermit_abi::errno::EADDRNOTAVAIL => io::ErrorKind::AddrNotAvailable, - hermit_abi::errno::EAGAIN => io::ErrorKind::WouldBlock, - hermit_abi::errno::ECONNABORTED => io::ErrorKind::ConnectionAborted, - hermit_abi::errno::ECONNREFUSED => io::ErrorKind::ConnectionRefused, - hermit_abi::errno::ECONNRESET => io::ErrorKind::ConnectionReset, - hermit_abi::errno::EEXIST => io::ErrorKind::AlreadyExists, - hermit_abi::errno::EINTR => io::ErrorKind::Interrupted, - hermit_abi::errno::EINVAL => io::ErrorKind::InvalidInput, - hermit_abi::errno::ENOENT => io::ErrorKind::NotFound, - hermit_abi::errno::ENOTCONN => io::ErrorKind::NotConnected, - hermit_abi::errno::EPERM => io::ErrorKind::PermissionDenied, - hermit_abi::errno::EPIPE => io::ErrorKind::BrokenPipe, - hermit_abi::errno::ETIMEDOUT => io::ErrorKind::TimedOut, - _ => io::ErrorKind::Uncategorized, - } -} - #[doc(hidden)] pub trait IsNegative { fn is_negative(&self) -> bool; @@ -131,12 +105,7 @@ impl IsNegative for i32 { impl_is_negative! { i8 i16 i64 isize } pub fn cvt(t: T) -> io::Result { - if t.is_negative() { - let e = decode_error_kind(t.negate()); - Err(io::Error::from(e)) - } else { - Ok(t) - } + if t.is_negative() { Err(io::Error::from_raw_os_error(t.negate())) } else { Ok(t) } } pub fn cvt_r(mut f: F) -> io::Result diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 9681964ed9b2f..48a7cdcd2f763 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -5,14 +5,6 @@ use crate::path::{self, PathBuf}; use crate::sys::unsupported; use crate::{fmt, io}; -pub fn errno() -> i32 { - unsafe { hermit_abi::get_errno() } -} - -pub fn error_string(errno: i32) -> String { - hermit_abi::error_string(errno).to_string() -} - pub fn getcwd() -> io::Result { Ok(PathBuf::from("/")) } diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index 016fbe5c154c9..e5b99cea01d55 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -44,45 +44,6 @@ pub fn unsupported_err() -> io::Error { io::Error::UNSUPPORTED_PLATFORM } -pub fn is_interrupted(_code: io::RawOsError) -> bool { - false // Motor OS doesn't have signals. -} - -pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { - use moto_rt::error::*; - - if code < 0 || code > u16::MAX.into() { - return io::ErrorKind::Uncategorized; - } - - let error = moto_rt::Error::from(code as moto_rt::ErrorCode); - - match error { - moto_rt::Error::Unspecified => io::ErrorKind::Uncategorized, - moto_rt::Error::Unknown => io::ErrorKind::Uncategorized, - moto_rt::Error::NotReady => io::ErrorKind::WouldBlock, - moto_rt::Error::NotImplemented => io::ErrorKind::Unsupported, - moto_rt::Error::VersionTooHigh => io::ErrorKind::Unsupported, - moto_rt::Error::VersionTooLow => io::ErrorKind::Unsupported, - moto_rt::Error::InvalidArgument => io::ErrorKind::InvalidInput, - moto_rt::Error::OutOfMemory => io::ErrorKind::OutOfMemory, - moto_rt::Error::NotAllowed => io::ErrorKind::PermissionDenied, - moto_rt::Error::NotFound => io::ErrorKind::NotFound, - moto_rt::Error::InternalError => io::ErrorKind::Other, - moto_rt::Error::TimedOut => io::ErrorKind::TimedOut, - moto_rt::Error::AlreadyInUse => io::ErrorKind::AlreadyExists, - moto_rt::Error::UnexpectedEof => io::ErrorKind::UnexpectedEof, - moto_rt::Error::InvalidFilename => io::ErrorKind::InvalidFilename, - moto_rt::Error::NotADirectory => io::ErrorKind::NotADirectory, - moto_rt::Error::BadHandle => io::ErrorKind::InvalidInput, - moto_rt::Error::FileTooLarge => io::ErrorKind::FileTooLarge, - moto_rt::Error::NotConnected => io::ErrorKind::NotConnected, - moto_rt::Error::StorageFull => io::ErrorKind::StorageFull, - moto_rt::Error::InvalidData => io::ErrorKind::InvalidData, - _ => io::ErrorKind::Uncategorized, - } -} - pub fn abort_internal() -> ! { core::intrinsics::abort(); } diff --git a/library/std/src/sys/pal/motor/os.rs b/library/std/src/sys/pal/motor/os.rs index 0367c905b4535..cdf66e3958dbe 100644 --- a/library/std/src/sys/pal/motor/os.rs +++ b/library/std/src/sys/pal/motor/os.rs @@ -4,37 +4,8 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::motor::ffi::OsStrExt; use crate::path::{self, PathBuf}; -use crate::sys::io::RawOsError; use crate::{fmt, io}; -pub fn errno() -> RawOsError { - // Not used in Motor OS because it is ambiguous: Motor OS - // is micro-kernel-based, and I/O happens via a shared-memory - // ring buffer, so an I/O operation that on a unix is a syscall - // may involve no sycalls on Motor OS at all, or a syscall - // that e.g. waits for a notification from the I/O driver - // (sys-io); and the wait syscall may succeed, but the - // driver may report an I/O error; or a bunch of results - // for several I/O operations, some successful and some - // not. - // - // Also I/O operations in a Motor OS process are handled by a - // separate runtime background/I/O thread, so it is really hard - // to define what "last system error in the current thread" - // actually means. - let error_code: moto_rt::ErrorCode = moto_rt::Error::Unknown.into(); - error_code.into() -} - -pub fn error_string(errno: RawOsError) -> String { - let error: moto_rt::Error = match errno { - x if x < 0 => moto_rt::Error::Unknown, - x if x > u16::MAX.into() => moto_rt::Error::Unknown, - x => (x as moto_rt::ErrorCode).into(), /* u16 */ - }; - format!("{}", error) -} - pub fn getcwd() -> io::Result { moto_rt::fs::getcwd().map(PathBuf::from).map_err(map_motor_error) } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index a067480508c7c..7f1c81a0ff7bf 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -54,56 +54,6 @@ pub fn sgx_ineffective(v: T) -> io::Result { } } -#[inline] -pub fn is_interrupted(code: i32) -> bool { - code == fortanix_sgx_abi::Error::Interrupted as _ -} - -pub fn decode_error_kind(code: i32) -> io::ErrorKind { - use fortanix_sgx_abi::Error; - - // FIXME: not sure how to make sure all variants of Error are covered - if code == Error::NotFound as _ { - io::ErrorKind::NotFound - } else if code == Error::PermissionDenied as _ { - io::ErrorKind::PermissionDenied - } else if code == Error::ConnectionRefused as _ { - io::ErrorKind::ConnectionRefused - } else if code == Error::ConnectionReset as _ { - io::ErrorKind::ConnectionReset - } else if code == Error::ConnectionAborted as _ { - io::ErrorKind::ConnectionAborted - } else if code == Error::NotConnected as _ { - io::ErrorKind::NotConnected - } else if code == Error::AddrInUse as _ { - io::ErrorKind::AddrInUse - } else if code == Error::AddrNotAvailable as _ { - io::ErrorKind::AddrNotAvailable - } else if code == Error::BrokenPipe as _ { - io::ErrorKind::BrokenPipe - } else if code == Error::AlreadyExists as _ { - io::ErrorKind::AlreadyExists - } else if code == Error::WouldBlock as _ { - io::ErrorKind::WouldBlock - } else if code == Error::InvalidInput as _ { - io::ErrorKind::InvalidInput - } else if code == Error::InvalidData as _ { - io::ErrorKind::InvalidData - } else if code == Error::TimedOut as _ { - io::ErrorKind::TimedOut - } else if code == Error::WriteZero as _ { - io::ErrorKind::WriteZero - } else if code == Error::Interrupted as _ { - io::ErrorKind::Interrupted - } else if code == Error::Other as _ { - io::ErrorKind::Uncategorized - } else if code == Error::UnexpectedEof as _ { - io::ErrorKind::UnexpectedEof - } else { - io::ErrorKind::Uncategorized - } -} - pub fn abort_internal() -> ! { abi::usercalls::exit(true) } diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index 28d79963ac874..ba47af7ff88d7 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -1,25 +1,9 @@ -use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; - use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; +use crate::sys::{sgx_ineffective, unsupported}; use crate::{fmt, io}; -pub fn errno() -> i32 { - RESULT_SUCCESS -} - -pub fn error_string(errno: i32) -> String { - if errno == RESULT_SUCCESS { - "operation successful".into() - } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) { - format!("user-specified error {errno:08x}") - } else { - decode_error_kind(errno).as_str().into() - } -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 01477c7dc5e9b..4eec12dacd7ca 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -38,15 +38,6 @@ pub fn unsupported_err() -> io::Error { io::Error::UNSUPPORTED_PLATFORM } -#[inline] -pub fn is_interrupted(code: i32) -> bool { - crate::sys::net::is_interrupted(code) -} - -pub fn decode_error_kind(code: i32) -> io::ErrorKind { - error::decode_error_kind(code) -} - #[inline] pub fn abort_internal() -> ! { unsafe { libc::abort() } diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index cb6e2cbceae6c..c336a1042da40 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,4 +1,4 @@ -use super::{error, itron, unsupported}; +use super::{itron, unsupported}; use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; use crate::{fmt, io}; @@ -11,14 +11,6 @@ impl itron::error::ItronError { } } -pub fn errno() -> i32 { - 0 -} - -pub fn error_string(errno: i32) -> String { - if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{errno}") } -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index 627096b11c388..d40c10663fd17 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -38,61 +38,6 @@ pub unsafe fn cleanup() { // stack_overflow::cleanup(); } -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -// Note: code below is 1:1 copied from unix/mod.rs -pub fn decode_error_kind(errno: i32) -> io::ErrorKind { - use io::ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - libc::ENOTEMPTY => DirectoryNotEmpty, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - - libc::EACCES | libc::EPERM => PermissionDenied, - - // These two constants can have the same value on some systems, - // but different values on others, so we can't use a match - // clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - #[doc(hidden)] pub trait IsMinusOne { fn is_minus_one(&self) -> bool; diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index 512b3e2885bf0..a4b1d3c6ae670 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -7,10 +7,6 @@ use crate::ffi::{OsStr, OsString}; use crate::path::PathBuf; use crate::{fmt, io, path}; -pub fn errno() -> i32 { - unsafe { (*libc::__errno_location()) as i32 } -} - // Hardcoded to return 4096, since `sysconf` is only implemented as a stub. pub fn page_size() -> usize { // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; @@ -19,10 +15,6 @@ pub fn page_size() -> usize { // Everything below are stubs and copied from unsupported.rs -pub fn error_string(_errno: i32) -> String { - "error string unimplemented".to_string() -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index e2f99d6751f5e..b181d78c2345a 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -83,52 +83,6 @@ pub const fn unsupported_err() -> io::Error { io::const_error!(io::ErrorKind::Unsupported, "operation not supported on UEFI") } -pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { - use r_efi::efi::Status; - - match r_efi::efi::Status::from_usize(code) { - Status::ALREADY_STARTED - | Status::COMPROMISED_DATA - | Status::CONNECTION_FIN - | Status::CRC_ERROR - | Status::DEVICE_ERROR - | Status::END_OF_MEDIA - | Status::HTTP_ERROR - | Status::ICMP_ERROR - | Status::INCOMPATIBLE_VERSION - | Status::LOAD_ERROR - | Status::MEDIA_CHANGED - | Status::NO_MAPPING - | Status::NO_MEDIA - | Status::NOT_STARTED - | Status::PROTOCOL_ERROR - | Status::PROTOCOL_UNREACHABLE - | Status::TFTP_ERROR - | Status::VOLUME_CORRUPTED => io::ErrorKind::Other, - Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => io::ErrorKind::InvalidData, - Status::ABORTED => io::ErrorKind::ConnectionAborted, - Status::ACCESS_DENIED => io::ErrorKind::PermissionDenied, - Status::BUFFER_TOO_SMALL => io::ErrorKind::FileTooLarge, - Status::CONNECTION_REFUSED => io::ErrorKind::ConnectionRefused, - Status::CONNECTION_RESET => io::ErrorKind::ConnectionReset, - Status::END_OF_FILE => io::ErrorKind::UnexpectedEof, - Status::HOST_UNREACHABLE => io::ErrorKind::HostUnreachable, - Status::INVALID_PARAMETER => io::ErrorKind::InvalidInput, - Status::IP_ADDRESS_CONFLICT => io::ErrorKind::AddrInUse, - Status::NETWORK_UNREACHABLE => io::ErrorKind::NetworkUnreachable, - Status::NO_RESPONSE => io::ErrorKind::HostUnreachable, - Status::NOT_FOUND => io::ErrorKind::NotFound, - Status::NOT_READY => io::ErrorKind::ResourceBusy, - Status::OUT_OF_RESOURCES => io::ErrorKind::OutOfMemory, - Status::SECURITY_VIOLATION => io::ErrorKind::PermissionDenied, - Status::TIMEOUT => io::ErrorKind::TimedOut, - Status::UNSUPPORTED => io::ErrorKind::Unsupported, - Status::VOLUME_FULL => io::ErrorKind::StorageFull, - Status::WRITE_PROTECTED => io::ErrorKind::ReadOnlyFilesystem, - _ => io::ErrorKind::Uncategorized, - } -} - pub fn abort_internal() -> ! { if let Some(exit_boot_service_event) = NonNull::new(EXIT_BOOT_SERVICE_EVENT.load(Ordering::Acquire)) @@ -158,7 +112,3 @@ pub fn abort_internal() -> ! { extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); } - -pub fn is_interrupted(_code: io::RawOsError) -> bool { - false -} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 5593e195178de..178f7f506341e 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -9,59 +9,6 @@ use crate::path::{self, PathBuf}; use crate::ptr::NonNull; use crate::{fmt, io}; -pub fn errno() -> io::RawOsError { - 0 -} - -pub fn error_string(errno: io::RawOsError) -> String { - // Keep the List in Alphabetical Order - // The Messages are taken from UEFI Specification Appendix D - Status Codes - #[rustfmt::skip] - let msg = match r_efi::efi::Status::from_usize(errno) { - Status::ABORTED => "The operation was aborted.", - Status::ACCESS_DENIED => "Access was denied.", - Status::ALREADY_STARTED => "The protocol has already been started.", - Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.", - Status::BUFFER_TOO_SMALL => "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.", - Status::COMPROMISED_DATA => "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.", - Status::CONNECTION_FIN => "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.", - Status::CONNECTION_REFUSED => "The receiving or transmission operation fails because this connection is refused.", - Status::CONNECTION_RESET => "The connect fails because the connection is reset either by instance itself or the communication peer.", - Status::CRC_ERROR => "A CRC error was detected.", - Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.", - Status::END_OF_FILE => "The end of the file was reached.", - Status::END_OF_MEDIA => "Beginning or end of media was reached", - Status::HOST_UNREACHABLE => "The remote host is not reachable.", - Status::HTTP_ERROR => "A HTTP error occurred during the network operation.", - Status::ICMP_ERROR => "An ICMP error occurred during the network operation.", - Status::INCOMPATIBLE_VERSION => "The function encountered an internal version that was incompatible with a version requested by the caller.", - Status::INVALID_LANGUAGE => "The language specified was invalid.", - Status::INVALID_PARAMETER => "A parameter was incorrect.", - Status::IP_ADDRESS_CONFLICT => "There is an address conflict address allocation", - Status::LOAD_ERROR => "The image failed to load.", - Status::MEDIA_CHANGED => "The medium in the device has changed since the last access.", - Status::NETWORK_UNREACHABLE => "The network containing the remote host is not reachable.", - Status::NO_MAPPING => "A mapping to a device does not exist.", - Status::NO_MEDIA => "The device does not contain any medium to perform the operation.", - Status::NO_RESPONSE => "The server was not found or did not respond to the request.", - Status::NOT_FOUND => "The item was not found.", - Status::NOT_READY => "There is no data pending upon return.", - Status::NOT_STARTED => "The protocol has not been started.", - Status::OUT_OF_RESOURCES => "A resource has run out.", - Status::PROTOCOL_ERROR => "A protocol error occurred during the network operation.", - Status::PROTOCOL_UNREACHABLE => "An ICMP protocol unreachable error is received.", - Status::SECURITY_VIOLATION => "The function was not performed due to a security violation.", - Status::TFTP_ERROR => "A TFTP error occurred during the network operation.", - Status::TIMEOUT => "The timeout time expired.", - Status::UNSUPPORTED => "The operation is not supported.", - Status::VOLUME_FULL => "There is no more space on the file system.", - Status::VOLUME_CORRUPTED => "An inconstancy was detected on the file system causing the operating to fail.", - Status::WRITE_PROTECTED => "The device cannot be written to.", - _ => return format!("Status: {errno}"), - }; - msg.to_owned() -} - pub fn getcwd() -> io::Result { match helpers::open_shell() { Some(shell) => { diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index 265067d84d502..2948d3d594eaa 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -86,7 +86,7 @@ pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) } }; - match (r < 0).then(super::os::errno) { + match (r < 0).then(crate::sys::io::errno) { Some(libc::ETIMEDOUT) => return false, Some(libc::EINTR) => continue, _ => return true, @@ -167,7 +167,7 @@ pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) ) }; - r == 0 || super::os::errno() != libc::ETIMEDOUT + r == 0 || crate::sys::io::errno() != libc::ETIMEDOUT } #[cfg(target_os = "openbsd")] @@ -210,7 +210,7 @@ pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) libc::umtx_sleep(futex as *const Atomic as *const i32, expected as i32, timeout_ms) }; - r == 0 || super::os::errno() != libc::ETIMEDOUT + r == 0 || crate::sys::io::errno() != libc::ETIMEDOUT } // DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false. diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index f24898671af82..6127bb98f80ef 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -91,7 +91,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_vendor = "apple", )))] 'poll: { - use crate::sys::os::errno; + use crate::sys::io::errno; let pfds: &mut [_] = &mut [ libc::pollfd { fd: 0, events: 0, revents: 0 }, libc::pollfd { fd: 1, events: 0, revents: 0 }, @@ -135,7 +135,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "vita", )))] { - use crate::sys::os::errno; + use crate::sys::io::errno; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { open_devnull(); @@ -224,63 +224,6 @@ pub unsafe fn cleanup() { #[allow(unused_imports)] pub use libc::signal; -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -pub fn decode_error_kind(errno: i32) -> io::ErrorKind { - use io::ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - #[cfg(not(target_os = "aix"))] - libc::ENOTEMPTY => DirectoryNotEmpty, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - libc::EINPROGRESS => InProgress, - libc::EOPNOTSUPP => Unsupported, - - libc::EACCES | libc::EPERM => PermissionDenied, - - // These two constants can have the same value on some systems, - // but different values on others, so we can't use a match - // clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - #[doc(hidden)] pub trait IsMinusOne { fn is_minus_one(&self) -> bool; diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index ef3b4a02f8ae4..b8280a8f29a02 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -14,135 +14,8 @@ use crate::sys::cvt; use crate::sys::helpers::run_path_with_cstr; use crate::{fmt, io, iter, mem, ptr, slice, str}; -const TMPBUF_SZ: usize = 128; - const PATH_SEPARATOR: u8 = b':'; -unsafe extern "C" { - #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] - #[cfg_attr( - any( - target_os = "linux", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "l4re", - target_os = "hurd", - ), - link_name = "__errno_location" - )] - #[cfg_attr( - any( - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "android", - target_os = "redox", - target_os = "nuttx", - target_env = "newlib" - ), - link_name = "__errno" - )] - #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] - #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] - #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")] - #[cfg_attr(target_os = "haiku", link_name = "_errnop")] - #[cfg_attr(target_os = "aix", link_name = "_Errno")] - // SAFETY: this will always return the same pointer on a given thread. - #[unsafe(ffi_const)] - pub safe fn errno_location() -> *mut c_int; -} - -/// Returns the platform-specific value of errno -#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] -#[inline] -pub fn errno() -> i32 { - unsafe { (*errno_location()) as i32 } -} - -/// Sets the platform-specific value of errno -// needed for readdir and syscall! -#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] -#[allow(dead_code)] // but not all target cfgs actually end up using it -#[inline] -pub fn set_errno(e: i32) { - unsafe { *errno_location() = e as c_int } -} - -#[cfg(target_os = "vxworks")] -#[inline] -pub fn errno() -> i32 { - unsafe { libc::errnoGet() } -} - -#[cfg(target_os = "rtems")] -#[inline] -pub fn errno() -> i32 { - unsafe extern "C" { - #[thread_local] - static _tls_errno: c_int; - } - - unsafe { _tls_errno as i32 } -} - -#[cfg(target_os = "dragonfly")] -#[inline] -pub fn errno() -> i32 { - unsafe extern "C" { - #[thread_local] - static errno: c_int; - } - - unsafe { errno as i32 } -} - -#[cfg(target_os = "dragonfly")] -#[allow(dead_code)] -#[inline] -pub fn set_errno(e: i32) { - unsafe extern "C" { - #[thread_local] - static mut errno: c_int; - } - - unsafe { - errno = e; - } -} - -/// Gets a detailed string description for the given error number. -pub fn error_string(errno: i32) -> String { - unsafe extern "C" { - #[cfg_attr( - all( - any( - target_os = "linux", - target_os = "hurd", - target_env = "newlib", - target_os = "cygwin" - ), - not(target_env = "ohos") - ), - link_name = "__xpg_strerror_r" - )] - fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; - } - - let mut buf = [0 as c_char; TMPBUF_SZ]; - - let p = buf.as_mut_ptr(); - unsafe { - if strerror_r(errno as c_int, p, buf.len()) < 0 { - panic!("strerror_r failure"); - } - - let p = p as *const _; - // We can't always expect a UTF-8 environment. When we don't get that luxury, - // it's better to give a low-quality error message than none at all. - String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() - } -} - #[cfg(target_os = "espidf")] pub fn getcwd() -> io::Result { Ok(PathBuf::from("/")) diff --git a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs index 42eb0cd9a61af..45e2f09f303cd 100644 --- a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs +++ b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs @@ -29,7 +29,7 @@ use crate::hint::spin_loop; use crate::ops::Range; use crate::sync::Mutex; use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys::os::errno_location; +use crate::sys::io::errno_location; pub struct ThreadInfo { pub tid: u64, diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs index 34a766683830d..d94b9015d0f5f 100644 --- a/library/std/src/sys/pal/unsupported/common.rs +++ b/library/std/src/sys/pal/unsupported/common.rs @@ -16,14 +16,6 @@ pub fn unsupported_err() -> std_io::Error { std_io::Error::UNSUPPORTED_PLATFORM } -pub fn is_interrupted(_code: i32) -> bool { - false -} - -pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { - crate::io::ErrorKind::Uncategorized -} - pub fn abort_internal() -> ! { core::intrinsics::abort(); } diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 13d2a2044f48a..cb925ef4348db 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -4,14 +4,6 @@ use crate::marker::PhantomData; use crate::path::{self, PathBuf}; use crate::{fmt, io}; -pub fn errno() -> i32 { - 0 -} - -pub fn error_string(_errno: i32) -> String { - "operation successful".to_string() -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs index 049b435905d43..0abfc2fd79865 100644 --- a/library/std/src/sys/pal/vexos/mod.rs +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -5,9 +5,7 @@ pub mod time; #[path = "../unsupported/common.rs"] mod unsupported_common; -pub use unsupported_common::{ - decode_error_kind, init, is_interrupted, unsupported, unsupported_err, -}; +pub use unsupported_common::{init, unsupported, unsupported_err}; use crate::arch::global_asm; use crate::ptr; diff --git a/library/std/src/sys/pal/vexos/os.rs b/library/std/src/sys/pal/vexos/os.rs index 405f7c918f4a5..303b452a078ff 100644 --- a/library/std/src/sys/pal/vexos/os.rs +++ b/library/std/src/sys/pal/vexos/os.rs @@ -2,8 +2,8 @@ #[path = "../unsupported/os.rs"] mod unsupported_os; pub use unsupported_os::{ - JoinPathsError, SplitPaths, chdir, current_exe, errno, error_string, getcwd, getpid, home_dir, - join_paths, split_paths, temp_dir, + JoinPathsError, SplitPaths, chdir, current_exe, getcwd, getpid, home_dir, join_paths, + split_paths, temp_dir, }; pub use super::unsupported; diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs index 6bc41d469584d..4f2eb1148f0b3 100644 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ b/library/std/src/sys/pal/wasi/helpers.rs @@ -1,63 +1,11 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::io as std_io; - -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { - use std_io::ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - libc::EINPROGRESS => InProgress, - libc::EOPNOTSUPP => Unsupported, - libc::EACCES | libc::EPERM => PermissionDenied, - libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - pub fn abort_internal() -> ! { unsafe { libc::abort() } } #[inline] #[cfg(target_env = "p1")] -pub(crate) fn err2io(err: wasi::Errno) -> std_io::Error { - std_io::Error::from_raw_os_error(err.raw().into()) +pub(crate) fn err2io(err: wasi::Errno) -> crate::io::Error { + crate::io::Error::from_raw_os_error(err.raw().into()) } diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 5d36687df0977..9b49db9af6b05 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -26,9 +26,9 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. +pub(crate) use helpers::abort_internal; #[cfg(target_env = "p1")] pub(crate) use helpers::err2io; -pub(crate) use helpers::{abort_internal, decode_error_kind, is_interrupted}; #[cfg(not(target_env = "p1"))] pub use os::IsMinusOne; pub use os::{cvt, cvt_r}; diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index fee187a8adf3c..285be3ca9fda4 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -6,7 +6,7 @@ use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::unsupported; -use crate::{fmt, io, str}; +use crate::{fmt, io}; // Add a few symbols not in upstream `libc` just yet. pub mod libc { @@ -19,34 +19,6 @@ pub mod libc { } } -unsafe extern "C" { - #[thread_local] - #[link_name = "errno"] - static mut libc_errno: libc::c_int; -} - -pub fn errno() -> i32 { - unsafe { libc_errno as i32 } -} - -pub fn set_errno(val: i32) { - unsafe { - libc_errno = val; - } -} - -pub fn error_string(errno: i32) -> String { - let mut buf = [0 as libc::c_char; 1024]; - - let p = buf.as_mut_ptr(); - unsafe { - if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 { - panic!("strerror_r failure"); - } - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() - } -} - pub fn getcwd() -> io::Result { let mut buf = Vec::with_capacity(512); loop { diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 19f0259b1bf09..17e3cdbecd5c2 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -60,84 +60,6 @@ pub unsafe fn cleanup() { winsock::cleanup(); } -#[inline] -pub fn is_interrupted(_errno: i32) -> bool { - false -} - -pub fn decode_error_kind(errno: i32) -> io::ErrorKind { - use io::ErrorKind::*; - - match errno as u32 { - c::ERROR_ACCESS_DENIED => return PermissionDenied, - c::ERROR_ALREADY_EXISTS => return AlreadyExists, - c::ERROR_FILE_EXISTS => return AlreadyExists, - c::ERROR_BROKEN_PIPE => return BrokenPipe, - c::ERROR_FILE_NOT_FOUND - | c::ERROR_PATH_NOT_FOUND - | c::ERROR_INVALID_DRIVE - | c::ERROR_BAD_NETPATH - | c::ERROR_BAD_NET_NAME => return NotFound, - c::ERROR_NO_DATA => return BrokenPipe, - c::ERROR_INVALID_NAME | c::ERROR_BAD_PATHNAME => return InvalidFilename, - c::ERROR_INVALID_PARAMETER => return InvalidInput, - c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory, - c::ERROR_SEM_TIMEOUT - | c::WAIT_TIMEOUT - | c::ERROR_DRIVER_CANCEL_TIMEOUT - | c::ERROR_OPERATION_ABORTED - | c::ERROR_SERVICE_REQUEST_TIMEOUT - | c::ERROR_COUNTER_TIMEOUT - | c::ERROR_TIMEOUT - | c::ERROR_RESOURCE_CALL_TIMED_OUT - | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT - | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT - | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT - | c::ERROR_DS_TIMELIMIT_EXCEEDED - | c::DNS_ERROR_RECORD_TIMED_OUT - | c::ERROR_IPSEC_IKE_TIMED_OUT - | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT - | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return TimedOut, - c::ERROR_CALL_NOT_IMPLEMENTED => return Unsupported, - c::ERROR_HOST_UNREACHABLE => return HostUnreachable, - c::ERROR_NETWORK_UNREACHABLE => return NetworkUnreachable, - c::ERROR_DIRECTORY => return NotADirectory, - c::ERROR_DIRECTORY_NOT_SUPPORTED => return IsADirectory, - c::ERROR_DIR_NOT_EMPTY => return DirectoryNotEmpty, - c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem, - c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull, - c::ERROR_SEEK_ON_DEVICE => return NotSeekable, - c::ERROR_DISK_QUOTA_EXCEEDED => return QuotaExceeded, - c::ERROR_FILE_TOO_LARGE => return FileTooLarge, - c::ERROR_BUSY => return ResourceBusy, - c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, - c::ERROR_NOT_SAME_DEVICE => return CrossesDevices, - c::ERROR_TOO_MANY_LINKS => return TooManyLinks, - c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename, - c::ERROR_CANT_RESOLVE_FILENAME => return FilesystemLoop, - _ => {} - } - - match errno { - c::WSAEACCES => PermissionDenied, - c::WSAEADDRINUSE => AddrInUse, - c::WSAEADDRNOTAVAIL => AddrNotAvailable, - c::WSAECONNABORTED => ConnectionAborted, - c::WSAECONNREFUSED => ConnectionRefused, - c::WSAECONNRESET => ConnectionReset, - c::WSAEINVAL => InvalidInput, - c::WSAENOTCONN => NotConnected, - c::WSAEWOULDBLOCK => WouldBlock, - c::WSAETIMEDOUT => TimedOut, - c::WSAEHOSTUNREACH => HostUnreachable, - c::WSAENETDOWN => NetworkDown, - c::WSAENETUNREACH => NetworkUnreachable, - c::WSAEDQUOT => QuotaExceeded, - - _ => Uncategorized, - } -} - pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option { let ptr = haystack.as_ptr(); let mut start = haystack; diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 1b3c80c079bef..3eb6ec8278401 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -15,66 +15,6 @@ use crate::path::{self, PathBuf}; use crate::sys::pal::{c, cvt}; use crate::{fmt, io, ptr}; -pub fn errno() -> i32 { - api::get_last_error().code as i32 -} - -/// Gets a detailed string description for the given error number. -pub fn error_string(mut errnum: i32) -> String { - let mut buf = [0 as c::WCHAR; 2048]; - - unsafe { - let mut module = ptr::null_mut(); - let mut flags = 0; - - // NTSTATUS errors may be encoded as HRESULT, which may returned from - // GetLastError. For more information about Windows error codes, see - // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a - if (errnum & c::FACILITY_NT_BIT as i32) != 0 { - // format according to https://support.microsoft.com/en-us/help/259693 - const NTDLL_DLL: &[u16] = &[ - 'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _, - 'L' as _, 0, - ]; - module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); - - if !module.is_null() { - errnum ^= c::FACILITY_NT_BIT as i32; - flags = c::FORMAT_MESSAGE_FROM_HMODULE; - } - } - - let res = c::FormatMessageW( - flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, - module, - errnum as u32, - 0, - buf.as_mut_ptr(), - buf.len() as u32, - ptr::null(), - ) as usize; - if res == 0 { - // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId, - let fm_err = errno(); - return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})"); - } - - match String::from_utf16(&buf[..res]) { - Ok(mut msg) => { - // Trim trailing CRLF inserted by FormatMessageW - let len = msg.trim_end().len(); - msg.truncate(len); - msg - } - Err(..) => format!( - "OS Error {} (FormatMessageW() returned \ - invalid UTF-16)", - errnum - ), - } - } -} - pub struct SplitPaths<'a> { data: EncodeWide<'a>, must_yield: bool, diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 2da711f89dfaf..cd7b7b59d1127 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,7 +1,6 @@ use super::unsupported; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::{fmt, io}; @@ -63,14 +62,6 @@ mod c_compat { } } -pub fn errno() -> i32 { - 0 -} - -pub fn error_string(errno: i32) -> String { - Into::::into(errno).to_string() -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6dece1055a089..f09020820a031 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -33,14 +33,6 @@ pub fn unsupported_err() -> std_io::Error { std_io::Error::UNSUPPORTED_PLATFORM } -pub fn is_interrupted(_code: i32) -> bool { - false -} - -pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { - crate::io::ErrorKind::Uncategorized -} - pub fn abort_internal() -> ! { core::intrinsics::abort(); } diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 13d2a2044f48a..cb925ef4348db 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -4,14 +4,6 @@ use crate::marker::PhantomData; use crate::path::{self, PathBuf}; use crate::{fmt, io}; -pub fn errno() -> i32 { - 0 -} - -pub fn error_string(_errno: i32) -> String { - "operation successful".to_string() -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index d627a477dc1ab..31914aeb67c59 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -8,8 +8,8 @@ use crate::num::{NonZero, NonZeroI32}; use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fs::File; +use crate::sys::io::error_string; use crate::sys::pal::helpers; -use crate::sys::pal::os::error_string; use crate::sys::unsupported; use crate::{fmt, io}; diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 2e1cd7068d7f9..f6bbfed61ef31 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -61,7 +61,7 @@ cfg_select! { let bit = (signum - 1) as usize; if set.is_null() || bit >= (8 * size_of::()) { - crate::sys::pal::os::set_errno(libc::EINVAL); + crate::sys::io::set_errno(libc::EINVAL); return -1; } let raw = slice::from_raw_parts_mut( diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 5ba57e11679cf..53fe69805de31 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -194,7 +194,7 @@ impl Command { // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html #[cfg(target_os = "nto")] unsafe fn do_fork(&mut self) -> Result { - use crate::sys::os::errno; + use crate::sys::io::errno; let mut delay = MIN_FORKSPAWN_SLEEP; diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs index 53e2f1da67537..a5b790a1db42c 100644 --- a/library/std/src/sys/random/linux.rs +++ b/library/std/src/sys/random/linux.rs @@ -66,7 +66,7 @@ use crate::os::fd::AsRawFd; use crate::sync::OnceLock; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::atomic::{Atomic, AtomicBool}; -use crate::sys::pal::os::errno; +use crate::sys::io::errno; use crate::sys::pal::weak::syscall; fn getrandom(mut bytes: &mut [u8], insecure: bool) { diff --git a/library/std/src/sys/sync/condvar/windows7.rs b/library/std/src/sys/sync/condvar/windows7.rs index f03feef222124..c3f680be4bb5a 100644 --- a/library/std/src/sys/sync/condvar/windows7.rs +++ b/library/std/src/sys/sync/condvar/windows7.rs @@ -1,6 +1,6 @@ use crate::cell::UnsafeCell; +use crate::sys::c; use crate::sys::sync::{Mutex, mutex}; -use crate::sys::{c, os}; use crate::time::Duration; pub struct Condvar { @@ -30,7 +30,7 @@ impl Condvar { 0, ); if r == 0 { - debug_assert_eq!(os::errno() as usize, c::ERROR_TIMEOUT as usize); + debug_assert_eq!(crate::sys::io::errno() as usize, c::ERROR_TIMEOUT as usize); false } else { true diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index d0396ed713009..82e2e0456e649 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -14,10 +14,9 @@ use crate::num::NonZero; use crate::sys::weak::dlsym; #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; -use crate::sys::{os, stack_overflow}; use crate::thread::ThreadInit; use crate::time::Duration; -use crate::{cmp, io, ptr}; +use crate::{cmp, io, ptr, sys}; #[cfg(not(any( target_os = "l4re", target_os = "vxworks", @@ -77,7 +76,7 @@ impl Thread { // multiple of the system page size. Because it's definitely // >= PTHREAD_STACK_MIN, it must be an alignment issue. // Round up to the nearest page and try again. - let page_size = os::page_size(); + let page_size = sys::os::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); @@ -114,7 +113,7 @@ impl Thread { // Now that the thread information is set, set up our stack // overflow handler. - let _handler = stack_overflow::Handler::new(); + let _handler = sys::stack_overflow::Handler::new(); rust_start(); } @@ -536,7 +535,7 @@ pub fn sleep(dur: Duration) { secs -= ts.tv_sec as u64; let ts_ptr = &raw mut ts; if libc::nanosleep(ts_ptr, ts_ptr) == -1 { - assert_eq!(os::errno(), libc::EINTR); + assert_eq!(sys::io::errno(), libc::EINTR); secs += ts.tv_sec as u64; nsecs = ts.tv_nsec; } else { From 0f25fca1a1f36ae93c76333a11132d8f54350b7c Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 6 Jan 2026 13:38:57 +0100 Subject: [PATCH 21/32] update import in UI test --- tests/ui/stability-attribute/stability-in-private-module.rs | 2 +- tests/ui/stability-attribute/stability-in-private-module.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/stability-attribute/stability-in-private-module.rs b/tests/ui/stability-attribute/stability-in-private-module.rs index df94931690b7c..000de46ab45e3 100644 --- a/tests/ui/stability-attribute/stability-in-private-module.rs +++ b/tests/ui/stability-attribute/stability-in-private-module.rs @@ -1,4 +1,4 @@ fn main() { - let _ = std::sys::os::errno(); + let _ = std::sys::io::errno(); //~^ERROR module `sys` is private } diff --git a/tests/ui/stability-attribute/stability-in-private-module.stderr b/tests/ui/stability-attribute/stability-in-private-module.stderr index e65f8aa9b1fc1..ab27f703ab6bd 100644 --- a/tests/ui/stability-attribute/stability-in-private-module.stderr +++ b/tests/ui/stability-attribute/stability-in-private-module.stderr @@ -1,7 +1,7 @@ error[E0603]: module `sys` is private --> $DIR/stability-in-private-module.rs:2:18 | -LL | let _ = std::sys::os::errno(); +LL | let _ = std::sys::io::errno(); | ^^^ ----- function `errno` is not publicly re-exported | | | private module From 1d968067881c968c3e79558472988ab117eaf8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 10 Jan 2026 10:54:31 +0100 Subject: [PATCH 22/32] type params on eii --- tests/ui/eii/type_checking/type_params_149983.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/ui/eii/type_checking/type_params_149983.rs diff --git a/tests/ui/eii/type_checking/type_params_149983.rs b/tests/ui/eii/type_checking/type_params_149983.rs new file mode 100644 index 0000000000000..a0314657bb1d6 --- /dev/null +++ b/tests/ui/eii/type_checking/type_params_149983.rs @@ -0,0 +1,7 @@ +//@ check-fail +// Check that type parameters on EIIs are properly rejected. +// Specifically a regression test for https://github.com/rust-lang/rust/issues/149983. +#![feature(extern_item_impls)] + +#[eii] +fn foo() {} From d25f4718adf1f2358c63299af16c1c4c0519bb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 10 Jan 2026 16:00:46 +0100 Subject: [PATCH 23/32] deduplicate error message when EII has generics --- compiler/rustc_hir_analysis/messages.ftl | 4 ---- .../rustc_hir_analysis/src/check/check.rs | 9 +++++++- .../src/check/compare_eii.rs | 23 +------------------ compiler/rustc_hir_analysis/src/errors.rs | 10 -------- 4 files changed, 9 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 416a6b19edfc8..9ead1225d5f57 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -165,10 +165,6 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice .label = parameter captured again here -hir_analysis_eii_with_generics = - #[{$eii_name}] cannot have generic parameters other than lifetimes - .label = required by this attribute - hir_analysis_empty_specialization = specialization impl does not specialize any associated items .note = impl is a specialization of this impl diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4664bfcce853a..c37611ab2ee70 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -974,12 +974,19 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), (0, _) => ("const", "consts", None), _ => ("type or const", "types or consts", None), }; + let name = + if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiForeignItem) { + "externally implementable items" + } else { + "foreign items" + }; + let span = tcx.def_span(def_id); struct_span_code_err!( tcx.dcx(), span, E0044, - "foreign items may not have {kinds} parameters", + "{name} may not have {kinds} parameters", ) .with_span_label(span, format!("can't have {kinds} parameters")) .with_help( diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index d8afee9aafc98..c2a9b1fbdee0a 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -24,7 +24,7 @@ use super::potentially_plural_count; use crate::check::compare_impl_item::{ CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions, }; -use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii}; +use crate::errors::LifetimesOrBoundsMismatchOnEii; /// Checks whether the signature of some `external_impl`, matches /// the signature of `declaration`, which it is supposed to be compatible @@ -154,32 +154,11 @@ fn check_is_structurally_compatible<'tcx>( eii_name: Symbol, eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { - check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?; check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?; check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?; Ok(()) } -/// externally implementable items can't have generics -fn check_no_generics<'tcx>( - tcx: TyCtxt<'tcx>, - external_impl: LocalDefId, - _declaration: DefId, - eii_name: Symbol, - eii_attr_span: Span, -) -> Result<(), ErrorGuaranteed> { - let generics = tcx.generics_of(external_impl); - if generics.own_requires_monomorphization() { - tcx.dcx().emit_err(EiiWithGenerics { - span: tcx.def_span(external_impl), - attr: eii_attr_span, - eii_name, - }); - } - - Ok(()) -} - fn check_early_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, external_impl: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index b388396ac4fcd..7d4f65434dc42 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1652,13 +1652,3 @@ pub(crate) struct LifetimesOrBoundsMismatchOnEii { pub bounds_span: Vec, pub ident: Symbol, } - -#[derive(Diagnostic)] -#[diag(hir_analysis_eii_with_generics)] -pub(crate) struct EiiWithGenerics { - #[primary_span] - pub span: Span, - #[label] - pub attr: Span, - pub eii_name: Symbol, -} From b64a9be97ebe4195e55768e98f6eb4dcdd9220a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 11 Jan 2026 16:52:46 +0100 Subject: [PATCH 24/32] bless the tests --- tests/ui/eii/type_checking/type_params_149983.rs | 3 +++ tests/ui/eii/type_checking/type_params_149983.stderr | 11 +++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/ui/eii/type_checking/type_params_149983.stderr diff --git a/tests/ui/eii/type_checking/type_params_149983.rs b/tests/ui/eii/type_checking/type_params_149983.rs index a0314657bb1d6..6dc9b309712e1 100644 --- a/tests/ui/eii/type_checking/type_params_149983.rs +++ b/tests/ui/eii/type_checking/type_params_149983.rs @@ -5,3 +5,6 @@ #[eii] fn foo() {} +//~^ ERROR externally implementable items may not have type parameters + +fn main() {} diff --git a/tests/ui/eii/type_checking/type_params_149983.stderr b/tests/ui/eii/type_checking/type_params_149983.stderr new file mode 100644 index 0000000000000..11f06e6c88e20 --- /dev/null +++ b/tests/ui/eii/type_checking/type_params_149983.stderr @@ -0,0 +1,11 @@ +error[E0044]: externally implementable items may not have type parameters + --> $DIR/type_params_149983.rs:7:1 + | +LL | fn foo() {} + | ^^^^^^^^^^^ can't have type parameters + | + = help: replace the type parameters with concrete types like `u32` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0044`. From b454f76bd1ddf5c7459238f295647561a7895cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 11 Jan 2026 17:45:50 +0100 Subject: [PATCH 25/32] ensure generics are still properly reported on EII *implementations*, and test this --- compiler/rustc_hir_analysis/messages.ftl | 5 +++ .../src/check/compare_eii.rs | 38 ++++++++++++++++++- compiler/rustc_hir_analysis/src/errors.rs | 12 ++++++ .../type_checking/generic_implementation.rs | 13 +++++++ .../generic_implementation.stderr | 12 ++++++ 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/ui/eii/type_checking/generic_implementation.rs create mode 100644 tests/ui/eii/type_checking/generic_implementation.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 9ead1225d5f57..fa3c4cb05f961 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -165,6 +165,11 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice .label = parameter captured again here +hir_analysis_eii_with_generics = + `{$impl_name}` cannot have generic parameters other than lifetimes + .label = required by this attribute + .help = `#[{$eii_name}]` marks the implementation of an "externally implementable item" + hir_analysis_empty_specialization = specialization impl does not specialize any associated items .note = impl is a specialization of this impl diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index c2a9b1fbdee0a..2beb7eb09c119 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -8,8 +8,9 @@ use std::iter; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, E0806, struct_span_code_err}; +use rustc_hir::attrs::{AttributeKind, EiiImplResolution}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, FnSig, HirId, ItemKind}; +use rustc_hir::{self as hir, FnSig, HirId, ItemKind, find_attr}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -24,7 +25,7 @@ use super::potentially_plural_count; use crate::check::compare_impl_item::{ CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions, }; -use crate::errors::LifetimesOrBoundsMismatchOnEii; +use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii}; /// Checks whether the signature of some `external_impl`, matches /// the signature of `declaration`, which it is supposed to be compatible @@ -154,11 +155,44 @@ fn check_is_structurally_compatible<'tcx>( eii_name: Symbol, eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { + check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?; check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?; check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?; Ok(()) } +/// externally implementable items can't have generics +fn check_no_generics<'tcx>( + tcx: TyCtxt<'tcx>, + external_impl: LocalDefId, + _declaration: DefId, + eii_name: Symbol, + eii_attr_span: Span, +) -> Result<(), ErrorGuaranteed> { + let generics = tcx.generics_of(external_impl); + if generics.own_requires_monomorphization() + // When an EII implementation is automatically generated by the `#[eii]` macro, + // it will directly refer to the foreign item, not through a macro. + // We don't want to emit this error if it's an implementation that's generated by the `#[eii]` macro, + // since in that case it looks like a duplicate error: the declaration of the EII already can't contain generics. + // So, we check here if at least one of the eii impls has ImplResolution::Macro, which indicates it's + // not generated as part of the declaration. + && find_attr!( + tcx.get_all_attrs(external_impl), + AttributeKind::EiiImpls(impls) if impls.iter().any(|i| matches!(i.resolution, EiiImplResolution::Macro(_))) + ) + { + tcx.dcx().emit_err(EiiWithGenerics { + span: tcx.def_span(external_impl), + attr: eii_attr_span, + eii_name, + impl_name: tcx.item_name(external_impl), + }); + } + + Ok(()) +} + fn check_early_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, external_impl: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 7d4f65434dc42..185a822bdfa6a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1652,3 +1652,15 @@ pub(crate) struct LifetimesOrBoundsMismatchOnEii { pub bounds_span: Vec, pub ident: Symbol, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_eii_with_generics)] +#[help] +pub(crate) struct EiiWithGenerics { + #[primary_span] + pub span: Span, + #[label] + pub attr: Span, + pub eii_name: Symbol, + pub impl_name: Symbol, +} diff --git a/tests/ui/eii/type_checking/generic_implementation.rs b/tests/ui/eii/type_checking/generic_implementation.rs new file mode 100644 index 0000000000000..489fd2e645d84 --- /dev/null +++ b/tests/ui/eii/type_checking/generic_implementation.rs @@ -0,0 +1,13 @@ +//@ check-fail +// Check that type parameters on EIIs are properly rejected. +// Specifically a regression test for https://github.com/rust-lang/rust/issues/149983. +#![feature(extern_item_impls)] + +#[eii] +fn foo(); + +#[foo] +fn foo_impl() {} +//~^ ERROR `foo_impl` cannot have generic parameters other than lifetimes + +fn main() {} diff --git a/tests/ui/eii/type_checking/generic_implementation.stderr b/tests/ui/eii/type_checking/generic_implementation.stderr new file mode 100644 index 0000000000000..17a71998423d7 --- /dev/null +++ b/tests/ui/eii/type_checking/generic_implementation.stderr @@ -0,0 +1,12 @@ +error: `foo_impl` cannot have generic parameters other than lifetimes + --> $DIR/generic_implementation.rs:10:1 + | +LL | #[foo] + | ------ required by this attribute +LL | fn foo_impl() {} + | ^^^^^^^^^^^^^^^^ + | + = help: `#[foo]` marks the implementation of an "externally implementable item" + +error: aborting due to 1 previous error + From d616e6cb189c762789a8179cb00e065fcc7f9992 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Tue, 13 Jan 2026 21:36:53 +0900 Subject: [PATCH 26/32] Emit error instead of delayed bug when meeting mismatch type for const array --- .../src/hir_ty_lowering/mod.rs | 9 ++++---- ...array-expr-type-mismatch-in-where-bound.rs | 21 +++++++++++++++++++ ...y-expr-type-mismatch-in-where-bound.stderr | 15 +++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs create mode 100644 tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr 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 d2cbf89336d8b..a66a521975c53 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2415,11 +2415,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let ty::Array(elem_ty, _) = ty.kind() else { - return Const::new_error_with_message( - tcx, - array_expr.span, - "const array must have an array type", - ); + let e = tcx + .dcx() + .span_err(array_expr.span, format!("expected `{}`, found const array", ty)); + return Const::new_error(tcx, e); }; let elems = array_expr diff --git a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs new file mode 100644 index 0000000000000..cda519b96d4d8 --- /dev/null +++ b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs @@ -0,0 +1,21 @@ +//! regression test for +#![feature(min_generic_const_args)] +#![feature(adt_const_params)] +#![expect(incomplete_features)] + +trait Trait1 {} +trait Trait2 {} + +fn foo() +where + T: Trait1<{ [] }>, //~ ERROR: expected `usize`, found const array +{ +} + +fn bar() +where + T: Trait2<3>, //~ ERROR: mismatched types +{ +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr new file mode 100644 index 0000000000000..be40e44742267 --- /dev/null +++ b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr @@ -0,0 +1,15 @@ +error: expected `usize`, found const array + --> $DIR/array-expr-type-mismatch-in-where-bound.rs:11:17 + | +LL | T: Trait1<{ [] }>, + | ^^ + +error[E0308]: mismatched types + --> $DIR/array-expr-type-mismatch-in-where-bound.rs:17:15 + | +LL | T: Trait2<3>, + | ^ expected `[u8; 3]`, found integer + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From d1c2e5cf7bcfb402f8dbbaa2c651cfcb3a73b222 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Tue, 13 Jan 2026 20:53:48 +0800 Subject: [PATCH 27/32] Add test for issue 151048 --- .../mgca/tuple_expr_arg_bad-issue-151048.rs | 8 ++++++++ .../mgca/tuple_expr_arg_bad-issue-151048.stderr | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs create mode 100644 tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs new file mode 100644 index 0000000000000..4aecd30e86bbb --- /dev/null +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs @@ -0,0 +1,8 @@ +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +struct Y { + stuff: [u8; { ([1, 2], 3, [4, 5]) }], //~ ERROR expected `usize`, found const tuple +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr new file mode 100644 index 0000000000000..468bf703d90d4 --- /dev/null +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr @@ -0,0 +1,8 @@ +error: expected `usize`, found const tuple + --> $DIR/tuple_expr_arg_bad-issue-151048.rs:5:19 + | +LL | stuff: [u8; { ([1, 2], 3, [4, 5]) }], + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 84d59d073170aecbb15de9c1cdcf542bf7443673 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 12 Jan 2026 19:39:34 +0100 Subject: [PATCH 28/32] Port `#[rustc_dump_user_args]` --- .../rustc_attr_parsing/src/attributes/mod.rs | 1 + .../src/attributes/rustc_dump.rs | 17 +++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 ++ compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_typeck/src/writeback.rs | 9 +++++---- compiler/rustc_passes/src/check_attr.rs | 2 +- 7 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index e02d71a261581..26db2992d8af3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -57,6 +57,7 @@ pub(crate) mod pin_v2; pub(crate) mod proc_macro_attrs; pub(crate) mod prototype; pub(crate) mod repr; +pub(crate) mod rustc_dump; pub(crate) mod rustc_internal; pub(crate) mod semantics; pub(crate) mod stability; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs new file mode 100644 index 0000000000000..d5593a7e02f39 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -0,0 +1,17 @@ +use rustc_hir::Target; +use rustc_hir::attrs::AttributeKind; +use rustc_span::{Span, Symbol, sym}; + +use crate::attributes::prelude::Allow; +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; +use crate::target_checking::AllowedTargets; + +pub(crate) struct RustcDumpUserArgs; + +impl NoArgsAttributeParser for RustcDumpUserArgs { + const PATH: &[Symbol] = &[sym::rustc_dump_user_args]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6b1b1d484283f..4ba1936086a25 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -63,6 +63,7 @@ use crate::attributes::proc_macro_attrs::{ }; use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; +use crate::attributes::rustc_dump::RustcDumpUserArgs; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, @@ -267,6 +268,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 8d18d335b355b..42575f712d4a2 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -906,6 +906,9 @@ pub enum AttributeKind { /// Represents `#[rustc_coherence_is_core]` RustcCoherenceIsCore(Span), + /// Represents `#[rustc_dump_user_args]` + RustcDumpUserArgs, + /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 33655f4f00635..07dd9f8e3e065 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -97,6 +97,7 @@ impl AttributeKind { Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, RustcCoherenceIsCore(..) => No, + RustcDumpUserArgs => No, RustcHasIncoherentInherentImpls => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 960a8497a2668..0078cd9d06833 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -14,9 +14,10 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::{E0720, ErrorGuaranteed}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, InferKind, Visitor}; -use rustc_hir::{self as hir, AmbigArg, HirId}; +use rustc_hir::{self as hir, AmbigArg, HirId, find_attr}; use rustc_infer::traits::solve::Goal; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -25,7 +26,7 @@ use rustc_middle::ty::{ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions, }; -use rustc_span::{Span, sym}; +use rustc_span::Span; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::opaque_types::opaque_type_has_defining_use_args; use rustc_trait_selection::solve; @@ -45,8 +46,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This attribute causes us to dump some writeback information // in the form of errors, which is used for unit tests. - let rustc_dump_user_args = - self.has_rustc_attrs && self.tcx.has_attr(item_def_id, sym::rustc_dump_user_args); + let rustc_dump_user_args = self.has_rustc_attrs + && find_attr!(self.tcx.get_all_attrs(item_def_id), AttributeKind::RustcDumpUserArgs); let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_args); for param in body.params { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8f80822a81ab1..f088bd5b5119a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -309,6 +309,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::CfiEncoding { .. } | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::MustNotSupend { .. } + | AttributeKind::RustcDumpUserArgs ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -379,7 +380,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_variance_of_opaques | sym::rustc_hidden_type_of_opaques | sym::rustc_mir - | sym::rustc_dump_user_args | sym::rustc_effective_visibility | sym::rustc_outlives | sym::rustc_symbol_name From cf4d480eff238667a779c56938ebc51956f586d4 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 12 Jan 2026 19:43:31 +0100 Subject: [PATCH 29/32] Port `#[rustc_dump_def_parents]` --- compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 ++- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_analysis/src/collect/dump.rs | 5 +++-- compiler/rustc_passes/src/check_attr.rs | 2 +- 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index d5593a7e02f39..86cc88fe26e6a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -15,3 +15,12 @@ impl NoArgsAttributeParser for RustcDumpUserArgs { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs; } + +pub(crate) struct RustcDumpDefParents; + +impl NoArgsAttributeParser for RustcDumpDefParents { + const PATH: &[Symbol] = &[sym::rustc_dump_def_parents]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 4ba1936086a25..adcdbf7752a3c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -63,7 +63,7 @@ use crate::attributes::proc_macro_attrs::{ }; use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; -use crate::attributes::rustc_dump::RustcDumpUserArgs; +use crate::attributes::rustc_dump::{RustcDumpUserArgs, RustcDumpDefParents}; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, @@ -268,6 +268,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 42575f712d4a2..63a89c84ed4a3 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -906,6 +906,9 @@ pub enum AttributeKind { /// Represents `#[rustc_coherence_is_core]` RustcCoherenceIsCore(Span), + /// Represents `#[rustc_dump_def_parents]` + RustcDumpDefParents, + /// Represents `#[rustc_dump_user_args]` RustcDumpUserArgs, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 07dd9f8e3e065..aa2a7cb6d4b49 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -97,6 +97,7 @@ impl AttributeKind { Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, RustcCoherenceIsCore(..) => No, + RustcDumpDefParents => No, RustcDumpUserArgs => No, RustcHasIncoherentInherentImpls => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index b167f31a246c6..1ace60721eaed 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,6 +1,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_hir::intravisit; +use rustc_hir::{find_attr, intravisit}; +use rustc_hir::attrs::AttributeKind; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; @@ -54,7 +55,7 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { pub(crate) fn def_parents(tcx: TyCtxt<'_>) { for iid in tcx.hir_free_items() { let did = iid.owner_id.def_id; - if tcx.has_attr(did, sym::rustc_dump_def_parents) { + if find_attr!(tcx.get_all_attrs(did), AttributeKind::RustcDumpDefParents) { struct AnonConstFinder<'tcx> { tcx: TyCtxt<'tcx>, anon_consts: Vec, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f088bd5b5119a..dfa407964fc4b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -310,6 +310,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::MustNotSupend { .. } | AttributeKind::RustcDumpUserArgs + | AttributeKind::RustcDumpDefParents ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -369,7 +370,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_abi | sym::rustc_layout | sym::rustc_proc_macro_decls - | sym::rustc_dump_def_parents | sym::rustc_never_type_options | sym::rustc_autodiff | sym::rustc_capture_analysis From 2a455409e302df2d1db755edec775f5100641365 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 12 Jan 2026 19:47:18 +0100 Subject: [PATCH 30/32] Port `#[rustc_dump_item_bounds]` --- compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 5 +++-- compiler/rustc_hir/src/attrs/data_structures.rs | 5 ++++- compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_analysis/src/collect/dump.rs | 4 ++-- compiler/rustc_passes/src/check_attr.rs | 2 +- 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 86cc88fe26e6a..cc866f9297d26 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -24,3 +24,12 @@ impl NoArgsAttributeParser for RustcDumpDefParents { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents; } + +pub(crate) struct RustcDumpItemBounds; + +impl NoArgsAttributeParser for RustcDumpItemBounds { + const PATH: &[Symbol] = &[sym::rustc_dump_item_bounds]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index adcdbf7752a3c..5b959e553482c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -63,7 +63,7 @@ use crate::attributes::proc_macro_attrs::{ }; use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; -use crate::attributes::rustc_dump::{RustcDumpUserArgs, RustcDumpDefParents}; +use crate::attributes::rustc_dump::{RustcDumpDefParents, RustcDumpItemBounds, RustcDumpUserArgs}; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, @@ -268,7 +268,8 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, + Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 63a89c84ed4a3..051bd76e6eb7d 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -908,7 +908,10 @@ pub enum AttributeKind { /// Represents `#[rustc_dump_def_parents]` RustcDumpDefParents, - + + /// Represents `#[rustc_dump_item_bounds]` + RustcDumpItemBounds, + /// Represents `#[rustc_dump_user_args]` RustcDumpUserArgs, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index aa2a7cb6d4b49..f3da08bae5db6 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -98,6 +98,7 @@ impl AttributeKind { RustcBuiltinMacro { .. } => Yes, RustcCoherenceIsCore(..) => No, RustcDumpDefParents => No, + RustcDumpItemBounds => No, RustcDumpUserArgs => No, RustcHasIncoherentInherentImpls => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 1ace60721eaed..f87fb3a66095a 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{find_attr, intravisit}; -use rustc_hir::attrs::AttributeKind; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; @@ -39,7 +39,7 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { } diag.emit(); } - if tcx.has_attr(id, sym::rustc_dump_item_bounds) { + if find_attr!(tcx.get_all_attrs(id), AttributeKind::RustcDumpItemBounds) { let bounds = tcx.item_bounds(id).instantiate_identity(); let span = tcx.def_span(id); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index dfa407964fc4b..4f9f584a3a61e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -310,6 +310,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::MustNotSupend { .. } | AttributeKind::RustcDumpUserArgs + | AttributeKind::RustcDumpItemBounds | AttributeKind::RustcDumpDefParents ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { @@ -386,7 +387,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_evaluate_where_clauses | sym::rustc_dump_vtable | sym::rustc_delayed_bug_from_inside_query - | sym::rustc_dump_item_bounds | sym::rustc_def_path | sym::rustc_partition_reused | sym::rustc_partition_codegened From a4c34b421cf20e187f07c5254adedcee612aaad4 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 12 Jan 2026 19:51:50 +0100 Subject: [PATCH 31/32] Port `#[rustc_dump_vtable]` --- .../src/attributes/rustc_dump.rs | 12 ++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 5 ++++- .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir_analysis/src/collect/dump.rs | 19 ++++++++++--------- compiler/rustc_passes/src/check_attr.rs | 2 +- 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index cc866f9297d26..25e60d8994220 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -33,3 +33,15 @@ impl NoArgsAttributeParser for RustcDumpItemBounds { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; } + +pub(crate) struct RustcDumpVtable; + +impl NoArgsAttributeParser for RustcDumpVtable { + const PATH: &[Symbol] = &[sym::rustc_dump_vtable]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Impl { of_trait: true }), + Allow(Target::TyAlias), + ]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDumpVtable; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5b959e553482c..8cc09117e7108 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -63,7 +63,9 @@ use crate::attributes::proc_macro_attrs::{ }; use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; -use crate::attributes::rustc_dump::{RustcDumpDefParents, RustcDumpItemBounds, RustcDumpUserArgs}; +use crate::attributes::rustc_dump::{ + RustcDumpDefParents, RustcDumpItemBounds, RustcDumpUserArgs, RustcDumpVtable, +}; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, @@ -271,6 +273,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 051bd76e6eb7d..941c12c7d6fa7 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -915,6 +915,9 @@ pub enum AttributeKind { /// Represents `#[rustc_dump_user_args]` RustcDumpUserArgs, + /// Represents `#[rustc_dump_vtable]` + RustcDumpVtable(Span), + /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index f3da08bae5db6..df386a00dd117 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -100,6 +100,7 @@ impl AttributeKind { RustcDumpDefParents => No, RustcDumpItemBounds => No, RustcDumpUserArgs => No, + RustcDumpVtable(..) => No, RustcHasIncoherentInherentImpls => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index f87fb3a66095a..a61ea29a1a711 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -103,7 +103,9 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { for id in tcx.hir_free_items() { let def_id = id.owner_id.def_id; - let Some(attr) = tcx.get_attr(def_id, sym::rustc_dump_vtable) else { + let Some(&attr_span) = + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcDumpVtable(span) => span) + else { continue; }; @@ -112,14 +114,14 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { let trait_ref = tcx.impl_trait_ref(def_id).instantiate_identity(); if trait_ref.has_non_region_param() { tcx.dcx().span_err( - attr.span(), + attr_span, "`rustc_dump_vtable` must be applied to non-generic impl", ); continue; } if !tcx.is_dyn_compatible(trait_ref.def_id) { tcx.dcx().span_err( - attr.span(), + attr_span, "`rustc_dump_vtable` must be applied to dyn-compatible trait", ); continue; @@ -128,7 +130,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { .try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref) else { tcx.dcx().span_err( - attr.span(), + attr_span, "`rustc_dump_vtable` applied to impl header that cannot be normalized", ); continue; @@ -139,7 +141,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { let ty = tcx.type_of(def_id).instantiate_identity(); if ty.has_non_region_param() { tcx.dcx().span_err( - attr.span(), + attr_span, "`rustc_dump_vtable` must be applied to non-generic type", ); continue; @@ -148,14 +150,13 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) else { tcx.dcx().span_err( - attr.span(), + attr_span, "`rustc_dump_vtable` applied to type alias that cannot be normalized", ); continue; }; let ty::Dynamic(data, _) = *ty.kind() else { - tcx.dcx() - .span_err(attr.span(), "`rustc_dump_vtable` to type alias of dyn type"); + tcx.dcx().span_err(attr_span, "`rustc_dump_vtable` to type alias of dyn type"); continue; }; if let Some(principal) = data.principal() { @@ -168,7 +169,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { } _ => { tcx.dcx().span_err( - attr.span(), + attr_span, "`rustc_dump_vtable` only applies to impl, or type alias of dyn type", ); continue; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4f9f584a3a61e..3d51429f20d72 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -312,6 +312,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcDumpUserArgs | AttributeKind::RustcDumpItemBounds | AttributeKind::RustcDumpDefParents + | AttributeKind::RustcDumpVtable(..) ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -385,7 +386,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_outlives | sym::rustc_symbol_name | sym::rustc_evaluate_where_clauses - | sym::rustc_dump_vtable | sym::rustc_delayed_bug_from_inside_query | sym::rustc_def_path | sym::rustc_partition_reused From abcbf72bab56c56ab395b8c7cd44a87364ac43df Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 12 Jan 2026 19:54:54 +0100 Subject: [PATCH 32/32] Port `#[rustc_dump_predicates]` --- .../src/attributes/rustc_dump.rs | 15 +++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 4 +++- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_analysis/src/collect/dump.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 2 +- 6 files changed, 24 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 25e60d8994220..53120dece916e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -34,6 +34,21 @@ impl NoArgsAttributeParser for RustcDumpItemBounds { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; } +pub(crate) struct RustcDumpPredicates; + +impl NoArgsAttributeParser for RustcDumpPredicates { + const PATH: &[Symbol] = &[sym::rustc_dump_predicates]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::Trait), + Allow(Target::AssocTy), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpPredicates; +} + pub(crate) struct RustcDumpVtable; impl NoArgsAttributeParser for RustcDumpVtable { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 8cc09117e7108..8305d027d13ca 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -64,7 +64,8 @@ use crate::attributes::proc_macro_attrs::{ use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_dump::{ - RustcDumpDefParents, RustcDumpItemBounds, RustcDumpUserArgs, RustcDumpVtable, + RustcDumpDefParents, RustcDumpItemBounds, RustcDumpPredicates, RustcDumpUserArgs, + RustcDumpVtable, }; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, @@ -272,6 +273,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 941c12c7d6fa7..3d9362d2d923c 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -912,6 +912,9 @@ pub enum AttributeKind { /// Represents `#[rustc_dump_item_bounds]` RustcDumpItemBounds, + /// Represents `#[rustc_dump_predicates]` + RustcDumpPredicates, + /// Represents `#[rustc_dump_user_args]` RustcDumpUserArgs, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index df386a00dd117..48f3ceba9c399 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -99,6 +99,7 @@ impl AttributeKind { RustcCoherenceIsCore(..) => No, RustcDumpDefParents => No, RustcDumpItemBounds => No, + RustcDumpPredicates => No, RustcDumpUserArgs => No, RustcDumpVtable(..) => No, RustcHasIncoherentInherentImpls => Yes, diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index a61ea29a1a711..2dfd4ab6111fe 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -29,7 +29,7 @@ pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { for id in tcx.hir_crate_items(()).owners() { - if tcx.has_attr(id, sym::rustc_dump_predicates) { + if find_attr!(tcx.get_all_attrs(id), AttributeKind::RustcDumpPredicates) { let preds = tcx.predicates_of(id).instantiate_identity(tcx).predicates; let span = tcx.def_span(id); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3d51429f20d72..f047d66a82542 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -311,6 +311,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MustNotSupend { .. } | AttributeKind::RustcDumpUserArgs | AttributeKind::RustcDumpItemBounds + | AttributeKind::RustcDumpPredicates | AttributeKind::RustcDumpDefParents | AttributeKind::RustcDumpVtable(..) ) => { /* do nothing */ } @@ -377,7 +378,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_capture_analysis | sym::rustc_regions | sym::rustc_strict_coherence - | sym::rustc_dump_predicates | sym::rustc_variance | sym::rustc_variance_of_opaques | sym::rustc_hidden_type_of_opaques