diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2458eb5fafb1c..0fe906cb86223 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/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..53120dece916e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -0,0 +1,62 @@ +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; +} + +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; +} + +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; +} + +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 { + 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 6b1b1d484283f..8305d027d13ca 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -63,6 +63,10 @@ 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, RustcDumpPredicates, RustcDumpUserArgs, + RustcDumpVtable, +}; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, @@ -267,6 +271,11 @@ attribute_parsers!( Single>, Single>, 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 8d18d335b355b..3d9362d2d923c 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -906,6 +906,21 @@ pub enum AttributeKind { /// Represents `#[rustc_coherence_is_core]` RustcCoherenceIsCore(Span), + /// Represents `#[rustc_dump_def_parents]` + RustcDumpDefParents, + + /// Represents `#[rustc_dump_item_bounds]` + RustcDumpItemBounds, + + /// Represents `#[rustc_dump_predicates]` + RustcDumpPredicates, + + /// 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 33655f4f00635..48f3ceba9c399 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -97,6 +97,11 @@ impl AttributeKind { Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, RustcCoherenceIsCore(..) => No, + RustcDumpDefParents => No, + RustcDumpItemBounds => No, + RustcDumpPredicates => No, + RustcDumpUserArgs => No, + RustcDumpVtable(..) => No, RustcHasIncoherentInherentImpls => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 416a6b19edfc8..fa3c4cb05f961 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -166,8 +166,9 @@ hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twic .label = parameter captured again here hir_analysis_eii_with_generics = - #[{$eii_name}] cannot have generic parameters other than lifetimes + `{$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/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..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}; @@ -169,11 +170,23 @@ fn check_no_generics<'tcx>( eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { let generics = tcx.generics_of(external_impl); - if generics.own_requires_monomorphization() { + 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), }); } 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/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index b167f31a246c6..2dfd4ab6111fe 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::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_hir::intravisit; +use rustc_hir::{find_attr, intravisit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; @@ -28,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); @@ -38,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); @@ -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, @@ -102,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; }; @@ -111,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; @@ -127,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; @@ -138,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; @@ -147,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() { @@ -167,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_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index b388396ac4fcd..185a822bdfa6a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1655,10 +1655,12 @@ pub(crate) struct LifetimesOrBoundsMismatchOnEii { #[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/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 72d21371f66cc..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, }; @@ -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,19 @@ 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) => { + 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, ty).into() + } }; // Find any late-bound regions declared in `ty` that are not @@ -871,7 +870,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..6e1e6c157a91e 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)) => { @@ -1269,10 +1240,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 +1259,43 @@ 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, + ) + }); + + // 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) + }); + let ty = bounds::check_assoc_const_binding_type( + self, + constraint.ident, + ty, + constraint.hir_id, + ); + + self.lower_const_arg(ct, ty).into() } }; if term.references_error() { @@ -2310,16 +2320,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. @@ -2329,7 +2333,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, @@ -2341,7 +2345,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", @@ -2351,7 +2355,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", @@ -2360,12 +2364,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)); @@ -2389,16 +2393,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) } } } @@ -2406,26 +2406,21 @@ 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, - 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 .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); @@ -2508,7 +2503,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::>(); @@ -2527,15 +2522,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); @@ -2544,7 +2535,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); @@ -2631,7 +2622,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 => { @@ -2993,7 +2984,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, tcx.types.usize); Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Infer(()) => { @@ -3033,8 +3024,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, 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 9d7e09b020a75..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,7 +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::No)), + 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/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..f047d66a82542 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -309,6 +309,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::CfiEncoding { .. } | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::MustNotSupend { .. } + | AttributeKind::RustcDumpUserArgs + | AttributeKind::RustcDumpItemBounds + | AttributeKind::RustcDumpPredicates + | AttributeKind::RustcDumpDefParents + | AttributeKind::RustcDumpVtable(..) ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -368,25 +373,20 @@ 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 | 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 | sym::rustc_mir - | sym::rustc_dump_user_args | sym::rustc_effective_visibility | 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_dump_item_bounds | sym::rustc_def_path | sym::rustc_partition_reused | sym::rustc_partition_codegened 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 81b921b3744f4..ea98d326eeb91 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/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() } } } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 898ed0f7469cb..e6c6f7d766c02 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/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 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(()) } } } 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 bb2ee6ae10ed4..ca9d3fd592bf4 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -850,7 +850,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; @@ -988,7 +988,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 df64a1716d523..460d5ec0f0747 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 { 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/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}}] 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 diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 597a85f397695..b42ee3c0fb329 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; @@ -469,11 +468,17 @@ 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>, + 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); + // 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)) } } @@ -650,7 +655,9 @@ 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, lower_ty(cx.tcx, ty)).to_string(), + ) }), }, ), @@ -1531,7 +1538,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 +1819,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::No); + 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) @@ -1823,7 +1830,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::No); + let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, cx.tcx.types.usize); print_const(cx, ct) } }; @@ -2516,6 +2523,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 +2547,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 +2576,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 +3142,27 @@ 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, cx) } } hir::AssocItemConstraintKind::Bound { bounds } => AssocItemConstraintKind::Bound { bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 07d6efaa97e15..debea4fc0cec6 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -309,24 +309,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, @@ -336,58 +339,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.def_id_to_res(self_id), - 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. @@ -400,7 +351,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; } @@ -430,13 +381,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( @@ -482,7 +435,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::>() @@ -503,233 +456,310 @@ impl<'tcx> LinkCollector<'_, 'tcx> { } } } +} - /// Convert a DefId to a Res, where possible. - /// - /// This is used for resolving type aliases. - fn def_id_to_res(&self, ty_id: DefId) -> Option { - use PrimitiveType::*; - Some(match *self.cx.tcx.type_of(ty_id).instantiate_identity().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, +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)) +} + +/// Given a primitive type, try to resolve an associated item. +fn resolve_primitive_inherent_assoc_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::>() +} + +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; } - /// 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; + 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, + }; - // 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, - }) + 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)), } +} - /// 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; +/// 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, + }) +} - 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.def_id_to_res(did) else { return Vec::new() }; - self.resolve_associated_item(res, item_name, ns, disambiguator, module_id) - } - 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!(), - } - } +/// 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::*; - 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::>() - }; + // 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::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(aliased_res) = ty_to_res(tcx, tcx.type_of(alias_did).instantiate_identity()) + else { + return vec![]; + }; + 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_ident, 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(did) +// 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() - .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::>(); - } + .collect::>() + }) + .unwrap_or_default() + } +} - debug!("got associated item {assoc_items:?}"); +fn resolve_assoc_on_adt<'tcx>( + tcx: TyCtxt<'tcx>, + adt_def_id: DefId, + item_ident: Ident, + ns: Namespace, + disambiguator: Option, + module_id: DefId, +) -> Vec<(Res, DefId)> { + 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"); + // 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_ident.name { + return vec![(root_res, variant.def_id)]; + } + } + } - if !assoc_items.is_empty() { - return assoc_items; - } + if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator + && (adt_def.is_struct() || adt_def.is_union()) + { + return resolve_structfield(adt_def, item_ident.name) + .into_iter() + .map(|did| (root_res, did)) + .collect(); + } - if ns != Namespace::ValueNS { - return Vec::new(); - } + 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; + } - search_for_field() - } - Res::Def(DefKind::Trait, did) => filter_assoc_items_by_name_and_namespace( - tcx, - did, - Ident::with_dummy_span(item_name), - ns, - ) - .map(|item| { - let res = Res::Def(item.as_def_kind(), item.def_id); - (res, item.def_id) - }) - .collect::>(), - _ => Vec::new(), - } + if ns == Namespace::ValueNS && (adt_def.is_struct() || adt_def.is_union()) { + return resolve_structfield(adt_def, item_ident.name) + .into_iter() + .map(|did| (root_res, did)) + .collect(); } + + vec![] } -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)) +/// "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 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`. @@ -737,12 +767,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 @@ -750,22 +780,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 @@ -800,13 +825,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) { @@ -1540,7 +1564,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) }; @@ -1926,7 +1950,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, 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" 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..6d5454bbaf200 --- /dev/null +++ b/tests/rustdoc-html/intra-doc/adt-through-alias.rs @@ -0,0 +1,71 @@ +#![crate_name = "foo"] + +//! [`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`] + +//@ 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, +} + +pub type TheStructAlias = TheStruct; + +pub enum TheEnum { + TheVariant { the_field: i32 }, +} + +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, 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/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 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`. 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 + 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 + 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..6dc9b309712e1 --- /dev/null +++ b/tests/ui/eii/type_checking/type_params_149983.rs @@ -0,0 +1,10 @@ +//@ 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() {} +//~^ 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`. 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/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 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`. 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 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<&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[_]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>`