From 7488140d1c3958dfb3fb5fc82c67749814167f97 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 15 Sep 2026 14:31:19 +0200 Subject: [PATCH 01/65] Use nightly sync mode for josh-sync --- compiler/rustc_codegen_cranelift/josh-sync.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_codegen_cranelift/josh-sync.toml b/compiler/rustc_codegen_cranelift/josh-sync.toml index 2de9f9489cce3..d8b49ebac131d 100644 --- a/compiler/rustc_codegen_cranelift/josh-sync.toml +++ b/compiler/rustc_codegen_cranelift/josh-sync.toml @@ -1,3 +1,4 @@ repo = "rustc_codegen_cranelift" filter = ":~(history=\"keep-trivial-merges,no-splice\")[:rev(<=5e120485964f4857f1ad70f7d661fd244d087668:prefix=compiler/rustc_codegen_cranelift,<=7bd21608dfab11ea536f7be8936cc7dfac5864fb:SQUASH)]:/compiler/rustc_codegen_cranelift" filter-version = 2 +pull-mode = "Nightly" From 574e8d285b4bfd05b6e4c9a4a889955df4e37794 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 17 Sep 2026 15:51:50 +0300 Subject: [PATCH 02/65] Introduce `record_non_lazy` and `record_defaulted` macros to write to metadata tables --- compiler/rustc_metadata/src/rmeta/encoder.rs | 86 ++++++++++++-------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 12c8fac8cc2eb..4d87f752316ce 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -396,6 +396,7 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. +// Also those macros greatly simplify refactorings and modifications of writing to tables. macro_rules! record { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ { @@ -406,6 +407,22 @@ macro_rules! record { }}; } +macro_rules! record_non_lazy { + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ + { + $self.$tables.$table.set_some($def_id.index, $value); + } + }}; +} + +macro_rules! record_defaulted { + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ + { + $self.$tables.$table.set($def_id.index, $value); + } + }}; +} + // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_array($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. macro_rules! record_array { @@ -519,18 +536,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for def_id in std::iter::once(CRATE_DEF_ID) .chain(self.tcx.resolutions(()).proc_macros.iter().copied()) { - let def_key = self.lazy(defs.def_key(def_id)); + let def_key = defs.def_key(def_id); let def_path_hash = defs.def_path_hash(def_id); - self.tables.def_keys.set_some(def_id.local_def_index, def_key); - self.tables - .def_path_hashes - .set(def_id.local_def_index, def_path_hash.local_hash().as_u64()); + let def_id = def_id.to_def_id(); + + record!(self.tables.def_keys[def_id] <- def_key); + record_defaulted!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } else { for (def_index, def_key, def_path_hash) in defs.enumerated_keys_and_path_hashes() { - let def_key = self.lazy(def_key); - self.tables.def_keys.set_some(def_index, def_key); - self.tables.def_path_hashes.set(def_index, def_path_hash.local_hash().as_u64()); + let def_id = LocalDefId { local_def_index: def_index }.to_def_id(); + record!(self.tables.def_keys[def_id] <- def_key); + record_defaulted!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } } @@ -1405,7 +1422,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if state.is_doc_hidden { attr_flags |= AttrFlags::IS_DOC_HIDDEN; } - self.tables.attr_flags.set(def_id.local_def_index, attr_flags); + + record_defaulted!(self.tables.attr_flags[def_id.to_def_id()] <- attr_flags) } fn encode_def_ids(&mut self) { @@ -1422,7 +1440,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for local_id in tcx.iter_local_def_id() { let def_id = local_id.to_def_id(); let def_kind = tcx.def_kind(local_id); - self.tables.def_kind.set_some(def_id.index, def_kind); + record_non_lazy!(self.tables.def_kind[def_id] <- def_kind); // The `DefCollector` will sometimes create unnecessary `DefId`s // for trivial const arguments which are directly lowered to @@ -1506,11 +1524,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if should_encode_constness(def_kind) { let constness = self.tcx.constness(def_id); - self.tables.constness.set(def_id.index, constness); + record_defaulted!(self.tables.constness[def_id] <- constness) } if let DefKind::Fn | DefKind::AssocFn = def_kind { let asyncness = tcx.asyncness(def_id); - self.tables.asyncness.set(def_id.index, asyncness); + record_defaulted!(self.tables.asyncness[def_id] <- asyncness); record_array!(self.tables.fn_arg_idents[def_id] <- tcx.fn_arg_idents(def_id)); } if let Some(name) = tcx.intrinsic(def_id) { @@ -1556,22 +1574,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Closure | DefKind::SyntheticCoroutineBody = def_kind && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) { - self.tables.coroutine_kind.set(def_id.index, Some(coroutine_kind)) + record_defaulted!(self.tables.coroutine_kind[def_id] <- Some(coroutine_kind)) } if def_kind == DefKind::Closure && tcx.type_of(def_id).skip_binder().is_coroutine_closure() { let coroutine_for_closure = self.tcx.coroutine_for_closure(def_id); - self.tables - .coroutine_for_closure - .set_some(def_id.index, coroutine_for_closure.into()); + record_non_lazy!(self.tables.coroutine_for_closure[def_id] <- coroutine_for_closure.into()); // If this async closure has a by-move body, record it too. if tcx.needs_coroutine_by_move_body_def_id(coroutine_for_closure) { - self.tables.coroutine_by_move_body_def_id.set_some( - coroutine_for_closure.index, - self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure).into(), - ); + let id = self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure); + record_non_lazy!( + self.tables.coroutine_by_move_body_def_id[coroutine_for_closure] <- id.into() + ) } } if let DefKind::Static { .. } = def_kind { @@ -1601,9 +1617,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_info_for_macro(local_id); } if let DefKind::TyAlias = def_kind { - self.tables - .type_alias_is_checked - .set(def_id.index, self.tcx.type_alias_is_checked(def_id)); + record_defaulted!(self.tables.type_alias_is_checked[def_id] <- self.tcx.type_alias_is_checked(def_id)); + if self.tcx.type_alias_is_checked(def_id) { record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } @@ -1714,7 +1729,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { })); for field in &variant.fields { - self.tables.safety.set(field.did.index, field.safety); + record_defaulted!(self.tables.safety[field.did] <- field.safety); record!( self.tables.mut_restriction[field.did] <- field.mut_restriction ); @@ -1787,7 +1802,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let item = tcx.associated_item(def_id); if matches!(item.container, AssocContainer::Trait | AssocContainer::TraitImpl(_)) { - self.tables.defaultness.set(def_id.index, item.defaultness(tcx)); + record_defaulted!(self.tables.defaultness[def_id] <- item.defaultness(tcx)); } record!(self.tables.assoc_container[def_id] <- item.container); @@ -1840,9 +1855,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EntryBuilder::encode_mir({:?})", def_id); if encode_opt { record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); - self.tables - .cross_crate_inlinable - .set(def_id.to_def_id().index, self.tcx.cross_crate_inlinable(def_id)); + + record_defaulted!(self.tables.cross_crate_inlinable[def_id.to_def_id()] <- self.tcx.cross_crate_inlinable(def_id)); + record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] <- tcx.closure_saved_names_of_captured_variables(def_id)); @@ -1950,7 +1965,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let (_, macro_def, _) = tcx.hir_expect_item(def_id).expect_macro(); - self.tables.is_macro_rules.set(def_id.local_def_index, macro_def.macro_rules); + record_defaulted!(self.tables.is_macro_rules[def_id.to_def_id()] <- macro_def.macro_rules); record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); } @@ -2005,7 +2020,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tables.proc_macro_quoted_spans.set_some(i, span); } - self.tables.def_kind.set_some(LOCAL_CRATE.as_def_id().index, DefKind::Mod); + record_non_lazy!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local()); let vis = tcx @@ -2064,7 +2079,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = id.to_def_id(); - self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind.into())); + record_non_lazy!(self.tables.def_kind[def_id] <- DefKind::Macro(macro_kind.into())); + self.encode_attrs(id); record!(self.tables.def_keys[def_id] <- def_key); record!(self.tables.def_ident_span[def_id] <- span); @@ -2229,11 +2245,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let impl_is_fully_generic_for_reflection = tcx.impl_is_fully_generic_for_reflection(def_id); - self.tables - .impl_is_fully_generic_for_reflection - .set(def_id.index, impl_is_fully_generic_for_reflection); - self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id)); + record_defaulted!(self.tables.impl_is_fully_generic_for_reflection[def_id] <- impl_is_fully_generic_for_reflection); + record_defaulted!(self.tables.defaultness[def_id] <- tcx.defaultness(def_id)); let trait_ref = header.trait_ref.instantiate_identity().skip_norm_wip(); let simplified_self_ty = fast_reject::simplify_type( @@ -2250,7 +2264,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Ok(mut an) = trait_def.ancestors(tcx, def_id) && let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { - self.tables.impl_parent.set_some(def_id.index, parent.into()); + record_non_lazy!(self.tables.impl_parent[def_id] <- parent.into()); } // if this is an impl of `CoerceUnsized`, create its From d3cc844844c9d3d2d2b95596017cbb8026869320 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 18 Sep 2026 10:41:13 +0200 Subject: [PATCH 03/65] Prepare for merging from rust-lang/rust This updates the rust-toolchain.toml file to nightly-2026-09-18. --- compiler/rustc_codegen_cranelift/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index d7e313a7a33ed..ec7694364a8c9 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-11" +channel = "nightly-2026-09-18" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From 020ffe16fcc425bf4efbe6909ca2b990a15a55ce Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 18 Sep 2026 12:04:18 +0300 Subject: [PATCH 04/65] Move `proc_macro_quoted_spans` to `ProcMacroData`, as it is filled only when `ProcMacroData` is `Some` --- compiler/rustc_metadata/src/rmeta/decoder.rs | 6 ++++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 18 +++++++++++++----- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index c816c5a34d3e8..7577c97e99704 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1545,10 +1545,12 @@ impl CrateMetadata { fn get_proc_macro_quoted_span(&self, tcx: TyCtxt<'_>, index: usize) -> Span { self.root - .tables + .proc_macro_data + .as_ref() + .unwrap_or_else(|| panic!("missing proc macro data")) .proc_macro_quoted_spans .get(self, index) - .unwrap_or_else(|| panic!("Missing proc macro quoted span: {index:?}")) + .unwrap_or_else(|| panic!("missing proc macro quoted span: {index:?}")) .decode((self, tcx)) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 4d87f752316ce..12f7305b29058 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2015,10 +2015,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let proc_macro_decls_static = tcx.proc_macro_decls_static(()).unwrap().local_def_index; let stability = tcx.lookup_stability(CRATE_DEF_ID); - for (i, span) in self.tcx.sess.proc_macro_quoted_spans() { - let span = self.lazy(span); - self.tables.proc_macro_quoted_spans.set_some(i, span); - } record_non_lazy!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); @@ -2093,7 +2089,19 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let macros = self.lazy_array(macros); - Some(ProcMacroData { proc_macro_decls_static, stability, macros }) + let mut proc_macro_quoted_spans = TableBuilder::default(); + for (i, span) in self.tcx.sess.proc_macro_quoted_spans() { + proc_macro_quoted_spans.set_some(i, self.lazy(span)); + } + + let proc_macro_quoted_spans = proc_macro_quoted_spans.encode(&mut self.opaque); + + Some(ProcMacroData { + proc_macro_decls_static, + stability, + macros, + proc_macro_quoted_spans, + }) } else { None } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index f89be5368da9a..8abcc20a81ab5 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -194,6 +194,7 @@ pub(crate) struct ProcMacroData { proc_macro_decls_static: DefIndex, stability: Option, macros: LazyArray<(DefIndex, LazyValue)>, + proc_macro_quoted_spans: LazyTable>>, } #[derive(MetadataEncodable, LazyDecodable)] @@ -470,7 +471,6 @@ define_tables! { // `DefPathTable` up front, since we may only ever use a few // definitions from any given crate. def_keys: Table>, - proc_macro_quoted_spans: Table>, variant_data: Table>, assoc_container: Table>, macro_definition: Table>, From 3033e33b218a854563235a497e47881c4a6a3702 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 18 Sep 2026 15:42:52 +0300 Subject: [PATCH 05/65] Rename `record` -> `record_some_lazy`, `record_non_lazy` -> `record_some` and `record_defaulted` -> `record_value` --- compiler/rustc_metadata/src/rmeta/encoder.rs | 176 ++++++++++--------- 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 12f7305b29058..17ea85332306f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -397,7 +397,7 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. // Also those macros greatly simplify refactorings and modifications of writing to tables. -macro_rules! record { +macro_rules! record_some_lazy { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ { let value = $value; @@ -407,7 +407,7 @@ macro_rules! record { }}; } -macro_rules! record_non_lazy { +macro_rules! record_some { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ { $self.$tables.$table.set_some($def_id.index, $value); @@ -415,7 +415,7 @@ macro_rules! record_non_lazy { }}; } -macro_rules! record_defaulted { +macro_rules! record_value { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ { $self.$tables.$table.set($def_id.index, $value); @@ -540,14 +540,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let def_path_hash = defs.def_path_hash(def_id); let def_id = def_id.to_def_id(); - record!(self.tables.def_keys[def_id] <- def_key); - record_defaulted!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_value!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } else { for (def_index, def_key, def_path_hash) in defs.enumerated_keys_and_path_hashes() { let def_id = LocalDefId { local_def_index: def_index }.to_def_id(); - record!(self.tables.def_keys[def_id] <- def_key); - record_defaulted!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_value!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } } @@ -1423,7 +1423,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { attr_flags |= AttrFlags::IS_DOC_HIDDEN; } - record_defaulted!(self.tables.attr_flags[def_id.to_def_id()] <- attr_flags) + record_value!(self.tables.attr_flags[def_id.to_def_id()] <- attr_flags) } fn encode_def_ids(&mut self) { @@ -1440,7 +1440,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for local_id in tcx.iter_local_def_id() { let def_id = local_id.to_def_id(); let def_kind = tcx.def_kind(local_id); - record_non_lazy!(self.tables.def_kind[def_id] <- def_kind); + record_some!(self.tables.def_kind[def_id] <- def_kind); // The `DefCollector` will sometimes create unnecessary `DefId`s // for trivial const arguments which are directly lowered to @@ -1461,33 +1461,33 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { && let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id) && let Some(anon) = field.default { - record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); + record_some_lazy!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); } if should_encode_span(def_kind) { let def_span = tcx.def_span(local_id); - record!(self.tables.def_span[def_id] <- def_span); + record_some_lazy!(self.tables.def_span[def_id] <- def_span); } if should_encode_attrs(def_kind) { self.encode_attrs(local_id); } if should_encode_expn_that_defined(def_kind) { - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); + record_some_lazy!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); } if should_encode_span(def_kind) && let Some(ident_span) = tcx.def_ident_span(def_id) { - record!(self.tables.def_ident_span[def_id] <- ident_span); + record_some_lazy!(self.tables.def_ident_span[def_id] <- ident_span); } if def_kind.has_codegen_attrs() { - record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); + record_some_lazy!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); } if should_encode_visibility(def_kind) { let vis = self .tcx .local_visibility(local_id) .map_id(|mod_id| mod_id.to_local_def_id().local_def_index); - record!(self.tables.visibility[def_id] <- vis); + record_some_lazy!(self.tables.visibility[def_id] <- vis); } if should_encode_stability(def_kind) { self.encode_stability(def_id); @@ -1500,46 +1500,46 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_array!(self.tables.variances_of[def_id] <- v); } if should_encode_fn_sig(def_kind) { - record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record_some_lazy!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } if should_encode_generics(def_kind) { let g = tcx.generics_of(def_id); - record!(self.tables.generics_of[def_id] <- g); - record!(self.tables.explicit_clauses_of[def_id] <- self.tcx.explicit_clauses_of(def_id)); + record_some_lazy!(self.tables.generics_of[def_id] <- g); + record_some_lazy!(self.tables.explicit_clauses_of[def_id] <- self.tcx.explicit_clauses_of(def_id)); let inferred_outlives = self.tcx.inferred_outlives_of(def_id); record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives); for param in &g.own_params { if let ty::GenericParamDefKind::Const { has_default: true, .. } = param.kind { let default = self.tcx.const_param_default(param.def_id); - record!(self.tables.const_param_default[param.def_id] <- default); + record_some_lazy!(self.tables.const_param_default[param.def_id] <- default); } } } if tcx.is_conditionally_const(def_id) { - record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); + record_some_lazy!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); } if should_encode_type(tcx, local_id, def_kind) { - record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); + record_some_lazy!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); } if should_encode_constness(def_kind) { let constness = self.tcx.constness(def_id); - record_defaulted!(self.tables.constness[def_id] <- constness) + record_value!(self.tables.constness[def_id] <- constness) } if let DefKind::Fn | DefKind::AssocFn = def_kind { let asyncness = tcx.asyncness(def_id); - record_defaulted!(self.tables.asyncness[def_id] <- asyncness); + record_value!(self.tables.asyncness[def_id] <- asyncness); record_array!(self.tables.fn_arg_idents[def_id] <- tcx.fn_arg_idents(def_id)); } if let Some(name) = tcx.intrinsic(def_id) { - record!(self.tables.intrinsic[def_id] <- name); + record_some_lazy!(self.tables.intrinsic[def_id] <- name); } if let DefKind::TyParam | DefKind::Trait = def_kind { let default = self.tcx.object_lifetime_default(def_id); - record!(self.tables.object_lifetime_default[def_id] <- default); + record_some_lazy!(self.tables.object_lifetime_default[def_id] <- default); } if let DefKind::Trait = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record_some_lazy!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record_defaulted_array!(self.tables.explicit_super_clauses_of[def_id] <- self.tcx.explicit_super_clauses_of(def_id).skip_binder()); record_defaulted_array!(self.tables.explicit_implied_clauses_of[def_id] <- @@ -1553,7 +1553,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } if let DefKind::TraitAlias = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record_some_lazy!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record_defaulted_array!(self.tables.explicit_super_clauses_of[def_id] <- self.tcx.explicit_super_clauses_of(def_id).skip_binder()); record_defaulted_array!(self.tables.explicit_implied_clauses_of[def_id] <- @@ -1574,18 +1574,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Closure | DefKind::SyntheticCoroutineBody = def_kind && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) { - record_defaulted!(self.tables.coroutine_kind[def_id] <- Some(coroutine_kind)) + record_value!(self.tables.coroutine_kind[def_id] <- Some(coroutine_kind)) } if def_kind == DefKind::Closure && tcx.type_of(def_id).skip_binder().is_coroutine_closure() { let coroutine_for_closure = self.tcx.coroutine_for_closure(def_id); - record_non_lazy!(self.tables.coroutine_for_closure[def_id] <- coroutine_for_closure.into()); + record_some!(self.tables.coroutine_for_closure[def_id] <- coroutine_for_closure.into()); // If this async closure has a by-move body, record it too. if tcx.needs_coroutine_by_move_body_def_id(coroutine_for_closure) { let id = self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure); - record_non_lazy!( + record_some!( self.tables.coroutine_by_move_body_def_id[coroutine_for_closure] <- id.into() ) } @@ -1593,7 +1593,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Static { .. } = def_kind { if !self.tcx.is_foreign_item(def_id) { match self.tcx.eval_static_initializer(def_id) { - Ok(data) => record!(self.tables.eval_static_initializer[def_id] <- data), + Ok(data) => { + record_some_lazy!(self.tables.eval_static_initializer[def_id] <- data) + } Err(err) => match err { interpret::ErrorHandled::Reported(_, _) => { self.tcx.dcx().delayed_bug(format!( @@ -1617,49 +1619,49 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_info_for_macro(local_id); } if let DefKind::TyAlias = def_kind { - record_defaulted!(self.tables.type_alias_is_checked[def_id] <- self.tcx.type_alias_is_checked(def_id)); + record_value!(self.tables.type_alias_is_checked[def_id] <- self.tcx.type_alias_is_checked(def_id)); if self.tcx.type_alias_is_checked(def_id) { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); self.encode_explicit_item_self_bounds(def_id); - record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); + record_some_lazy!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } if let DefKind::AssocTy = def_kind { let assoc_item = tcx.associated_item(def_id); match assoc_item.container { ty::AssocContainer::Trait => { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } ty::AssocContainer::InherentImpl => { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } ty::AssocContainer::TraitImpl(_) => {} } } if let DefKind::AnonConst = def_kind { - record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); + record_some_lazy!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if let DefKind::Const | DefKind::AssocConst = def_kind { - record!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id)); + record_some_lazy!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id)); } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); + record_some_lazy!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); - record!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); + record_some_lazy!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); } } @@ -1671,7 +1673,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } for (def_id, res_map) in &tcx.resolutions(()).doc_link_resolutions { - record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); + record_some_lazy!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); } for (def_id, traits) in &tcx.resolutions(()).doc_link_traits_in_scope { @@ -1698,10 +1700,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let def_id = local_def_id.to_def_id(); let tcx = self.tcx; let adt_def = tcx.adt_def(def_id); - record!(self.tables.repr_options[def_id] <- adt_def.repr()); + record_some_lazy!(self.tables.repr_options[def_id] <- adt_def.repr()); let params_in_repr = self.tcx.params_in_repr(def_id); - record!(self.tables.params_in_repr[def_id] <- params_in_repr); + record_some_lazy!(self.tables.params_in_repr[def_id] <- params_in_repr); if adt_def.is_enum() { let module_children = tcx.module_children_local(local_def_id); @@ -1721,7 +1723,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)), is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - record!(self.tables.variant_data[variant.def_id] <- data); + record_some_lazy!(self.tables.variant_data[variant.def_id] <- data); record_array!(self.tables.associated_item_or_field_def_ids[variant.def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); @@ -1729,8 +1731,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { })); for field in &variant.fields { - record_defaulted!(self.tables.safety[field.did] <- field.safety); - record!( + record_value!(self.tables.safety[field.did] <- field.safety); + record_some_lazy!( self.tables.mut_restriction[field.did] <- field.mut_restriction ); } @@ -1738,16 +1740,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { let fn_sig = tcx.fn_sig(ctor_def_id); // FIXME only encode signature for ctor_def_id - record!(self.tables.fn_sig[variant.def_id] <- fn_sig); + record_some_lazy!(self.tables.fn_sig[variant.def_id] <- fn_sig); } } if let Some(destructor) = tcx.adt_destructor(local_def_id) { - record!(self.tables.adt_destructor[def_id] <- destructor); + record_some_lazy!(self.tables.adt_destructor[def_id] <- destructor); } if let Some(destructor) = tcx.adt_async_destructor(local_def_id) { - record!(self.tables.adt_async_destructor[def_id] <- destructor); + record_some_lazy!(self.tables.adt_async_destructor[def_id] <- destructor); } } @@ -1763,7 +1765,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // items - we encode information about proc-macros later on. if self.is_proc_macro { // Encode this here because we don't do it in encode_def_ids. - record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); + record_some_lazy!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { let module_children = tcx.module_children_local(local_def_id); @@ -1802,10 +1804,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let item = tcx.associated_item(def_id); if matches!(item.container, AssocContainer::Trait | AssocContainer::TraitImpl(_)) { - record_defaulted!(self.tables.defaultness[def_id] <- item.defaultness(tcx)); + record_value!(self.tables.defaultness[def_id] <- item.defaultness(tcx)); } - record!(self.tables.assoc_container[def_id] <- item.container); + record_some_lazy!(self.tables.assoc_container[def_id] <- item.container); if let AssocContainer::Trait = item.container && item.is_type() @@ -1818,7 +1820,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } if let ty::AssocKind::Type { data: ty::AssocTypeData::Rpitit(rpitit_info) } = item.kind { - record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); + record_some_lazy!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) { record_array!( self.tables.assumed_wf_types_for_rpitit[def_id] @@ -1854,53 +1856,53 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EntryBuilder::encode_mir({:?})", def_id); if encode_opt { - record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); + record_some_lazy!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); - record_defaulted!(self.tables.cross_crate_inlinable[def_id.to_def_id()] <- self.tcx.cross_crate_inlinable(def_id)); + record_value!(self.tables.cross_crate_inlinable[def_id.to_def_id()] <- self.tcx.cross_crate_inlinable(def_id)); - record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] + record_some_lazy!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] <- tcx.closure_saved_names_of_captured_variables(def_id)); if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record_some_lazy!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } } let mut is_trivial = false; if encode_const { if let Some((val, ty)) = tcx.trivial_const(def_id) { is_trivial = true; - record!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty)); + record_some_lazy!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty)); } else { is_trivial = false; - record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id)); + record_some_lazy!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id)); } // FIXME(generic_const_exprs): this feels wrong to have in `encode_mir` let abstract_const = tcx.thir_abstract_const(def_id); if let Ok(Some(abstract_const)) = abstract_const { - record!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const); + record_some_lazy!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const); } if should_encode_const(tcx.def_kind(def_id)) { let qualifs = tcx.mir_const_qualif(def_id); - record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); + record_some_lazy!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); let body = tcx.hir_maybe_body_owned_by(def_id); if let Some(body) = body { let const_data = rendered_const(self.tcx, &body, def_id); - record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); + record_some_lazy!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); } } } if !is_trivial { - record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id)); + record_some_lazy!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id)); } if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record_some_lazy!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } } @@ -1926,7 +1928,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_stability(def_id) { - record!(self.tables.lookup_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_stability[def_id] <- stab) } } } @@ -1937,7 +1939,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_const_stability(def_id) { - record!(self.tables.lookup_const_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_const_stability[def_id] <- stab) } } } @@ -1948,7 +1950,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_default_body_stability(def_id) { - record!(self.tables.lookup_default_body_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_default_body_stability[def_id] <- stab) } } } @@ -1956,7 +1958,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn encode_deprecation(&mut self, def_id: DefId) { if let Some(depr) = self.tcx.lookup_deprecation(def_id) { - record!(self.tables.lookup_deprecation_entry[def_id] <- depr); + record_some_lazy!(self.tables.lookup_deprecation_entry[def_id] <- depr); } } @@ -1965,8 +1967,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let (_, macro_def, _) = tcx.hir_expect_item(def_id).expect_macro(); - record_defaulted!(self.tables.is_macro_rules[def_id.to_def_id()] <- macro_def.macro_rules); - record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); + record_value!(self.tables.is_macro_rules[def_id.to_def_id()] <- macro_def.macro_rules); + record_some_lazy!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); } fn encode_native_libraries(&mut self) -> LazyArray { @@ -2016,19 +2018,19 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let proc_macro_decls_static = tcx.proc_macro_decls_static(()).unwrap().local_def_index; let stability = tcx.lookup_stability(CRATE_DEF_ID); - record_non_lazy!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); - record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); + record_some!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); + record_some_lazy!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local()); let vis = tcx .local_visibility(CRATE_DEF_ID) .map_id(|mod_id| mod_id.to_local_def_id().local_def_index); - record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis); + record_some_lazy!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis); if let Some(stability) = stability { - record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); + record_some_lazy!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); } self.encode_deprecation(LOCAL_CRATE.as_def_id()); if let Some(res_map) = tcx.resolutions(()).doc_link_resolutions.get(&CRATE_MOD_ID) { - record!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); + record_some_lazy!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); } if let Some(traits) = tcx.resolutions(()).doc_link_traits_in_scope.get(&CRATE_MOD_ID) { record_array!(self.tables.doc_link_traits_in_scope[LOCAL_CRATE.as_def_id()] <- traits); @@ -2075,15 +2077,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = id.to_def_id(); - record_non_lazy!(self.tables.def_kind[def_id] <- DefKind::Macro(macro_kind.into())); + record_some!(self.tables.def_kind[def_id] <- DefKind::Macro(macro_kind.into())); self.encode_attrs(id); - record!(self.tables.def_keys[def_id] <- def_key); - record!(self.tables.def_ident_span[def_id] <- span); - record!(self.tables.def_span[def_id] <- span); - record!(self.tables.visibility[def_id] <- ty::Visibility::Public); + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_some_lazy!(self.tables.def_ident_span[def_id] <- span); + record_some_lazy!(self.tables.def_span[def_id] <- span); + record_some_lazy!(self.tables.visibility[def_id] <- ty::Visibility::Public); if let Some(stability) = stability { - record!(self.tables.lookup_stability[def_id] <- stability); + record_some_lazy!(self.tables.lookup_stability[def_id] <- stability); } } @@ -2249,13 +2251,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if of_trait { let header = tcx.impl_trait_header(def_id); - record!(self.tables.impl_trait_header[def_id] <- header); + record_some_lazy!(self.tables.impl_trait_header[def_id] <- header); let impl_is_fully_generic_for_reflection = tcx.impl_is_fully_generic_for_reflection(def_id); - record_defaulted!(self.tables.impl_is_fully_generic_for_reflection[def_id] <- impl_is_fully_generic_for_reflection); - record_defaulted!(self.tables.defaultness[def_id] <- tcx.defaultness(def_id)); + record_value!(self.tables.impl_is_fully_generic_for_reflection[def_id] <- impl_is_fully_generic_for_reflection); + record_value!(self.tables.defaultness[def_id] <- tcx.defaultness(def_id)); let trait_ref = header.trait_ref.instantiate_identity().skip_norm_wip(); let simplified_self_ty = fast_reject::simplify_type( @@ -2272,14 +2274,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Ok(mut an) = trait_def.ancestors(tcx, def_id) && let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { - record_non_lazy!(self.tables.impl_parent[def_id] <- parent.into()); + record_some!(self.tables.impl_parent[def_id] <- parent.into()); } // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None if tcx.is_lang_item(trait_ref.def_id, LangItem::CoerceUnsized) { let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap(); - record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); + record_some_lazy!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); } } } From 0ef25a5183ecb4099051786228c9fb53af1161ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 18 Sep 2026 14:46:02 +0200 Subject: [PATCH 06/65] Merge pull request #1702 from Kobzol/josh-sync-script Update `rustup.sh` to use `rustc-josh-sync` --- .../rustc_codegen_cranelift/scripts/rustup.sh | 52 +++---------------- 1 file changed, 7 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index e26be307328f0..991524a1be912 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -4,19 +4,8 @@ set -e TOOLCHAIN=${TOOLCHAIN:-$(date +%Y-%m-%d)} -function check_git_fixed_subtree() { - if [[ ! -e ./git-fixed-subtree.sh ]]; then - echo "Missing git-fixed-subtree.sh. Please run the following commands to download it:" - echo "curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/bjorn3/git/tqc-subtree-portable/contrib/subtree/git-subtree.sh -o git-fixed-subtree.sh" - echo "chmod u+x git-fixed-subtree.sh" - exit 1 - fi - if [[ ! -x ./git-fixed-subtree.sh ]]; then - echo "git-fixed-subtree.sh is not executable. Please run the following command to make it executable:" - echo "chmod u+x git-fixed-subtree.sh" - exit 1 - fi -} +export GIT_CONFIG_GLOBAL=../rust/josh.gitconfig +export RUSTC_GIT=../rust case $1 in "prepare") @@ -33,44 +22,17 @@ case $1 in ./y.sh prepare ;; - "commit") - git add rust-toolchain.toml - git commit -m "Rustup to $(rustc -V)" - ;; "push") - check_git_fixed_subtree - - cg_clif=$(pwd) - pushd ../rust - git pull origin main + username=${2:-bjorn3} branch=sync_cg_clif-$(date +%Y-%m-%d) - git checkout -b "$branch" - "$cg_clif/git-fixed-subtree.sh" pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/rust-lang/rustc_codegen_cranelift.git main - git push -u my "$branch" - - # immediately merge the merge commit into cg_clif to prevent merge conflicts when syncing - # from rust-lang/rust later - "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust - popd - git merge sync_from_rust + rustc-josh-sync push "$branch" "$username" ;; "pull") - check_git_fixed_subtree - - RUST_VERS=$(curl "https://static.rust-lang.org/dist/$TOOLCHAIN/channel-rust-nightly-git-commit-hash.txt") - echo "Pulling $RUST_VERS ($TOOLCHAIN)" - - cg_clif=$(pwd) - pushd ../rust - git fetch origin main - git -c advice.detachedHead=false checkout "$RUST_VERS" - "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust - popd - git merge sync_from_rust -m "Sync from rust $RUST_VERS" - git branch -d sync_from_rust + git checkout -b sync_from_rust + rustc-josh-sync pull ;; *) echo "Unknown command '$1'" - echo "Usage: ./rustup.sh prepare|commit" + echo "Usage: ./rustup.sh prepare|pull|push [fork]" ;; esac From 9204d85c7980961d8e46ef38b062abaa38dcc81c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 20 Sep 2026 15:34:45 +0200 Subject: [PATCH 07/65] enable `__powitf2` --- compiler/rustc_codegen_cranelift/src/compiler_builtins.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index ca9157daae584..8e339167a5b12 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -135,8 +135,8 @@ builtin_functions! { // float intrinsics fn __powisf2(a: f32, b: i32) -> f32; fn __powidf2(a: f64, b: i32) -> f64; - // FIXME(f16_f128): `compiler-builtins` doesn't currently support `__powitf2` on MSVC. - // fn __powitf2(a: f128, b: i32) -> f128; + #[cfg(not(all(target_os = "windows", target_env = "gnu")))] + fn __powitf2(a: f128, b: i32) -> f128; fn powf(a: f32, b: f32) -> f32; fn pow(a: f64, b: f64) -> f64; fn expf(f: f32) -> f32; From f952ea04604448f7ce44d56f82cb0b37a524541c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 20 Sep 2026 18:25:09 +0200 Subject: [PATCH 08/65] support `vpclmulqdq` as emitted by the `_mm256_clmulepi64_epi128` and ` _mm512_clmulepi64_epi128` intrinsics --- .../example/std_example.rs | 77 +++++++++++++++++++ .../src/intrinsics/llvm_x86.rs | 75 +++++++++++------- 2 files changed, 123 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index e1287683d495d..5fc5f17682cc8 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -294,6 +294,19 @@ unsafe fn test_simd() { if is_x86_feature_detected!("avx512f") { test_zmm_roundtrip(); } + + #[cfg(not(jit))] + { + if is_x86_feature_detected!("pclmulqdq") { + test_mm_clmulepi64_si128(); + } + if is_x86_feature_detected!("vpclmulqdq") { + test_mm256_clmulepi64_epi128(); + } + if is_x86_feature_detected!("vpclmulqdq") && is_x86_feature_detected!("avx512f") { + test_mm512_clmulepi64_epi128(); + } + } } } @@ -395,6 +408,14 @@ pub fn assert_eq_m256i(a: __m256i, b: __m256i) { } } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx512f")] +pub fn assert_eq_m512i(a: __m512i, b: __m512i) { + unsafe { + assert_eq!(std::mem::transmute::<_, [u64; 8]>(a), std::mem::transmute::<_, [u64; 8]>(b)) + } +} + #[cfg(target_arch = "x86_64")] #[target_feature(enable = "sse2")] unsafe fn test_mm_cvtsi128_si64() { @@ -537,6 +558,62 @@ unsafe fn test_mm256_permutevar8x32_epi32() { assert_eq_m256i(r, e); } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "pclmulqdq")] +#[cfg(not(jit))] +unsafe fn test_mm_clmulepi64_si128() { + // Constants taken from https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf + let a = _mm_set_epi64x(0x7b5b546573745665, 0x63746f725d53475d); + let b = _mm_set_epi64x(0x4869285368617929, 0x5b477565726f6e5d); + let r00 = _mm_set_epi64x(0x1d4d84c85c3440c0, 0x929633d5d36f0451u64.cast_signed()); + let r01 = _mm_set_epi64x(0x1bd17c8d556ab5a1, 0x7fa540ac2a281315); + let r10 = _mm_set_epi64x(0x1a2bf6db3a30862f, 0xbabf262df4b7d5c9u64.cast_signed()); + let r11 = _mm_set_epi64x(0x1d1e1f2c592e7c45, 0xd66ee03e410fd4edu64.cast_signed()); + + assert_eq_m128i(_mm_clmulepi64_si128::<0x00>(a, b), r00); + assert_eq_m128i(_mm_clmulepi64_si128::<0x10>(a, b), r01); + assert_eq_m128i(_mm_clmulepi64_si128::<0x01>(a, b), r10); + assert_eq_m128i(_mm_clmulepi64_si128::<0x11>(a, b), r11); + + let a0 = _mm_set_epi64x(0x0000000000000000, 0x8000000000000000u64.cast_signed()); + let r = _mm_set_epi64x(0x4000000000000000, 0x0000000000000000); + assert_eq_m128i(_mm_clmulepi64_si128::<0x00>(a0, a0), r); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq")] +#[cfg(not(jit))] +unsafe fn test_mm256_clmulepi64_epi128() { + let a = _mm256_setr_epi64x(1, 2, 3, 4); + let b = _mm256_setr_epi64x(5, 6, 7, 8); + + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x00>(a, b), _mm256_setr_epi64x(5, 0, 9, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x01>(a, b), _mm256_setr_epi64x(10, 0, 28, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x10>(a, b), _mm256_setr_epi64x(6, 0, 24, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x11>(a, b), _mm256_setr_epi64x(12, 0, 32, 0)); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq,avx512f")] +#[cfg(not(jit))] +unsafe fn test_mm512_clmulepi64_epi128() { + let a = _mm512_setr_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_setr_epi64(9, 10, 11, 12, 13, 14, 15, 16); + + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x00>(a, b), + _mm512_setr_epi64(9, 0, 29, 0, 57, 0, 45, 0), + ); + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x10>(a, b), + _mm512_setr_epi64(10, 0, 20, 0, 54, 0, 112, 0), + ); + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x11>(a, b), + _mm512_setr_epi64(20, 0, 48, 0, 36, 0, 128, 0), + ); +} + #[cfg(target_arch = "x86_64")] #[target_feature(enable = "avx2")] #[cfg(not(jit))] diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 136e98964bc35..f3763b5895eba 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -855,42 +855,59 @@ pub(super) fn codegen_x86_llvm_intrinsic_call<'tcx>( ); } - "llvm.x86.pclmulqdq" => { + "llvm.x86.pclmulqdq" | "llvm.x86.pclmulqdq.256" | "llvm.x86.pclmulqdq.512" => { // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128&ig_expand=772 + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_clmulepi64_epi128&ig_expand=752 + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_clmulepi64_epi128&ig_expand=753 intrinsic_args!(fx, args => (a, b, _imm8); intrinsic); - let a = a.load_scalar(fx); - let b = b.load_scalar(fx); + let (lane_count, intrinsic_name) = match intrinsic { + "llvm.x86.pclmulqdq" => (1, "_mm_clmulepi64_si128"), + "llvm.x86.pclmulqdq.256" => (2, "_mm256_clmulepi64_epi128"), + "llvm.x86.pclmulqdq.512" => (4, "_mm512_clmulepi64_epi128"), + _ => unreachable!(), + }; - let imm8 = - if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2].node) { - imm8 - } else { - fx.tcx.dcx().span_fatal( - span, - "Index argument for `_mm_clmulepi64_si128` is not a constant", - ); - }; + let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2].node) else { + fx.tcx.dcx().span_fatal( + span, + format!("Index argument for `{intrinsic_name}` is not a constant"), + ) + }; let imm8 = imm8.to_u8(); - codegen_inline_asm_inner( - fx, - &[InlineAsmTemplatePiece::String(format!("pclmulqdq xmm0, xmm1, {imm8}").into())], - &[ - CInlineAsmOperand::InOut { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), - _late: true, - in_value: a, - out_place: Some(ret), - }, - CInlineAsmOperand::In { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), - value: b, - }, - ], - InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, - ); + // Cranelift does not currently support 256-bit or 512-bit SSA values, so we use the + // 128-bit instruction on each 128-bit lane. + for lane in 0..lane_count { + let a = a.value_typed_lane(fx, fx.tcx.types.u128, lane).load_scalar(fx); + let b = b.value_typed_lane(fx, fx.tcx.types.u128, lane).load_scalar(fx); + let ret_lane = ret.place_typed_lane(fx, fx.tcx.types.u128, lane); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + format!("pclmulqdq xmm0, xmm1, {imm8}").into(), + )], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86( + X86InlineAsmReg::xmm0, + )), + _late: true, + in_value: a, + out_place: Some(ret_lane), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86( + X86InlineAsmReg::xmm1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } } "llvm.x86.aesni.aeskeygenassist" => { From ff88d0d231495e609bd61a62800419ffaa93eb4d Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Sat, 13 Jun 2026 21:31:36 +0100 Subject: [PATCH 09/65] Integrate `stdarch` tests for aarch64 --- .../stdarch_skip/aarch64_core.txt | 301 +++++ .../stdarch_skip/aarch64_intrinsics_clang.txt | 1 + .../aarch64_intrinsics_common.txt | 1173 +++++++++++++++++ .../build_system/tests.rs | 118 +- compiler/rustc_codegen_cranelift/config.txt | 3 +- .../rustc_codegen_cranelift/example/neon.rs | 963 -------------- .../patches/0001-stdarch-Disable-SVE.patch | 51 + 7 files changed, 1645 insertions(+), 965 deletions(-) create mode 100644 compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt create mode 100644 compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt create mode 100644 compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt delete mode 100644 compiler/rustc_codegen_cranelift/example/neon.rs create mode 100644 compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt new file mode 100644 index 0000000000000..3a028cd380029 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt @@ -0,0 +1,301 @@ +vaba_s16 +vaba_s32 +vaba_s8 +vaba_u16 +vaba_u32 +vaba_u8 +vabaq_s16 +vabaq_s32 +vabaq_s8 +vabaq_u16 +vabaq_u32 +vabaq_u8 +vhadd_s16 +vhadd_s32 +vhadd_s8 +vhadd_u16 +vhadd_u32 +vhadd_u8 +vhaddq_s16 +vhaddq_s32 +vhaddq_s8 +vhaddq_u16 +vhaddq_u32 +vhaddq_u8 +vhsub_s16 +vhsub_s32 +vhsub_s8 +vhsub_u16 +vhsub_u32 +vhsub_u8 +vhsubq_s16 +vhsubq_s32 +vhsubq_s8 +vhsubq_u16 +vhsubq_u32 +vhsubq_u8 +vld1_f16_x2 +vld1_f16_x3 +vld1_f16_x4 +vld1_f32_x2 +vld1_f32_x3 +vld1_f32_x4 +vld1_f64_x2 +vld1_f64_x3 +vld1_f64_x4 +vld1_p16_x2 +vld1_p16_x3 +vld1_p16_x4 +vld1_p64_x2 +vld1_p64_x3 +vld1_p64_x4 +vld1_p8_x2 +vld1_p8_x3 +vld1_p8_x4 +vld1_s16_x2 +vld1_s16_x3 +vld1_s16_x4 +vld1_s32_x2 +vld1_s32_x3 +vld1_s32_x4 +vld1_s64_x2 +vld1_s64_x3 +vld1_s64_x4 +vld1_s8_x2 +vld1_s8_x3 +vld1_s8_x4 +vld1_u16_x2 +vld1_u16_x3 +vld1_u16_x4 +vld1_u32_x2 +vld1_u32_x3 +vld1_u32_x4 +vld1_u64_x2 +vld1_u64_x3 +vld1_u64_x4 +vld1_u8_x2 +vld1_u8_x3 +vld1_u8_x4 +vld1q_f16_x2 +vld1q_f16_x3 +vld1q_f16_x4 +vld1q_f32_x2 +vld1q_f32_x3 +vld1q_f32_x4 +vld1q_f64_x2 +vld1q_f64_x3 +vld1q_f64_x4 +vld1q_p16_x2 +vld1q_p16_x3 +vld1q_p16_x4 +vld1q_p64_x2 +vld1q_p64_x3 +vld1q_p64_x4 +vld1q_p8_x2 +vld1q_p8_x3 +vld1q_p8_x4 +vld1q_s16_x2 +vld1q_s16_x3 +vld1q_s16_x4 +vld1q_s32_x2 +vld1q_s32_x3 +vld1q_s32_x4 +vld1q_s64_x2 +vld1q_s64_x3 +vld1q_s64_x4 +vld1q_s8_x2 +vld1q_s8_x3 +vld1q_s8_x4 +vld1q_u16_x2 +vld1q_u16_x3 +vld1q_u16_x4 +vld1q_u32_x2 +vld1q_u32_x3 +vld1q_u32_x4 +vld1q_u64_x2 +vld1q_u64_x3 +vld1q_u64_x4 +vld1q_u8_x2 +vld1q_u8_x3 +vld1q_u8_x4 +vld2_f16 +vld2_f32 +vld2_lane_f16 +vld2_lane_f32 +vld2_lane_s16 +vld2_lane_s32 +vld2_lane_s64 +vld2_lane_s8 +vld2_lane_u16 +vld2_lane_u32 +vld2_lane_u64 +vld2_lane_u8 +vld2_p16 +vld2_p8 +vld2_s16 +vld2_s32 +vld2_s64 +vld2_s8 +vld2_u16 +vld2_u32 +vld2_u64 +vld2_u8 +vld2q_f16 +vld2q_f32 +vld2q_f64 +vld2q_lane_f16 +vld2q_lane_f32 +vld2q_lane_s16 +vld2q_lane_s32 +vld2q_lane_s64 +vld2q_lane_s8 +vld2q_lane_u16 +vld2q_lane_u32 +vld2q_lane_u64 +vld2q_lane_u8 +vld2q_p16 +vld2q_p8 +vld2q_s16 +vld2q_s32 +vld2q_s64 +vld2q_s8 +vld2q_u16 +vld2q_u32 +vld2q_u64 +vld2q_u8 +vld3_f16 +vld3_lane_f16 +vld3_lane_f32 +vld3_lane_s16 +vld3_lane_s32 +vld3_lane_s64 +vld3_lane_s8 +vld3_lane_u16 +vld3_lane_u32 +vld3_lane_u64 +vld3_lane_u8 +vld3q_f16 +vld3q_lane_f16 +vld3q_lane_f32 +vld3q_lane_s16 +vld3q_lane_s32 +vld3q_lane_s64 +vld3q_lane_s8 +vld3q_lane_u16 +vld3q_lane_u32 +vld3q_lane_u64 +vld3q_lane_u8 +vld4_f16 +vld4_lane_f16 +vld4_lane_f32 +vld4_lane_s16 +vld4_lane_s32 +vld4_lane_s64 +vld4_lane_s8 +vld4_lane_u16 +vld4_lane_u32 +vld4_lane_u64 +vld4_lane_u8 +vld4q_f16 +vld4q_lane_f16 +vld4q_lane_f32 +vld4q_lane_s16 +vld4q_lane_s32 +vld4q_lane_s64 +vld4q_lane_s8 +vld4q_lane_u16 +vld4q_lane_u32 +vld4q_lane_u64 +vld4q_lane_u8 +vqtbl2_p8 +vqtbl2_s8 +vqtbl2_u8 +vqtbl2q_p8 +vqtbl2q_s8 +vqtbl2q_u8 +vqtbl3_p8 +vqtbl3_s8 +vqtbl3_u8 +vqtbl3q_p8 +vqtbl3q_s8 +vqtbl3q_u8 +vqtbl4_p8 +vqtbl4_s8 +vqtbl4_u8 +vqtbl4q_p8 +vqtbl4q_s8 +vqtbl4q_u8 +vqtbx1_p8 +vqtbx1_s8 +vqtbx1_u8 +vqtbx1q_p8 +vqtbx1q_s8 +vqtbx1q_u8 +vqtbx2_p8 +vqtbx2_s8 +vqtbx2_u8 +vqtbx2q_p8 +vqtbx2q_s8 +vqtbx2q_u8 +vqtbx3_p8 +vqtbx3_s8 +vqtbx3_u8 +vqtbx3q_p8 +vqtbx3q_s8 +vqtbx3q_u8 +vqtbx4_p8 +vqtbx4_s8 +vqtbx4_u8 +vqtbx4q_p8 +vqtbx4q_s8 +vqtbx4q_u8 +vrhadd_s16 +vrhadd_s32 +vrhadd_s8 +vrhadd_u16 +vrhadd_u32 +vrhadd_u8 +vrhaddq_s16 +vrhaddq_s32 +vrhaddq_s8 +vrhaddq_u16 +vrhaddq_u32 +vrhaddq_u8 +vsli_n_p16 +vsli_n_p8 +vsli_n_s16 +vsli_n_s32 +vsli_n_s64 +vsli_n_s8 +vsli_n_u16 +vsli_n_u32 +vsli_n_u64 +vsli_n_u8 +vsliq_n_p16 +vsliq_n_p8 +vsliq_n_s16 +vsliq_n_s32 +vsliq_n_s64 +vsliq_n_s8 +vsliq_n_u16 +vsliq_n_u32 +vsliq_n_u64 +vsliq_n_u8 +vtbl3_p8 +vtbl3_s8 +vtbl3_u8 +vtbl4_p8 +vtbl4_s8 +vtbl4_u8 +vtbx1_p8 +vtbx1_s8 +vtbx1_u8 +vtbx2_p8 +vtbx2_s8 +vtbx2_u8 +vtbx3_p8 +vtbx3_s8 +vtbx3_u8 +vtbx4_p8 +vtbx4_s8 +vtbx4_u8 diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt new file mode 100644 index 0000000000000..5f11867fd3ee9 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt @@ -0,0 +1 @@ +vcvth_s16_f16 diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt new file mode 100644 index 0000000000000..a452f0c16b593 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt @@ -0,0 +1,1173 @@ +__jcvt +vaba_s16 +vaba_s32 +vaba_s8 +vaba_u16 +vaba_u32 +vaba_u8 +vabal_high_s16 +vabal_high_s32 +vabal_high_s8 +vabal_high_u16 +vabal_high_u32 +vabal_high_u8 +vabal_s16 +vabal_s32 +vabal_s8 +vabal_u16 +vabal_u32 +vabal_u8 +vabaq_s16 +vabaq_s32 +vabaq_s8 +vabaq_u16 +vabaq_u32 +vabaq_u8 +vabd_f16 +vabd_f32 +vabd_f64 +vabd_s16 +vabd_s32 +vabd_s8 +vabd_u16 +vabd_u32 +vabd_u8 +vabdd_f64 +vabdh_f16 +vabdl_high_s16 +vabdl_high_s32 +vabdl_high_s8 +vabdl_high_u16 +vabdl_high_u32 +vabdl_high_u8 +vabdl_s16 +vabdl_s32 +vabdl_s8 +vabdl_u16 +vabdl_u32 +vabdl_u8 +vabdq_f16 +vabdq_f32 +vabdq_f64 +vabdq_s16 +vabdq_s32 +vabdq_s8 +vabdq_u16 +vabdq_u32 +vabdq_u8 +vabds_f32 +vabsd_s64 +vaddlv_s16 +vaddlv_s32 +vaddlv_s8 +vaddlv_u16 +vaddlv_u32 +vaddlv_u8 +vaddlvq_s16 +vaddlvq_s32 +vaddlvq_s8 +vaddlvq_u16 +vaddlvq_u32 +vaddvq_f64 +vamax_f16 +vamaxq_f16 +vamin_f16 +vaminq_f16 +vcadd_rot270_f16 +vcadd_rot270_f32 +vcadd_rot90_f16 +vcadd_rot90_f32 +vcaddq_rot270_f16 +vcaddq_rot270_f32 +vcaddq_rot270_f64 +vcaddq_rot90_f16 +vcaddq_rot90_f32 +vcaddq_rot90_f64 +vcage_f16 +vcage_f32 +vcage_f64 +vcaged_f64 +vcageh_f16 +vcageq_f16 +vcageq_f32 +vcageq_f64 +vcages_f32 +vcagt_f16 +vcagt_f32 +vcagt_f64 +vcagtd_f64 +vcagth_f16 +vcagtq_f16 +vcagtq_f32 +vcagtq_f64 +vcagts_f32 +vcale_f16 +vcale_f32 +vcale_f64 +vcaled_f64 +vcaleh_f16 +vcaleq_f16 +vcaleq_f32 +vcaleq_f64 +vcales_f32 +vcalt_f16 +vcalt_f32 +vcalt_f64 +vcaltd_f64 +vcalth_f16 +vcaltq_f16 +vcaltq_f32 +vcaltq_f64 +vcalts_f32 +vcmla_f16 +vcmla_f32 +vcmla_lane_f16 +vcmla_lane_f32 +vcmla_laneq_f16 +vcmla_laneq_f32 +vcmla_rot180_f16 +vcmla_rot180_f32 +vcmla_rot180_lane_f16 +vcmla_rot180_lane_f32 +vcmla_rot180_laneq_f16 +vcmla_rot180_laneq_f32 +vcmla_rot270_f16 +vcmla_rot270_f32 +vcmla_rot270_lane_f16 +vcmla_rot270_lane_f32 +vcmla_rot270_laneq_f16 +vcmla_rot270_laneq_f32 +vcmla_rot90_f16 +vcmla_rot90_f32 +vcmla_rot90_lane_f16 +vcmla_rot90_lane_f32 +vcmla_rot90_laneq_f16 +vcmla_rot90_laneq_f32 +vcmlaq_f16 +vcmlaq_f32 +vcmlaq_f64 +vcmlaq_lane_f16 +vcmlaq_lane_f32 +vcmlaq_laneq_f16 +vcmlaq_laneq_f32 +vcmlaq_rot180_f16 +vcmlaq_rot180_f32 +vcmlaq_rot180_f64 +vcmlaq_rot180_lane_f16 +vcmlaq_rot180_lane_f32 +vcmlaq_rot180_laneq_f16 +vcmlaq_rot180_laneq_f32 +vcmlaq_rot270_f16 +vcmlaq_rot270_f32 +vcmlaq_rot270_f64 +vcmlaq_rot270_lane_f16 +vcmlaq_rot270_lane_f32 +vcmlaq_rot270_laneq_f16 +vcmlaq_rot270_laneq_f32 +vcmlaq_rot90_f16 +vcmlaq_rot90_f32 +vcmlaq_rot90_f64 +vcmlaq_rot90_lane_f16 +vcmlaq_rot90_lane_f32 +vcmlaq_rot90_laneq_f16 +vcmlaq_rot90_laneq_f32 +vcvt_n_f16_s16 +vcvt_n_f16_u16 +vcvt_n_f32_s32 +vcvt_n_f32_u32 +vcvt_n_f64_s64 +vcvt_n_f64_u64 +vcvt_n_s16_f16 +vcvt_n_s32_f32 +vcvt_n_s64_f64 +vcvt_n_u16_f16 +vcvt_n_u32_f32 +vcvt_n_u64_f64 +vcvt_s32_f32 +vcvt_s64_f64 +vcvt_u32_f32 +vcvt_u64_f64 +vcvta_s16_f16 +vcvta_s32_f32 +vcvta_s64_f64 +vcvta_u16_f16 +vcvta_u32_f32 +vcvta_u64_f64 +vcvtad_s64_f64 +vcvtad_u64_f64 +vcvtah_s16_f16 +vcvtah_s32_f16 +vcvtah_s64_f16 +vcvtah_u16_f16 +vcvtah_u32_f16 +vcvtah_u64_f16 +vcvtaq_s16_f16 +vcvtaq_s32_f32 +vcvtaq_s64_f64 +vcvtaq_u16_f16 +vcvtaq_u32_f32 +vcvtaq_u64_f64 +vcvtas_s32_f32 +vcvtas_u32_f32 +vcvtd_n_f64_s64 +vcvtd_n_f64_u64 +vcvtd_n_s64_f64 +vcvtd_n_u64_f64 +vcvth_n_f16_s16 +vcvth_n_f16_s32 +vcvth_n_f16_s64 +vcvth_n_f16_u16 +vcvth_n_f16_u32 +vcvth_n_f16_u64 +vcvth_n_s16_f16 +vcvth_n_s32_f16 +vcvth_n_s64_f16 +vcvth_n_u16_f16 +vcvth_n_u32_f16 +vcvth_n_u64_f16 +vcvtm_s16_f16 +vcvtm_s32_f32 +vcvtm_s64_f64 +vcvtm_u16_f16 +vcvtm_u32_f32 +vcvtm_u64_f64 +vcvtmd_s64_f64 +vcvtmd_u64_f64 +vcvtmh_s16_f16 +vcvtmh_s32_f16 +vcvtmh_s64_f16 +vcvtmh_u16_f16 +vcvtmh_u32_f16 +vcvtmh_u64_f16 +vcvtmq_s16_f16 +vcvtmq_s32_f32 +vcvtmq_s64_f64 +vcvtmq_u16_f16 +vcvtmq_u32_f32 +vcvtmq_u64_f64 +vcvtms_s32_f32 +vcvtms_u32_f32 +vcvtn_s16_f16 +vcvtn_s32_f32 +vcvtn_s64_f64 +vcvtn_u16_f16 +vcvtn_u32_f32 +vcvtn_u64_f64 +vcvtnd_s64_f64 +vcvtnd_u64_f64 +vcvtnh_s16_f16 +vcvtnh_s32_f16 +vcvtnh_s64_f16 +vcvtnh_u16_f16 +vcvtnh_u32_f16 +vcvtnh_u64_f16 +vcvtnq_s16_f16 +vcvtnq_s64_f64 +vcvtnq_u16_f16 +vcvtnq_u32_f32 +vcvtnq_u64_f64 +vcvtns_s32_f32 +vcvtns_u32_f32 +vcvtp_s16_f16 +vcvtp_s32_f32 +vcvtp_s64_f64 +vcvtp_u16_f16 +vcvtp_u32_f32 +vcvtp_u64_f64 +vcvtpd_s64_f64 +vcvtpd_u64_f64 +vcvtph_s16_f16 +vcvtph_s32_f16 +vcvtph_s64_f16 +vcvtph_u16_f16 +vcvtph_u32_f16 +vcvtph_u64_f16 +vcvtpq_s16_f16 +vcvtpq_s32_f32 +vcvtpq_s64_f64 +vcvtpq_u16_f16 +vcvtpq_u32_f32 +vcvtpq_u64_f64 +vcvtps_s32_f32 +vcvtps_u32_f32 +vcvtq_n_f16_s16 +vcvtq_n_f16_u16 +vcvtq_n_f32_s32 +vcvtq_n_f32_u32 +vcvtq_n_f64_s64 +vcvtq_n_f64_u64 +vcvtq_n_s16_f16 +vcvtq_n_s32_f32 +vcvtq_n_s64_f64 +vcvtq_n_u16_f16 +vcvtq_n_u32_f32 +vcvtq_n_u64_f64 +vcvtq_s64_f64 +vcvtq_u32_f32 +vcvtq_u64_f64 +vcvts_n_f32_s32 +vcvts_n_f32_u32 +vcvts_n_s32_f32 +vcvts_n_u32_f32 +vcvtx_f32_f64 +vcvtx_high_f32_f64 +vcvtxd_f32_f64 +vdot_lane_s32 +vdot_lane_u32 +vdot_laneq_s32 +vdot_laneq_u32 +vdot_s32 +vdot_u32 +vdotq_lane_s32 +vdotq_lane_u32 +vdotq_laneq_s32 +vdotq_laneq_u32 +vdotq_s32 +vdotq_u32 +vfmlal_high_f16 +vfmlal_lane_high_f16 +vfmlal_lane_low_f16 +vfmlal_laneq_high_f16 +vfmlal_laneq_low_f16 +vfmlal_low_f16 +vfmlalq_high_f16 +vfmlalq_lane_high_f16 +vfmlalq_lane_low_f16 +vfmlalq_laneq_high_f16 +vfmlalq_laneq_low_f16 +vfmlalq_low_f16 +vfmlsl_high_f16 +vfmlsl_lane_high_f16 +vfmlsl_lane_low_f16 +vfmlsl_laneq_high_f16 +vfmlsl_laneq_low_f16 +vfmlsl_low_f16 +vfmlslq_high_f16 +vfmlslq_lane_high_f16 +vfmlslq_lane_low_f16 +vfmlslq_laneq_high_f16 +vfmlslq_laneq_low_f16 +vfmlslq_low_f16 +vhadd_s16 +vhadd_s32 +vhadd_s8 +vhadd_u16 +vhadd_u32 +vhadd_u8 +vhaddq_s16 +vhaddq_s32 +vhaddq_s8 +vhaddq_u16 +vhaddq_u32 +vhaddq_u8 +vhsub_s16 +vhsub_s32 +vhsub_s8 +vhsub_u16 +vhsub_u32 +vhsub_u8 +vhsubq_s16 +vhsubq_s32 +vhsubq_s8 +vhsubq_u16 +vhsubq_u32 +vhsubq_u8 +vld1_dup_f16 +vld1_f16 +vld1_f16_x2 +vld1_f16_x3 +vld1_f16_x4 +vld1_lane_f16 +vld1q_dup_f16 +vld1q_f16 +vld1q_f16_x2 +vld1q_f16_x3 +vld1q_f16_x4 +vld1q_lane_f16 +vld2_dup_f16 +vld2_f16 +vld2_lane_f16 +vld2q_dup_f16 +vld2q_f16 +vld2q_lane_f16 +vld3_dup_f16 +vld3_f16 +vld3_lane_f16 +vld3q_dup_f16 +vld3q_f16 +vld3q_lane_f16 +vld4_dup_f16 +vld4_f16 +vld4_lane_f16 +vld4q_dup_f16 +vld4q_f16 +vld4q_lane_f16 +vluti2_lane_f16 +vluti2_laneq_f16 +vluti2q_lane_f16 +vluti2q_laneq_f16 +vluti4q_lane_f16_x2 +vluti4q_laneq_f16_x2 +vmax_f16 +vmaxh_f16 +vmaxnm_f16 +vmaxnm_f32 +vmaxnm_f64 +vmaxnmh_f16 +vmaxnmq_f16 +vmaxnmq_f32 +vmaxnmq_f64 +vmaxnmv_f16 +vmaxnmv_f32 +vmaxnmvq_f16 +vmaxnmvq_f32 +vmaxnmvq_f64 +vmaxq_f16 +vmaxv_f16 +vmaxv_f32 +vmaxvq_f16 +vmaxvq_f32 +vmaxvq_f64 +vmin_f16 +vminh_f16 +vminnm_f16 +vminnm_f32 +vminnm_f64 +vminnmh_f16 +vminnmq_f16 +vminnmq_f32 +vminnmq_f64 +vminnmv_f16 +vminnmv_f32 +vminnmvq_f16 +vminnmvq_f32 +vminnmvq_f64 +vminq_f16 +vminv_f16 +vminv_f32 +vminvq_f16 +vminvq_f32 +vminvq_f64 +vmmlaq_s32 +vmmlaq_u32 +vmul_p8 +vmulq_p8 +vmulx_f16 +vmulx_f32 +vmulx_f64 +vmulx_lane_f16 +vmulx_lane_f32 +vmulx_lane_f64 +vmulx_laneq_f16 +vmulx_laneq_f32 +vmulx_laneq_f64 +vmulx_n_f16 +vmulxd_f64 +vmulxd_lane_f64 +vmulxd_laneq_f64 +vmulxh_f16 +vmulxh_lane_f16 +vmulxh_laneq_f16 +vmulxq_f16 +vmulxq_f32 +vmulxq_f64 +vmulxq_lane_f16 +vmulxq_lane_f32 +vmulxq_lane_f64 +vmulxq_laneq_f16 +vmulxq_laneq_f32 +vmulxq_laneq_f64 +vmulxq_n_f16 +vmulxs_f32 +vmulxs_lane_f32 +vmulxs_laneq_f32 +vpadd_f16 +vpadd_f32 +vpaddq_f16 +vpmax_f16 +vpmaxnm_f16 +vpmaxnm_f32 +vpmaxnmq_f16 +vpmaxnmq_f32 +vpmaxnmq_f64 +vpmaxnmqd_f64 +vpmaxnms_f32 +vpmaxq_f16 +vpmaxqd_f64 +vpmaxs_f32 +vpmin_f16 +vpminnm_f16 +vpminnm_f32 +vpminnmq_f16 +vpminnmq_f32 +vpminnmq_f64 +vpminnmqd_f64 +vpminnms_f32 +vpminq_f16 +vpminqd_f64 +vpmins_f32 +vqabs_s16 +vqabs_s32 +vqabs_s64 +vqabs_s8 +vqabsb_s8 +vqabsd_s64 +vqabsh_s16 +vqabsq_s16 +vqabsq_s32 +vqabsq_s64 +vqabsq_s8 +vqabss_s32 +vqaddd_s64 +vqaddd_u64 +vqadds_s32 +vqadds_u32 +vqdmlal_high_lane_s16 +vqdmlal_high_lane_s32 +vqdmlal_high_laneq_s16 +vqdmlal_high_laneq_s32 +vqdmlal_high_n_s16 +vqdmlal_high_n_s32 +vqdmlal_high_s16 +vqdmlal_high_s32 +vqdmlal_lane_s16 +vqdmlal_lane_s32 +vqdmlal_laneq_s16 +vqdmlal_laneq_s32 +vqdmlal_n_s16 +vqdmlal_n_s32 +vqdmlal_s16 +vqdmlal_s32 +vqdmlalh_lane_s16 +vqdmlalh_laneq_s16 +vqdmlalh_s16 +vqdmlals_lane_s32 +vqdmlals_laneq_s32 +vqdmlals_s32 +vqdmlsl_high_lane_s16 +vqdmlsl_high_lane_s32 +vqdmlsl_high_laneq_s16 +vqdmlsl_high_laneq_s32 +vqdmlsl_high_n_s16 +vqdmlsl_high_n_s32 +vqdmlsl_high_s16 +vqdmlsl_high_s32 +vqdmlsl_lane_s16 +vqdmlsl_lane_s32 +vqdmlsl_laneq_s16 +vqdmlsl_laneq_s32 +vqdmlsl_n_s16 +vqdmlsl_n_s32 +vqdmlsl_s16 +vqdmlsl_s32 +vqdmlslh_lane_s16 +vqdmlslh_laneq_s16 +vqdmlslh_s16 +vqdmlsls_lane_s32 +vqdmlsls_laneq_s32 +vqdmlsls_s32 +vqdmull_high_lane_s16 +vqdmull_high_lane_s32 +vqdmull_high_laneq_s16 +vqdmull_high_laneq_s32 +vqdmull_high_n_s16 +vqdmull_high_n_s32 +vqdmull_high_s16 +vqdmull_high_s32 +vqdmull_lane_s16 +vqdmull_lane_s32 +vqdmull_laneq_s16 +vqdmull_laneq_s32 +vqdmull_n_s16 +vqdmull_n_s32 +vqdmull_s16 +vqdmull_s32 +vqdmullh_lane_s16 +vqdmullh_laneq_s16 +vqdmullh_s16 +vqdmulls_lane_s32 +vqdmulls_laneq_s32 +vqdmulls_s32 +vqmovn_high_s16 +vqmovn_high_s32 +vqmovn_high_s64 +vqmovn_high_u16 +vqmovn_high_u32 +vqmovn_high_u64 +vqmovn_s16 +vqmovn_s32 +vqmovn_s64 +vqmovn_u16 +vqmovn_u32 +vqmovn_u64 +vqmovnd_s64 +vqmovnd_u64 +vqmovnh_s16 +vqmovnh_u16 +vqmovns_s32 +vqmovns_u32 +vqmovun_high_s16 +vqmovun_high_s32 +vqmovun_high_s64 +vqmovun_s16 +vqmovun_s32 +vqmovun_s64 +vqmovund_s64 +vqmovunh_s16 +vqmovuns_s32 +vqneg_s16 +vqneg_s32 +vqneg_s64 +vqneg_s8 +vqnegb_s8 +vqnegd_s64 +vqnegh_s16 +vqnegq_s16 +vqnegq_s32 +vqnegq_s64 +vqnegq_s8 +vqnegs_s32 +vqrdmlah_lane_s16 +vqrdmlah_lane_s32 +vqrdmlah_laneq_s16 +vqrdmlah_laneq_s32 +vqrdmlah_s16 +vqrdmlah_s32 +vqrdmlahh_lane_s16 +vqrdmlahh_laneq_s16 +vqrdmlahh_s16 +vqrdmlahq_lane_s16 +vqrdmlahq_lane_s32 +vqrdmlahq_laneq_s16 +vqrdmlahq_laneq_s32 +vqrdmlahq_s16 +vqrdmlahq_s32 +vqrdmlahs_lane_s32 +vqrdmlahs_laneq_s32 +vqrdmlahs_s32 +vqrdmlsh_lane_s16 +vqrdmlsh_lane_s32 +vqrdmlsh_laneq_s16 +vqrdmlsh_laneq_s32 +vqrdmlsh_s16 +vqrdmlsh_s32 +vqrdmlshh_lane_s16 +vqrdmlshh_laneq_s16 +vqrdmlshh_s16 +vqrdmlshq_lane_s16 +vqrdmlshq_lane_s32 +vqrdmlshq_laneq_s16 +vqrdmlshq_laneq_s32 +vqrdmlshq_s16 +vqrdmlshq_s32 +vqrdmlshs_lane_s32 +vqrdmlshs_laneq_s32 +vqrdmlshs_s32 +vqrdmulh_lane_s16 +vqrdmulh_lane_s32 +vqrdmulh_laneq_s16 +vqrdmulh_laneq_s32 +vqrdmulh_n_s16 +vqrdmulh_n_s32 +vqrdmulh_s16 +vqrdmulh_s32 +vqrdmulhh_lane_s16 +vqrdmulhh_laneq_s16 +vqrdmulhh_s16 +vqrdmulhq_lane_s16 +vqrdmulhq_lane_s32 +vqrdmulhq_laneq_s16 +vqrdmulhq_laneq_s32 +vqrdmulhq_n_s16 +vqrdmulhq_n_s32 +vqrdmulhq_s16 +vqrdmulhq_s32 +vqrdmulhs_lane_s32 +vqrdmulhs_laneq_s32 +vqrdmulhs_s32 +vqrshl_s16 +vqrshl_s32 +vqrshl_s64 +vqrshl_s8 +vqrshl_u16 +vqrshl_u32 +vqrshl_u64 +vqrshl_u8 +vqrshlb_s8 +vqrshlb_u8 +vqrshld_s64 +vqrshld_u64 +vqrshlh_s16 +vqrshlh_u16 +vqrshlq_s16 +vqrshlq_s32 +vqrshlq_s64 +vqrshlq_s8 +vqrshlq_u16 +vqrshlq_u32 +vqrshlq_u64 +vqrshlq_u8 +vqrshls_s32 +vqrshls_u32 +vqrshrn_high_n_s16 +vqrshrn_high_n_s32 +vqrshrn_high_n_s64 +vqrshrn_high_n_u16 +vqrshrn_high_n_u32 +vqrshrn_high_n_u64 +vqrshrn_n_s16 +vqrshrn_n_s32 +vqrshrn_n_s64 +vqrshrn_n_u16 +vqrshrn_n_u32 +vqrshrn_n_u64 +vqrshrnd_n_s64 +vqrshrnd_n_u64 +vqrshrnh_n_s16 +vqrshrnh_n_u16 +vqrshrns_n_s32 +vqrshrns_n_u32 +vqrshrun_high_n_s16 +vqrshrun_high_n_s32 +vqrshrun_high_n_s64 +vqrshrun_n_s16 +vqrshrun_n_s32 +vqrshrun_n_s64 +vqrshrund_n_s64 +vqrshrunh_n_s16 +vqrshruns_n_s32 +vqshl_n_s16 +vqshl_n_s32 +vqshl_n_s64 +vqshl_n_s8 +vqshl_n_u16 +vqshl_n_u32 +vqshl_n_u64 +vqshl_n_u8 +vqshl_s16 +vqshl_s32 +vqshl_s64 +vqshl_s8 +vqshl_u16 +vqshl_u32 +vqshl_u64 +vqshl_u8 +vqshlb_n_s8 +vqshlb_n_u8 +vqshlb_s8 +vqshlb_u8 +vqshld_n_s64 +vqshld_n_u64 +vqshld_s64 +vqshld_u64 +vqshlh_n_s16 +vqshlh_n_u16 +vqshlh_s16 +vqshlh_u16 +vqshlq_n_s16 +vqshlq_n_s32 +vqshlq_n_s64 +vqshlq_n_s8 +vqshlq_n_u16 +vqshlq_n_u32 +vqshlq_n_u64 +vqshlq_n_u8 +vqshlq_s16 +vqshlq_s32 +vqshlq_s64 +vqshlq_s8 +vqshlq_u16 +vqshlq_u32 +vqshlq_u64 +vqshlq_u8 +vqshls_n_s32 +vqshls_n_u32 +vqshls_s32 +vqshls_u32 +vqshlu_n_s16 +vqshlu_n_s32 +vqshlu_n_s64 +vqshlu_n_s8 +vqshlub_n_s8 +vqshlud_n_s64 +vqshluh_n_s16 +vqshluq_n_s16 +vqshluq_n_s32 +vqshluq_n_s64 +vqshluq_n_s8 +vqshlus_n_s32 +vqshrn_high_n_s16 +vqshrn_high_n_s32 +vqshrn_high_n_s64 +vqshrn_high_n_u16 +vqshrn_high_n_u32 +vqshrn_high_n_u64 +vqshrn_n_s16 +vqshrn_n_s32 +vqshrn_n_s64 +vqshrn_n_u16 +vqshrn_n_u32 +vqshrn_n_u64 +vqshrnd_n_s64 +vqshrnd_n_u64 +vqshrnh_n_s16 +vqshrnh_n_u16 +vqshrns_n_s32 +vqshrns_n_u32 +vqshrun_high_n_s16 +vqshrun_high_n_s32 +vqshrun_high_n_s64 +vqshrun_n_s16 +vqshrun_n_s32 +vqshrun_n_s64 +vqshrund_n_s64 +vqshrunh_n_s16 +vqshruns_n_s32 +vqsubd_s64 +vqsubd_u64 +vqsubs_s32 +vqsubs_u32 +vqtbl2_p8 +vqtbl2_s8 +vqtbl2_u8 +vqtbl2q_p8 +vqtbl2q_s8 +vqtbl2q_u8 +vqtbl3_p8 +vqtbl3_s8 +vqtbl3_u8 +vqtbl3q_p8 +vqtbl3q_s8 +vqtbl3q_u8 +vqtbl4_p8 +vqtbl4_s8 +vqtbl4_u8 +vqtbl4q_p8 +vqtbl4q_s8 +vqtbl4q_u8 +vqtbx1_p8 +vqtbx1_s8 +vqtbx1_u8 +vqtbx1q_p8 +vqtbx1q_s8 +vqtbx1q_u8 +vqtbx2_p8 +vqtbx2_s8 +vqtbx2_u8 +vqtbx2q_p8 +vqtbx2q_s8 +vqtbx2q_u8 +vqtbx3_p8 +vqtbx3_s8 +vqtbx3_u8 +vqtbx3q_p8 +vqtbx3q_s8 +vqtbx3q_u8 +vqtbx4_p8 +vqtbx4_s8 +vqtbx4_u8 +vqtbx4q_p8 +vqtbx4q_s8 +vqtbx4q_u8 +vraddhn_high_s16 +vraddhn_high_s32 +vraddhn_high_s64 +vraddhn_high_u16 +vraddhn_high_u32 +vraddhn_high_u64 +vraddhn_s16 +vraddhn_s32 +vraddhn_s64 +vraddhn_u16 +vraddhn_u32 +vraddhn_u64 +vrecpe_f16 +vrecpe_f32 +vrecpe_f64 +vrecpe_u32 +vrecped_f64 +vrecpeh_f16 +vrecpeq_f16 +vrecpeq_f64 +vrecpeq_u32 +vrecpes_f32 +vrecps_f16 +vrecps_f32 +vrecps_f64 +vrecpsd_f64 +vrecpsh_f16 +vrecpsq_f16 +vrecpsq_f64 +vrecpss_f32 +vrecpxd_f64 +vrecpxh_f16 +vrecpxs_f32 +vreinterpretq_f16_p128 +vrhadd_s16 +vrhadd_s32 +vrhadd_s8 +vrhadd_u16 +vrhadd_u32 +vrhadd_u8 +vrhaddq_s16 +vrhaddq_s32 +vrhaddq_s8 +vrhaddq_u16 +vrhaddq_u32 +vrhaddq_u8 +vrnd32x_f32 +vrnd32x_f64 +vrnd32xq_f32 +vrnd32xq_f64 +vrnd32z_f32 +vrnd32z_f64 +vrnd32zq_f32 +vrnd32zq_f64 +vrnd64x_f32 +vrnd64x_f64 +vrnd64xq_f32 +vrnd64xq_f64 +vrnd64z_f32 +vrnd64z_f64 +vrnd64zq_f32 +vrnd64zq_f64 +vrndi_f16 +vrndi_f32 +vrndi_f64 +vrndih_f16 +vrndiq_f16 +vrndiq_f32 +vrndiq_f64 +vrndn_f16 +vrndnh_f16 +vrndnq_f16 +vrndns_f32 +vrshl_s16 +vrshl_s32 +vrshl_s64 +vrshl_s8 +vrshl_u16 +vrshl_u32 +vrshl_u64 +vrshl_u8 +vrshld_s64 +vrshld_u64 +vrshlq_s16 +vrshlq_s32 +vrshlq_s64 +vrshlq_s8 +vrshlq_u16 +vrshlq_u32 +vrshlq_u64 +vrshlq_u8 +vrshr_n_s16 +vrshr_n_s32 +vrshr_n_s64 +vrshr_n_s8 +vrshr_n_u16 +vrshr_n_u32 +vrshr_n_u64 +vrshr_n_u8 +vrshrd_n_s64 +vrshrd_n_u64 +vrshrn_high_n_s16 +vrshrn_high_n_s32 +vrshrn_high_n_s64 +vrshrn_high_n_u16 +vrshrn_high_n_u32 +vrshrn_high_n_u64 +vrshrn_n_s16 +vrshrn_n_s32 +vrshrn_n_s64 +vrshrn_n_u16 +vrshrn_n_u32 +vrshrn_n_u64 +vrshrq_n_s16 +vrshrq_n_s32 +vrshrq_n_s64 +vrshrq_n_s8 +vrshrq_n_u16 +vrshrq_n_u32 +vrshrq_n_u64 +vrshrq_n_u8 +vrsqrte_f16 +vrsqrte_f32 +vrsqrte_f64 +vrsqrte_u32 +vrsqrted_f64 +vrsqrteh_f16 +vrsqrteq_f16 +vrsqrteq_f32 +vrsqrteq_f64 +vrsqrteq_u32 +vrsqrtes_f32 +vrsqrts_f16 +vrsqrts_f32 +vrsqrts_f64 +vrsqrtsd_f64 +vrsqrtsh_f16 +vrsqrtsq_f16 +vrsqrtsq_f32 +vrsqrtsq_f64 +vrsqrtss_f32 +vrsra_n_s16 +vrsra_n_s32 +vrsra_n_s64 +vrsra_n_s8 +vrsra_n_u16 +vrsra_n_u32 +vrsra_n_u64 +vrsra_n_u8 +vrsrad_n_s64 +vrsrad_n_u64 +vrsraq_n_s16 +vrsraq_n_s32 +vrsraq_n_s64 +vrsraq_n_s8 +vrsraq_n_u16 +vrsraq_n_u32 +vrsraq_n_u64 +vrsraq_n_u8 +vrsubhn_high_s16 +vrsubhn_high_s32 +vrsubhn_high_s64 +vrsubhn_high_u16 +vrsubhn_high_u32 +vrsubhn_high_u64 +vrsubhn_s16 +vrsubhn_s32 +vrsubhn_s64 +vrsubhn_u16 +vrsubhn_u32 +vrsubhn_u64 +vscale_f16 +vscaleq_f16 +vshl_s16 +vshl_s32 +vshl_s64 +vshl_s8 +vshl_u16 +vshl_u32 +vshl_u64 +vshl_u8 +vshld_s64 +vshld_u64 +vshlq_s16 +vshlq_s32 +vshlq_s64 +vshlq_s8 +vshlq_u16 +vshlq_u32 +vshlq_u64 +vshlq_u8 +vsli_n_p16 +vsli_n_p64 +vsli_n_p8 +vsli_n_s16 +vsli_n_s32 +vsli_n_s64 +vsli_n_s8 +vsli_n_u16 +vsli_n_u32 +vsli_n_u64 +vsli_n_u8 +vslid_n_s64 +vslid_n_u64 +vsliq_n_p16 +vsliq_n_p64 +vsliq_n_p8 +vsliq_n_s16 +vsliq_n_s32 +vsliq_n_s64 +vsliq_n_s8 +vsliq_n_u16 +vsliq_n_u32 +vsliq_n_u64 +vsliq_n_u8 +vsm3partw1q_u32 +vsm3partw2q_u32 +vsm3ss1q_u32 +vsm3tt1aq_u32 +vsm3tt1bq_u32 +vsm3tt2aq_u32 +vsm3tt2bq_u32 +vsm4ekeyq_u32 +vsm4eq_u32 +vsqadd_u16 +vsqadd_u32 +vsqadd_u64 +vsqadd_u8 +vsqaddb_u8 +vsqaddd_u64 +vsqaddh_u16 +vsqaddq_u16 +vsqaddq_u32 +vsqaddq_u64 +vsqaddq_u8 +vsqadds_u32 +vst1_f16 +vst1_f16_x2 +vst1_f16_x3 +vst1_f16_x4 +vst1_lane_f16 +vst1q_f16 +vst1q_f16_x2 +vst1q_f16_x3 +vst1q_f16_x4 +vst1q_lane_f16 +vst2_f16 +vst2_lane_f16 +vst2q_f16 +vst2q_lane_f16 +vst3_f16 +vst3_lane_f16 +vst3q_f16 +vst3q_lane_f16 +vst4_f16 +vst4_lane_f16 +vst4q_f16 +vst4q_lane_f16 +vsudot_lane_s32 +vsudot_laneq_s32 +vsudotq_lane_s32 +vsudotq_laneq_s32 +vtbl2_p8 +vtbl2_s8 +vtbl2_u8 +vtbl3_p8 +vtbl3_s8 +vtbl3_u8 +vtbl4_p8 +vtbl4_s8 +vtbl4_u8 +vtbx1_p8 +vtbx1_s8 +vtbx1_u8 +vtbx2_p8 +vtbx2_s8 +vtbx2_u8 +vtbx3_p8 +vtbx3_s8 +vtbx3_u8 +vtbx4_p8 +vtbx4_s8 +vtbx4_u8 +vuqadd_s16 +vuqadd_s32 +vuqadd_s64 +vuqadd_s8 +vuqaddb_s8 +vuqaddd_s64 +vuqaddh_s16 +vuqaddq_s16 +vuqaddq_s32 +vuqaddq_s64 +vuqaddq_s8 +vuqadds_s32 +vusdot_lane_s32 +vusdot_laneq_s32 +vusdot_s32 +vusdotq_lane_s32 +vusdotq_laneq_s32 +vusdotq_s32 +vusmmlaq_s32 diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 5ae76dc1e591e..3b9ae2608abfe 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -1,4 +1,5 @@ use std::ffi::OsStr; +use std::fs; use std::path::PathBuf; use std::process::Command; @@ -108,7 +109,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ }), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), - TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]), TestCase::build_bin_and_run("aot.gen_block_iterate", "example/gen_block_iterate.rs", &[]), TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]), TestCase::custom("test.sysroot", &|runner| { @@ -182,6 +182,12 @@ static SYSROOT_TESTS_SRC: RelPath = RelPath::build("sysroot_tests"); static SYSROOT_TESTS: CargoProject = CargoProject::new(SYSROOT_TESTS_SRC, "sysroot_tests_target"); +static STDARCH_SRC: RelPath = RelPath::build("stdarch"); + +static STDARCH: CargoProject = CargoProject::new(STDARCH_SRC, "stdarch_target"); +static STDARCH_RUST_PROGRAMS: CargoProject = + CargoProject::new(RelPath::build("stdarch/rust_programs"), "stdarch_rust_programs_target"); + const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ TestCase::custom("test.rust-random/rand", &|runner| { RAND_REPO.patch(&runner.dirs); @@ -283,6 +289,116 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(test_cmd); } }), + TestCase::custom("test.stdarch-core", &|runner| { + let target = runner.target_compiler.target.as_str(); + let (arch, _) = target.split_once('-').unwrap(); + + // FIXME: Add support for remaining architectures. + if !["aarch64"].contains(&arch) { + eprintln!("Skipping `stdarch-core` tests: unsupported target"); + return; + } + + apply_patches( + &runner.dirs, + "stdarch", + &runner.stdlib_source.join("library/stdarch"), + &STDARCH_SRC.to_path(&runner.dirs), + ); + + STDARCH.clean(&runner.dirs); + + let skip = format!("build_system/stdarch_skip/{arch}_core.txt"); + let skip = fs::read_to_string(runner.dirs.source_dir.join(skip)).unwrap_or_default(); + + let skip_functions = skip + .lines() + .map(str::trim) + .filter(|line| !line.is_empty() && !line.starts_with('#')) + .collect::>() + .join(","); + + if runner.is_native { + let mut test_cmd = STDARCH.test(&runner.target_compiler, &runner.dirs); + + test_cmd.env("TARGET", target); + test_cmd.env("STDARCH_TEST_SKIP_FUNCTION", &skip_functions); + + test_cmd.args(["-p", "core_arch", "--tests", "--", "-q"]); + spawn_and_wait(test_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = STDARCH.build(&runner.target_compiler, &runner.dirs); + + build_cmd.env("TARGET", target); + + build_cmd.args(["-p", "core_arch", "--tests"]); + spawn_and_wait(build_cmd); + } + }), + TestCase::custom("test.stdarch-intrinsics", &|runner| { + let target = runner.target_compiler.target.as_str(); + let (arch, _) = target.split_once('-').unwrap(); + + // FIXME: Add support for remaining architectures. + let input = match arch { + "aarch64" => "intrinsics_data/arm_intrinsics.json", + _ => { + eprintln!("Skipping `stdarch-intrinsics` tests: unsupported target"); + return; + } + }; + + apply_patches( + &runner.dirs, + "stdarch", + &runner.stdlib_source.join("library/stdarch"), + &STDARCH_SRC.to_path(&runner.dirs), + ); + + STDARCH_RUST_PROGRAMS.clean(&runner.dirs); + + if !runner.is_native { + eprintln!("Cross-Compiling: Not running tests"); + return; + } + + let cc = if target.contains("apple") { "clang" } else { "gcc" }; + + let mut generate_cmd = STDARCH.run(&runner.target_compiler, &runner.dirs); + generate_cmd.current_dir(STDARCH.source_dir(&runner.dirs)); + generate_cmd.args([ + "-p", + "intrinsic-test", + "--", + input, + "--target", + target, + "--cc-arg-style", + cc, + "--skip", + &format!("crates/intrinsic-test/missing_{arch}_common.txt"), + "--skip", + &format!("crates/intrinsic-test/missing_{arch}_{cc}.txt"), + ]); + + for skip in [ + format!("build_system/stdarch_skip/{arch}_intrinsics_common.txt"), + format!("build_system/stdarch_skip/{arch}_intrinsics_{cc}.txt"), + ] { + let skip = runner.dirs.source_dir.join(skip); + + if skip.exists() { + generate_cmd.arg("--skip").arg(skip); + } + } + + spawn_and_wait(generate_cmd); + + let mut test_cmd = STDARCH_RUST_PROGRAMS.test(&runner.target_compiler, &runner.dirs); + test_cmd.args(["--workspace", "--tests", "--no-fail-fast", "--", "-q"]); + spawn_and_wait(test_cmd); + }), ]; pub(crate) fn run_tests( diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index ca126f4e39e29..c1068e6257d73 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -22,7 +22,6 @@ aot.float-minmax-pass aot.powi_libcall_signature aot.issue-72793 aot.issue-59326 -aot.neon aot.gen_block_iterate aot.raw-dylib test.sysroot @@ -32,3 +31,5 @@ test.rust-random/rand test.regex test.graviola test.portable-simd +test.stdarch-core +test.stdarch-intrinsics diff --git a/compiler/rustc_codegen_cranelift/example/neon.rs b/compiler/rustc_codegen_cranelift/example/neon.rs deleted file mode 100644 index ba63333daa339..0000000000000 --- a/compiler/rustc_codegen_cranelift/example/neon.rs +++ /dev/null @@ -1,963 +0,0 @@ -// Most of these tests are copied from https://github.com/japaric/stdsimd/blob/0f4413d01c4f0c3ffbc5a69e9a37fbc7235b31a9/coresimd/arm/neon.rs - -#![cfg_attr(target_arch = "aarch64", feature(portable_simd))] - -#[cfg(target_arch = "aarch64")] -use std::arch::aarch64::*; -#[cfg(target_arch = "aarch64")] -use std::mem::transmute; -#[cfg(target_arch = "aarch64")] -use std::simd::*; - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "crc")] -unsafe fn test_crc32() { - assert!(std::arch::is_aarch64_feature_detected!("crc")); - - let a: u32 = 42; - let b: u64 = 0xdeadbeef; - - assert_eq!(__crc32b(a, b as u8), 0xEB0E363F); - assert_eq!(__crc32h(a, b as u16), 0x9A54BD80); - assert_eq!(__crc32w(a, b as u32), 0xF491F059); - assert_eq!(__crc32d(a, b as u64), 0xD14BBEA6); - - assert_eq!(__crc32cb(a, b as u8), 0xF67C32D8); - assert_eq!(__crc32ch(a, b as u16), 0x479108B8); - assert_eq!(__crc32cw(a, b as u32), 0x979F49F8); - assert_eq!(__crc32cd(a, b as u64), 0x0E6BE593); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s8() { - let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); - let b = i8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = i8x8::from([-2, -4, 5, 7, 0, 2, 4, 6]); - let r: i8x8 = unsafe { transmute(vpmin_s8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s16() { - let a = i16x4::from([1, 2, 3, -4]); - let b = i16x4::from([0, 3, 2, 5]); - let e = i16x4::from([1, -4, 0, 2]); - let r: i16x4 = unsafe { transmute(vpmin_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s32() { - let a = i32x2::from([1, -2]); - let b = i32x2::from([0, 3]); - let e = i32x2::from([-2, 0]); - let r: i32x2 = unsafe { transmute(vpmin_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = u8x8::from([1, 3, 5, 7, 0, 2, 4, 6]); - let r: u8x8 = unsafe { transmute(vpmin_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([0, 3, 2, 5]); - let e = u16x4::from([1, 3, 0, 2]); - let r: u16x4 = unsafe { transmute(vpmin_u16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([0, 3]); - let e = u32x2::from([1, 0]); - let r: u32x2 = unsafe { transmute(vpmin_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_f32() { - let a = f32x2::from([1., -2.]); - let b = f32x2::from([0., 3.]); - let e = f32x2::from([-2., 0.]); - let r: f32x2 = unsafe { transmute(vpmin_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s8() { - let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); - let b = i8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = i8x8::from([1, 3, 6, 8, 3, 5, 7, 9]); - let r: i8x8 = unsafe { transmute(vpmax_s8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s16() { - let a = i16x4::from([1, 2, 3, -4]); - let b = i16x4::from([0, 3, 2, 5]); - let e = i16x4::from([2, 3, 3, 5]); - let r: i16x4 = unsafe { transmute(vpmax_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s32() { - let a = i32x2::from([1, -2]); - let b = i32x2::from([0, 3]); - let e = i32x2::from([1, 3]); - let r: i32x2 = unsafe { transmute(vpmax_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = u8x8::from([2, 4, 6, 8, 3, 5, 7, 9]); - let r: u8x8 = unsafe { transmute(vpmax_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([0, 3, 2, 5]); - let e = u16x4::from([2, 4, 3, 5]); - let r: u16x4 = unsafe { transmute(vpmax_u16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([0, 3]); - let e = u32x2::from([2, 3]); - let r: u32x2 = unsafe { transmute(vpmax_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_f32() { - let a = f32x2::from([1., -2.]); - let b = f32x2::from([0., 3.]); - let e = f32x2::from([1., 3.]); - let r: f32x2 = unsafe { transmute(vpmax_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s16() { - let a = i16x4::from([1, 2, 3, 4]); - let b = i16x4::from([0, -1, -2, -3]); - let r: i16x4 = unsafe { transmute(vpadd_s16(transmute(a), transmute(b))) }; - let e = i16x4::from([3, 7, -1, -5]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s32() { - let a = i32x2::from([1, 2]); - let b = i32x2::from([0, -1]); - let r: i32x2 = unsafe { transmute(vpadd_s32(transmute(a), transmute(b))) }; - let e = i32x2::from([3, -1]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s8() { - let a = i8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = i8x8::from([0, -1, -2, -3, -4, -5, -6, -7]); - let r: i8x8 = unsafe { transmute(vpadd_s8(transmute(a), transmute(b))) }; - let e = i8x8::from([3, 7, 11, 15, -1, -5, -9, -13]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([30, 31, 32, 33]); - let r: u16x4 = unsafe { transmute(vpadd_u16(transmute(a), transmute(b))) }; - let e = u16x4::from([3, 7, 61, 65]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([30, 31]); - let r: u32x2 = unsafe { transmute(vpadd_u32(transmute(a), transmute(b))) }; - let e = u32x2::from([3, 61]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([30, 31, 32, 33, 34, 35, 36, 37]); - let r: u8x8 = unsafe { transmute(vpadd_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([3, 7, 11, 15, 61, 65, 69, 73]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqsub_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 0xff]); - let b = u8x8::from([30, 1, 1, 1, 34, 0xff, 36, 37]); - let r: u8x8 = unsafe { transmute(vqsub_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([0, 1, 2, 3, 0, 0, 0, 218]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqadd_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 0xff]); - let b = u8x8::from([30, 1, 1, 1, 34, 0xff, 36, 37]); - let r: u8x8 = unsafe { transmute(vqadd_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([31, 3, 4, 5, 39, 0xff, 43, 0xff]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vmaxq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.fmax.v4f32 - let a = f32x4::from([0., -1., 2., -3.]); - let b = f32x4::from([-4., 5., -6., 7.]); - let e = f32x4::from([0., 5., 2., 7.]); - let r: f32x4 = unsafe { transmute(vmaxq_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vminq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.fmin.v4f32 - let a = f32x4::from([0., -1., 2., -3.]); - let b = f32x4::from([-4., 5., -6., 7.]); - let e = f32x4::from([-4., -1., -6., -3.]); - let r: f32x4 = unsafe { transmute(vminq_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vaddvq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.faddv.f32.v4f32 - let a = f32x4::from([0., 1., 2., 3.]); - let e = 6f32; - let r = unsafe { vaddvq_f32(transmute(a)) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vrndnq_f32() { - // llvm intrinsic: llvm.roundeven.v4f32 - let a = f32x4::from([0.1, -1.9, 4.5, 5.5]); - let e = f32x4::from([0., -2., 4., 6.]); - let r: f32x4 = unsafe { transmute(vrndnq_f32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaeseq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aese - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let e = u8x16::from([ - 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xca, - ]); - let r: u8x16 = unsafe { transmute(vaeseq_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesdq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesd - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let e = u8x16::from([ - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, - ]); - let r: u8x16 = unsafe { transmute(vaesdq_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesmcq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesmc - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let e = u8x16::from([2, 7, 0, 5, 6, 3, 4, 1, 10, 15, 8, 13, 14, 11, 12, 9]); - let r: u8x16 = unsafe { transmute(vaesmcq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesimcq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesimc - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let e = u8x16::from([10, 15, 8, 13, 14, 11, 12, 9, 2, 7, 0, 5, 6, 3, 4, 1]); - let r: u8x16 = unsafe { transmute(vaesimcq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1cq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1c - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x40072911, 0x40003948, 0x80000072, 0x80000003]); - let r: u32x4 = unsafe { transmute(vsha1cq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -fn test_vsha1h_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1h - let a = 8; - let e = 0x00000002; - let r = vsha1h_u32(a); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1mq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1m - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x4007a107, 0x00003d08, 0x0000007a, 0xc0000003]); - let r: u32x4 = unsafe { transmute(vsha1mq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1pq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1p - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x80062d18, 0x4000315c, 0x90000062, 0x00000003]); - let r: u32x4 = unsafe { transmute(vsha1pq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1su0q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1su0 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d]); - let r: u32x4 = unsafe { transmute(vsha1su0q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1su1q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1su1 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x0000000a, 0x0000000e, 0x0000000a, 0x00000012]); - let r: u32x4 = unsafe { transmute(vsha1su1q_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256hq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x27bb4ae0, 0xd8f61f7c, 0xb7c1ecdc, 0x10800215]); - let r: u32x4 = unsafe { transmute(vsha256hq_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256h2q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h2 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x6989ee0d, 0x4b055920, 0x52800a12, 0x00000014]); - let r: u32x4 = unsafe { transmute(vsha256h2q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256su0q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su0 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x02004000, 0x04008001, 0x0600c002, 0x08010003]); - let r: u32x4 = unsafe { transmute(vsha256su0q_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256su1q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su1 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x00044005, 0x0004e007, 0xa802211b, 0xec036145]); - let r: u32x4 = unsafe { transmute(vsha256su1q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512hq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512h - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x001c805053800005, 0x0015400002800003]); - let r: u64x2 = unsafe { transmute(vsha512hq_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512h2q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512h2 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x401000514a000405, 0x0000004108000005]); - let r: u64x2 = unsafe { transmute(vsha512h2q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512su0q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512su0 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([0x8100000000000000, 0x0200000000000002]); - let r: u64x2 = unsafe { transmute(vsha512su0q_u64(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512su1q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512su1 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x0000400000000014, 0x000060000000001e]); - let r: u64x2 = unsafe { transmute(vsha512su1q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v16i8 - let a = i8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = i8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = i8x16::from([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]); - let r: i8x16 = unsafe { transmute(veor3q_s8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v8i16 - let a = i16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = i16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = i16x8::from([24, 25, 26, 27, 28, 29, 30, 31]); - let r: i16x8 = unsafe { transmute(veor3q_s16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v4i32 - let a = i32x4::from([0, 1, 2, 3]); - let b = i32x4::from([4, 5, 6, 7]); - let c = i32x4::from([8, 9, 10, 11]); - let e = i32x4::from([12, 13, 14, 15]); - let r: i32x4 = unsafe { transmute(veor3q_s32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v2i64 - let a = i64x2::from([0, 1]); - let b = i64x2::from([2, 3]); - let c = i64x2::from([4, 5]); - let e = i64x2::from([6, 7]); - let r: i64x2 = unsafe { transmute(veor3q_s64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v16i8 - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = u8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = u8x16::from([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]); - let r: u8x16 = unsafe { transmute(veor3q_u8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v8i16 - let a = u16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = u16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = u16x8::from([24, 25, 26, 27, 28, 29, 30, 31]); - let r: u16x8 = unsafe { transmute(veor3q_u16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v4i32 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([12, 13, 14, 15]); - let r: u32x4 = unsafe { transmute(veor3q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v2i64 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([6, 7]); - let r: u64x2 = unsafe { transmute(veor3q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v16i8 - let a = i8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = i8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let r: i8x16 = unsafe { transmute(vbcaxq_s8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v8i16 - let a = i16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = i16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let r: i16x8 = unsafe { transmute(vbcaxq_s16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v4i32 - let a = i32x4::from([0, 1, 2, 3]); - let b = i32x4::from([4, 5, 6, 7]); - let c = i32x4::from([8, 9, 10, 11]); - let e = i32x4::from([4, 5, 6, 7]); - let r: i32x4 = unsafe { transmute(vbcaxq_s32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v2i64 - let a = i64x2::from([0, 1]); - let b = i64x2::from([2, 3]); - let c = i64x2::from([4, 5]); - let e = i64x2::from([2, 3]); - let r: i64x2 = unsafe { transmute(vbcaxq_s64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v16i8 - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = u8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let r: u8x16 = unsafe { transmute(vbcaxq_u8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v8i16 - let a = u16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = u16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let r: u16x8 = unsafe { transmute(vbcaxq_u16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v4i32 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([4, 5, 6, 7]); - let r: u32x4 = unsafe { transmute(vbcaxq_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v2i64 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([2, 3]); - let r: u64x2 = unsafe { transmute(vbcaxq_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vrax1q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.rax1 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([4, 7]); - let r: u64x2 = unsafe { transmute(vrax1q_u64(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vxarq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.xar - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([4, 4]); - let r: u64x2 = unsafe { transmute(vxarq_u64::<63>(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -fn test_vmull_p64() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull64 - let a: u64 = 3; - let b: u64 = 6; - let e: u128 = 10; - let r: u128 = vmull_p64(a, b); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vmull_p8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull.v8i16 - let a = u8x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u8x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let e = u16x8::from([0x0000, 0x0009, 0x0014, 0x001d, 0x0030, 0x0039, 0x0024, 0x002d]); - let r: u16x8 = unsafe { transmute(vmull_p8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulh_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i16 - let a = i16x4::from([1, 2, 4, 8]); - let b = i16x4::from([16384, 16384, 16384, 16384]); - let e = i16x4::from([0, 1, 2, 4]); - let r: i16x4 = unsafe { transmute(vqdmulh_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulh_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v2i32 - let a = i32x2::from([1, 2]); - let b = i32x2::from([1073741824, 1073741824]); - let e = i32x2::from([0, 1]); - let r: i32x2 = unsafe { transmute(vqdmulh_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulhq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v8i16 - let a = i16x8::from([1, 2, 4, 8, 16, 32, 64, 128]); - let b = i16x8::from([16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384]); - let e = i16x8::from([0, 1, 2, 4, 8, 16, 32, 64]); - let r: i16x8 = unsafe { transmute(vqdmulhq_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulhq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i32 - let a = i32x4::from([1, 2, 4, 8]); - let b = i32x4::from([1073741824, 1073741824, 1073741824, 1073741824]); - let e = i32x4::from([0, 1, 2, 4]); - let r: i32x4 = unsafe { transmute(vqdmulhq_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i16.v8i8 - let a = i8x8::from([1, 2, 3, 4, -5, -6, -7, -8]); - let e = i16x4::from([3, 7, -11, -15]); - let r: i16x4 = unsafe { transmute(vpaddl_s8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i32.v4i16 - let a = i16x4::from([1, 2, -3, -4]); - let e = i32x2::from([3, -7]); - let r: i32x2 = unsafe { transmute(vpaddl_s16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v1i64.v2i32 - let a = i32x2::from([1, -2]); - let e = i64x1::from([-1]); - let r: i64x1 = unsafe { transmute(vpaddl_s32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v8i16.v16i8 - let a = i8x16::from([1, 2, 3, 4, 5, 6, 7, 8, -9, -10, -11, -12, -13, -14, -15, -16]); - let e = i16x8::from([3, 7, 11, 15, -19, -23, -27, -31]); - let r: i16x8 = unsafe { transmute(vpaddlq_s8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i32.v8i16 - let a = i16x8::from([1, 2, 3, 4, -5, -6, -7, -8]); - let e = i32x4::from([3, 7, -11, -15]); - let r: i32x4 = unsafe { transmute(vpaddlq_s16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i64.v4i32 - let a = i32x4::from([1, 2, -3, -4]); - let e = i64x2::from([3, -7]); - let r: i64x2 = unsafe { transmute(vpaddlq_s32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i16.v8i8 - let a = u8x8::from([255, 254, 253, 252, 251, 250, 249, 248]); - let e = u16x4::from([509, 505, 501, 497]); - let r: u16x4 = unsafe { transmute(vpaddl_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i32.v4i16 - let a = u16x4::from([65535, 65534, 65533, 65532]); - let e = u32x2::from([131069, 131065]); - let r: u32x2 = unsafe { transmute(vpaddl_u16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v1i64.v2i32 - let a = u32x2::from([4294967295, 4294967294]); - let e = u64x1::from([8589934589]); - let r: u64x1 = unsafe { transmute(vpaddl_u32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v8i16.v16i8 - let a = u8x16::from([ - 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, - ]); - let e = u16x8::from([509, 505, 501, 497, 493, 489, 485, 481]); - let r: u16x8 = unsafe { transmute(vpaddlq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i32.v8i16 - let a = u16x8::from([65535, 65534, 65533, 65532, 65531, 65530, 65529, 65528]); - let e = u32x4::from([131069, 131065, 131061, 131057]); - let r: u32x4 = unsafe { transmute(vpaddlq_u16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i64.v4i32 - let a = u32x4::from([4294967295, 4294967294, 4294967293, 4294967292]); - let e = u64x2::from([8589934589, 8589934585]); - let r: u64x2 = unsafe { transmute(vpaddlq_u32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -fn main() { - unsafe { - test_vpmin_s8(); - test_vpmin_s16(); - test_vpmin_s32(); - test_vpmin_u8(); - test_vpmin_u16(); - test_vpmin_u32(); - test_vpmin_f32(); - test_vpmax_s8(); - test_vpmax_s16(); - test_vpmax_s32(); - test_vpmax_u8(); - test_vpmax_u16(); - test_vpmax_u32(); - test_vpmax_f32(); - - test_vpadd_s16(); - test_vpadd_s32(); - test_vpadd_s8(); - test_vpadd_u16(); - test_vpadd_u32(); - test_vpadd_u8(); - - test_vqsub_u8(); - test_vqadd_u8(); - - test_vmaxq_f32(); - test_vminq_f32(); - test_vaddvq_f32(); - test_vrndnq_f32(); - - test_crc32(); - - test_vaeseq_u8(); - test_vaesdq_u8(); - test_vaesmcq_u8(); - test_vaesimcq_u8(); - - test_vsha1cq_u32(); - test_vsha1h_u32(); - test_vsha1mq_u32(); - test_vsha1pq_u32(); - test_vsha1su0q_u32(); - test_vsha1su1q_u32(); - - test_vsha256hq_u32(); - test_vsha256h2q_u32(); - test_vsha256su0q_u32(); - test_vsha256su1q_u32(); - - if std::arch::is_aarch64_feature_detected!("sha3") { - test_vsha512hq_u64(); - test_vsha512h2q_u64(); - test_vsha512su0q_u64(); - test_vsha512su1q_u64(); - - test_veor3q_s8(); - test_veor3q_s16(); - test_veor3q_s32(); - test_veor3q_s64(); - test_veor3q_u8(); - test_veor3q_u16(); - test_veor3q_u32(); - test_veor3q_u64(); - - test_vbcaxq_s8(); - test_vbcaxq_s16(); - test_vbcaxq_s32(); - test_vbcaxq_s64(); - test_vbcaxq_u8(); - test_vbcaxq_u16(); - test_vbcaxq_u32(); - test_vbcaxq_u64(); - - test_vrax1q_u64(); - test_vxarq_u64(); - } - - test_vmull_p64(); - test_vmull_p8(); - - test_vqdmulh_s16(); - test_vqdmulh_s32(); - test_vqdmulhq_s16(); - test_vqdmulhq_s32(); - - test_vpaddl_s8(); - test_vpaddl_s16(); - test_vpaddl_s32(); - test_vpaddlq_s8(); - test_vpaddlq_s16(); - test_vpaddlq_s32(); - - test_vpaddl_u8(); - test_vpaddl_u16(); - test_vpaddl_u32(); - test_vpaddlq_u8(); - test_vpaddlq_u16(); - test_vpaddlq_u32(); - } -} - -#[cfg(not(target_arch = "aarch64"))] -fn main() {} diff --git a/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch b/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch new file mode 100644 index 0000000000000..e7038699bc998 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch @@ -0,0 +1,51 @@ +From e90274ba5bc4bee03a24e8c134ea223497c3a46b Mon Sep 17 00:00:00 2001 +From: Cathal Mullan +Date: Sat, 13 Jun 2026 20:32:02 +0100 +Subject: [PATCH] Disable SVE + +Cranelift doesn't support scalable vectors. +--- + crates/core_arch/src/aarch64/mod.rs | 8 ++++---- + crates/intrinsic-test/src/arm/mod.rs | 1 + + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/crates/core_arch/src/aarch64/mod.rs b/crates/core_arch/src/aarch64/mod.rs +index 34fa5c8..a870e31 100644 +--- a/crates/core_arch/src/aarch64/mod.rs ++++ b/crates/core_arch/src/aarch64/mod.rs +@@ -19,16 +19,16 @@ mod neon; + pub use self::neon::*; + + // The rest of `core_arch::aarch64` is available on `arm64ec` but SVE is not supported on `arm64ec`. +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + mod sve; +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] + pub use self::sve::*; + + // The rest of `core_arch::aarch64` is available on `arm64ec` but SVE is not supported on `arm64ec`. +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + mod sve2; +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] + pub use self::sve2::*; + +diff --git a/crates/intrinsic-test/src/arm/mod.rs b/crates/intrinsic-test/src/arm/mod.rs +index 9c2d0f3..3825f41 100644 +--- a/crates/intrinsic-test/src/arm/mod.rs ++++ b/crates/intrinsic-test/src/arm/mod.rs +@@ -66,6 +66,7 @@ impl SupportedArchitecture for Arm { + + let intrinsics = intrinsics + .into_iter() ++ .filter(|i| i.extension != "SVE" && i.extension != "SVE2") + // Skip intrinsics that don't return a value. + .filter(|i| i.results.kind() != TypeKind::Void) + // Skip bfloat intrinsics - not currently supported +-- +2.54.0 + From 711bae498cd3b533c618ae03e61f1d54520b58c9 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 22 Sep 2026 12:13:32 +0200 Subject: [PATCH 10/65] Prepare for merging from rust-lang/rust This updates the rust-toolchain.toml file to nightly-2026-09-22. --- compiler/rustc_codegen_cranelift/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index ec7694364a8c9..a4cf19824d3d2 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-18" +channel = "nightly-2026-09-22" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From 29ff6f98fc92c4adb5111422daffacc93ef800b2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 23 Sep 2026 17:15:03 +0200 Subject: [PATCH 11/65] Prepare for merging from rust-lang/rust This updates the rust-toolchain.toml file to nightly-2026-09-23 (6bb1652a020e80cef79332741d89e996d71933c9). --- compiler/rustc_codegen_cranelift/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index a4cf19824d3d2..84d28e7f293e0 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-22" +channel = "nightly-2026-09-23" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From dc39f011b8ebb2ee32511de7ffca1b47ad3454fd Mon Sep 17 00:00:00 2001 From: eleocraft Date: Thu, 24 Sep 2026 05:01:45 +0200 Subject: [PATCH 12/65] making clamp_magnitude functions const + adding clamp_magnitude to NonZero --- library/core/src/num/f128.rs | 5 ++-- library/core/src/num/f16.rs | 5 ++-- library/core/src/num/f32.rs | 6 ++--- library/core/src/num/f64.rs | 6 ++--- library/core/src/num/int_macros.rs | 7 ++--- library/core/src/num/nonzero.rs | 26 +++++++++++++++++++ .../coretests/tests/num/clamp_magnitude.rs | 8 +++--- 7 files changed, 46 insertions(+), 17 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index db05d7fdc5087..e53e4df1ea43d 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1544,9 +1544,10 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "clamp_magnitude", issue = "148519")] - #[must_use = "this returns the clamped value and does not modify the original"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f128) -> f128 { + pub const fn clamp_magnitude(self, limit: f128) -> f128 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 273ef3688ca5f..4ad41bd6cfa4b 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1530,9 +1530,10 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "clamp_magnitude", issue = "148519")] - #[must_use = "this returns the clamped value and does not modify the original"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f16) -> f16 { + pub const fn clamp_magnitude(self, limit: f16) -> f16 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index d3dea38dc2fcf..2dc3ddf31cac5 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1699,11 +1699,11 @@ impl f32 { /// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0); /// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0); /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f32) -> f32 { + pub const fn clamp_magnitude(self, limit: f32) -> f32 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 7c5082749cd11..cd3cfe1bd4c60 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1677,11 +1677,11 @@ impl f64 { /// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0); /// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0); /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f64) -> f64 { + pub const fn clamp_magnitude(self, limit: f64) -> f64 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index bb37a734824fa..2328fb1e03a31 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3968,10 +3968,11 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")] #[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")] /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] - pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self { + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + pub const fn clamp_magnitude(self, limit: $UnsignedT) -> Self { if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) { self.clamp(-limit, limit) } else { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index d0ae6c31267f3..37223932a0b0f 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -2454,6 +2454,32 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) } } + /// clamps this number to a symmetric range centred around zero. the method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// this is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + /// # use std::num::NonZero; + /// # + #[doc = concat!("let limit = NonZero::<", stringify!($Uint), ">::new(100).unwrap();")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit), NonZero::new(100).unwrap());")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit), NonZero::new(-100).unwrap());")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit), NonZero::new(80).unwrap());")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit), NonZero::new(-80).unwrap());")] + /// ``` + #[inline] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + pub const fn clamp_magnitude(self, limit: NonZero<$Uint>) -> Self { + // SAFETY: a non-zero value clamped to the magnitude of a non-zero value is still non-zero. + unsafe { Self::new_unchecked(self.get().clamp_magnitude(limit.get())) } + } + }; } diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs index 0f96e55f6914e..b8ba402a6660a 100644 --- a/library/coretests/tests/num/clamp_magnitude.rs +++ b/library/coretests/tests/num/clamp_magnitude.rs @@ -115,25 +115,25 @@ fn test_clamp_magnitude_f64() { } #[test] -#[should_panic(expected = "limit must be non-negative")] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f32_panic_negative_limit() { let _ = 1.0f32.clamp_magnitude(-1.0); } #[test] -#[should_panic(expected = "limit must be non-negative")] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f64_panic_negative_limit() { let _ = 1.0f64.clamp_magnitude(-1.0); } #[test] -#[should_panic] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f32_panic_nan_limit() { let _ = 1.0f32.clamp_magnitude(f32::NAN); } #[test] -#[should_panic] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f64_panic_nan_limit() { let _ = 1.0f64.clamp_magnitude(f64::NAN); } From c614782ce4a59e46e11e8179e44d0a1ef7063045 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 24 Sep 2026 04:34:41 +0000 Subject: [PATCH 13/65] Prepare for merging from rust-lang/rust This updates the rust-version file to 3670d2532bdf51abbe0b8fea22284d7ca340ffe3. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 89c3e1334a546..b9900a7795af4 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -88638df1e741778019e42fcdf307072865d19501 +3670d2532bdf51abbe0b8fea22284d7ca340ffe3 From ec11203ad6328e1ffd83d3dbb22a2e6c3d75cb45 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 24 Sep 2026 08:08:44 +0200 Subject: [PATCH 14/65] temporarily disable broken test on freebsd --- .../tests/pass/shims/available-parallelism-miri-num-cpus.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs b/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs index 0d96bc2f5e737..fa83de8bc127e 100644 --- a/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs +++ b/src/tools/miri/tests/pass/shims/available-parallelism-miri-num-cpus.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-num-cpus=1024 +// FIXME: Disabled on FreeBSD due to +//@ignore-target: freebsd use std::num::NonZero; use std::thread::available_parallelism; From d6d250c7dd35b423f519e0948f373ab279f8276c Mon Sep 17 00:00:00 2001 From: eleocraft Date: Thu, 24 Sep 2026 21:27:29 +0200 Subject: [PATCH 15/65] adding coretests and fixing docs Adding tests addressing the changes in coretests/tests/num/clamp_magnitude.rs --- library/core/src/num/nonzero.rs | 6 +- .../coretests/tests/num/clamp_magnitude.rs | 95 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 37223932a0b0f..513166bf12cae 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -2454,9 +2454,11 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) } } - /// clamps this number to a symmetric range centred around zero. the method clamps the number's magnitude (absolute value) to be at most `limit`. + /// Clamps this number to a symmetric range centred around zero. /// - /// this is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more /// explicit about the intent. /// /// # Examples diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs index b8ba402a6660a..11ae7a7549111 100644 --- a/library/coretests/tests/num/clamp_magnitude.rs +++ b/library/coretests/tests/num/clamp_magnitude.rs @@ -1,3 +1,5 @@ +use core::num::*; + macro_rules! check_int_clamp { ($t:ty, $ut:ty) => { let min = <$t>::MIN; @@ -32,6 +34,16 @@ macro_rules! check_int_clamp { // Limit larger than type max (uN > iN::MAX) assert_eq!(max.clamp_magnitude(max_u), max); assert_eq!(min.clamp_magnitude(max_u), min); + + // Const clamping + const C1: $t = (100 as $t).clamp_magnitude(50); + assert_eq!(C1, 50); + const C2: $t = (-100 as $t).clamp_magnitude(50); + assert_eq!(C2, -50); + const C3: $t = (30 as $t).clamp_magnitude(50); + assert_eq!(C3, 30); + const C4: $t = (-30 as $t).clamp_magnitude(50); + assert_eq!(C4, -30); }; } @@ -65,6 +77,79 @@ fn test_clamp_magnitude_isize() { check_int_clamp!(isize, usize); } +macro_rules! check_nonzero_clamp { + ($t:ty, $ut:ty) => { + let min = <$t>::MIN; + let max = <$t>::MAX; + let max_u = <$ut>::MAX; + + // Basic clamping + let lim = <$ut>::new(50).unwrap(); + assert_eq!(<$t>::new(100).unwrap().clamp_magnitude(lim), <$t>::new(50).unwrap()); + assert_eq!(<$t>::new(-100).unwrap().clamp_magnitude(lim), <$t>::new(-50).unwrap()); + assert_eq!(<$t>::new(30).unwrap().clamp_magnitude(lim), <$t>::new(30).unwrap()); + assert_eq!(<$t>::new(-30).unwrap().clamp_magnitude(lim), <$t>::new(-30).unwrap()); + + // Exact boundary + assert_eq!(<$t>::new(50).unwrap().clamp_magnitude(lim), <$t>::new(50).unwrap()); + assert_eq!(<$t>::new(-50).unwrap().clamp_magnitude(lim), <$t>::new(-50).unwrap()); + + // MIN/MAX values + // Symmetric range [-MAX, MAX] + assert_eq!(max.clamp_magnitude(max.try_into().unwrap()), max); + assert_eq!(min.clamp_magnitude(max.try_into().unwrap()), -max); + + // Full range (limit covers MIN) + let min_abs = min.unsigned_abs(); + assert_eq!(min.clamp_magnitude(min_abs), min); + + // Limit larger than type max (uN > iN::MAX) + assert_eq!(max.clamp_magnitude(max_u), max); + assert_eq!(min.clamp_magnitude(max_u), min); + + // Const clamping + const LIM: $ut = <$ut>::new(50).unwrap(); + const C1: $t = <$t>::new(100).unwrap().clamp_magnitude(LIM); + assert_eq!(C1, <$t>::new(50).unwrap()); + const C2: $t = <$t>::new(-100).unwrap().clamp_magnitude(LIM); + assert_eq!(C2, <$t>::new(-50).unwrap()); + const C3: $t = <$t>::new(30).unwrap().clamp_magnitude(LIM); + assert_eq!(C3, <$t>::new(30).unwrap()); + const C4: $t = <$t>::new(-30).unwrap().clamp_magnitude(LIM); + assert_eq!(C4, <$t>::new(-30).unwrap()); + }; +} + +#[test] +fn test_clamp_magnitude_nonzero_i8() { + check_nonzero_clamp!(NonZeroI8, NonZeroU8); +} + +#[test] +fn test_clamp_magnitude_nonzero_i16() { + check_nonzero_clamp!(NonZeroI16, NonZeroU16); +} + +#[test] +fn test_clamp_magnitude_nonzero_i32() { + check_nonzero_clamp!(NonZeroI32, NonZeroU32); +} + +#[test] +fn test_clamp_magnitude_nonzero_i64() { + check_nonzero_clamp!(NonZeroI64, NonZeroU64); +} + +#[test] +fn test_clamp_magnitude_nonzero_i128() { + check_nonzero_clamp!(NonZeroI128, NonZeroU128); +} + +#[test] +fn test_clamp_magnitude_nonzero_isize() { + check_nonzero_clamp!(NonZeroIsize, NonZeroUsize); +} + macro_rules! check_float_clamp { ($t:ty) => { // Basic clamping @@ -101,6 +186,16 @@ macro_rules! check_float_clamp { let huge = 1e30; assert_eq!(max.clamp_magnitude(huge), huge); assert_eq!(min.clamp_magnitude(huge), -huge); + + // Const clamping + const C1: $t = (5.0 as $t).clamp_magnitude(3.0); + assert_eq!(C1, 3.0); + const C2: $t = (-5.0 as $t).clamp_magnitude(3.0); + assert_eq!(C2, -3.0); + const C3: $t = (2.0 as $t).clamp_magnitude(3.0); + assert_eq!(C3, 2.0); + const C4: $t = (-2.0 as $t).clamp_magnitude(3.0); + assert_eq!(C4, -2.0); }; } From 76384db023d5844f27d8568a9214a5014d7facde Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 25 Sep 2026 04:34:55 +0000 Subject: [PATCH 16/65] Prepare for merging from rust-lang/rust This updates the rust-version file to 0d38a842626a6e3b70e2c1efd76a8f22d2556d73. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b9900a7795af4..ab61f3f74a54a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3670d2532bdf51abbe0b8fea22284d7ca340ffe3 +0d38a842626a6e3b70e2c1efd76a8f22d2556d73 From 79cbf77c912d0209615051164f1227cac6c483b5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 20 Sep 2026 21:16:48 +0200 Subject: [PATCH 17/65] run windows-fs tests natively --- src/tools/miri/src/shims/windows/fs.rs | 5 +++- .../miri/tests/pass-dep/shims/windows-fs.rs | 23 +++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index 9aae6677faeb2..6a9963159b1bc 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -377,7 +377,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let attributes = if file_type.is_dir() { this.eval_windows_u32("c", "FILE_ATTRIBUTE_DIRECTORY") } else if file_type.is_file() { - this.eval_windows_u32("c", "FILE_ATTRIBUTE_NORMAL") + // Normal files seem to have the "archive" attribute. There's also + // `FILE_ATTRIBUTE_NORMAL` but that's for files without any other attribute which, + // apparently, is not normal. + this.eval_windows_u32("c", "FILE_ATTRIBUTE_ARCHIVE") } else { this.eval_windows_u32("c", "FILE_ATTRIBUTE_DEVICE") }; diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs index 80f8ef9359d3b..ecac238240872 100644 --- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -1,5 +1,7 @@ //@only-target: windows # this directly tests windows-only functions //@compile-flags: -Zmiri-disable-isolation +//@run-native + #![allow(nonstandard_style)] use std::io::{ErrorKind, Read, Seek, SeekFrom, Write}; @@ -19,15 +21,19 @@ use windows_sys::Win32::Foundation::{ }; use windows_sys::Win32::Storage::FileSystem::{ BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW, - FILE_ALLOCATION_INFO, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, - FILE_CURRENT, FILE_END_OF_FILE_INFO, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, - FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, FileAllocationInfo, FileEndOfFileInfo, - FlushFileBuffers, GetFileInformationByHandle, MoveFileExW, OPEN_ALWAYS, OPEN_EXISTING, - SetFileInformationByHandle, SetFilePointerEx, + FILE_ALLOCATION_INFO, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, + FILE_BEGIN, FILE_CURRENT, FILE_END_OF_FILE_INFO, FILE_FLAG_BACKUP_SEMANTICS, + FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, + FileAllocationInfo, FileEndOfFileInfo, FlushFileBuffers, GetFileInformationByHandle, + MoveFileExW, OPEN_ALWAYS, OPEN_EXISTING, SetFileInformationByHandle, SetFilePointerEx, }; use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; use windows_sys::Win32::System::Threading::GetCurrentProcess; +// Windows seems to usually but not always set FILE_ATTRIBUTE_ARCHIVE for new files. +// So we accept either "ARCHIVE" or "NORMAL". +const REGULAR_FILE: u32 = FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL; + fn main() { unsafe { test_create_dir_file(); @@ -87,9 +93,8 @@ unsafe fn test_create_normal_file() { if GetFileInformationByHandle(handle, &mut info) == 0 { panic!("Failed to get file information: {}", GetLastError()) }; - // FIXME: this test is wrong, somehow. It doesn't pass when run natively. - // - assert!(info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL != 0); + assert!(info.dwFileAttributes & REGULAR_FILE != 0); + assert!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == 0); if CloseHandle(handle) == 0 { panic!("Failed to close file") }; @@ -121,7 +126,7 @@ unsafe fn test_create_normal_file() { if GetFileInformationByHandle(handle, &mut info) == 0 { panic!("Failed to get file information: {}", GetLastError()) }; - assert!(info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL != 0); + assert!(info.dwFileAttributes & REGULAR_FILE != 0); if CloseHandle(handle) == 0 { panic!("Failed to close file") }; From 3f2e9ee8614715d3579ae7571985663e6de8f4e3 Mon Sep 17 00:00:00 2001 From: eleocraft Date: Fri, 25 Sep 2026 15:26:40 +0200 Subject: [PATCH 18/65] Applying requested changes Adding mod clamp_magnitude in coretests/num/mod.rs Activating feature(clamp_magnitude) in coretests/lib.rs Adding test coverage for f16, f128, and NaN as well as zero cases Modifying nonzero and int_macros documentation for better readablity --- library/core/src/num/int_macros.rs | 6 +- library/core/src/num/nonzero.rs | 26 ++++++-- library/coretests/tests/lib.rs | 1 + .../coretests/tests/num/clamp_magnitude.rs | 62 ++++++++++++++++--- library/coretests/tests/num/mod.rs | 1 + 5 files changed, 78 insertions(+), 18 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 2328fb1e03a31..1d272c0e5632e 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3964,13 +3964,13 @@ macro_rules! int_impl { /// ``` /// #![feature(clamp_magnitude)] #[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")] - #[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")] + #[doc = concat!("assert_eq!((-120", stringify!($SelfT), ").clamp_magnitude(100), -100);")] #[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")] - #[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")] + #[doc = concat!("assert_eq!((-80", stringify!($SelfT), ").clamp_magnitude(100), -80);")] /// ``` #[inline] #[must_use = "method returns a new number and does not mutate the original value"] - #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[unstable(feature = "clamp_magnitude", issue = "148519")] pub const fn clamp_magnitude(self, limit: $UnsignedT) -> Self { if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 513166bf12cae..d7b451c53f059 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -2468,14 +2468,30 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # use std::num::NonZero; /// # #[doc = concat!("let limit = NonZero::<", stringify!($Uint), ">::new(100).unwrap();")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit), NonZero::new(100).unwrap());")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit), NonZero::new(-100).unwrap());")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit), NonZero::new(80).unwrap());")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit), NonZero::new(-80).unwrap());")] + /// + /// assert_eq!( + #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit),")] + #[doc = "\tNonZero::new(100).unwrap(),"] + /// ); + /// + /// assert_eq!( + #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit),")] + #[doc = "\tNonZero::new(-100).unwrap(),"] + /// ); + /// + /// assert_eq!( + #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit),")] + #[doc = "\tNonZero::new(80).unwrap(),"] + /// ); + /// + /// assert_eq!( + #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit),")] + #[doc = "\tNonZero::new(-80).unwrap(),"] + /// ); /// ``` #[inline] #[must_use = "method returns a new number and does not mutate the original value"] - #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[unstable(feature = "clamp_magnitude", issue = "148519")] pub const fn clamp_magnitude(self, limit: NonZero<$Uint>) -> Self { // SAFETY: a non-zero value clamped to the magnitude of a non-zero value is still non-zero. diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 69bc6edd1b833..40543ee794ee7 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(cfg_overflow_checks)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] +#![feature(clamp_magnitude)] #![feature(clamp_to)] #![feature(clone_to_uninit)] #![feature(cmp_minmax)] diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs index 11ae7a7549111..5bccc4ad45e20 100644 --- a/library/coretests/tests/num/clamp_magnitude.rs +++ b/library/coretests/tests/num/clamp_magnitude.rs @@ -1,5 +1,7 @@ use core::num::*; +use crate::num::assert_biteq; + macro_rules! check_int_clamp { ($t:ty, $ut:ty) => { let min = <$t>::MIN; @@ -151,7 +153,7 @@ fn test_clamp_magnitude_nonzero_isize() { } macro_rules! check_float_clamp { - ($t:ty) => { + ($t:ty, $huge: expr) => { // Basic clamping assert_eq!((5.0 as $t).clamp_magnitude(3.0), 3.0); assert_eq!((-5.0 as $t).clamp_magnitude(3.0), -3.0); @@ -163,10 +165,13 @@ macro_rules! check_float_clamp { assert_eq!((-3.0 as $t).clamp_magnitude(3.0), -3.0); // Zero cases - assert_eq!((0.0 as $t).clamp_magnitude(1.0), 0.0); - assert_eq!((-0.0 as $t).clamp_magnitude(1.0), 0.0); - assert_eq!((5.0 as $t).clamp_magnitude(0.0), 0.0); - assert_eq!((-5.0 as $t).clamp_magnitude(0.0), 0.0); + type Float = $t; + assert_biteq!((0.0 as $t).clamp_magnitude(1.0), 0.0); + assert_biteq!((-0.0 as $t).clamp_magnitude(1.0), -0.0); + assert_biteq!((5.0 as $t).clamp_magnitude(0.0), 0.0); + assert_biteq!((-5.0 as $t).clamp_magnitude(0.0), -0.0); + assert_biteq!((5.0 as $t).clamp_magnitude(-0.0), 0.0); + assert_biteq!((-5.0 as $t).clamp_magnitude(-0.0), -0.0); // Special values - Infinity let inf = <$t>::INFINITY; @@ -183,9 +188,12 @@ macro_rules! check_float_clamp { let max = <$t>::MAX; let min = <$t>::MIN; // Large limit - let huge = 1e30; - assert_eq!(max.clamp_magnitude(huge), huge); - assert_eq!(min.clamp_magnitude(huge), -huge); + assert_eq!(max.clamp_magnitude($huge), $huge); + assert_eq!(min.clamp_magnitude($huge), -$huge); + + // NaN + let nan = <$t>::NAN; + assert!(nan.clamp_magnitude(1.0).is_nan()); // Const clamping const C1: $t = (5.0 as $t).clamp_magnitude(3.0); @@ -199,14 +207,30 @@ macro_rules! check_float_clamp { }; } +#[test] +fn test_clamp_magnitude_f16() { + check_float_clamp!(f16, 1e3); +} + #[test] fn test_clamp_magnitude_f32() { - check_float_clamp!(f32); + check_float_clamp!(f32, 1e30); } #[test] fn test_clamp_magnitude_f64() { - check_float_clamp!(f64); + check_float_clamp!(f64, 1e300); +} + +#[test] +fn test_clamp_magnitude_f128() { + check_float_clamp!(f128, 1e3000); +} + +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f16_panic_negative_limit() { + let _ = 1.0f16.clamp_magnitude(-1.0); } #[test] @@ -221,6 +245,18 @@ fn test_clamp_magnitude_f64_panic_negative_limit() { let _ = 1.0f64.clamp_magnitude(-1.0); } +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f128_panic_negative_limit() { + let _ = 1.0f128.clamp_magnitude(-1.0); +} + +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f16_panic_nan_limit() { + let _ = 1.0f16.clamp_magnitude(f16::NAN); +} + #[test] #[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f32_panic_nan_limit() { @@ -232,3 +268,9 @@ fn test_clamp_magnitude_f32_panic_nan_limit() { fn test_clamp_magnitude_f64_panic_nan_limit() { let _ = 1.0f64.clamp_magnitude(f64::NAN); } + +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f128_panic_nan_limit() { + let _ = 1.0f128.clamp_magnitude(f128::NAN); +} diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index b1c3001790f07..f892734d8b95d 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -24,6 +24,7 @@ mod u8; mod bignum; mod carryless_mul; mod cast; +mod clamp_magnitude; mod complex; mod const_from; mod dec2flt; From eb7e04db8ae77067bc713443573f4232b3680c73 Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Sat, 26 Sep 2026 00:44:05 +0200 Subject: [PATCH 19/65] Disable RA diagnostics due to many false positives --- src/tools/miri/etc/rust_analyzer_zed.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/miri/etc/rust_analyzer_zed.json b/src/tools/miri/etc/rust_analyzer_zed.json index e5ff400e989e7..f2debc2000acb 100644 --- a/src/tools/miri/etc/rust_analyzer_zed.json +++ b/src/tools/miri/etc/rust_analyzer_zed.json @@ -19,6 +19,10 @@ "--message-format=json" ] }, + "diagnostics": { + // RA reports false errors due to + "enable": false + }, "cargo": { "extraEnv": { "MIRI_AUTO_OPS": "no", From 47f1cc50f5f13da841285728b8c45e79debbda1f Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Sat, 5 Sep 2026 15:42:24 +0200 Subject: [PATCH 20/65] Add socket2, integrate socket2 sockets in TCP socket shim Shimmed TCP sockets are now always backed by a host socket. Before `connect` or `listen` is invoked on the shimmed socket, we just have a `socket2::Socket`. This is socket is then coverted to a `mio::TcpStream` or `mio::TcpListener` after encountering a `connect` or `listen`, respectively. Furthermore, a socket can always be viewed as a `socket2::SockRef`. This allows us to support more socket options than mio provides on its TCP socket abstractions. --- src/tools/miri/Cargo.lock | 11 + src/tools/miri/Cargo.toml | 1 + src/tools/miri/priroda/Cargo.lock | 11 + src/tools/miri/src/shims/unix/socket.rs | 18 +- src/tools/miri/src/shims/unix/tcp_socket.rs | 567 +++++++++++------- .../pass-dep/libc/libc-socket-no-blocking.rs | 92 ++- .../miri/tests/pass-dep/libc/libc-socket.rs | 184 +++++- 7 files changed, 635 insertions(+), 249 deletions(-) diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 166d45491242c..c71ec790d39fe 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -1020,6 +1020,7 @@ dependencies = [ "serde", "serde_json", "smallvec", + "socket2", "tempfile", "ui_test", ] @@ -1484,6 +1485,16 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spanned" version = "0.4.1" diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index de4687715c06b..9f2a1e884fd70 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -28,6 +28,7 @@ directories = "6" bitflags = "2.6" serde_json = { version = "1.0", optional = true } mio = { version = "1.1.1", features = ["os-poll", "net"] } +socket2 = "0.6.5" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/src/tools/miri/priroda/Cargo.lock b/src/tools/miri/priroda/Cargo.lock index 8d4272630e08a..7b8ec9b0f08e6 100644 --- a/src/tools/miri/priroda/Cargo.lock +++ b/src/tools/miri/priroda/Cargo.lock @@ -688,6 +688,7 @@ dependencies = [ "rand 0.10.2", "serde", "smallvec", + "socket2", ] [[package]] @@ -1115,6 +1116,16 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spanned" version = "0.4.1" diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index 8c442df216a47..ce1f5c06b6b2c 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -10,14 +10,6 @@ use crate::shims::unix::socket_address::EvalContextExt as _; use crate::shims::unix::tcp_socket::TcpSocket; use crate::*; -#[derive(Debug, PartialEq)] -pub enum SocketFamily { - // IPv4 internet protocols - IPv4, - // IPv6 internet protocols - IPv6, -} - /// Represents unix-specific socket file descriptions. /// /// Not to be confused with Unix domain sockets. @@ -204,9 +196,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let family = if domain == this.eval_libc_i32("AF_INET") { - SocketFamily::IPv4 + socket2::Domain::IPV4 } else if domain == this.eval_libc_i32("AF_INET6") { - SocketFamily::IPv6 + socket2::Domain::IPV6 } else { throw_unsup_format!( "socket: domain {:#x} is unsupported, only AF_INET and \ @@ -229,8 +221,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } + let socket = match TcpSocket::new(family, is_non_block) { + Ok(socket) => socket, + Err(e) => return this.set_errno_and_return_neg1_i32(e), + }; let fds = &mut this.machine.fds; - let fd = fds.new_ref(TcpSocket::new(family, is_non_block)); + let fd = fds.new_ref(socket); interp_ok(Scalar::from_i32(fds.insert(fd))) } diff --git a/src/tools/miri/src/shims/unix/tcp_socket.rs b/src/tools/miri/src/shims/unix/tcp_socket.rs index b59174cd71c2d..db0010beaa46b 100644 --- a/src/tools/miri/src/shims/unix/tcp_socket.rs +++ b/src/tools/miri/src/shims/unix/tcp_socket.rs @@ -1,7 +1,7 @@ use std::cell::{Cell, RefCell, RefMut}; use std::io; use std::io::Read; -use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4}; +use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::sync::atomic::AtomicBool; use std::time::Duration; @@ -14,7 +14,7 @@ use rustc_target::spec::Os; use crate::shims::files::{EvalContextExt as _, FdNum, FileDescription, FileDescriptionRef}; use crate::shims::sig::Varargs; use crate::shims::unix::UnixFileDescription; -use crate::shims::unix::socket::{SocketFamily, UnixSocketFileDescription}; +use crate::shims::unix::socket::UnixSocketFileDescription; use crate::*; /// On Linux a TCP socket is initally in the TCP_CLOSE state: @@ -35,12 +35,10 @@ const INITIAL_TCP_SOCKET_READINESS: Readiness = #[derive(Debug)] enum SocketState { /// No syscall after `socket` has been made. - Initial, - /// The `bind` syscall has been called on the socket. - /// This is only reachable from the [`SocketState::Initial`] state. - Bound(SocketAddr), + Initial(socket2::Socket), /// The `listen` syscall has been called on the socket. - /// This is only reachable from the [`SocketState::Bound`] state. + /// This is reachable from the [`SocketState::Initial`] and the + /// [`SocketState::Listening`] states. Listening(TcpListener), /// The `connect` syscall has been called and we weren't yet able /// to ensure the connection is established. This is only reachable @@ -59,17 +57,55 @@ enum SocketState { /// and thus nothing (except destroying the socket) should be /// supported when a socket is in this state. ConnectionFailed(TcpStream), + /// The socket state machine is transitioning between two states + /// which contain an underlying host socket. This state is never + /// observable, it's only needed as a placeholder state to safely + /// acquire ownership of the underlying host socket. + // FIXME: This would not be needed with `replace_with`, but sadly + // this isn't part of the standard library and we don't want to add + // an extra dependency for this. + None, +} + +impl SocketState { + /// View the underlying host socket as a [`socket2::SockRef`]. + /// + /// **Note**: Potentially blocking operations need to be performed on the + /// underlying [`TcpStream`] and [`TcpListener`] as it would break the mio + /// poll on Windows hosts when performed directly on the [`socket2::SockRef`]. + fn as_socket_ref<'a>(&'a self) -> socket2::SockRef<'a> { + match self { + SocketState::Initial(socket) => socket.into(), + SocketState::Listening(listener) => listener.into(), + SocketState::Connecting(stream) + | SocketState::Connected(stream) + | SocketState::ConnectionFailed(stream) => stream.into(), + SocketState::None => unreachable!(), + } + } + + /// Invoke `cb` with a [`socket2::SockRef`] to the underlying host socket. + /// + /// **Note**: Potentially blocking operations need to be performed on the + /// underlying [`TcpStream`] and [`TcpListener`] as it would break the mio + /// poll on Windows hosts when performed directly on the [`socket2::SockRef`]. + fn with_socket_ref<'a, T>(&'a self, cb: impl FnOnce(socket2::SockRef<'a>) -> T) -> T { + let socket = self.as_socket_ref(); + cb(socket) + } } #[derive(Debug)] pub(super) struct TcpSocket { /// Family of the socket, used to ensure socket only binds/connects to address of /// same family. - family: SocketFamily, + family: socket2::Domain, /// Current state of the inner socket. state: RefCell, /// Whether this fd is non-blocking or not. is_non_block: Cell, + /// Whether the socket is implicitly or explicitly bound to an address. + is_bound: Cell, /// The current blocking I/O readiness of the file description. io_readiness: RefCell, /// [`Some`] when the socket had an async error which has not yet been fetched via `SO_ERROR`. @@ -91,17 +127,31 @@ pub(super) struct TcpSocket { } impl TcpSocket { - pub fn new(family: SocketFamily, is_non_block: bool) -> Self { - TcpSocket { + pub fn new(family: socket2::Domain, is_non_block: bool) -> io::Result { + let socket = + socket2::Socket::new(family, socket2::Type::STREAM, Some(socket2::Protocol::TCP))?; + // The underlying host socket needs to be non-blocking. The actual + // blocking mode of the socket is stored in `is_non_block`. + socket.set_nonblocking(true)?; + + // We don't add the newly created socket to the blocking I/O manager + // until we can create a mio TcpStream or TcpListener because + // `socket2::Socket` doesn't implement `mio::Source`. + // This is fine since the readiness of a TCP socket (at least under Linux) + // does have a static initial readiness until `connect` or `listen` is + // invoked on it. + + Ok(TcpSocket { family, - state: RefCell::new(SocketState::Initial), + state: RefCell::new(SocketState::Initial(socket)), is_non_block: Cell::new(is_non_block), + is_bound: Cell::new(false), io_readiness: RefCell::new(INITIAL_TCP_SOCKET_READINESS), error: RefCell::new(None), read_timeout: Cell::new(None), write_timeout: Cell::new(None), watched: ReadinessWatched::default(), - } + }) } } @@ -251,98 +301,158 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "bind")?; - let mut state = self.state.borrow_mut(); + let address_family = match &address { + SocketAddr::V4(_) => socket2::Domain::IPV4, + SocketAddr::V6(_) => socket2::Domain::IPV6, + }; - match *state { - SocketState::Initial => { - let address_family = match &address { - SocketAddr::V4(_) => SocketFamily::IPv4, - SocketAddr::V6(_) => SocketFamily::IPv6, - }; + if self.family != address_family { + // Attempted to bind an address from a family that doesn't match + // the family of the socket. + let err = if matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android) { + // Linux man page states that `EINVAL` is used when there is an address family mismatch. + // See + LibcError("EINVAL") + } else { + // POSIX man page states that `EAFNOSUPPORT` should be used when there is an address + // family mismatch. + // See + LibcError("EAFNOSUPPORT") + }; + return interp_ok(Err(err)); + } - if self.family != address_family { - // Attempted to bind an address from a family that doesn't match - // the family of the socket. - let err = if matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android) { - // Linux man page states that `EINVAL` is used when there is an address family mismatch. - // See - LibcError("EINVAL") - } else { - // POSIX man page states that `EAFNOSUPPORT` should be used when there is an address - // family mismatch. - // See - LibcError("EAFNOSUPPORT") - }; - return interp_ok(Err(err)); - } + let state = self.state.borrow(); + let socket = state.as_socket_ref(); - *state = SocketState::Bound(address); - } - SocketState::Connecting(_) | SocketState::Connected(_) => - throw_unsup_format!( - "bind: tcp socket is already connected and binding a - connected socket is unsupported" - ), - SocketState::Bound(_) | SocketState::Listening(_) => - throw_unsup_format!( - "bind: tcp socket is already bound and binding a socket \ - multiple times is unsupported" - ), - SocketState::ConnectionFailed(_) => unreachable!(), + // `bind` is a non-blocking operation for TCP sockets. + if let Err(e) = socket.bind(&socket2::SockAddr::from(address)) { + return interp_ok(Err(IoError::HostError(e))); } + // The socket has been explicitly bound to a local address. + self.is_bound.set(true); + interp_ok(Ok(())) } fn listen<'tcx>( self: FileDescriptionRef, communicate_allowed: bool, - // Since the backlog value is just a performance hint we can ignore it. - _backlog: i32, + backlog: i32, ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, Result<(), IoError>> { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "listen")?; - let mut state = self.state.borrow_mut(); + let state = self.state.borrow(); - match *state { - SocketState::Bound(socket_addr) => - match TcpListener::bind(socket_addr) { - Ok(listener) => { - *state = SocketState::Listening(listener); - drop(state); - - // After invoking `listen` on a TCP socket, it transitions out of the - // TCP_CLOSE state which affects the socket's readiness. Because we - // register the socket afterwards to the blocking I/O manager, we just - // clear its readiness here as the blocking I/O manager will update its - // readiness accordingly. - self.io_readiness.replace(Readiness::EMPTY); - ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; - - // Register the socket to the blocking I/O manager because - // we now have an associated host socket. - ecx.machine.blocking_io.register(self); - } - Err(e) => return interp_ok(Err(IoError::HostError(e))), - }, - SocketState::Initial => { - throw_unsup_format!( - "listen: listening on a tcp socket which isn't bound is unsupported" - ) - } - SocketState::Listening(_) => { - throw_unsup_format!( - "listen: listening on a tcp socket multiple times is unsupported" - ) - } - SocketState::Connecting(_) | SocketState::Connected(_) => { - throw_unsup_format!("listen: listening on a connected tcp socket is unsupported") + // We'll only attempt to invoke `listen` on the socket if it's not already + // connected because POSIX specifies that EINVAL should be returned in this + // case. + let socket = match &*state { + SocketState::Initial(_) | SocketState::Listening(_) => state.as_socket_ref(), + // POSIX states that connected sockets cannot start listening, + // so we can return early here to simplify the remaining shim. + SocketState::Connecting(_) | SocketState::Connected(_) => + return interp_ok(Err(LibcError("EINVAL"))), + SocketState::ConnectionFailed(_) | SocketState::None => unreachable!(), + }; + + // Despite not being specified by POSIX, on most of our supported Unix-like platforms + // (confirmed on Linux, macOS, FreeBSD, and Solaris) TCP sockets get implicitly bound + // to the unspecified address at a random port when `listen` is invoked on an unbound + // socket. Thus, Miri models the same non-POSIX behavior and treats a socket as bound + // once `listen` has successfully been invoked on an unbound socket. + + if cfg!(windows) && !self.is_bound.get() { + // Implicitly binding a TCP socket by invoking `listen` on an + // unbound socket causes EINVAL on Windows hosts. We thus + // explicitly bind the socket to an unspecified address with + // a random port before listening. + // + // When the subsequent `listen` fails we behave incorrectly + // because a previously unbound socket is now bound. However, + // since `listen` can only fail for system resource errors + // (e.g., ENOBUFS) this isn't a problem in reality. + + let address = if self.family == socket2::Domain::IPV4 { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, /* port */ 0)) + } else { + SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + /* port */ 0, + /* flowinfo */ 0, + /* scope_id */ 0, + )) + }; + + if let Err(e) = socket.bind(&socket2::SockAddr::from(address)) { + return interp_ok(Err(IoError::HostError(e))); } - SocketState::ConnectionFailed(_) => unreachable!(), + + // We just explicitly bound the socket to a local address. + self.is_bound.set(true); + } + + if let Err(e) = socket.listen(backlog) { + return interp_ok(Err(IoError::HostError(e))); } + // As mentioned above, we treat listening sockets as implicitly bound to a + // local address if they weren't already bound before the `listen` invocation. + self.is_bound.set(true); + + if let SocketState::Listening(_) = &*state { + // The socket was already listening; we just changed the backlog. + // The socket is already registered to the blocking I/O manager + // and we also don't need to perform a state transition. + return interp_ok(Ok(())); + } + + drop(state); + + // Temporarily use dummy state to take ownership of the underlying socket. + let SocketState::Initial(socket) = self.state.replace(SocketState::None) else { + unreachable!() + }; + + // FIXME: Rustfmt has a bug where it incorrectly removes the outer braces of {{ .. }} inside + // a `cfg_select!` block. See . + #[rustfmt::skip] + let listener = cfg_select! { + // Turn the `socket2::Socket` into a `mio::TcpListener`. + // We follow Tokio's TCP socket implementation: + // See + // SAFETY: mio specifies that it is safe to use `from_raw_fd` and + // `from_raw_socket` as long as it's ensured that the socket is non-blocking. + // Because we immediately make the socket non-blocking after creation, that + // is always guaranteed. + + unix => {{ + use std::os::fd::{IntoRawFd, FromRawFd}; + let raw_fd = socket.into_raw_fd(); + unsafe { TcpListener::from_raw_fd(raw_fd) } + }}, + windows => {{ + use std::os::windows::io::{IntoRawSocket, FromRawSocket}; + let raw_socket = socket.into_raw_socket(); + unsafe { TcpListener::from_raw_socket(raw_socket) } + }}, + _ => unreachable!("unsupported host platform") + }; + + self.state.replace(SocketState::Listening(listener)); + + // Clear the "artificial" readiness as we are about to put the blocking + // I/O manager in charge of handling readiness. + self.io_readiness.replace(Readiness::EMPTY); + ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; + + // Register the socket to the blocking I/O manager. Can only happen + // now that we have created a mio TcpListener. + ecx.machine.blocking_io.register(self); + interp_ok(Ok(())) } @@ -394,52 +504,110 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "connect")?; - match &*self.state.borrow() { - SocketState::Initial => { /* fall-through to below */ } + let state = self.state.borrow(); + + // We'll only attempt to invoke `connect` on the socket if it's not already + // connected or listening because POSIX specifies error codes which should + // be returned in those cases. + let socket = match &*state { + SocketState::Initial(socket) => socket, + SocketState::Listening(_) => { + let err = if matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android) { + // Linux-like targets return EISCONN when attempting to invoke + // `connect` on an already listening socket. + // See + LibcError("EISCONN") + } else { + // POSIX specifies to return EOPNOTSUPP when attempting to invoke + // `connect` on an already listening socket. + // See + LibcError("EOPNOTSUPP") + }; + return finish.call(ecx, Err(err)); + } + // The socket is already connected. + // Subsequent connection attempts return EISCONN for TCP sockets. + SocketState::Connected(_) => return finish.call(ecx, Err(LibcError("EISCONN"))), // The socket is already in a connecting state. + // Subsequent connection attempts return EALREADY for TCP sockets. SocketState::Connecting(_) => return finish.call(ecx, Err(LibcError("EALREADY"))), - // We don't return EISCONN for already connected sockets, for which we're - // sure that the connection is established, since TCP sockets are usually - // allowed to be connected multiple times. - _ => - throw_unsup_format!( - "connect: connecting is only supported for tcp sockets which are neither \ - bound, listening nor already connected" - ), + SocketState::ConnectionFailed(_) | SocketState::None => unreachable!(), + }; + + let result = socket.connect(&socket2::SockAddr::from(address)); + + drop(state); + + // Boolean whether the connection attempt "failed" because it could not be + // completed immediately without blocking. + let is_in_progress = result.as_ref().is_err_and(|e| { + // On Windows hosts non-blocking connects fail with EWOULDBLOCK when the connection cannot + // be established immediately, while on Unix-like hosts they fail with EINPROGRESS. + matches!(e.kind(), io::ErrorKind::WouldBlock | io::ErrorKind::InProgress) + }); + + if !is_in_progress && let Err(e) = result { + // There was a "real" error during connection establishment. + return finish.call(ecx, Err(IoError::HostError(e))); } - // This begins establishing the connection, but does not block until the stream is fully connected. - // We deal with that below. - match TcpStream::connect(address) { - Ok(stream) => { - *self.state.borrow_mut() = SocketState::Connecting(stream); - - // After invoking `connect` on a TCP socket, it transitions out of the - // TCP_CLOSE state which affects the socket's readiness. Because we - // register the socket afterwards to the blocking I/O manager, we just - // clear its readiness here as the blocking I/O manager will update its - // readiness accordingly. - self.io_readiness.replace(Readiness::EMPTY); - ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; - - // Register the socket to the blocking I/O manager because - // we now have an associated host socket. - ecx.machine.blocking_io.register(self.clone()); - } - Err(e) => return finish.call(ecx, Err(IoError::HostError(e))), + // Temporarily use dummy state to take ownership of the underlying socket. + let SocketState::Initial(socket) = self.state.replace(SocketState::None) else { + unreachable!() }; + // FIXME: Rustfmt has a bug where it incorrectly removes the outer braces of {{ .. }} inside + // a `cfg_select!` block. See . + #[rustfmt::skip] + let stream = cfg_select! { + // Turn the `socket2::Socket` into a `mio::TcpStream`. + // We follow Tokio's TCP socket implementation: + // See + // SAFETY: mio specifies that it is safe to use `from_raw_fd` and + // `from_raw_socket` as long as it's ensured that the socket is non-blocking. + // Because we immediately make the socket non-blocking after creation, that + // is always guaranteed. + + unix => {{ + use std::os::fd::{IntoRawFd, FromRawFd}; + let raw_fd = socket.into_raw_fd(); + unsafe { TcpStream::from_raw_fd(raw_fd) } + }}, + windows => {{ + use std::os::windows::io::{IntoRawSocket, FromRawSocket}; + let raw_socket = socket.into_raw_socket(); + unsafe { TcpStream::from_raw_socket(raw_socket) } + }}, + _ => unreachable!("unsupported host platform") + }; + + self.state.replace(SocketState::Connecting(stream)); + // Connecting sockets are implicitly bound to a local address. + self.is_bound.set(true); + + // Clear the "artificial" readiness as we are about to put the blocking + // I/O manager in charge of handling readiness. + self.io_readiness.replace(Readiness::EMPTY); + ecx.update_fd_readiness(self.clone(), ReadinessUpdateFlags::DEFAULT)?; + + // Register the socket to the blocking I/O manager. Can only happen + // now that we have created a mio TcpStream. + ecx.machine.blocking_io.register(self.clone()); + if self.is_non_block.get() { // We have a non-blocking socket and thus don't want to block until // the connection is established. - // Since the [`TcpStream::connect`] function of mio hides the EINPROGRESS - // we just always return EINPROGRESS and check whether the connection succeeded - // once we want to use the connected socket. - finish.call(ecx, Err(LibcError("EINPROGRESS"))) + if is_in_progress { + finish.call(ecx, Err(LibcError("EINPROGRESS"))) + } else { + finish.call(ecx, Ok(())) + } } else { // The socket is in blocking mode and thus the connect call should block - // until the connection with the server is established. + // until the connection with the server is established. A potential + // EWOULDBLOCK or EINPROGRESS error code is ignored as we emulate a + // blocking socket. if self.write_timeout.get().is_some() { // Some Unixes like Linux also apply the SO_SNDTIMEO socket option @@ -647,19 +815,7 @@ impl UnixSocketFileDescription for TcpSocket { let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.u32); let ttl = ecx.read_scalar(&option_value)?.to_u32()?; - let result = match &*self.state.borrow() { - SocketState::Initial | SocketState::Bound(_) => - throw_unsup_format!( - "setsockopt: setting option IP_TTL on level IPPROTO_IP is only supported \ - on connected and listening tcp sockets" - ), - SocketState::Listening(listener) => listener.set_ttl(ttl), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.set_ttl(ttl), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - return match result { + return match self.state.borrow().with_socket_ref(|s| s.set_ttl_v4(ttl)) { Ok(_) => interp_ok(Ok(())), Err(e) => interp_ok(Err(IoError::HostError(e))), }; @@ -679,18 +835,7 @@ impl UnixSocketFileDescription for TcpSocket { let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32); let nodelay = ecx.read_scalar(&option_value)?.to_i32()? != 0; - let result = match &*self.state.borrow() { - SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) => - throw_unsup_format!( - "setsockopt: setting option TCP_NODELAY on level IPPROTO_TCP is only supported \ - on connected tcp sockets" - ), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.set_nodelay(nodelay), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - return match result { + return match self.state.borrow().with_socket_ref(|s| s.set_tcp_nodelay(nodelay)) { Ok(_) => interp_ok(Ok(())), Err(e) => interp_ok(Err(IoError::HostError(e))), }; @@ -772,19 +917,7 @@ impl UnixSocketFileDescription for TcpSocket { let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL"); if option == opt_ip_ttl { - let ttl = match &*self.state.borrow() { - SocketState::Initial | SocketState::Bound(_) => - throw_unsup_format!( - "getsockopt: reading option IP_TTL on level IPPROTO_IP is only supported \ - on connected and listening tcp sockets" - ), - SocketState::Listening(listener) => listener.ttl(), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.ttl(), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - let ttl = match ttl { + let ttl = match self.state.borrow().with_socket_ref(|s| s.ttl_v4()) { Ok(ttl) => ttl, Err(e) => return interp_ok(Err(IoError::HostError(e))), }; @@ -802,18 +935,7 @@ impl UnixSocketFileDescription for TcpSocket { let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY"); if option == opt_tcp_nodelay { - let nodelay = match &*self.state.borrow() { - SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) => - throw_unsup_format!( - "getsockopt: reading option TCP_NODELAY on level IPPROTO_TCP is only supported \ - on connected tcp sockets" - ), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.nodelay(), - SocketState::ConnectionFailed(_) => unreachable!(), - }; - - let nodelay = match nodelay { + let nodelay = match self.state.borrow().with_socket_ref(|s| s.tcp_nodelay()) { Ok(nodelay) => nodelay, Err(e) => return interp_ok(Err(IoError::HostError(e))), }; @@ -843,52 +965,45 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "getsockname")?; - let state = self.state.borrow(); + if !self.is_bound.get() { + // Since Windows returns EINVAL when invoking `getsockname` on + // a socket which hasn't been bound yet, we need to manually + // return an unspecified address here. - let address = match &*state { - SocketState::Bound(address) => { - if address.port() == 0 { - // The socket is bound to a zero-port which means it gets assigned a random - // port. Since we don't yet have an underlying socket, we don't know what this - // random port will be and thus this is unsupported. - throw_unsup_format!( - "getsockname: when the port is 0, getting the tcp socket address before \ - calling `listen` or `connect` is unsupported" - ) - } + let address = if self.family == socket2::Domain::IPV4 { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, /* port */ 0)) + } else { + SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + /* port */ 0, + /* flowinfo */ 0, + /* scope_id */ 0, + )) + }; + return interp_ok(Ok(address)); + } - *address - } - SocketState::Listening(listener) => - match listener.local_addr() { - Ok(address) => address, - Err(e) => return interp_ok(Err(IoError::HostError(e))), - }, - SocketState::Connecting(stream) | SocketState::Connected(stream) => { - if cfg!(windows) && matches!(&*state, SocketState::Connecting(_)) { - // FIXME: On Windows hosts `TcpStream::local_addr` returns `0.0.0.0:0` whilst - // the socket is connecting: - // - // This is problematic because UNIX targets could expect a real local address even - // for a connecting non-blocking socket. - - static DEDUP: AtomicBool = AtomicBool::new(false); - if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { - ecx.emit_diagnostic(NonHaltingDiagnostic::ConnectingSocketGetsockname); - } - } - match stream.local_addr() { - Ok(address) => address, - Err(e) => return interp_ok(Err(IoError::HostError(e))), - } + let state = self.state.borrow(); + let socket = state.as_socket_ref(); + + if cfg!(windows) + && let SocketState::Connecting(_) = &*state + { + // FIXME: On Windows hosts `getsockname` returns `0.0.0.0:0` whilst the socket is connecting: + // + // This is problematic because UNIX targets could expect a real local address even + // for a connecting non-blocking socket. + + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { + ecx.emit_diagnostic(NonHaltingDiagnostic::ConnectingSocketGetsockname); } - // For non-bound sockets the POSIX manual says the returned address is unspecified. - // Often this is 0.0.0.0:0 and thus we set it to this value. - SocketState::Initial => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)), - SocketState::ConnectionFailed(_) => unreachable!(), - }; + } - interp_ok(Ok(address)) + match socket.local_addr() { + Ok(address) => interp_ok(Ok(address.as_socket().unwrap())), + Err(e) => interp_ok(Err(IoError::HostError(e))), + } } fn getpeername<'tcx>( @@ -1077,14 +1192,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let family = match addr { - SocketAddr::V4(_) => SocketFamily::IPv4, - SocketAddr::V6(_) => SocketFamily::IPv6, + SocketAddr::V4(_) => socket2::Domain::IPV4, + SocketAddr::V6(_) => socket2::Domain::IPV6, }; let fd = this.machine.fds.new_ref(TcpSocket { family, state: RefCell::new(SocketState::Connected(stream)), is_non_block: Cell::new(is_client_sock_nonblock), + // Connected sockets are implicitly bound to a local address. + is_bound: Cell::new(true), io_readiness: RefCell::new(Readiness::EMPTY), error: RefCell::new(None), read_timeout: Cell::new(None), @@ -1484,13 +1601,11 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The connection is established. // Temporarily use dummy state to take ownership of the stream. - let mut state = socket.state.borrow_mut(); - let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else { + let SocketState::Connecting(stream) = socket.state.replace(SocketState::None) else { // At the start of the function we ensured that we're currently connecting. unreachable!() }; - *state = SocketState::Connected(stream); - drop(state); + socket.state.replace(SocketState::Connected(stream)); action.call(this, Ok(())) } ), @@ -1523,16 +1638,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// state because we know that `socket` can no longer successfully establish a /// connection. fn update_last_error(&self, socket: &FileDescriptionRef) { - let mut state = socket.state.borrow_mut(); - - let new_error = match &*state { - SocketState::Listening(listener) => - listener.take_error().expect("Reading SO_ERROR should not fail"), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.take_error().expect("Reading SO_ERROR should not fail"), - SocketState::Initial | SocketState::Bound(_) | SocketState::ConnectionFailed(_) => None, - }; + let state = socket.state.borrow(); + let new_error = + state.as_socket_ref().take_error().expect("Reading SO_ERROR should not fail"); let Some(new_error) = new_error else { return }; // Store the error such that we can return it when @@ -1545,13 +1654,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // specification, the socket is now in an unspecified state. // We thus change the socket state to `ConnectionFailed`. + drop(state); + // Temporarily use dummy state to take ownership of the stream. - let SocketState::Connecting(stream) = - std::mem::replace(&mut *state, SocketState::Initial) - else { + let SocketState::Connecting(stream) = socket.state.replace(SocketState::None) else { unreachable!() }; - *state = SocketState::ConnectionFailed(stream); + socket.state.replace(SocketState::ConnectionFailed(stream)); } } } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs index de718feb47990..451e9feef3b55 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs @@ -58,6 +58,9 @@ fn main() { test_getpeername_ipv4_nonblock(); test_getpeername_ipv4_nonblock_no_peer(); + + test_listen_connecting(); + test_connect_connecting(); } /// Test that setting the O_NONBLOCK flag changes the blocking state of a socket. @@ -231,9 +234,10 @@ fn test_connect_nonblock() { loop { // There should be no error during async connection. - let errno = net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR) - .unwrap(); - assert_eq!(errno, 0); + let so_error = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR) + .unwrap(); + assert_eq!(so_error, 0); let result = net::sockname_ipv4(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) @@ -769,9 +773,9 @@ fn test_getpeername_ipv4_nonblock_no_peer() { assert_eq!(err.kind(), ErrorKind::InProgress); // There should be no error during async connection. - let errno = + let so_error = net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); - assert_eq!(errno, 0); + assert_eq!(so_error, 0); // Since we're never accepting the connection, the socket should never be // successfully connected and thus we should be unable to read the peername. @@ -782,3 +786,81 @@ fn test_getpeername_ipv4_nonblock_no_peer() { }; assert_eq!(err.kind(), ErrorKind::NotConnected); } + +/// Test that `listen` returns EINVAL for a non-blocking IPv4 +/// socket which is stuck at connecting to a remote address. +fn test_listen_connecting() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // We cannot attempt to connect to a localhost address because + // it could be the case that a socket from another test is + // currently listening on `localhost:12321` because we bind to + // random ports everywhere. For `192.0.2.1` we know that nothing is + // listening because it's a blackhole address: + // + // The port `12321` is just a random non-zero port because Windows + // and Apple hosts return EADDRNOTAVAIL when attempting to connect to + // a zero port. + let addr = net::sock_addr_ipv4([192, 0, 2, 1], 12321); + + // Non-blocking connect should fail with EINPROGRESS. + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InProgress); + + // There should be no error during async connection. + let so_error = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); + assert_eq!(so_error, 0); + + // Connecting sockets cannot start listening, thus the operation should + // fail with EINVAL. + let err = unsafe { errno_result(libc::listen(client_sockfd, 16)).unwrap_err() }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that invoking `connect` on an already connecting client +/// TCP socket returns EALREADY. +fn test_connect_connecting() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // We cannot attempt to connect to a localhost address because + // it could be the case that a socket from another test is + // currently listening on `localhost:12321` because we bind to + // random ports everywhere. For `192.0.2.1` we know that nothing is + // listening because it's a blackhole address: + // + // The port `12321` is just a random non-zero port because Windows + // and Apple hosts return EADDRNOTAVAIL when attempting to connect to + // a zero port. + let addr = net::sock_addr_ipv4([192, 0, 2, 1], 12321); + + // Non-blocking connect should fail with EINPROGRESS. + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InProgress); + + // There should be no error during async connection. + let so_error = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); + assert_eq!(so_error, 0); + + // Create a socket which listens on `addr`. + let (_, addr) = net::make_listener_ipv4().unwrap(); + + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + // The standard library doesn't provide an error kind for EALREADY; + // we thus cannot assert the correct error kind. + assert_eq!(err.raw_os_error(), Some(libc::EALREADY)); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index 85d6302a0175d..d581f358cac95 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -33,10 +33,17 @@ fn main() { } test_bind_ipv4_invalid_addr_len(); test_bind_ipv6(); + test_bind_twice(); + test_bind_connected(); + test_bind_listening(); test_listen(); + test_listen_connected(); + test_listen_listening(); test_accept_connect(); test_connect_error(); + test_connect_connected(); + test_connect_listening(); test_send_peek_recv(); test_write_read(); test_readv(); @@ -47,6 +54,7 @@ fn main() { test_getsockname_ipv4_unbound(); test_getsockname_ipv4_connect(); test_getsockname_ipv6(); + test_getsockname_ipv6_unbound(); test_getpeername_ipv4(); test_getpeername_ipv6(); @@ -236,21 +244,123 @@ fn test_bind_ipv6() { } } -fn test_listen() { +/// Test that invoking `bind` on an already bound TCP socket +/// returns EINVAL. +fn test_bind_twice() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); - unsafe { + let err = unsafe { errno_check(libc::bind( sockfd, (&addr as *const libc::sockaddr_in).cast::(), size_of::() as libc::socklen_t, )); - } + errno_result(libc::bind( + sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )) + .unwrap_err() + }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that invoking `bind` on an already connected client +/// TCP socket returns EINVAL. +fn test_bind_connected() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); + + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + let err = unsafe { + errno_result(libc::bind( + client_sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )) + .unwrap_err() + }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that invoking `bind` on an already listening server +/// TCP socket returns EINVAL. +fn test_bind_listening() { + let server_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; unsafe { - errno_check(libc::listen(sockfd, 16)); + errno_check(libc::listen(server_sockfd, 16)); } + + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + let err = unsafe { + errno_result(libc::bind( + server_sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )) + .unwrap_err() + }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that a socket can start listening. This also tests +/// whether sockets are bound implicitly after `listen` has been +/// invoked on them. +fn test_listen() { + let sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + unsafe { errno_check(libc::listen(sockfd, 16)) }; + + let (_, sock_addr) = + net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); + + // Libc representation of an unspecified IPv4 address with zero port. + let addr = net::sock_addr_ipv4([0, 0, 0, 0], 0); + + // Ensure that the socket got implicitly bound to the unspecified + // local address with a non-zero port. + assert!(sock_addr.sin_port > 0); + assert_eq!(addr.sin_family, sock_addr.sin_family); + assert_eq!(addr.sin_addr.s_addr, sock_addr.sin_addr.s_addr); +} + +/// Test that `listen` returns EINVAL for a client TCP socket which +/// is already connected to a peer socket. +fn test_listen_connected() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); + + // Connected sockets cannot start listening, thus the operation should + // fail with EINVAL. + let err = unsafe { errno_result(libc::listen(client_sockfd, 16)).unwrap_err() }; + // Check that it is the right error. + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); +} + +/// Test that `listen` succeeds for a server TCP socket which is already +/// listening. +fn test_listen_listening() { + let (server_sockfd, _) = net::make_listener_ipv4().unwrap(); + + // Invoking `listen` multiple times should be allowed as it can be used to change + // the backlog value. + unsafe { errno_check(libc::listen(server_sockfd, 16)) }; } /// Test accepting connections by running a server in a separate thread and connecting clients @@ -318,6 +428,46 @@ fn test_connect_error() { )); } +/// Test that invoking `connect` on an already connected client +/// TCP socket returns EISCONN. +fn test_connect_connected() { + let (server_sockfd1, addr1) = net::make_listener_ipv4().unwrap(); + let (_, addr2) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr1).unwrap(); + net::accept_ipv4(server_sockfd1).unwrap(); + + let err = net::connect_ipv4(client_sockfd, addr2).unwrap_err(); + // The standard library doesn't provide an error kind for EISCONN; + // we thus cannot assert the correct error kind. + assert_eq!(err.raw_os_error(), Some(libc::EISCONN)); +} + +/// Test that invoking `connect` on an already listening server +/// TCP socket returns EOPNOTSUPP on non-Linux-like targets and +/// EISCONN on Linux-like targets. +fn test_connect_listening() { + let (_, addr1) = net::make_listener_ipv4().unwrap(); + let (server_sockfd2, _) = net::make_listener_ipv4().unwrap(); + + let err = net::connect_ipv4(server_sockfd2, addr1).unwrap_err(); + + if cfg!(any(target_os = "linux", target_os = "android")) { + // Linux-like targets return EISCONN when attempting to invoke + // `connect` on an already listening socket. + + // The standard library doesn't provide an error kind for EISCONN; + // we thus cannot assert the correct error kind. + assert_eq!(err.raw_os_error(), Some(libc::EISCONN)); + } else { + // POSIX specifies EOPNOTSUPP when attempting to invoke + // `connect` on an already listening socket. + assert_eq!(err.raw_os_error(), Some(libc::EOPNOTSUPP)); + } +} + /// Test sending bytes into a connected stream and then peeking and receiving /// them from the other end. /// We especially want to test that the peeking doesn't remove the bytes from @@ -606,6 +756,32 @@ fn test_getsockname_ipv6() { assert_eq!(addr.sin6_addr.s6_addr, sock_addr.sin6_addr.s6_addr); } +/// Test the `getsockname` syscall on an IPv6 socket which is not bound. +/// The `getsockname` syscall should return [::]:0 with flowinfo and scope id +/// of zero. +fn test_getsockname_ipv6_unbound() { + let sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; + + let (_, sock_addr) = + net::sockname_ipv6(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); + + // Libc representation of an unspecified IPv6 address with zero port, flowinfo and scope id. + let addr = net::sock_addr_full_ipv6( + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + /* port */ 0, + /* flowinfo */ 0, + /* scope_id */ 0, + ); + + assert_eq!(addr.sin6_family, sock_addr.sin6_family); + assert_eq!(addr.sin6_port, sock_addr.sin6_port); + assert_eq!(addr.sin6_flowinfo, sock_addr.sin6_flowinfo); + assert_eq!(addr.sin6_scope_id, sock_addr.sin6_scope_id); + assert_eq!(addr.sin6_addr.s6_addr, sock_addr.sin6_addr.s6_addr); +} + /// Test the `getpeername` syscall on an IPv4 socket. /// For a connected socket, the `getpeername` syscall should /// return the same address as the socket was connected to. From 9afae373d778e65aa19de7ba5c79d6946d6b1001 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Sep 2026 09:41:08 +0200 Subject: [PATCH 21/65] avoid re-opening RefCells quite so often --- src/tools/miri/src/shims/unix/tcp_socket.rs | 67 +++++++++++---------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/tools/miri/src/shims/unix/tcp_socket.rs b/src/tools/miri/src/shims/unix/tcp_socket.rs index db0010beaa46b..394f80a151f06 100644 --- a/src/tools/miri/src/shims/unix/tcp_socket.rs +++ b/src/tools/miri/src/shims/unix/tcp_socket.rs @@ -1,9 +1,9 @@ use std::cell::{Cell, RefCell, RefMut}; -use std::io; use std::io::Read; use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::sync::atomic::AtomicBool; use std::time::Duration; +use std::{io, mem}; use mio::event::Source; use mio::net::{TcpListener, TcpStream}; @@ -83,16 +83,6 @@ impl SocketState { SocketState::None => unreachable!(), } } - - /// Invoke `cb` with a [`socket2::SockRef`] to the underlying host socket. - /// - /// **Note**: Potentially blocking operations need to be performed on the - /// underlying [`TcpStream`] and [`TcpListener`] as it would break the mio - /// poll on Windows hosts when performed directly on the [`socket2::SockRef`]. - fn with_socket_ref<'a, T>(&'a self, cb: impl FnOnce(socket2::SockRef<'a>) -> T) -> T { - let socket = self.as_socket_ref(); - cb(socket) - } } #[derive(Debug)] @@ -153,6 +143,16 @@ impl TcpSocket { watched: ReadinessWatched::default(), }) } + + /// Invoke `cb` with a [`socket2::SockRef`] to the underlying host socket. + /// + /// **Note**: Potentially blocking operations need to be performed on the + /// underlying [`TcpStream`] and [`TcpListener`] as it would break the mio + /// poll on Windows hosts when performed directly on the [`socket2::SockRef`]. + fn with_socket_ref(&self, cb: impl FnOnce(socket2::SockRef<'_>) -> T) -> T { + let state = self.state.borrow(); + cb(state.as_socket_ref()) + } } impl FileDescription for TcpSocket { @@ -345,7 +345,7 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "listen")?; - let state = self.state.borrow(); + let mut state = self.state.borrow_mut(); // We'll only attempt to invoke `listen` on the socket if it's not already // connected because POSIX specifies that EINVAL should be returned in this @@ -410,10 +410,8 @@ impl UnixSocketFileDescription for TcpSocket { return interp_ok(Ok(())); } - drop(state); - // Temporarily use dummy state to take ownership of the underlying socket. - let SocketState::Initial(socket) = self.state.replace(SocketState::None) else { + let SocketState::Initial(socket) = mem::replace(&mut *state, SocketState::None) else { unreachable!() }; @@ -442,7 +440,8 @@ impl UnixSocketFileDescription for TcpSocket { _ => unreachable!("unsupported host platform") }; - self.state.replace(SocketState::Listening(listener)); + *state = SocketState::Listening(listener); + drop(state); // Clear the "artificial" readiness as we are about to put the blocking // I/O manager in charge of handling readiness. @@ -504,7 +503,7 @@ impl UnixSocketFileDescription for TcpSocket { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); ecx.ensure_not_failed(&self, "connect")?; - let state = self.state.borrow(); + let mut state = self.state.borrow_mut(); // We'll only attempt to invoke `connect` on the socket if it's not already // connected or listening because POSIX specifies error codes which should @@ -536,8 +535,6 @@ impl UnixSocketFileDescription for TcpSocket { let result = socket.connect(&socket2::SockAddr::from(address)); - drop(state); - // Boolean whether the connection attempt "failed" because it could not be // completed immediately without blocking. let is_in_progress = result.as_ref().is_err_and(|e| { @@ -552,7 +549,7 @@ impl UnixSocketFileDescription for TcpSocket { } // Temporarily use dummy state to take ownership of the underlying socket. - let SocketState::Initial(socket) = self.state.replace(SocketState::None) else { + let SocketState::Initial(socket) = mem::replace(&mut *state, SocketState::None) else { unreachable!() }; @@ -581,7 +578,9 @@ impl UnixSocketFileDescription for TcpSocket { _ => unreachable!("unsupported host platform") }; - self.state.replace(SocketState::Connecting(stream)); + *state = SocketState::Connecting(stream); + drop(state); + // Connecting sockets are implicitly bound to a local address. self.is_bound.set(true); @@ -815,7 +814,7 @@ impl UnixSocketFileDescription for TcpSocket { let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.u32); let ttl = ecx.read_scalar(&option_value)?.to_u32()?; - return match self.state.borrow().with_socket_ref(|s| s.set_ttl_v4(ttl)) { + return match self.with_socket_ref(|s| s.set_ttl_v4(ttl)) { Ok(_) => interp_ok(Ok(())), Err(e) => interp_ok(Err(IoError::HostError(e))), }; @@ -835,7 +834,7 @@ impl UnixSocketFileDescription for TcpSocket { let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32); let nodelay = ecx.read_scalar(&option_value)?.to_i32()? != 0; - return match self.state.borrow().with_socket_ref(|s| s.set_tcp_nodelay(nodelay)) { + return match self.with_socket_ref(|s| s.set_tcp_nodelay(nodelay)) { Ok(_) => interp_ok(Ok(())), Err(e) => interp_ok(Err(IoError::HostError(e))), }; @@ -917,7 +916,7 @@ impl UnixSocketFileDescription for TcpSocket { let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL"); if option == opt_ip_ttl { - let ttl = match self.state.borrow().with_socket_ref(|s| s.ttl_v4()) { + let ttl = match self.with_socket_ref(|s| s.ttl_v4()) { Ok(ttl) => ttl, Err(e) => return interp_ok(Err(IoError::HostError(e))), }; @@ -935,7 +934,7 @@ impl UnixSocketFileDescription for TcpSocket { let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY"); if option == opt_tcp_nodelay { - let nodelay = match self.state.borrow().with_socket_ref(|s| s.tcp_nodelay()) { + let nodelay = match self.with_socket_ref(|s| s.tcp_nodelay()) { Ok(nodelay) => nodelay, Err(e) => return interp_ok(Err(IoError::HostError(e))), }; @@ -1598,14 +1597,17 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // In other words, we are assuming that there will be no spurious // wakeups while establishing the connection. - // The connection is established. + // The connection is established. Update the state. + let mut state = socket.state.borrow_mut(); // Temporarily use dummy state to take ownership of the stream. - let SocketState::Connecting(stream) = socket.state.replace(SocketState::None) else { + let SocketState::Connecting(stream) = mem::replace(&mut *state, SocketState::None) else { // At the start of the function we ensured that we're currently connecting. unreachable!() }; - socket.state.replace(SocketState::Connected(stream)); + *state = SocketState::Connected(stream); + drop(state); + action.call(this, Ok(())) } ), @@ -1638,7 +1640,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// state because we know that `socket` can no longer successfully establish a /// connection. fn update_last_error(&self, socket: &FileDescriptionRef) { - let state = socket.state.borrow(); + let mut state = socket.state.borrow_mut(); let new_error = state.as_socket_ref().take_error().expect("Reading SO_ERROR should not fail"); @@ -1654,13 +1656,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // specification, the socket is now in an unspecified state. // We thus change the socket state to `ConnectionFailed`. - drop(state); - // Temporarily use dummy state to take ownership of the stream. - let SocketState::Connecting(stream) = socket.state.replace(SocketState::None) else { + let SocketState::Connecting(stream) = mem::replace(&mut *state, SocketState::None) + else { unreachable!() }; - socket.state.replace(SocketState::ConnectionFailed(stream)); + *state = SocketState::ConnectionFailed(stream); } } } From fd41c62116d98b0068bfed00095824d8d3433c73 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Sat, 26 Sep 2026 10:49:01 +0300 Subject: [PATCH 22/65] Disable rust-analyzer diagnostics for Helix --- src/tools/miri/etc/rust_analyzer_helix.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/miri/etc/rust_analyzer_helix.toml b/src/tools/miri/etc/rust_analyzer_helix.toml index 1015b4baa6b09..3b31ec68774d6 100644 --- a/src/tools/miri/etc/rust_analyzer_helix.toml +++ b/src/tools/miri/etc/rust_analyzer_helix.toml @@ -17,6 +17,10 @@ overrideCommand = [ "--message-format=json", ] +[language-server.rust-analyzer.config.diagnostics] +# RA reports false errors due to +enable = false + [language-server.rust-analyzer.config.cargo.extraEnv] MIRI_AUTO_OPS = "no" MIRI_IN_RA = "1" From 8c3ae6023e42df78c644e4040fd3a8bb15cd4616 Mon Sep 17 00:00:00 2001 From: Kurashina Hinata Date: Sat, 26 Sep 2026 18:07:13 +0900 Subject: [PATCH 23/65] Don't build format string suggestions from `concat!` offsets When a format string does not come directly from a string literal in the source, e.g. when it is produced by `concat!`, the parser's inner offsets are relative to the expanded string rather than to the source. The primary error span and the `RemoveRawIdent` suggestion already check `is_source_literal` before calling `fmt_span.from_inner`, but the `UsePositional`, `ReorderFormatParameter` and `AddMissingColon` suggestions did not. As a result, these suggestions could point into the middle of a multibyte character and ICE when rendered, or suggest a bogus argument copied from unrelated source text. Only emit them when the format string is a source literal, like the other suggestions do. The crash test is moved to `tests/ui/fmt`, with cases covering each of these suggestions. --- compiler/rustc_builtin_macros/src/format.rs | 26 ++++++---- tests/crashes/156101.rs | 4 -- ...format-args-concat-multibyte-suggestion.rs | 38 ++++++++++++++ ...at-args-concat-multibyte-suggestion.stderr | 52 +++++++++++++++++++ 4 files changed, 106 insertions(+), 14 deletions(-) delete mode 100644 tests/crashes/156101.rs create mode 100644 tests/ui/fmt/format-args-concat-multibyte-suggestion.rs create mode 100644 tests/ui/fmt/format-args-concat-multibyte-suggestion.stderr diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index e23c057f8a651..c58b7e234d4d6 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -339,7 +339,9 @@ fn make_format_args( parse::Suggestion::UsePositional => { let captured_arg_span = fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)); - if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) { + if is_source_literal + && let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) + { let span = match args.unnamed_args().last() { Some(arg) => arg.expr.span, None => fmt_span, @@ -360,17 +362,21 @@ fn make_format_args( } } parse::Suggestion::ReorderFormatParameter(span, replacement) => { - let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = - Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { - span, - replacement, - }); + if is_source_literal { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { + span, + replacement, + }); + } } parse::Suggestion::AddMissingColon(span) => { - let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = - Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); + if is_source_literal { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); + } } parse::Suggestion::UseRustDebugPrintingMacro => { // This targets `println!("{=}", x);` and `println!("{0=}", x);` diff --git a/tests/crashes/156101.rs b/tests/crashes/156101.rs deleted file mode 100644 index c95361fab2ecc..0000000000000 --- a/tests/crashes/156101.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #156101 -fn main() { - format_args!(concat!("𐏿", "{f:?#}")); -} diff --git a/tests/ui/fmt/format-args-concat-multibyte-suggestion.rs b/tests/ui/fmt/format-args-concat-multibyte-suggestion.rs new file mode 100644 index 0000000000000..51504607d2b61 --- /dev/null +++ b/tests/ui/fmt/format-args-concat-multibyte-suggestion.rs @@ -0,0 +1,38 @@ +// Regression test for #156101. +// +// When the format string comes from `concat!`, the parser's inner offsets are relative to the +// expanded string, not to the source, so they must not be turned into spans in the source. +// Before the fix, suggestions built from those offsets went wrong in two ways: +// +// - If the span landed inside a multibyte character, rendering the suggestion caused an ICE. +// The `ReorderFormatParameter` and `AddMissingColon` cases below are padded so that the span +// falls inside `é` (2 bytes) or `𐏿` (4 bytes). +// - If the span landed on unrelated source text, `UsePositional` suggested a bogus argument +// copied from that text. +// +// After the fix, no suggestion is emitted for these format strings. + +fn main() { + // The original reproducer from the issue. + format_args!(concat!("𐏿", "{f:?#}")); + //~^ ERROR invalid format string + + // `Suggestion::ReorderFormatParameter` + format_args!(concat!("é", "aaa{:?#}"), 1); + //~^ ERROR invalid format string + format_args!(concat!("𐏿", "a{:?#}"), 1); + //~^ ERROR invalid format string + + // `Suggestion::AddMissingColon` + format_args!(concat!("é", "aaaa{x?}")); + //~^ ERROR invalid format string + format_args!(concat!("𐏿", "aa{x?}")); + //~^ ERROR invalid format string + + // `Suggestion::UsePositional`: these did not ICE, but before the fix they suggested a bogus + // argument taken from the `concat!` invocation instead of from the format string. + format_args!(concat!("{}{a.b}", "")); + //~^ ERROR invalid format string + format_args!(concat!("{}{a.0}", "")); + //~^ ERROR invalid format string +} diff --git a/tests/ui/fmt/format-args-concat-multibyte-suggestion.stderr b/tests/ui/fmt/format-args-concat-multibyte-suggestion.stderr new file mode 100644 index 0000000000000..5eb8a4848a612 --- /dev/null +++ b/tests/ui/fmt/format-args-concat-multibyte-suggestion.stderr @@ -0,0 +1,52 @@ +error: invalid format string: expected `}`, found `#` + --> $DIR/format-args-concat-multibyte-suggestion.rs:17:18 + | +LL | format_args!(concat!("𐏿", "{f:?#}")); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + +error: invalid format string: expected `}`, found `#` + --> $DIR/format-args-concat-multibyte-suggestion.rs:21:18 + | +LL | format_args!(concat!("é", "aaa{:?#}"), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + +error: invalid format string: expected `}`, found `#` + --> $DIR/format-args-concat-multibyte-suggestion.rs:23:18 + | +LL | format_args!(concat!("𐏿", "a{:?#}"), 1); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + +error: invalid format string: expected `}`, found `?` + --> $DIR/format-args-concat-multibyte-suggestion.rs:27:18 + | +LL | format_args!(concat!("é", "aaaa{x?}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `:` before `?` to format with `Debug` in format string + | + = note: to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `?` + --> $DIR/format-args-concat-multibyte-suggestion.rs:29:18 + | +LL | format_args!(concat!("𐏿", "aa{x?}")); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `:` before `?` to format with `Debug` in format string + | + = note: to print `{`, you can escape it using `{{` + +error: invalid format string: field access isn't supported + --> $DIR/format-args-concat-multibyte-suggestion.rs:34:18 + | +LL | format_args!(concat!("{}{a.b}", "")); + | ^^^^^^^^^^^^^^^^^^^^^^ not supported in format string + | + = note: consider moving this expression to a local variable and then using the local here instead + +error: invalid format string: tuple index access isn't supported + --> $DIR/format-args-concat-multibyte-suggestion.rs:36:18 + | +LL | format_args!(concat!("{}{a.0}", "")); + | ^^^^^^^^^^^^^^^^^^^^^^ not supported in format string + | + = note: consider moving this expression to a local variable and then using the local here instead + +error: aborting due to 7 previous errors + From c22ad42ed8423bb2c7872f01468830f9f9886d01 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 27 Sep 2026 04:36:24 +0000 Subject: [PATCH 24/65] Prepare for merging from rust-lang/rust This updates the rust-version file to c23137133458bf7a49f4a55d3adb9e2c7c33bd79. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ab61f3f74a54a..2579ec7acaec9 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -0d38a842626a6e3b70e2c1efd76a8f22d2556d73 +c23137133458bf7a49f4a55d3adb9e2c7c33bd79 From b1d3d37f5fe8efef91cf3b1c93cee3e3cfbfeb38 Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Sun, 27 Sep 2026 18:50:45 +0200 Subject: [PATCH 25/65] Don't assert EINPROGRESS for non-blocking `connect` on TCP sockets When running on a native host, the `connect` might succeed immediately which would cause some of the non-blocking socket tests to fail. This assertion was only needed to ensure that the `connect` did not fail for any real reason. Thus, the assertion has been adjusted to not fail when the `connect` succeeds immediately. --- .../libc/libc-socket-no-blocking-epoll.rs | 11 +++---- .../pass-dep/libc/libc-socket-no-blocking.rs | 30 ++++++++++++++----- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index c91f3474793e3..563c2a3842a19 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -57,9 +57,9 @@ fn test_connect_nonblock() { net::accept_ipv4(server_sockfd).unwrap(); }); - // Non-blocking connects always "fail" with EINPROGRESS. - let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InProgress); + let result = net::connect_ipv4(client_sockfd, addr); + // This might succeed immediately or return EINPROGRESS. + assert!(result.is_ok() || result.is_err_and(|e| e.kind() == ErrorKind::InProgress)); // Add client socket with WRITABLE interest to epoll. epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLET | EPOLLERR).unwrap(); @@ -145,9 +145,10 @@ fn test_connect_nonblock_err() { // a zero port. let addr = net::sock_addr_ipv4([127, 0, 1, 1], 12321); - // Non-blocking connect should fail with EINPROGRESS. let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InProgress); + // Since the address is not bound, the `connect` can never succeed + // immediately and thus it always "fails" with EINPROGRESS. + assert!(err.kind() == ErrorKind::InProgress); // Add interest for client socket. epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLET | libc::EPOLLERR).unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs index de718feb47990..4405b2a9fc947 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs @@ -225,8 +225,14 @@ fn test_connect_nonblock() { // Yield to server thread to ensure that it's currently accepting. thread::sleep(Duration::from_millis(10)); - // Non-blocking connects always "fail" with EINPROGRESS. - let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + let Err(err) = net::connect_ipv4(client_sockfd, addr) else { + // The connection could be established immediately. We don't + // need to enter the loop and check whether the connection could + // really be established. + return server_thread.join().unwrap(); + }; + // The connection could not be established immediately. The `connect` + // should thus return EINPROGRESS. assert_eq!(err.kind(), ErrorKind::InProgress); loop { @@ -667,9 +673,10 @@ fn test_getsockname_ipv4_connect_nonblock() { // a zero port. let addr = net::sock_addr_ipv4([192, 0, 2, 1], 12321); - // Non-blocking connect should fail with EINPROGRESS. let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InProgress); + // Since we're connecting to a blackhole address, the `connect` can + // never succeed immediately and thus it always "fails" with EINPROGRESS. + assert!(err.kind() == ErrorKind::InProgress); let (_, sock_addr) = net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(client_sockfd, storage, len) @@ -712,8 +719,14 @@ fn test_getpeername_ipv4_nonblock() { // Yield to server thread to ensure that it's currently accepting. thread::sleep(Duration::from_millis(10)); - // Non-blocking connects always "fail" with EINPROGRESS. - let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + let Err(err) = net::connect_ipv4(client_sockfd, addr) else { + // The connection could be established immediately. We don't + // need to enter the loop and check whether the connection could + // really be established. + return server_thread.join().unwrap(); + }; + // The connection could not be established immediately. The `connect` + // should thus return EINPROGRESS. assert_eq!(err.kind(), ErrorKind::InProgress); loop { @@ -764,9 +777,10 @@ fn test_getpeername_ipv4_nonblock_no_peer() { // a zero port. let addr = net::sock_addr_ipv4([192, 0, 2, 1], 12321); - // Non-blocking connect should fail with EINPROGRESS. let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InProgress); + // Since we're connecting to a blackhole address, the `connect` can + // never succeed immediately and thus it always "fails" with EINPROGRESS. + assert!(err.kind() == ErrorKind::InProgress); // There should be no error during async connection. let errno = From 742e775b72a5180317ff28efd58d7437d74be36a Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 28 Sep 2026 04:37:20 +0000 Subject: [PATCH 26/65] Prepare for merging from rust-lang/rust This updates the rust-version file to 22697761c6a01e8d12adcd91e4a5bc755596767a. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2579ec7acaec9..ddf759b5d0c48 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -c23137133458bf7a49f4a55d3adb9e2c7c33bd79 +22697761c6a01e8d12adcd91e4a5bc755596767a From 5c42b6dc73bb5c9917c3c24eb7593a793ea90db4 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 28 Sep 2026 04:40:14 +0000 Subject: [PATCH 27/65] fmt --- src/tools/miri/src/machine.rs | 2 +- src/tools/miri/src/shims/foreign_items.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index fa025f839057f..65eb6e10d4518 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -14,11 +14,11 @@ use rustc_abi::{Align, ExternAbi, Size}; use rustc_apfloat::{Float, FloatConvert}; use rustc_ast::Mutability; use rustc_ast::expand::allocator::{self, SpecialAllocatorMethod}; +use rustc_attr_ir::{InlineAttr, Linkage}; use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; -use rustc_attr_ir::{InlineAttr, Linkage}; use rustc_hir::def::DefKind; use rustc_log::tracing; use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index f9ca42dfa0e1f..fab412e6d92a8 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -4,8 +4,8 @@ use std::path::Path; use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE; -use rustc_data_structures::either::Either; use rustc_attr_ir::Linkage; +use rustc_data_structures::either::Either; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; From 42f663b933c0e077669f8a586886be327d4f662b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 28 Sep 2026 11:07:30 +0200 Subject: [PATCH 28/65] Prepare for merging from rust-lang/rust This updates the rust-toolchain.toml file to nightly-2026-09-28 (d080e7dff1b0fc54541545252818f8cccf995d05). --- compiler/rustc_codegen_cranelift/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index 84d28e7f293e0..9e21c2ed9a9b2 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-23" +channel = "nightly-2026-09-28" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From f9d83558b951201bbf1f7da20dd3a8b3f919890c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 28 Sep 2026 11:21:50 +0200 Subject: [PATCH 29/65] Simplify rustup.sh a bit --- compiler/rustc_codegen_cranelift/scripts/rustup.sh | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index 991524a1be912..80e9c4f78c4a6 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -2,22 +2,19 @@ set -e -TOOLCHAIN=${TOOLCHAIN:-$(date +%Y-%m-%d)} - export GIT_CONFIG_GLOBAL=../rust/josh.gitconfig export RUSTC_GIT=../rust case $1 in "prepare") - echo "=> Installing new nightly" - rustup toolchain install --profile minimal "nightly-${TOOLCHAIN}" # Sanity check to see if the nightly exists - sed -i "s/\"nightly-.*\"/\"nightly-${TOOLCHAIN}\"/" rust-toolchain.toml - echo "=> Uninstalling all old nightlies" - for nightly in $(rustup toolchain list | grep nightly | grep -v "$TOOLCHAIN" | grep -v nightly-x86_64); do + for nightly in $(RUSTUP_AUTO_INSTALL=0 rustup toolchain list | grep nightly | grep -v nightly-x86_64); do rustup toolchain uninstall "$nightly" done + echo "=> Installing new nightly" + rustup toolchain install + ./clean_all.sh ./y.sh prepare From b9e7e711bc293c5757150cf6d32fd1d8015fd391 Mon Sep 17 00:00:00 2001 From: eleocraft Date: Mon, 28 Sep 2026 14:02:16 +0200 Subject: [PATCH 30/65] nonzero doc: replacing tabs with spaces --- library/core/src/num/nonzero.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index d7b451c53f059..cdfda32bad43a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -2470,23 +2470,23 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[doc = concat!("let limit = NonZero::<", stringify!($Uint), ">::new(100).unwrap();")] /// /// assert_eq!( - #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit),")] - #[doc = "\tNonZero::new(100).unwrap(),"] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(100).unwrap(),")] /// ); /// /// assert_eq!( - #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit),")] - #[doc = "\tNonZero::new(-100).unwrap(),"] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-100).unwrap(),")] /// ); /// /// assert_eq!( - #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit),")] - #[doc = "\tNonZero::new(80).unwrap(),"] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap(),")] /// ); /// /// assert_eq!( - #[doc = concat!("\tNonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit),")] - #[doc = "\tNonZero::new(-80).unwrap(),"] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap(),")] /// ); /// ``` #[inline] From d10b01c846c0dc222d9fcf36efdb2cc0fc0c2d9f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 29 Sep 2026 13:45:01 +1000 Subject: [PATCH 31/65] Use `{IntTy,UintTy}::name` in `Ty::primitive_symbol` --- compiler/rustc_middle/src/ty/sty.rs | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 243ad73e3dfb8..925915c88cdb4 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2115,28 +2115,9 @@ impl<'tcx> Ty<'tcx> { match self.kind() { ty::Bool => Some(sym::bool), ty::Char => Some(sym::char), - ty::Float(f) => match f { - ty::FloatTy::F16 => Some(sym::f16), - ty::FloatTy::F32 => Some(sym::f32), - ty::FloatTy::F64 => Some(sym::f64), - ty::FloatTy::F128 => Some(sym::f128), - }, - ty::Int(f) => match f { - ty::IntTy::Isize => Some(sym::isize), - ty::IntTy::I8 => Some(sym::i8), - ty::IntTy::I16 => Some(sym::i16), - ty::IntTy::I32 => Some(sym::i32), - ty::IntTy::I64 => Some(sym::i64), - ty::IntTy::I128 => Some(sym::i128), - }, - ty::Uint(f) => match f { - ty::UintTy::Usize => Some(sym::usize), - ty::UintTy::U8 => Some(sym::u8), - ty::UintTy::U16 => Some(sym::u16), - ty::UintTy::U32 => Some(sym::u32), - ty::UintTy::U64 => Some(sym::u64), - ty::UintTy::U128 => Some(sym::u128), - }, + ty::Int(i) => Some(i.name()), + ty::Uint(u) => Some(u.name()), + ty::Float(f) => Some(f.name()), ty::Str => Some(sym::str), _ => None, } From 935c565a18c3768389877b116000baad08649dd8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 27 Jul 2026 16:38:02 +1000 Subject: [PATCH 32/65] Factor out repeated `get_normal_item` calls --- .../rustc_attr_parsing/src/attributes/cfg.rs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 8102208ec519d..d244e6c59e5f9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -306,7 +306,8 @@ pub fn parse_cfg_attr( features: Option<&Features>, lint_node_id: ast::NodeId, ) -> Option<(CfgEntry, Vec<(WithTokens, Span)>)> { - match &cfg_attr.get_normal_item().args { + let item = cfg_attr.get_normal_item(); + match &item.args { ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => { check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim); match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| { @@ -316,11 +317,11 @@ pub fn parse_cfg_attr( Err(e) => { let suggestions = CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ); e.with_span_suggestions( - cfg_attr.get_normal_item().span, + item.span, "must be of the form", suggestions, Applicability::HasPlaceholders, @@ -334,25 +335,24 @@ pub fn parse_cfg_attr( } } _ => { - let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = - cfg_attr.get_normal_item().args - { - (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) - } else { - (cfg_attr.get_normal_item().span, AttributeParseErrorReason::ExpectedList) - }; + let (span, reason) = + if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = item.args { + (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) + } else { + (item.span, AttributeParseErrorReason::ExpectedList) + }; sess.dcx().emit_err(AttributeParseError { span, - inner_span: cfg_attr.get_normal_item().span, + inner_span: item.span, template: CFG_ATTR_TEMPLATE, - path: AttrPath::from_ast(&cfg_attr.get_normal_item().path, identity), + path: AttrPath::from_ast(&item.path, identity), description: ParsedDescription::Attribute, reason, suggestions: diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ), ), @@ -389,13 +389,14 @@ fn parse_cfg_attr_internal<'a>( )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); + let item = attribute.get_normal_item(); let cfg_predicate = AttributeParser::parse_single_args( sess, attribute.span, - attribute.get_normal_item().span, + item.span, attribute.style, AttrPath { segments: attribute.path().into_boxed_slice(), span: attribute.span }, - Some(attribute.get_normal_item().unsafety), + Some(item.unsafety), AttributeSafety::Normal, ParsedDescription::Attribute, pred_span, From bc7618a8dab2415ec18057b4243fba6f3c665ee1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 27 Jul 2026 19:03:15 +1000 Subject: [PATCH 33/65] Add test case for an invalid attribute with nested `cfg_attr` Prior to #159633, the compiler made an invalid suggestion: ``` help: must be of the form | 1 - #[cfg_attr(true, cfg_attr(true, 3))] 1 + #[cfg_attr(true, #[cfg_attr(predicate, attr1, attr2, ...)])] ``` Now it is correct: ``` help: must be of the form | 1 - #[cfg_attr(true, cfg_attr(true, 3))] 1 + #[cfg_attr(true, cfg_attr(predicate, attr1, attr2, ...))] ``` But there was no test for it. This commit adds one. --- .../cfg_attr-attr-syntax-validation.rs | 3 +++ .../cfg_attr-attr-syntax-validation.stderr | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs index 907e1b966766f..508b449358964 100644 --- a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs +++ b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs @@ -46,6 +46,9 @@ struct S12; //~| WARN previously accepted struct S13; +#[cfg_attr(true, cfg_attr(true, 3))] //~ ERROR expected identifier, found `3` +struct S14; + #[cfg_attr(true, inline())] //~ ERROR malformed `inline` attribute input fn f1() {} diff --git a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr index e901460a4eb88..048affd1c4191 100644 --- a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr @@ -120,6 +120,19 @@ LL - #[cfg_attr(true)] LL + #[cfg_attr(predicate, attr1, attr2, ...)] | +error: expected identifier, found `3` + --> $DIR/cfg_attr-attr-syntax-validation.rs:49:33 + | +LL | #[cfg_attr(true, cfg_attr(true, 3))] + | ^ expected identifier + | + = note: for more information, visit +help: must be of the form + | +LL - #[cfg_attr(true, cfg_attr(true, 3))] +LL + #[cfg_attr(true, cfg_attr(predicate, attr1, attr2, ...))] + | + error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable --> $DIR/cfg_attr-attr-syntax-validation.rs:30:30 | @@ -157,7 +170,7 @@ LL | #[cfg_attr(true, link_section = "name")] | ++++++++ error[E0805]: malformed `inline` attribute input - --> $DIR/cfg_attr-attr-syntax-validation.rs:49:18 + --> $DIR/cfg_attr-attr-syntax-validation.rs:52:18 | LL | #[cfg_attr(true, inline())] | ^^^^^^-- @@ -185,7 +198,7 @@ LL | #[cfg_attr(true, link_section)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: requested on the command line with `-W unused-attributes` -error: aborting due to 13 previous errors; 1 warning emitted +error: aborting due to 14 previous errors; 1 warning emitted Some errors have detailed explanations: E0539, E0565, E0805. For more information about an error, try `rustc --explain E0539`. From 7af2cce3b04ebe3cb42807d7e75520888021aefc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 29 Sep 2026 14:25:40 +1000 Subject: [PATCH 34/65] Remove unused `rustc_middle::ty::cast::IntTy::is_signed` --- compiler/rustc_middle/src/ty/cast.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 25fc0775b1a32..e53f53d5e5648 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -17,12 +17,6 @@ pub enum IntTy { Char, } -impl IntTy { - pub fn is_signed(self) -> bool { - matches!(self, Self::I) - } -} - // Valid types for the result of a non-coercion cast #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CastTy<'tcx> { From a4cd051442bb2ac4e5b79a7cbb01300688365d92 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 29 Sep 2026 04:36:15 +0000 Subject: [PATCH 35/65] Prepare for merging from rust-lang/rust This updates the rust-version file to 5d8937173e7c40406c167f4e94737da8925333ad. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ddf759b5d0c48..028e73378b28b 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -22697761c6a01e8d12adcd91e4a5bc755596767a +5d8937173e7c40406c167f4e94737da8925333ad From 52e0abb6befe780561dcbea6c1d444b1d3288caa Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 29 Sep 2026 04:39:43 +0000 Subject: [PATCH 36/65] fmt --- src/tools/miri/src/shims/foreign_items.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index e7e283b757036..66d65ead7c7bd 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -170,8 +170,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // picked by the linker. // Make sure we are consistent wrt what is 'first' and 'second'. - let original_span = - tcx.def_span(original.instance.def_id()); + let original_span = tcx.def_span(original.instance.def_id()); let span = tcx.def_span(def_id); if original_span.lo_hi() < span.lo_hi() { throw_machine_stop!( From 3e5b244994234f4144b3c11fa590a1e1b2834f18 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 28 Jul 2026 11:20:23 +1000 Subject: [PATCH 37/65] Mark duplicate attr messages as not machine applicable This commit adds a new test for some uncovered cases. The thing to note here is the varying correctness of the `help:` lines. This is correct: ``` LL | #[inline] | ^^^^^^^^^ help: remove this attribute ``` The following two are half correct. They give the right idea but if applied literally they will produce syntax that triggers either a warning (the former) or an error (the latter). ``` LL | #[cfg_attr(true, inline)] | ^^^^^^ help: remove this attribute LL | #[cfg_attr(true, inline, deprecated)] | ^^^^^^ help: remove this attribute ``` For this reason, this commit also marks these help messages as not machine-applicable. This means we lose the machine applicability on some cases where it's correct, but it seems better to be conservative. --- .../rustc_attr_parsing/src/diagnostics.rs | 4 +- .../cfg-attr-duplicate-attrs.rs | 34 ++++++++ .../cfg-attr-duplicate-attrs.stderr | 78 +++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs create mode 100644 tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index fb4a32d864fbe..a43cd918c8f35 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -1235,7 +1235,7 @@ pub(crate) struct UnknownVersionLiteral { #[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "unspecified")] pub this: Span, #[note("attribute also specified here")] pub other: Span, @@ -2061,7 +2061,7 @@ pub(crate) struct AdditionalCommaSuggestion { #[derive(Diagnostic)] #[diag("unused attribute")] pub(crate) struct UnusedDuplicate { - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "unspecified")] pub this: Span, #[note("attribute also specified here")] pub other: Span, diff --git a/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs new file mode 100644 index 0000000000000..30434569dbe73 --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs @@ -0,0 +1,34 @@ +// Test for handling of duplicated attributes within `cfg_attr`, in particular the suggestions of +// what to remove. + +#[inline] +#[inline] +//~^ WARN unused attribute +//~| WARN this was previously accepted +fn f1() {} + +#[deprecated] +#[deprecated] +//~^ ERROR multiple `deprecated` attributes +fn f2() {} + +#[inline] +#[cfg_attr(true, inline)] +//~^ WARN unused attribute +//~| WARN this was previously accepted +fn f3() {} + +#[deprecated] +#[cfg_attr(true, deprecated)] +//~^ ERROR multiple `deprecated` attributes +fn f4() {} + +#[inline] +#[deprecated] +#[cfg_attr(true, inline, deprecated)] +//~^ WARN unused attribute +//~| WARN this was previously accepted +//~| ERROR multiple `deprecated` attributes +fn f5() {} + +fn main() {} diff --git a/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr new file mode 100644 index 0000000000000..c30bc26b22ce7 --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr @@ -0,0 +1,78 @@ +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:11:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:10:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:22:18 + | +LL | #[cfg_attr(true, deprecated)] + | ^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:21:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:28:26 + | +LL | #[cfg_attr(true, inline, deprecated)] + | ^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:27:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:5:1 + | +LL | #[inline] + | ^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:4:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: requested on the command line with `-W unused-attributes` + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:16:18 + | +LL | #[cfg_attr(true, inline)] + | ^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:15:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:28:18 + | +LL | #[cfg_attr(true, inline, deprecated)] + | ^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:26:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 3 previous errors; 3 warnings emitted + From da3a40895ea4fe8c4efc444be02d4270a7d144e6 Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Tue, 29 Sep 2026 10:16:10 +0000 Subject: [PATCH 38/65] add relnotes 1.99.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Asger Hautop Drewsen Co-authored-by: Boxy Co-authored-by: Mark Rousskov Co-authored-by: 许杰友 Jieyou Xu (Joe) <39484203+jieyouxu@users.noreply.github.com> --- RELEASES.md | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 34984a29eff51..77b58ce2bcf25 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,133 @@ +Version 1.99.0 (2026-10-01) +========================== + + + +Language +-------- +- [Add allow-by-default `raw_borrows_via_references` lint that checks for references that decay immediately into raw borrows](https://github.com/rust-lang/rust/pull/138230) +- [Extend `unconditional_panic` lint to function calls that panic when the chunks/windows size is zero](https://github.com/rust-lang/rust/pull/153563) +- [Stabilize C-variadic function definitions](https://github.com/rust-lang/rust/pull/155697) +- [Stabilize the ability to use `#[unsafe(naked)]` functions to define C-variadic functions (`#![feature(c_variadic_naked_functions)]`).](https://github.com/rust-lang/rust/pull/159746) +- [Trait methods are now resolved on an adjusted never type (producing a FCW)](https://github.com/rust-lang/rust/pull/156047) +- [Coerce from inference variables to trait objects if the inference variable is related via subtyping to a type that is known to be `Sized`](https://github.com/rust-lang/rust/pull/157820) +- [Stabilize `#[my_macro] mod foo;`](https://github.com/rust-lang/rust/pull/157857). This allows outlined modules (`mod foo;`) anywhere in the body of a custom attribute or derive macro. +- [Fix the `overflowing_literals` lint with repeated negation](https://github.com/rust-lang/rust/pull/158302). For instance, it will now no longer lint on `--128_i8`, which is already detected by the `arithmetic_overflow` lint. +- [Add POSIX symbols to the `invalid_runtime_symbol_definitions` and `suspicious_runtime_symbol_definitions` lints](https://github.com/rust-lang/rust/pull/158522) +- [Lint unused `#[path]` attributes on inline modules](https://github.com/rust-lang/rust/pull/158835) +- [Enable `unreachable_cfg_select_predicates` lint as part of `unused` lint group](https://github.com/rust-lang/rust/pull/159179) +- [Stabilize passing 128-bit integers via vector registers with `asm!` on x86](https://github.com/rust-lang/rust/pull/159525) +- [Explicitly document that some allocations are allowed to grow in-place (but none are allowed to shrink)](https://github.com/rust-lang/rust/pull/159729) +- We now [guarantee](https://github.com/rust-lang/rust/pull/159730) that the contents of an `UnsafeCell` can be accessed without going through `get` + - [The `invalid_reference_casting` lint was adjusted accordingly](https://github.com/rust-lang/rust/pull/159960) +- [Account for globally enabled target features in `global_asm!`](https://github.com/rust-lang/rust/pull/160594) +- [Warn if an invalid `doc` attribute is used on a macro invocation](https://github.com/rust-lang/rust/pull/161003) + + + + +Compiler +-------- +- [Convert `-Ctarget-cpu` into a target-modifier for AVR, AMDGCN and NVPTX](https://github.com/rust-lang/rust/pull/150732) +- [Enable `static_position_independent_executables` on all gnu targets](https://github.com/rust-lang/rust/pull/158510) +- When providing a suggestion about a missing method, rustc now prefers an exactly matching name from a [doc alias attribute](https://doc.rust-lang.org/rustdoc/advanced-features.html#add-aliases-for-an-item-in-documentation-search) over a similarity search from other method names. If your new users sometimes expect a method under a different name, adding a doc alias will now help them find it via rustc suggestions, in addition to helping them find it via rustdoc search: [When suggesting method names, prefer *exact* doc aliases over similar names](https://github.com/rust-lang/rust/pull/160369) + + + + +Platform Support +---------------- +- [Promote `riscv64-unknown-linux-musl` to Tier 2 with host tools](https://github.com/rust-lang/rust/pull/158766) + + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + +Libraries +--------- +- Iteration on `RangeInclusive` (`a..=b` ranges) is now [optimized better in some circumstances](https://github.com/rust-lang/rust/pull/155114). As a side effect of this, the behavior of `RangeInclusive` values that has already been exhausted (as an iterator) has changed. For example, the return values of `start()` and `end()` on such ranges may return different values, and using such ranges as slice indexes may have different behavior. These behaviors were not guaranteed to be stable, so these changes are considered to not be breaking changes. +- [Relax `transmute_copy` to accept `?Sized` types](https://github.com/rust-lang/rust/pull/155989) +- [Update `transmute_copy` to use a non-unwinding panic](https://github.com/rust-lang/rust/pull/155989) +- [Don't escape U+FF9E and U+FF9F in `escape_debug_ext`](https://github.com/rust-lang/rust/pull/158057) +- [Re-export `core::fmt::NumBuffer` in `alloc` (and `std`)](https://github.com/rust-lang/rust/pull/161430) + + + + +Stabilized APIs +--------------- + +- [`IntoIterator` for `Box<[T; N]>`](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html#impl-IntoIterator-for-Box%3C%5BT;+N%5D,+A%3E) +- [`IntoIterator` for `&Box<[T; N]>`](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html#impl-IntoIterator-for-%26Box%3C%5BT;+N%5D,+A%3E) +- [`IntoIterator` for `&mut Box<[T; N]>`](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html#impl-IntoIterator-for-%26mut+Box%3C%5BT;+N%5D,+A%3E) +- [`VecDeque::retain_back`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.retain_back) +- [`core::ffi::VaList`](https://doc.rust-lang.org/stable/core/ffi/struct.VaList.html) +- [`Box::into_non_null`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.into_non_null) +- [`Box::from_non_null`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.from_non_null) +- [`Vec::into_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.into_parts) +- [`Vec::from_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_parts) +- [`core::mem::size_of_val_raw`](https://doc.rust-lang.org/stable/core/mem/fn.size_of_val_raw.html) +- [`core::mem::align_of_val_raw`](https://doc.rust-lang.org/stable/core/mem/fn.align_of_val_raw.html) +- [`core::alloc::Layout::for_value_raw`](https://doc.rust-lang.org/stable/core/alloc/struct.Layout.html#method.for_value_raw) +- [`String::from_utf8_lossy_owned`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.from_utf8_lossy_owned) +- [`string::FromUtf8Error::into_utf8_lossy`](https://doc.rust-lang.org/stable/std/string/struct.FromUtf8Error.html#method.into_utf8_lossy) +- [`FusedIterator for StepBy`](https://doc.rust-lang.org/stable/std/iter/struct.StepBy.html#impl-FusedIterator-for-StepBy%3CI%3E) +- [`std::fs::set_times`](https://doc.rust-lang.org/stable/std/fs/fn.set_times.html) +- [`std::fs::set_times_nofollow`](https://doc.rust-lang.org/stable/std/fs/fn.set_times_nofollow.html) + + + +Cargo +----- +- Add a new built-in profile `debug`. This is a preparation for transitioning the `dev` profile away from debugging to give a saner default for faster development iterations. Currently there is no difference between `dev` and `debug` profiles. [docs](https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#debug-1) [#17214](https://github.com/rust-lang/cargo/pull/17214) +- Workspace members on edition 2024 or later can now override an inherited workspace dependency's `default-features` field. For example, `serde = { workspace = true, default-features = false }` now turns off default features even when the workspace definition enables them. On earlier editions, `default-features = false` is ignored with a warning. ([RFC 3945](https://github.com/rust-lang/rfcs/pull/3945)) [#17126](https://github.com/rust-lang/cargo/pull/17126) + +See also the [full Cargo changelog](https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-199-2026-10-01) + + + +Rustdoc +----- +- [Add new `unused_footnote_definition` rustdoc lint](https://github.com/rust-lang/rust/pull/137858) +- Smarter filtering of trait impls yields performance improvements of 20% on average and up to 40% on some real-world crates. ([1](https://github.com/rust-lang/rust/pull/159623), [2](https://github.com/rust-lang/rust/pull/159721), [3](https://github.com/rust-lang/rust/pull/159779), [4](https://github.com/rust-lang/rust/pull/159854), [5](https://github.com/rust-lang/rust/pull/159091)) + + + +Compatibility Notes +------------------- +- [Fully deprecate the legacy integral modules](https://github.com/rust-lang/rust/pull/146882). For example, `std::i32::MAX` should be accessed via `i32::MAX` instead. +- [Upgrade `no_mangle_generic_items` into hard error](https://github.com/rust-lang/rust/pull/154585) +- [The `Pin::new_unchecked` has had its safety invariants changed slightly](https://github.com/rust-lang/rust/pull/156935) +- [Do not promote references to extern statics](https://github.com/rust-lang/rust/pull/157641) +- [Ensure that the inferred types of `let` patterns typecheck](https://github.com/rust-lang/rust/pull/157841) +- [hermit/fs: Return `unsupported()` instead of `from_raw_os_error(22)`](https://github.com/rust-lang/rust/pull/158247) +- [Fixed a bug where `#[repr(simd)]` was accidentally allowed on macro invocations on stable Rust](https://github.com/rust-lang/rust/pull/158523) +- [Abort const-eval when there are generics in the type of the value being produced](https://github.com/rust-lang/rust/pull/159504) +- [Attributes not applying to anything are now an error in code blocks in doc comments](https://github.com/rust-lang/rust/pull/159849) +- [`Box::leak`: tell people to avoid unleaking](https://github.com/rust-lang/rust/pull/160323) +- [PowerPC inline ASM: Fix scalar floats being in the wrong vector lane on little endian](https://github.com/rust-lang/rust/pull/160441) +- [Do not take `doc(cfg())` into account when filtering doctests](https://github.com/rust-lang/rust/pull/159014) +- [Infer anonymous lifetimes in the types of associated consts as `'static`](https://github.com/rust-lang/rust/pull/156508) +- Macros that expand to a semicolon now produce a warning lint (`semicolon_in_expressions_from_non_local_macros`) even when the macro comes from another crate. Previously, such warnings only appeared for macros from the same crate, to avoid showing warnings that can't be fixed locally; however, this masked problems, as integration tests from the crate providing the macro get compiled as a separate crate, so tests often wouldn't reveal this issue. If you encounter a lint like this, please make sure to report it to the crate providing the macro so they can fix it; don't just silence it in your own crate. + - [`semicolon_in_expressions_from_macros`: Lint on non-local macros too](https://github.com/rust-lang/rust/pull/159222) + - [Split non-local `semicolon_in_expressions_from_macros` into a separate lint](https://github.com/rust-lang/rust/pull/159700) +- In Cargo, incremental compilation is now disabled by default when running in CI. CI is detected via the `CI` environment variable. [#17220](https://github.com/rust-lang/cargo/pull/17220) + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Update to LLVM 23](https://github.com/rust-lang/rust/pull/158734) + Version 1.98.1 (2026-09-03) =========================== From bc0240cad290c98c0ee608b08754cf61c7069c9e Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 28 Sep 2026 13:21:26 -0400 Subject: [PATCH 39/65] Don't use the metadata based crate_hash for rustdoc runs --- compiler/rustc_middle/src/ty/context.rs | 2 +- tests/run-make/metrics-dir-crate-hash/lib.rs | 4 ++ tests/run-make/metrics-dir-crate-hash/main.rs | 6 +++ .../run-make/metrics-dir-crate-hash/rmake.rs | 51 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/metrics-dir-crate-hash/lib.rs create mode 100644 tests/run-make/metrics-dir-crate-hash/main.rs create mode 100644 tests/run-make/metrics-dir-crate-hash/rmake.rs diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5cd7322ced683..8da0603715ff9 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1135,7 +1135,7 @@ impl<'tcx> TyCtxt<'tcx> { | CrateType::Cdylib | CrateType::Sdylib => false, CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, - }) + }) && !self.sess.opts.actually_rustdoc } pub fn needs_hir_hash(self) -> bool { diff --git a/tests/run-make/metrics-dir-crate-hash/lib.rs b/tests/run-make/metrics-dir-crate-hash/lib.rs new file mode 100644 index 0000000000000..e0673f64605f5 --- /dev/null +++ b/tests/run-make/metrics-dir-crate-hash/lib.rs @@ -0,0 +1,4 @@ +#![feature(ascii_char)] // random lib feature +#![feature(test_unstable_lint)] // random lang feature + +pub fn documented() {} diff --git a/tests/run-make/metrics-dir-crate-hash/main.rs b/tests/run-make/metrics-dir-crate-hash/main.rs new file mode 100644 index 0000000000000..844b362c9f179 --- /dev/null +++ b/tests/run-make/metrics-dir-crate-hash/main.rs @@ -0,0 +1,6 @@ +#![feature(ascii_char)] // random lib feature +#![feature(test_unstable_lint)] // random lang feature + +fn main() { + println!("foobar"); +} diff --git a/tests/run-make/metrics-dir-crate-hash/rmake.rs b/tests/run-make/metrics-dir-crate-hash/rmake.rs new file mode 100644 index 0000000000000..90e0b16859c04 --- /dev/null +++ b/tests/run-make/metrics-dir-crate-hash/rmake.rs @@ -0,0 +1,51 @@ +// Verifies that when a crate hash is required solely for a metrics dir, that +// crate hash is successfully generated, either by the fallback mechanism or +// metadata generation. +// See https://github.com/rust-lang/rust/issues/163426. + +//@ ignore-cross-compile + +use std::path::{Path, PathBuf}; + +use run_make_support::rfs::create_dir_all; +use run_make_support::{ + cwd, filename_contains, has_extension, run_in_tmpdir, rustc, rustdoc, shallow_find_files, +}; + +fn find_feature_usage_metrics>(dir: P) -> Vec { + shallow_find_files(dir, |path| { + filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") + }) +} + +fn main() { + let metrics_dir = "metrics-rustdoc"; + create_dir_all(&metrics_dir); + rustdoc() + .input("lib.rs") + .out_dir("doc") + .env("RUST_BACKTRACE", "short") + .arg(format!("-Zmetrics-dir={}", metrics_dir)) + .run() + .assert_stderr_not_contains("internal compiler error"); + assert_eq!( + find_feature_usage_metrics(&metrics_dir).len(), + 1, + "rustdoc should dump exactly one metrics file" + ); + + let metrics_dir = "metrics-rustc"; + create_dir_all(&metrics_dir); + rustc() + .input("main.rs") + .crate_type("bin") + .env("RUST_BACKTRACE", "short") + .arg(format!("-Zmetrics-dir={}", metrics_dir)) + .run() + .assert_stderr_not_contains("internal compiler error"); + assert_eq!( + find_feature_usage_metrics(&metrics_dir).len(), + 1, + "rustc should dump exactly one metrics file" + ); +} From 0883f590731023f43c3108aedece437285a5e44e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Sep 2026 16:52:18 +0200 Subject: [PATCH 40/65] x86_64: cleanup `reg_component` --- compiler/rustc_target/src/callconv/x86_64.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 5ab2834807d67..57e997f45d4c5 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -131,17 +131,16 @@ where } fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option { - if *i >= cls.len() { + let Some(Some(class)) = cls.get(*i) else { return None; - } + }; - match cls[*i] { - None => None, - Some(Class::Int) => { + match class { + Class::Int => { *i += 1; Some(if size.bytes() < 8 { Reg { kind: RegKind::Integer, size } } else { Reg::i64() }) } - Some(Class::Sse) => { + Class::Sse => { let vec_len = 1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count(); *i += vec_len; @@ -154,7 +153,7 @@ fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option unreachable!("reg_component: unhandled class {:?}", c), + c => unreachable!("reg_component: unhandled class {:?}", c), } } From 7f5fdabc90944de7d2d38fee3d43f6ad9bbd17db Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 26 Sep 2026 13:54:41 +0200 Subject: [PATCH 41/65] x86 callconv: refactor into separate classify methods --- compiler/rustc_target/src/callconv/x86.rs | 207 ++++++++++++---------- 1 file changed, 112 insertions(+), 95 deletions(-) diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 3476f41e3bc75..6f88706dbf4b7 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -2,7 +2,7 @@ use rustc_abi::{ AddressSpace, Align, BackendRepr, Float, HasDataLayout, Primitive, Reg, RegKind, TyAndLayout, }; -use crate::callconv::{ArgAttribute, FnAbi, PassMode, TyAbiInterface}; +use crate::callconv::{ArgAbi, ArgAttribute, FnAbi, PassMode, TyAbiInterface}; use crate::spec::{HasTargetSpec, RustcAbi}; /// Is this a struct with a single float field? @@ -37,65 +37,136 @@ where } } -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub(crate) enum Flavor { General, FastcallOrVectorcall, } +#[derive(Clone, Copy)] pub(crate) struct X86Options { pub flavor: Flavor, pub regparm: Option, pub reg_struct_return: bool, } -pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) +fn classify_ret<'a, Ty, C>(cx: &C, opts: X86Options, ret: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if !fn_abi.ret.is_ignore() { - if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() { - // Returning a structure. Most often, this will use - // a hidden first argument. On some platforms, though, - // small structs are returned as integers. - // - // Some links: - // https://www.angelcode.com/dev/callconv/callconv.html - // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp - let t = cx.target_spec(); - if let Some(Float::F16) = fn_abi.ret.layout.complex_float(cx) { - // `_Complex _Float16` is returned as `<2 x half>`. - let kind = RegKind::Vector { hint_vector_elem: Primitive::Float(Float::F16) }; - fn_abi.ret.cast_to(Reg { kind, size: fn_abi.ret.layout.size }); - } else if t.abi_return_struct_as_int - || opts.reg_struct_return - || fn_abi.ret.layout.is_complex_number(cx) - { - // According to Clang, everyone but MSVC returns single-element - // float aggregates directly in a floating-point register. - if is_single_fp_element(fn_abi.ret.layout, cx) { - match fn_abi.ret.layout.size.bytes() { - 2 => fn_abi.ret.cast_to(Reg::f16()), - 4 => fn_abi.ret.cast_to(Reg::f32()), - 8 => fn_abi.ret.cast_to(Reg::f64()), - _ => fn_abi.ret.make_indirect(), - } - } else { - match fn_abi.ret.layout.size.bytes() { - 1 => fn_abi.ret.cast_to(Reg::i8()), - 2 => fn_abi.ret.cast_to(Reg::i16()), - 4 => fn_abi.ret.cast_to(Reg::i32()), - 8 => fn_abi.ret.cast_to(Reg::i64()), - _ => fn_abi.ret.make_indirect(), - } + if ret.layout.is_aggregate() && ret.layout.is_sized() { + // Returning a structure. Most often, this will use + // a hidden first argument. On some platforms, though, + // small structs are returned as integers. + // + // Some links: + // https://www.angelcode.com/dev/callconv/callconv.html + // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp + let t = cx.target_spec(); + if let Some(Float::F16) = ret.layout.complex_float(cx) { + // `_Complex _Float16` is returned as `<2 x half>`. + let kind = RegKind::Vector { hint_vector_elem: Primitive::Float(Float::F16) }; + ret.cast_to(Reg { kind, size: ret.layout.size }); + } else if t.abi_return_struct_as_int + || opts.reg_struct_return + || ret.layout.is_complex_number(cx) + { + // According to Clang, everyone but MSVC returns single-element + // float aggregates directly in a floating-point register. + if is_single_fp_element(ret.layout, cx) { + match ret.layout.size.bytes() { + 2 => ret.cast_to(Reg::f16()), + 4 => ret.cast_to(Reg::f32()), + 8 => ret.cast_to(Reg::f64()), + _ => ret.make_indirect(), } } else { - fn_abi.ret.make_indirect(); + match ret.layout.size.bytes() { + 1 => ret.cast_to(Reg::i8()), + 2 => ret.cast_to(Reg::i16()), + 4 => ret.cast_to(Reg::i32()), + 8 => ret.cast_to(Reg::i64()), + _ => ret.make_indirect(), + } } } else { - fn_abi.ret.extend_integer_width_to(32); + ret.make_indirect(); + } + } else { + ret.extend_integer_width_to(32); + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + let t = cx.target_spec(); + let align_4 = Align::from_bytes(4).unwrap(); + let align_16 = Align::from_bytes(16).unwrap(); + + if arg.layout.is_aggregate() { + // We need to compute the alignment of the `byval` argument. The rules can be found in + // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized + // here, they are: + // + // 1. If the natural alignment of the type is <= 4, the alignment is 4. + // + // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment. + // This doesn't matter here because we only pass aggregates via `byval`, not vectors. + // + // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector + // type is 16. + // + // 4. If none of these conditions are true, the alignment is 4. + + fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match layout.backend_repr { + BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false, + BackendRepr::SimdVector { .. } => true, + BackendRepr::Memory { .. } => { + for i in 0..layout.fields.count() { + if contains_vector(cx, layout.field(cx, i)) { + return true; + } + } + false + } + BackendRepr::SimdScalableVector { .. } => { + panic!("scalable vectors are unsupported") + } + } } + + let byval_align = if arg.layout.align.abi < align_4 { + // (1.) + align_4 + } else if t.is_like_darwin && contains_vector(cx, arg.layout) { + // (3.) + align_16 + } else { + // (4.) + align_4 + }; + + arg.pass_by_stack_offset(Some(byval_align)); + } else { + arg.extend_integer_width_to(32); + } +} + +pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, opts, &mut fn_abi.ret); } for arg in fn_abi.args.iter_mut() { @@ -108,61 +179,7 @@ where continue; } - let t = cx.target_spec(); - let align_4 = Align::from_bytes(4).unwrap(); - let align_16 = Align::from_bytes(16).unwrap(); - - if arg.layout.is_aggregate() { - // We need to compute the alignment of the `byval` argument. The rules can be found in - // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized - // here, they are: - // - // 1. If the natural alignment of the type is <= 4, the alignment is 4. - // - // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment. - // This doesn't matter here because we only pass aggregates via `byval`, not vectors. - // - // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector - // type is 16. - // - // 4. If none of these conditions are true, the alignment is 4. - - fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool - where - Ty: TyAbiInterface<'a, C> + Copy, - { - match layout.backend_repr { - BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false, - BackendRepr::SimdVector { .. } => true, - BackendRepr::Memory { .. } => { - for i in 0..layout.fields.count() { - if contains_vector(cx, layout.field(cx, i)) { - return true; - } - } - false - } - BackendRepr::SimdScalableVector { .. } => { - panic!("scalable vectors are unsupported") - } - } - } - - let byval_align = if arg.layout.align.abi < align_4 { - // (1.) - align_4 - } else if t.is_like_darwin && contains_vector(cx, arg.layout) { - // (3.) - align_16 - } else { - // (4.) - align_4 - }; - - arg.pass_by_stack_offset(Some(byval_align)); - } else { - arg.extend_integer_width_to(32); - } + classify_arg(cx, arg); } fill_inregs(cx, fn_abi, opts, false); From 457af24e1eec19ab240ecfe2047fb2b86bb03b71 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 26 Sep 2026 14:04:40 +0200 Subject: [PATCH 42/65] x86 callconv: put regparam into `Flavor::General` --- compiler/rustc_target/src/callconv/mod.rs | 10 +++++----- compiler/rustc_target/src/callconv/x86.rs | 21 ++++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index edc23b6c50b45..487de8f54ee62 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -733,17 +733,17 @@ impl<'a, Ty> FnAbi<'a, Ty> { let spec = cx.target_spec(); match &spec.arch { Arch::X86 => { - let (flavor, regparm) = match abi { + let flavor = match abi { ExternAbi::Fastcall { .. } | ExternAbi::Vectorcall { .. } => { - (x86::Flavor::FastcallOrVectorcall, None) + x86::Flavor::FastcallOrVectorcall } ExternAbi::C { .. } | ExternAbi::Cdecl { .. } | ExternAbi::Stdcall { .. } => { - (x86::Flavor::General, cx.x86_abi_opt().regparm) + x86::Flavor::General { regparam: cx.x86_abi_opt().regparm } } - _ => (x86::Flavor::General, None), + _ => x86::Flavor::General { regparam: None }, }; let reg_struct_return = cx.x86_abi_opt().reg_struct_return; - let opts = x86::X86Options { flavor, regparm, reg_struct_return }; + let opts = x86::X86Options { flavor, reg_struct_return }; if spec.is_like_msvc { x86_win32::compute_abi_info(cx, self, opts); } else { diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 6f88706dbf4b7..efe88bff6504c 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -39,14 +39,13 @@ where #[derive(Clone, Copy, PartialEq)] pub(crate) enum Flavor { - General, + General { regparam: Option }, FastcallOrVectorcall, } #[derive(Clone, Copy)] pub(crate) struct X86Options { pub flavor: Flavor, - pub regparm: Option, pub reg_struct_return: bool, } @@ -193,9 +192,6 @@ pub(crate) fn fill_inregs<'a, Ty, C>( ) where Ty: TyAbiInterface<'a, C> + Copy, { - if opts.flavor != Flavor::FastcallOrVectorcall && opts.regparm.is_none_or(|x| x == 0) { - return; - } // Mark arguments as InReg like clang does it, // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall. @@ -205,8 +201,19 @@ pub(crate) fn fill_inregs<'a, Ty, C>( // IsSoftFloatABI is only set to true on ARM platforms, // which in turn can't be x86? - // 2 for fastcall/vectorcall, regparm limited by 3 otherwise - let mut free_regs = opts.regparm.unwrap_or(2).into(); + // The number of registers available for argument passing. + // + // An `extern "fastcall"` and `extern "vectorcall"` function always have 2 registers available. + // Otherwise the `regparam` count (in the range 0..=3) determines the number of available + // registers. If unspecified, no registers are used for argument passing. + let mut free_regs = match opts.flavor { + Flavor::FastcallOrVectorcall => 2, + Flavor::General { regparam } => u64::from(regparam.unwrap_or(0)), + }; + + if free_regs == 0 { + return; + } // For types generating PassMode::Cast, InRegs will not be set. // Maybe, this is a FIXME From 330b5d11cf9af448ad9542827e287885cc5605c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Tue, 29 Sep 2026 18:40:17 +0200 Subject: [PATCH 43/65] miri: implement again shims for LLVM intrinsics for `_mm_mulhi_epi16` and `_mm_mulhi_epu16` These are being used again (https://github.com/rust-lang/stdarch/pull/2219) due to performance regression of the generic implementation This partially reverts https://github.com/rust-lang/rust/pull/116609 --- src/tools/miri/src/intrinsics/x86/sse2.rs | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/tools/miri/src/intrinsics/x86/sse2.rs b/src/tools/miri/src/intrinsics/x86/sse2.rs index b39bfcaedea59..d9ef809598765 100644 --- a/src/tools/miri/src/intrinsics/x86/sse2.rs +++ b/src/tools/miri/src/intrinsics/x86/sse2.rs @@ -1,4 +1,5 @@ use rustc_apfloat::ieee::Double; +use rustc_middle::mir; use rustc_span::Symbol; use super::{ @@ -272,6 +273,41 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pmaddwd(this, left, right, dest)?; } + // Used to implement the _mm_mulhi_epi16 and _mm_mulhi_epu16 functions. + "pmulh.w" | "pmulhu.w" => { + let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let left = this.read_immediate(&this.project_index(&left, i)?)?; + let right = this.read_immediate(&this.project_index(&right, i)?)?; + let dest = this.project_index(&dest, i)?; + + // Widen the operands to avoid overflow + let twice_wide = this.layout_of(this.get_twice_wide_int_ty(left.layout.ty))?; + let left = this.int_to_int_or_float(&left, twice_wide)?; + let right = this.int_to_int_or_float(&right, twice_wide)?; + + // Multiply + let multiplied = this.binary_op(mir::BinOp::Mul, &left, &right)?; + // Keep the high half + let high = this.binary_op( + mir::BinOp::Shr, + &multiplied, + &ImmTy::from_uint(dest.layout.size.bits(), twice_wide), + )?; + + // Narrow back to the original type + let res = this.int_to_int_or_float(&high, dest.layout)?; + this.write_immediate(*res, &dest)?; + } + } _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) From 14d09db59484630e56870e09a094254890b4b27f Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 29 Sep 2026 18:38:13 +0200 Subject: [PATCH 44/65] cycle handling: mirror old solver --- .../src/solve/eval_ctxt/mod.rs | 87 ++++++++----------- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +- .../src/solve/normalizes_to.rs | 4 +- .../src/solve/project_goals/free_alias.rs | 4 +- .../src/solve/project_goals/inherent.rs | 4 +- .../src/solve/project_goals/mod.rs | 2 +- .../src/solve/project_goals/opaque_types.rs | 7 +- .../src/solve/fulfill/derive_errors.rs | 2 +- compiler/rustc_type_ir/src/solve/mod.rs | 24 ++--- .../sized-hierarchy/overflow.current.stderr | 12 +-- tests/ui/sized-hierarchy/overflow.next.stderr | 29 +++++++ tests/ui/sized-hierarchy/overflow.rs | 7 +- ...inductive-step-needed-trait.current.stderr | 6 +- ...-coinductive-step-needed-trait.next.stderr | 15 ++++ .../only-one-coinductive-step-needed-trait.rs | 4 +- ...one-coinductive-step-needed.current.stderr | 4 +- ...ly-one-coinductive-step-needed.next.stderr | 43 +++++++++ .../only-one-coinductive-step-needed.rs | 11 ++- 18 files changed, 166 insertions(+), 103 deletions(-) create mode 100644 tests/ui/sized-hierarchy/overflow.next.stderr create mode 100644 tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.next.stderr create mode 100644 tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.next.stderr diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c841845392429..40febd26806a6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -63,6 +63,8 @@ enum CurrentGoalKind { /// These are currently the only goals whose impl where-clauses are considered to be /// productive steps. CoinductiveTrait, + /// A `ProjectionGoal`. These are temporarily special wrt to cycle handling. + Projection, // FIXME: Consider renaming `PredicateKind::NormalizesTo` to match with this /// Unlike other goals, `NormalizesTo` goals aren't independent goals but just implementation /// details for handling projections of associated terms. When we encounter a `Projection` goal @@ -89,6 +91,7 @@ impl CurrentGoalKind { CurrentGoalKind::Misc } } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => CurrentGoalKind::Projection, ty::PredicateKind::NormalizesTo(_) => { CurrentGoalKind::ProjectionComputeAssocTermCandidate } @@ -428,46 +431,29 @@ where } /// Computes the `PathKind` for the step from the current goal to the - /// nested goal required due to `source`. + /// nested goal. See #136824 for a more detailed reasoning for this why we care about + /// the step from a goal to its nested goals. /// - /// See #136824 for a more detailed reasoning for this behavior. We - /// consider cycles to be coinductive if they 'step into' a where-clause - /// of a coinductive trait. We will likely extend this function in the future - /// and will need to clearly document it in the rustc-dev-guide before - /// stabilization. - pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind { - match source { - // We treat these goals as unknown for now. It is likely that most miscellaneous - // nested goals will be converted to an inductive variant in the future. - // - // Having unknown cycles is always the safer option, as changing that to either - // succeed or hard error is backwards compatible. If we incorrectly treat a cycle - // as inductive even though it should not be, it may be unsound during coherence and - // fixing it may cause inference breakage or introduce ambiguity. - GoalSource::Misc => PathKind::Unknown, - GoalSource::NormalizeGoal(path_kind) => path_kind, - GoalSource::ImplWhereBound => match self.current_goal_kind { - // We currently only consider a cycle coinductive if it steps - // into a where-clause of a coinductive trait. - CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, - // We probably want to make all traits coinductive in the future, - // so we treat cycles involving where-clauses of not-yet coinductive - // traits as ambiguous for now. - CurrentGoalKind::Misc | CurrentGoalKind::ProjectionComputeAssocTermCandidate => { - PathKind::Unknown - } - }, - // Relating types is always unproductive. If we were to map proof trees to - // corecursive functions as explained in #136824, relating types never - // introduces a constructor which could cause the recursion to be guarded. - // - // FIXME(-Znext-solver=coinductive): For now we treat all inductive cycles as - // `Unknown`. See the comment in `fn initial_provisional_result`. - GoalSource::TypeRelating => PathKind::Unknown, - // These goal sources are likely unproductive and can be changed to - // `PathKind::Inductive`. Keeping them as unknown until we're confident - // about this and have an example where it is necessary. - GoalSource::AliasBoundConstCondition | GoalSource::AliasWellFormed => PathKind::Unknown, + /// For now we're entirely ignoring the reason why the current goal depends on the + /// nested goal and instead only depend on the current goal itself. This closely + /// matches the old solver. This is something we'll likely improve as we go forward + /// afterwards. + // FIXME(-Znext-solver=coinductive): Nothing interesting going on here right now + // and we're ignoring the goal source. We should change this going forward. + pub(super) fn step_kind_to_nested(&self, _source: GoalSource) -> PathKind { + match self.current_goal_kind { + // We currently consider a cycle involving a trait goal for a coinductive + // trait as coinductive as long as it otherwise only includes normalization. + CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, + // We do need to treat cycles involving only coinductive trait goals + // and normalization as coinductive due to trait-system-refactor-initiative#10. + CurrentGoalKind::ProjectionComputeAssocTermCandidate | CurrentGoalKind::Projection => { + PathKind::Unknown + } + // We probably want to make all traits coinductive in the future, + // so we treat cycles involving where-clauses of not-yet coinductive + // traits as ambiguous for now. + CurrentGoalKind::Misc => PathKind::ForcedAmbiguity, } } @@ -771,7 +757,7 @@ where let (goal, opaque_types) = self.delegate.deeply_resolve_via_unification_table((goal, opaque_types)); let typing_mode = self.typing_mode(); - let step_kind = self.step_kind_for_source(source); + let step_kind = self.step_kind_to_nested(source); let tracing_span = tracing::span!( Level::DEBUG, @@ -1109,11 +1095,8 @@ where source: GoalSource, mut goal: Goal, ) -> Result<(), NoSolutionOrRerunNonErased> { - goal.predicate = self.normalize( - GoalSource::NormalizeGoal(self.step_kind_for_source(source)), - goal.param_env, - ty::Unnormalized::new_wip(goal.predicate), - )?; + goal.predicate = + self.normalize(goal.param_env, ty::Unnormalized::new_wip(goal.predicate))?; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); if let Some(GoalEvaluation { goal, certainty, has_changed: _, stalled_on }) = @@ -1339,10 +1322,12 @@ where let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { - ty::PredicateKind::Subtype { .. } - | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { - GoalSource::TypeRelating + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { + GoalSource::Normalization } + // FIXME(-Znext-solver=coinductive): subtyping goals should + // likely be unproductive + ty::PredicateKind::Subtype { .. } => GoalSource::Misc, // FIXME(-Znext-solver=coinductive): should these WF goals also be unproductive? ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc, p => unreachable!("unexpected nested goal in `relate`: {p:?}"), @@ -1519,9 +1504,7 @@ where match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} } - self.delegate.evaluate_const(param_env, alias_const, |ty| { - self.normalize(GoalSource::Misc, param_env, ty) - }) + self.delegate.evaluate_const(param_env, alias_const, |ty| self.normalize(param_env, ty)) } pub(super) fn evaluate_const_and_instantiate_projection_term( @@ -1803,7 +1786,6 @@ where pub(super) fn normalize>( &mut self, - source: GoalSource, param_env: I::ParamEnv, value: ty::Unnormalized, ) -> Result { @@ -1819,6 +1801,7 @@ where let infer_term = self.next_term_infer_of_alias_kind(alias_term); let pred = ty::ProjectionClause { projection_term: alias_term, term: infer_term }; let goal = Goal::new(self.cx(), param_env, pred); + let source = GoalSource::Normalization; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); let GoalEvaluation { goal, certainty, has_changed: _, stalled_on } = self.evaluate_goal(source, goal, None)?; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index dddc78373b1b5..9ecb7619d7c93 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -90,7 +90,7 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let ty::OutlivesClause(ty, lt) = goal.predicate; - let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?; + let ty = self.normalize(goal.param_env, ty::Unnormalized::new_wip(ty))?; // The normalized type can still contain non-rigid higher ranked aliases if their // normalization ends up with ambiguity. Or we have non-rigid aliases inside rigid ones. @@ -405,7 +405,7 @@ where ); // We normalize the self type to be able to relate it with // types from candidates. - self.add_goal(GoalSource::TypeRelating, projection_goal)?; + self.add_goal(GoalSource::Normalization, projection_goal)?; self.try_evaluate_added_goals()?; Ok(self.deeply_resolve_ignoring_regions(normalized_term)) } else { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index 388b334ccf618..4203ae8c509cc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -439,7 +439,7 @@ where let term = match target_item_kind { ty::AliasTermKind::ProjectionTy { .. } => { let t = cx.type_of(target_item_def_id).instantiate(cx, target_args); - let t = ecx.normalize(GoalSource::Misc, goal.param_env, t)?; + let t = ecx.normalize(goal.param_env, t)?; t.into() } ty::AliasTermKind::ProjectionConst { def_id } @@ -447,7 +447,7 @@ where cx.const_of_item(ty::AliasConstKind::Projection { def_id }) => { let c = c.instantiate(cx, target_args); - let c = ecx.normalize(GoalSource::Misc, goal.param_env, c)?; + let c = ecx.normalize(goal.param_env, c)?; c.into() } ty::AliasTermKind::ProjectionConst { .. } => { diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs index 4481e1bc144ac..dfcc89b91a4f2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs @@ -34,14 +34,14 @@ where let actual = match free_alias.kind { ty::AliasTermKind::FreeTy { def_id } => { let free = cx.type_of(def_id.into()).instantiate(cx, free_alias.args); - let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + let free = self.normalize(goal.param_env, free)?; free.into() } ty::AliasTermKind::FreeConst { def_id } if let Some(free) = cx.const_of_item(ty::AliasConstKind::Free { def_id }) => { let free = free.instantiate(cx, free_alias.args); - let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + let free = self.normalize(goal.param_env, free)?; free.into() } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs index d519d1e538f1a..45ece5a0589fb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs @@ -45,7 +45,7 @@ where let normalized: I::Term = match inherent_kind { ty::AliasTermKind::InherentTy { def_id } => { let inherent = cx.type_of(def_id.into()).instantiate(cx, inherent_args); - let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + let inherent = self.normalize(goal.param_env, inherent)?; inherent.into() } ty::AliasTermKind::InherentConstImpl { def_id } @@ -53,7 +53,7 @@ where cx.const_of_item(ty::AliasConstKind::InherentImpl { def_id }) => { let inherent = inherent.instantiate(cx, inherent_args); - let normalized_ct = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + let normalized_ct = self.normalize(goal.param_env, inherent)?; let normalized = normalized_ct.into(); let term = ty::AliasTerm::new_from_args(cx, inherent_kind, inherent_args); self.push_const_arg_has_type_goal(goal.param_env, term, normalized)?; diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index 9d6b8875071ba..f6521e579d722 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -69,7 +69,7 @@ where let ( NestedNormalizationGoals(nested_goals), GoalEvaluation { goal: _, certainty, stalled_on: _, has_changed: _ }, - ) = self.evaluate_goal_raw(GoalSource::TypeRelating, normalizes_to)?; + ) = self.evaluate_goal_raw(GoalSource::Normalization, normalizes_to)?; trace!(?nested_goals); diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs index 2f56056449779..048c8bb45361b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs @@ -99,8 +99,7 @@ where _ => re, }) }); - let actual = - self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; } TypingMode::Coherence @@ -145,7 +144,7 @@ where _ => re, }) }); - let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) @@ -154,7 +153,7 @@ where TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => { // FIXME: Add an assertion that opaque type storage is empty. let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); - let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index d30421a762ea9..5ccd7ff7ef55b 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -510,7 +510,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { match (child_mode, nested_goal.source()) { ( ChildMode::Trait(_) | ChildMode::Host(_), - GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), + GoalSource::Misc | GoalSource::Normalization, ) => { continue; } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index c6d88bb6603de..5d1472d5941f2 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -16,7 +16,6 @@ use tracing::debug; use crate::inherent::*; use crate::lang_items::SolverTraitLangItem; use crate::region_constraint::RegionConstraint; -use crate::search_graph::PathKind; use crate::{ self as ty, Canonical, CanonicalVarValues, CantBeErased, Const, ConstVid, FloatVid, GenericArgKind, InferConst, IntVid, Interner, TermKind, TyVid, TypingMode, Upcast, @@ -411,22 +410,15 @@ impl Goal { /// Why a specific goal has to be proven. /// -/// This is necessary as we treat nested goals different depending on -/// their source. This is used to decide whether a cycle is coinductive. -/// See the documentation of `EvalCtxt::step_kind_for_source` for more details -/// about this. +/// This is used by proof tree visitors, especially for diagnostics purposes. /// -/// It is also used by proof tree visitors, e.g. for diagnostics purposes. +/// FIXME(-Znext-solver=coinductive): This will also matter in the future when +/// deciding whether a step in a cycle is coinductive. We're currently still +/// matching the old solver behavior here for now, so the `GoalSource` is ignored. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(StableHash))] pub enum GoalSource { Misc, - /// A nested goal required to prove that types are equal/subtypes. - /// This is always an unproductive step. - /// - /// This is also used for all `NormalizesTo` goals as we they are used - /// to relate types in `AliasRelate`. - TypeRelating, /// We're proving a where-bound of an impl. ImplWhereBound, /// Const conditions that need to hold for `[const]` alias bounds to hold. @@ -438,12 +430,8 @@ pub enum GoalSource { /// 2. for rigid projections's trait goal, /// 3. for GAT where clauses. AliasWellFormed, - /// In case normalizing aliases in nested goals cycles, eagerly normalizing these - /// aliases in the context of the parent may incorrectly change the cycle kind. - /// Normalizing aliases in goals therefore tracks the original path kind for this - /// nested goal. See the comment of the `ReplaceAliasWithInfer` visitor for more - /// details. - NormalizeGoal(PathKind), + /// Normalizing happens in the current context and is unproductive by itself. + Normalization, } #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, Goal)] diff --git a/tests/ui/sized-hierarchy/overflow.current.stderr b/tests/ui/sized-hierarchy/overflow.current.stderr index 9853be2a7f8e0..3573ee48db57c 100644 --- a/tests/ui/sized-hierarchy/overflow.current.stderr +++ b/tests/ui/sized-hierarchy/overflow.current.stderr @@ -1,11 +1,11 @@ error[E0275]: overflow evaluating the requirement `Element: MetaSized` - --> $DIR/overflow.rs:17:16 + --> $DIR/overflow.rs:15:16 | LL | struct Element(> as ParseTokens>::Output); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required for `Box` to implement `ParseTokens` - --> $DIR/overflow.rs:13:31 + --> $DIR/overflow.rs:11:31 | LL | impl ParseTokens for Box { | ------ ^^^^^^^^^^^ ^^^^^^ @@ -15,25 +15,25 @@ LL | impl ParseTokens for Box { = note: required for `Box>` to implement `ParseTokens` error[E0275]: overflow evaluating the requirement `Box: ParseTokens` - --> $DIR/overflow.rs:19:22 + --> $DIR/overflow.rs:18:22 | LL | impl ParseTokens for Element { | ^^^^^^^ | note: required for `Box>` to implement `ParseTokens` - --> $DIR/overflow.rs:13:31 + --> $DIR/overflow.rs:11:31 | LL | impl ParseTokens for Box { | ----------- ^^^^^^^^^^^ ^^^^^^ | | | unsatisfied trait bound introduced here note: required because it appears within the type `Element` - --> $DIR/overflow.rs:17:8 + --> $DIR/overflow.rs:15:8 | LL | struct Element(> as ParseTokens>::Output); | ^^^^^^^ note: required by a bound in `ParseTokens` - --> $DIR/overflow.rs:10:1 + --> $DIR/overflow.rs:8:1 | LL | / trait ParseTokens { LL | | type Output; diff --git a/tests/ui/sized-hierarchy/overflow.next.stderr b/tests/ui/sized-hierarchy/overflow.next.stderr new file mode 100644 index 0000000000000..69842a298f87f --- /dev/null +++ b/tests/ui/sized-hierarchy/overflow.next.stderr @@ -0,0 +1,29 @@ +error[E0275]: overflow evaluating the requirement `> as ParseTokens>::Output == _` + --> $DIR/overflow.rs:15:16 + | +LL | struct Element(> as ParseTokens>::Output); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating whether `> as ParseTokens>::Output` is well-formed + --> $DIR/overflow.rs:15:16 + | +LL | struct Element(> as ParseTokens>::Output); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `Element: MetaSized` + --> $DIR/overflow.rs:18:22 + | +LL | impl ParseTokens for Element { + | ^^^^^^^ + | +note: required by a bound in `ParseTokens` + --> $DIR/overflow.rs:8:1 + | +LL | / trait ParseTokens { +LL | | type Output; +LL | | } + | |_^ required by this bound in `ParseTokens` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/sized-hierarchy/overflow.rs b/tests/ui/sized-hierarchy/overflow.rs index 31c2ca8a49171..f69f56ef898e2 100644 --- a/tests/ui/sized-hierarchy/overflow.rs +++ b/tests/ui/sized-hierarchy/overflow.rs @@ -1,8 +1,6 @@ //@ compile-flags: --crate-type=lib //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) -//@[current] check-fail -//@[next] check-pass //@[next] compile-flags: -Znext-solver use std::marker::PhantomData; @@ -15,8 +13,9 @@ impl ParseTokens for Box { } struct Element(> as ParseTokens>::Output); -//[current]~^ ERROR: overflow +//~^ ERROR: overflow +//[next]~| ERROR: overflow impl ParseTokens for Element { -//[current]~^ ERROR: overflow +//~^ ERROR: overflow type Output = (); } diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr index 89b8b53c3f4d9..d6cd080cbfd0b 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr @@ -1,18 +1,18 @@ error[E0275]: overflow evaluating the requirement `u32: SendIndir>` - --> $DIR/only-one-coinductive-step-needed-trait.rs:24:5 + --> $DIR/only-one-coinductive-step-needed-trait.rs:25:5 | LL | is_send::>(); | ^^^^^^^^^^^^^^^^^^^^^ | note: required for `Foo` to implement `Send` - --> $DIR/only-one-coinductive-step-needed-trait.rs:17:35 + --> $DIR/only-one-coinductive-step-needed-trait.rs:18:35 | LL | unsafe impl>> Send for Foo {} | ----------------- ^^^^ ^^^^^^ | | | unsatisfied trait bound introduced here note: required by a bound in `is_send` - --> $DIR/only-one-coinductive-step-needed-trait.rs:22:15 + --> $DIR/only-one-coinductive-step-needed-trait.rs:23:15 | LL | fn is_send() {} | ^^^^ required by this bound in `is_send` diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.next.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.next.stderr new file mode 100644 index 0000000000000..40ae37e495204 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.next.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `Foo: Send` + --> $DIR/only-one-coinductive-step-needed-trait.rs:25:15 + | +LL | is_send::>(); + | ^^^^^^^^ + | +note: required by a bound in `is_send` + --> $DIR/only-one-coinductive-step-needed-trait.rs:23:15 + | +LL | fn is_send() {} + | ^^^^ required by this bound in `is_send` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs index 294156869284a..a8d23129fb87c 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs @@ -1,7 +1,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass // #136824 changed cycles to be coinductive if they have at least // one productive step, causing this test to pass with the new solver. @@ -12,6 +11,8 @@ // - `Foo: Send` cycle // // The old solver treats this cycle as inductive due to the `T: SendIndir` step. +// We later changed the new solver to temporarily be closer to the old solver +// again, so this now errors with both solvers. struct Foo(T); unsafe impl>> Send for Foo {} @@ -23,4 +24,5 @@ fn is_send() {} fn main() { is_send::>(); //[current]~^ ERROR overflow evaluating the requirement `u32: SendIndir>` + //[next]~^^ ERROR overflow evaluating the requirement `Foo: Send` } diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr index ec21987805869..1d7916f32b1db 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr @@ -1,11 +1,11 @@ error[E0275]: overflow evaluating the requirement `Foo: SendIndir` - --> $DIR/only-one-coinductive-step-needed.rs:17:15 + --> $DIR/only-one-coinductive-step-needed.rs:18:15 | LL | struct Foo( as Trait>::Assoc); | ^^^^^^^^^^^^^^^^^^^^^^^^ | note: required for `Foo` to implement `Trait` - --> $DIR/only-one-coinductive-step-needed.rs:26:20 + --> $DIR/only-one-coinductive-step-needed.rs:29:20 | LL | impl Trait for T { | --------- ^^^^^ ^ diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.next.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.next.stderr new file mode 100644 index 0000000000000..071945d760345 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.next.stderr @@ -0,0 +1,43 @@ +error[E0275]: overflow evaluating the requirement ` as Trait>::Assoc == _` + --> $DIR/only-one-coinductive-step-needed.rs:18:15 + | +LL | struct Foo( as Trait>::Assoc); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating whether ` as Trait>::Assoc` is well-formed + --> $DIR/only-one-coinductive-step-needed.rs:18:15 + | +LL | struct Foo( as Trait>::Assoc); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `Foo: Send` + --> $DIR/only-one-coinductive-step-needed.rs:36:15 + | +LL | is_send::>(); + | ^^^^^^^^ + | +note: required by a bound in `is_send` + --> $DIR/only-one-coinductive-step-needed.rs:33:15 + | +LL | fn is_send() {} + | ^^^^ required by this bound in `is_send` + +error[E0275]: overflow evaluating the requirement `Foo: Sized` + --> $DIR/only-one-coinductive-step-needed.rs:36:15 + | +LL | is_send::>(); + | ^^^^^^^^ + | +note: required by an implicit `Sized` bound in `is_send` + --> $DIR/only-one-coinductive-step-needed.rs:33:12 + | +LL | fn is_send() {} + | ^ required by the implicit `Sized` requirement on this type parameter in `is_send` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn is_send() {} + | ++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs index e41f7d7f3ceb3..9a13c0e945339 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs @@ -1,7 +1,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass // #136824 changed cycles to be coinductive if they have at least // one productive step, causing this test to pass with the new solver. @@ -12,10 +11,14 @@ // - `Foo: SendIndir`, via impl requires // - `Foo: Send` cycle // -// The old solver treats this cycle as inductive due to the `Foo: SendIndir` step. +// The old solver treats this cycle as inductive due to the `Foo: SendIndir` step. We've +// since changed the new solver to more closely match the old one here, also +// resulting in an inductive cycle for now. struct Foo( as Trait>::Assoc); -//[current]~^ ERROR overflow evaluating the requirement `Foo: SendIndir` +//[current]~^ ERROR: overflow evaluating the requirement `Foo: SendIndir` +//[next]~^^ ERROR: overflow evaluating the requirement ` as Trait>::Assoc == _` +//[next]~| ERROR: overflow evaluating whether ` as Trait>::Assoc` is well-formed trait SendIndir {} impl SendIndir for T {} @@ -31,4 +34,6 @@ fn is_send() {} fn main() { is_send::>(); + //[next]~^ ERROR: overflow evaluating the requirement `Foo: Send` + //[next]~| ERROR: overflow evaluating the requirement `Foo: Sized` } From 36358d0b2a1acaa99008ca13d1e41eac4adb1009 Mon Sep 17 00:00:00 2001 From: eleocraft Date: Tue, 29 Sep 2026 20:20:16 +0200 Subject: [PATCH 45/65] gating f16 and f128 tests behind target_has_reliable --- library/coretests/tests/num/clamp_magnitude.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs index 5bccc4ad45e20..fdb605780a2c6 100644 --- a/library/coretests/tests/num/clamp_magnitude.rs +++ b/library/coretests/tests/num/clamp_magnitude.rs @@ -208,6 +208,7 @@ macro_rules! check_float_clamp { } #[test] +#[cfg(target_has_reliable_f16)] fn test_clamp_magnitude_f16() { check_float_clamp!(f16, 1e3); } @@ -223,11 +224,13 @@ fn test_clamp_magnitude_f64() { } #[test] +#[cfg(target_has_reliable_f128)] fn test_clamp_magnitude_f128() { check_float_clamp!(f128, 1e3000); } #[test] +#[cfg(target_has_reliable_f16)] #[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f16_panic_negative_limit() { let _ = 1.0f16.clamp_magnitude(-1.0); @@ -246,12 +249,14 @@ fn test_clamp_magnitude_f64_panic_negative_limit() { } #[test] +#[cfg(target_has_reliable_f128)] #[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f128_panic_negative_limit() { let _ = 1.0f128.clamp_magnitude(-1.0); } #[test] +#[cfg(target_has_reliable_f16)] #[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f16_panic_nan_limit() { let _ = 1.0f16.clamp_magnitude(f16::NAN); @@ -270,6 +275,7 @@ fn test_clamp_magnitude_f64_panic_nan_limit() { } #[test] +#[cfg(target_has_reliable_f128)] #[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f128_panic_nan_limit() { let _ = 1.0f128.clamp_magnitude(f128::NAN); From 2eeeb39a9785812aa101e75486c88a7f5c4b0619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 29 Sep 2026 22:41:25 +0000 Subject: [PATCH 46/65] Use more default field values in `Resolver` --- compiler/rustc_data_structures/src/fx.rs | 10 ++++- compiler/rustc_middle/src/middle/privacy.rs | 6 +-- compiler/rustc_resolve/src/lib.rs | 44 +++++++-------------- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs index cad775cc98641..76b2c428ec84d 100644 --- a/compiler/rustc_data_structures/src/fx.rs +++ b/compiler/rustc_data_structures/src/fx.rs @@ -28,7 +28,7 @@ macro_rules! define_stable_id_collections { } pub mod default { - use super::{FxBuildHasher, FxHashMap, FxHashSet}; + use super::{FxBuildHasher, FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; // FIXME: These two functions will become unnecessary after // lands and we start using the corresponding @@ -40,4 +40,12 @@ pub mod default { pub const fn fx_hash_set() -> FxHashSet { FxHashSet::with_hasher(FxBuildHasher) } + + pub const fn fx_index_map() -> FxIndexMap { + FxIndexMap::with_hasher(FxBuildHasher) + } + + pub const fn fx_index_set() -> FxIndexSet { + FxIndexSet::with_hasher(FxBuildHasher) + } } diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 9685ac89011e5..3a008514f7d07 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -5,7 +5,7 @@ use std::cmp::Ordering; use std::hash::Hash; -use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry, default}; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_hir::def::DefKind; use rustc_hir::{ItemKind, Node, UseKind, UseTree}; @@ -277,9 +277,9 @@ impl EffectiveVisibilities { } } -impl Default for EffectiveVisibilities { +const impl Default for EffectiveVisibilities { fn default() -> Self { - EffectiveVisibilities { map: Default::default() } + EffectiveVisibilities { map: default::fx_index_map() } } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 772fdb8c9f14d..fc151de0decf9 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1417,11 +1417,11 @@ pub struct Resolver<'ra, 'tcx> { extern_module_map: RwLock>>, /// Maps glob imports to the names of items actually imported. - glob_map: FxIndexMap>, + glob_map: FxIndexMap> = default::fx_index_map(), glob_error: Option = None, visibilities_for_hashing: Vec<(LocalDefId, Visibility)> = Vec::new(), used_imports: FxHashSet = default::fx_hash_set(), - maybe_unused_trait_imports: FxIndexSet, + maybe_unused_trait_imports: FxIndexSet = default::fx_index_set(), /// Privacy errors are delayed until the end in order to deduplicate them. privacy_errors: Vec> = Vec::new(), @@ -1442,7 +1442,7 @@ pub struct Resolver<'ra, 'tcx> { builtin_macros: FxHashMap = default::fx_hash_map(), registered_attr_tools: &'tcx RegisteredTools, registered_lint_tools: &'tcx RegisteredTools, - macro_use_prelude: FxIndexMap>, + macro_use_prelude: FxIndexMap> = default::fx_index_map(), /// Eagerly populated map of all local macro definitions. local_macro_map: FxHashMap> = default::fx_hash_map(), /// Lazily populated cache of macro definitions loaded from external crates. @@ -1452,9 +1452,9 @@ pub struct Resolver<'ra, 'tcx> { non_macro_attr: &'ra Arc, local_macro_def_scopes: FxHashMap> = default::fx_hash_map(), ast_transform_scopes: FxHashMap> = default::fx_hash_map(), - unused_macros: FxIndexMap, + unused_macros: FxIndexMap = default::fx_index_map(), /// A map from the macro to all its potentially unused arms and the `LocalDefId` of the macro itself. - unused_macro_rules: FxIndexMap)>, + unused_macro_rules: FxIndexMap)> = default::fx_index_map(), proc_macro_stubs: FxHashSet = default::fx_hash_set(), /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -1521,25 +1521,25 @@ pub struct Resolver<'ra, 'tcx> { /// Generic args to suggest for required params (e.g. `<'_>`, `<_, _>`), if any. item_required_generic_args_suggestions: FxHashMap = default::fx_hash_map(), delegation_fn_sigs: LocalDefIdMap = Default::default(), - delegation_infos: FxIndexMap, - delegation_inherent_fn_map: FxIndexMap>, + delegation_infos: FxIndexMap = default::fx_index_map(), + delegation_inherent_fn_map: FxIndexMap> = default::fx_index_map(), main_def: Option = None, - trait_impls: FxIndexMap>, + trait_impls: FxIndexMap> = default::fx_index_map(), /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec = Vec::new(), - paths_matching_assoc_types: UnordSet, - confused_type_with_std_module: FxIndexMap, + paths_matching_assoc_types: UnordSet = Default::default(), + confused_type_with_std_module: FxIndexMap = default::fx_index_map(), /// Names of items that were stripped out via cfg with their corresponding cfg meta item. stripped_cfg_items: Vec> = Vec::new(), - effective_visibilities: EffectiveVisibilities, - macro_reachable_adts: FxIndexMap>, + effective_visibilities: EffectiveVisibilities = Default::default(), + macro_reachable_adts: FxIndexMap> = default::fx_index_map(), - doc_link_resolutions: FxIndexMap, - doc_link_traits_in_scope: FxIndexMap>, + doc_link_resolutions: FxIndexMap = default::fx_index_map(), + doc_link_traits_in_scope: FxIndexMap> = default::fx_index_map(), all_macro_rules: UnordSet = Default::default(), /// Invocation ids of all glob delegations. @@ -1852,9 +1852,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { local_module_map, extern_module_map: Default::default(), - glob_map: Default::default(), - maybe_unused_trait_imports: Default::default(), - arenas, dummy_decl: arenas.new_pub_def_decl(Res::Err, DUMMY_SP, LocalExpnId::ROOT), builtin_type_decls: PrimTy::ALL @@ -1883,31 +1880,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect(), registered_attr_tools, registered_lint_tools, - macro_use_prelude: Default::default(), extern_macro_map: Default::default(), dummy_ext_bang: arenas.alloc_macro(SyntaxExtension::dummy_bang(edition)), dummy_ext_derive: arenas.alloc_macro(SyntaxExtension::dummy_derive(edition)), non_macro_attr: arenas.alloc_macro(SyntaxExtension::non_macro_attr(edition)), - unused_macros: Default::default(), - unused_macro_rules: Default::default(), single_segment_macro_resolutions: Default::default(), multi_segment_macro_resolutions: Default::default(), lint_buffer: LintBuffer::default(), owners, current_owner: PerOwnerResolverData::new(DUMMY_NODE_ID, CRATE_DEF_ID), invocation_parents, - trait_impls: Default::default(), - confused_type_with_std_module: Default::default(), - paths_matching_assoc_types: Default::default(), - stripped_cfg_items: Default::default(), - effective_visibilities: Default::default(), - macro_reachable_adts: Default::default(), - doc_link_resolutions: Default::default(), - doc_link_traits_in_scope: Default::default(), current_crate_outer_attr_insert_span, disambiguators: Default::default(), - delegation_infos: Default::default(), - delegation_inherent_fn_map: Default::default(), features: tcx.features(), .. }; From 728205bd952173da7d3dcf617205d78d7818b7f6 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 29 Sep 2026 22:55:17 -0400 Subject: [PATCH 47/65] Forbid `Reborrow` impls for types with destructors Construction of such types should never be so implicit. --- .../rustc_hir_analysis/src/coherence/builtin.rs | 15 ++++++++++++++- compiler/rustc_hir_analysis/src/diagnostics.rs | 7 ++++--- tests/ui/reborrow/reborrow-drop.rs | 14 ++++++++++++++ tests/ui/reborrow/reborrow-drop.stderr | 15 +++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 tests/ui/reborrow/reborrow-drop.rs create mode 100644 tests/ui/reborrow/reborrow-drop.stderr diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index efed5994f63fe..e7f20766bc8eb 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -115,7 +115,11 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran Err(CopyImplementationError::HasDestructor(did)) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; let impl_ = tcx.def_span(did); - Err(tcx.dcx().emit_err(diagnostics::CopyImplOnTypeWithDtor { span, impl_ })) + Err(tcx.dcx().emit_err(diagnostics::TraitImplOnTypeWithDtor { + span, + impl_, + trait_name: sym::Copy, + })) } Err(CopyImplementationError::HasUnsafeFields) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; @@ -537,6 +541,15 @@ pub(crate) fn reborrow_info<'tcx>( assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field.ty, field.span)?; } + if let Some(did) = def.destructor(tcx).map(|dtor| dtor.did) { + let impl_ = tcx.def_span(did); + return Err(tcx.dcx().emit_err(diagnostics::TraitImplOnTypeWithDtor { + span, + impl_, + trait_name: sym::Reborrow, + })); + } + Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index ff5f7dbb119f1..e4f7f2d9229fc 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -292,13 +292,14 @@ pub(crate) struct FieldAlreadyDeclaredNestedHelp { } #[derive(Diagnostic)] -#[diag("the trait `Copy` cannot be implemented for this type; the type has a destructor", code = E0184)] -pub(crate) struct CopyImplOnTypeWithDtor { +#[diag("the trait `{$trait_name}` cannot be implemented for this type; the type has a destructor", code = E0184)] +pub(crate) struct TraitImplOnTypeWithDtor { #[primary_span] - #[label("`Copy` not allowed on types with destructors")] + #[label("`{$trait_name}` not allowed on types with destructors")] pub span: Span, #[note("destructor declared here")] pub impl_: Span, + pub trait_name: Symbol, } #[derive(Diagnostic)] diff --git a/tests/ui/reborrow/reborrow-drop.rs b/tests/ui/reborrow/reborrow-drop.rs new file mode 100644 index 0000000000000..bc88bb79b9b4b --- /dev/null +++ b/tests/ui/reborrow/reborrow-drop.rs @@ -0,0 +1,14 @@ +//@ compile-flags: --crate-type=lib + +#![feature(reborrow)] + +use std::marker::Reborrow; + +struct MyMut<'a>(&'a mut ()); + +impl Reborrow for MyMut<'_> {} +//~^ ERROR the trait `Reborrow` cannot be implemented for this type; the type has a destructor [E0184] + +impl Drop for MyMut<'_> { + fn drop(&mut self) {} +} diff --git a/tests/ui/reborrow/reborrow-drop.stderr b/tests/ui/reborrow/reborrow-drop.stderr new file mode 100644 index 0000000000000..d35b36530d588 --- /dev/null +++ b/tests/ui/reborrow/reborrow-drop.stderr @@ -0,0 +1,15 @@ +error[E0184]: the trait `Reborrow` cannot be implemented for this type; the type has a destructor + --> $DIR/reborrow-drop.rs:9:1 + | +LL | impl Reborrow for MyMut<'_> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Reborrow` not allowed on types with destructors + | +note: destructor declared here + --> $DIR/reborrow-drop.rs:13:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0184`. From a5e0df2d551723906c68eda7e0e7ce399db4629e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 29 Sep 2026 16:47:32 +1000 Subject: [PATCH 48/65] Remove `rustc_middle::ty::cast::IntTy` Currently it covers "int-like" types: integers, integer inference variables, bools, chars, and C-like enums. It's a bit weird, especially the asymmetry between the fielded `U` and fieldless `I` variants. This commit merges it into `CastTy`, and adds an `is_int_like` method. Some of the checking is now more strict, accepting only ints where before it accepted int-likes (e.g. casting a bool to a ptr) in `TypeChecker::visit_rvalue` and `mir_cast_kind`. `CastCheck::do_check` already rejects these int-like cases, which means they can't occur in MIR built from THIR. The only way to hit the stricter checking is in custom MIR. The commit also removes some unused derives from `CastKind` and fixes a couple of stale comments. --- compiler/rustc_borrowck/src/type_check/mod.rs | 10 ++-- compiler/rustc_hir_typeck/src/cast.rs | 29 ++++----- compiler/rustc_middle/src/ty/cast.rs | 59 +++++++++---------- 3 files changed, 48 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 0a6fb9d5da47a..0e8565aef3f0b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1361,7 +1361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => (), + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int)) => (), _ => { span_mirbug!( self, @@ -1379,7 +1379,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Int), Some(CastTy::Ptr(_))) => (), _ => { span_mirbug!( self, @@ -1396,7 +1396,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => (), + (Some(from), Some(to)) if from.is_int_like() && to.is_int_like() => (), _ => { span_mirbug!( self, @@ -1413,7 +1413,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Float)) => (), + (Some(CastTy::Int), Some(CastTy::Float)) => (), _ => { span_mirbug!( self, @@ -1430,7 +1430,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Float), Some(CastTy::Int(_))) => (), + (Some(CastTy::Float), Some(CastTy::Int)) => (), _ => { span_mirbug!( self, diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 02e6b8188a876..7eced89c95341 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -912,7 +912,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// directly. coercion-cast is handled in check instead of here. fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result> { use rustc_middle::ty::cast::CastTy::*; - use rustc_middle::ty::cast::IntTy::*; let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) { @@ -947,7 +946,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // a cast. ty::Ref(_, inner_ty, mutbl) => { return match t_cast { - Int(_) | Float => match *inner_ty.kind() { + Int | CEnum | Bool | Char | Float => match *inner_ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) @@ -979,19 +978,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { } match (t_from, t_cast) { // These types have invariants! can't cast into them. - (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), + (_, CEnum | FnPtr) => Err(CastError::NonScalar), // * -> Bool - (_, Int(Bool)) => Err(CastError::CastToBool), + (_, Bool) => Err(CastError::CastToBool), // * -> Char - (Int(U(ty::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast - (_, Int(Char)) => Err(CastError::CastToChar), + (Int, Char) if self.expr_ty == fcx.tcx.types.u8 => { + Ok(CastKind::U8CharCast) // u8-char-cast + } + (_, Char) => Err(CastError::CastToChar), // prim -> float,ptr - (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), + (Bool | CEnum | Char, Float) => Err(CastError::NeedViaInt), - (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { + (Bool | CEnum | Char | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { Err(CastError::IllegalCast) } @@ -999,24 +1000,24 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast - (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), + (Ptr(m_expr), Int) => self.check_ptr_addr_cast(fcx, m_expr), - (FnPtr, Int(_)) => { + (FnPtr, Int) => { // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) } // addr-ptr-cast - (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), + (Int, Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // fn-ptr-cast (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), // enum -> int - (Int(CEnum), Int(_)) => self.check_enum_cast(fcx), + (CEnum, Int) => self.check_enum_cast(fcx), // prim -> prim - (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), + (Char | Bool, Int) => Ok(CastKind::PrimIntCast), - (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), + (Int | Float, Int | Float) => Ok(CastKind::NumericCast), } } diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index e53f53d5e5648..32af08eaaadb3 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -1,29 +1,20 @@ -// Helpers for handling cast expressions, used in both -// typeck and codegen. +// Helpers for handling cast expressions. -use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use rustc_span::bug; use crate::mir; use crate::ty::{self, Ty}; -/// Types that are represented as ints. +/// Valid types for the result of a non-coercion cast #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum IntTy { - U(ty::UintTy), - I, +pub enum CastTy<'tcx> { + /// `iN`, `uN`, and integer inference variables. + Int, + /// C-like (fieldless) enums. CEnum, Bool, Char, -} - -// Valid types for the result of a non-coercion cast -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum CastTy<'tcx> { - /// Various types that are represented as ints and handled mostly - /// in the same way, merged for easier matching. - Int(IntTy), - /// Floating-point types. + /// `fN` and float inference variables. Float, /// Function pointers. FnPtr, @@ -32,8 +23,8 @@ pub enum CastTy<'tcx> { } /// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) -/// (or rustc_hir_analysis/check/cast.rs). -#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] +/// (or rustc_hir_typeck/src/cast.rs). +#[derive(Copy, Clone, Debug)] pub enum CastKind { PtrPtrCast, PtrAddrCast, @@ -52,19 +43,23 @@ impl<'tcx> CastTy<'tcx> { /// Casts like unsizing casts will return `None`. pub fn from_ty(t: Ty<'tcx>) -> Option> { match *t.kind() { - ty::Bool => Some(CastTy::Int(IntTy::Bool)), - ty::Char => Some(CastTy::Int(IntTy::Char)), - ty::Int(_) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), - ty::Uint(u) => Some(CastTy::Int(IntTy::U(u))), - ty::Float(_) => Some(CastTy::Float), - ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), + ty::Bool => Some(CastTy::Bool), + ty::Char => Some(CastTy::Char), + ty::Int(_) | ty::Uint(_) | ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int), + ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), + ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::CEnum), ty::RawPtr(ty, mutbl) => Some(CastTy::Ptr(ty::TypeAndMut { ty, mutbl })), ty::FnPtr(..) => Some(CastTy::FnPtr), _ => None, } } + + pub fn is_int_like(self) -> bool { + match self { + CastTy::Int | CastTy::CEnum | CastTy::Bool | CastTy::Char => true, + CastTy::Float | CastTy::FnPtr | CastTy::Ptr(_) => false, + } + } } /// Returns `mir::CastKind` from the given parameters. @@ -72,15 +67,17 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin let from = CastTy::from_ty(from_ty); let cast = CastTy::from_ty(cast_ty); let cast_kind = match (from, cast) { - (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { + (Some(from), Some(cast)) if from.is_int_like() && cast.is_int_like() => { + mir::CastKind::IntToInt + } + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int)) => { mir::CastKind::PointerExposeProvenance } - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, - (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt, + (Some(CastTy::Int), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr, - (Some(CastTy::Float), Some(CastTy::Int(_))) => mir::CastKind::FloatToInt, - (Some(CastTy::Int(_)), Some(CastTy::Float)) => mir::CastKind::IntToFloat, + (Some(CastTy::Float), Some(CastTy::Int)) => mir::CastKind::FloatToInt, + (Some(CastTy::Int), Some(CastTy::Float)) => mir::CastKind::IntToFloat, (Some(CastTy::Float), Some(CastTy::Float)) => mir::CastKind::FloatToFloat, (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PtrToPtr, From 9bdf58f54fc2ec1fc735276789b103c81529dba6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 29 Sep 2026 20:45:28 +1000 Subject: [PATCH 49/65] Move `rustc_middle::ty::cast::CastKind` It's not used in `rustc_middle`, it's only used in `rustc_hir_typeck` and clippy. (It's always good to remove things from `rustc_middle`.) --- compiler/rustc_hir_typeck/src/cast.rs | 18 +++++++++++++++++- compiler/rustc_middle/src/ty/cast.rs | 16 ---------------- .../transmutes_expressible_as_ptr_casts.rs | 3 +-- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7eced89c95341..14cb73e28c871 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -39,7 +39,7 @@ use rustc_lint_defs::builtin::{TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::cast::{CastKind, CastTy}; +use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, Unnormalized, VariantDef, elaborate, @@ -52,6 +52,22 @@ use tracing::{debug, instrument}; use super::FnCtxt; use crate::{diagnostics, type_error_struct}; +/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) +/// (or rustc_hir_typeck/src/cast.rs). +#[derive(Copy, Clone, Debug)] +pub enum CastKind { + PtrPtrCast, + PtrAddrCast, + AddrPtrCast, + NumericCast, + EnumCast, + PrimIntCast, + U8CharCast, + ArrayPtrCast, + FnPtrPtrCast, + FnPtrAddrCast, +} + /// Reifies a cast check to be checked once we have full type information for /// a function context. #[derive(Debug)] diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 32af08eaaadb3..fc8a6de5d232e 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -22,22 +22,6 @@ pub enum CastTy<'tcx> { Ptr(ty::TypeAndMut<'tcx>), } -/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) -/// (or rustc_hir_typeck/src/cast.rs). -#[derive(Copy, Clone, Debug)] -pub enum CastKind { - PtrPtrCast, - PtrAddrCast, - AddrPtrCast, - NumericCast, - EnumCast, - PrimIntCast, - U8CharCast, - ArrayPtrCast, - FnPtrPtrCast, - FnPtrAddrCast, -} - impl<'tcx> CastTy<'tcx> { /// Returns `Some` for integral/pointer casts. /// Casts like unsizing casts will return `None`. diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 18897fbb5b8f0..0f5adb8b983e2 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -4,10 +4,9 @@ use clippy_utils::sugg::Sugg; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; -use rustc_hir_typeck::cast::check_cast; +use rustc_hir_typeck::cast::{CastKind, check_cast}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_middle::ty::cast::CastKind; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. From f69dfad6d65f072818b77c17388c86215fbff6fb Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 30 Sep 2026 04:36:47 +0000 Subject: [PATCH 50/65] Prepare for merging from rust-lang/rust This updates the rust-version file to 5c543b0b8c73c7b72bc8284ced4fb22ead15734d. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 028e73378b28b..eb8c4fb20edfe 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -5d8937173e7c40406c167f4e94737da8925333ad +5c543b0b8c73c7b72bc8284ced4fb22ead15734d From 9970383a9bece341e44dc49dc45b98e10a83e6dc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Sep 2026 11:14:08 +1000 Subject: [PATCH 51/65] Document `Result` case for the `arena_cache` query modifier --- compiler/rustc_middle/src/query/arena_cached.rs | 14 ++++++++------ compiler/rustc_middle/src/query/modifiers.rs | 9 ++++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs index 4ab2fbe914965..8faf6f68f49c9 100644 --- a/compiler/rustc_middle/src/query/arena_cached.rs +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -5,13 +5,15 @@ use rustc_span::ErrorGuaranteed; use crate::ty::TyCtxt; -/// Helper trait that allows `arena_cache` queries to return `Option<&T>` -/// instead of `&Option`, and avoid allocating `None` in the arena. +/// Helper trait that allows `arena_cache` queries to return: +/// - `Option<&T>` instead of `&Option`, and avoid allocating `None` in the arena; or +/// - `Result<&T, ErrorGuaranteed>` instead of `&Result`, and avoid allocating +/// `Err(ErrorGuaranteed)` in the arena. /// -/// An arena-cached query must be declared to return a type that implements -/// this trait, i.e. either `&'tcx T` or `Option<&'tcx T>`. This trait then -/// determines the types returned by the provider and stored in the arena, -/// and provides a function to bridge between the three types. +/// An arena-cached query must be declared to return a type that implements this trait, i.e. +/// `&'tcx T`, `Option<&'tcx T>`, or `Result<&'tcx T, ErrorGuaranteed>`. This trait then determines +/// the types returned by the provider and stored in the arena, and provides a function to bridge +/// between the three types. pub trait ArenaCached<'tcx>: Sized { /// Type that is returned by the query provider. type Provided; diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 4fd91caa94cd7..3e057e96ddb6b 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -1,4 +1,5 @@ -//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` proc macro. +//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` +//! proc macro. //! //! The dummy items in this module are used to enable hover documentation for //! modifier names in the query list, and to allow find-all-references to list @@ -10,10 +11,12 @@ /// # `arena_cache` query modifier /// /// Query return values must impl `Copy` and be small, but some queries must return values that -/// doesn't meet those criteria. Queries marked with this modifier have their values allocated in -/// an arena and the query returns a reference to the value. There are two cases. +/// don't meet those criteria. Queries marked with this modifier have their values allocated in +/// an arena and the query returns a reference to the value. There are three cases. /// - If the provider function returns `T` then the query will return `&'tcx T`. /// - If the provider function returns `Option` then the query will return `Option<&'tcx T>`. +/// - If the provider function returns `Result` then the query will return +/// `Result<&'tcx T, ErrorGuaranteed>`. /// /// The query plumbing takes care of the arenas and the type manipulations. pub(crate) struct arena_cache; From 52fd615ddc6c0df634756f153596c0bdc0bf0af3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 30 Sep 2026 07:30:48 +0200 Subject: [PATCH 52/65] use cfg_select in a few more places --- src/tools/miri/src/shims/unix/fs.rs | 98 +++++++++++++++-------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 98a7c2ff3c2cf..da9738868de27 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -387,15 +387,16 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { }) } - #[cfg(unix)] fn host_permissions_from_mode(&self, mode: u32) -> InterpResult<'tcx, fs::Permissions> { - use std::os::unix::fs::PermissionsExt; - interp_ok(fs::Permissions::from_mode(mode)) - } - - #[cfg(not(unix))] - fn host_permissions_from_mode(&self, _mode: u32) -> InterpResult<'tcx, fs::Permissions> { - throw_unsup_format!("setting file permissions is only supported on Unix hosts") + cfg_select! { + unix => { + use std::os::unix::fs::PermissionsExt; + interp_ok(fs::Permissions::from_mode(mode)) + } + _ => { + throw_unsup_format!("setting file permissions is only supported on Unix hosts") + } + } } } @@ -481,20 +482,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; let mode = this.read_scalar(mode)?.to_u32()?; - #[cfg(unix)] - { - // Support all modes on UNIX host - use std::os::unix::fs::OpenOptionsExt; - options.mode(mode); - } - #[cfg(not(unix))] - { - // Only support default mode for non-UNIX (i.e. Windows) host - if mode != 0o666 { - throw_unsup_format!( - "non-default mode 0o{:o} is not supported on non-Unix hosts", - mode - ); + cfg_select! { + unix => { + // Support all modes on UNIX host + use std::os::unix::fs::OpenOptionsExt; + options.mode(mode); + } + _ => { + // Only support default mode for non-UNIX (i.e. Windows) host + if mode != 0o666 { + throw_unsup_format!( + "non-default mode 0o{:o} is not supported on non-Unix hosts", + mode + ); + } } } @@ -523,20 +524,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let o_nofollow = this.eval_libc_i32("O_NOFOLLOW"); if flag & o_nofollow == o_nofollow { flag &= !o_nofollow; - #[cfg(unix)] - { - use std::os::unix::fs::OpenOptionsExt; - options.custom_flags(libc::O_NOFOLLOW); - } - // Strictly speaking, this emulation is not equivalent to the O_NOFOLLOW flag behavior: - // the path could change between us checking it here and the later call to `open`. - // But it's good enough for Miri purposes. - #[cfg(not(unix))] - { - // O_NOFOLLOW only fails when the trailing component is a symlink; - // the entire rest of the path can still contain symlinks. - if path.is_symlink() { - return this.set_errno_and_return_neg1_i32(LibcError("ELOOP")); + cfg_select! { + unix => { + use std::os::unix::fs::OpenOptionsExt; + options.custom_flags(libc::O_NOFOLLOW); + } + _ => { + // Strictly speaking, this emulation is not equivalent to the O_NOFOLLOW flag behavior: + // the path could change between us checking it here and the later call to `open`. + // But it's good enough for Miri purposes. + + // O_NOFOLLOW only fails when the trailing component is a symlink; + // the entire rest of the path can still contain symlinks. + if path.is_symlink() { + return this.set_errno_and_return_neg1_i32(LibcError("ELOOP")); + } } } } @@ -618,15 +620,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { target_op: &OpTy<'tcx>, linkpath_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> { - #[cfg(unix)] fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { - std::os::unix::fs::symlink(src, dst) - } - - #[cfg(windows)] - fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { - use std::os::windows::fs; - if src.is_dir() { fs::symlink_dir(src, dst) } else { fs::symlink_file(src, dst) } + cfg_select! { + unix => std::os::unix::fs::symlink(src, dst), + windows => { + use std::os::windows::fs; + // This is racy, but not much we can do about that. + if src.is_dir() { + fs::symlink_dir(src, dst) + } else { + fs::symlink_file(src, dst) + } + } + } } let this = self.eval_context_mut(); @@ -1859,9 +1865,6 @@ fn extract_sec_and_nsec<'tcx>( } fn file_type_to_mode_name(file_type: std::fs::FileType) -> &'static str { - #[cfg(unix)] - use std::os::unix::fs::FileTypeExt; - if file_type.is_file() { "S_IFREG" } else if file_type.is_dir() { @@ -1872,6 +1875,7 @@ fn file_type_to_mode_name(file_type: std::fs::FileType) -> &'static str { // Certain file types are only available when the host is a Unix system. #[cfg(unix)] { + use std::os::unix::fs::FileTypeExt; if file_type.is_socket() { return "S_IFSOCK"; } else if file_type.is_fifo() { From b02b23fa962b4fade108ea1e7988172fa8f624b3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 30 Sep 2026 07:39:01 +0200 Subject: [PATCH 53/65] windows host: use FILE_FLAG_OPEN_REPARSE_POINT to implement O_NOFOLLOW --- src/tools/miri/Cargo.lock | 1 + src/tools/miri/Cargo.toml | 5 +++ src/tools/miri/priroda/Cargo.lock | 1 + src/tools/miri/src/shims/unix/fs.rs | 36 +++++++++++-------- .../tests/pass-dep/libc/libc-fs-symlink.rs | 1 - 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index c71ec790d39fe..8cffd4defac3d 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -1023,6 +1023,7 @@ dependencies = [ "socket2", "tempfile", "ui_test", + "windows-sys", ] [[package]] diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 9f2a1e884fd70..1c52d35bf5bb2 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -48,6 +48,11 @@ capstone = { version = "0.14", features = ["arch_x86", "full"], default-features [target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies] genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true } +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.61", features = [ + "Win32_Storage_FileSystem", +] } + [dev-dependencies] ui_test = "0.30.2" colored = "3" diff --git a/src/tools/miri/priroda/Cargo.lock b/src/tools/miri/priroda/Cargo.lock index 7b8ec9b0f08e6..a690264e158fc 100644 --- a/src/tools/miri/priroda/Cargo.lock +++ b/src/tools/miri/priroda/Cargo.lock @@ -689,6 +689,7 @@ dependencies = [ "serde", "smallvec", "socket2", + "windows-sys", ] [[package]] diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index da9738868de27..6baf24442e747 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -394,6 +394,7 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(fs::Permissions::from_mode(mode)) } _ => { + let _ = mode; throw_unsup_format!("setting file permissions is only supported on Unix hosts") } } @@ -522,23 +523,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let o_nofollow = this.eval_libc_i32("O_NOFOLLOW"); + let mut nofollow = false; if flag & o_nofollow == o_nofollow { flag &= !o_nofollow; + nofollow = true; cfg_select! { unix => { use std::os::unix::fs::OpenOptionsExt; options.custom_flags(libc::O_NOFOLLOW); } - _ => { - // Strictly speaking, this emulation is not equivalent to the O_NOFOLLOW flag behavior: - // the path could change between us checking it here and the later call to `open`. - // But it's good enough for Miri purposes. - - // O_NOFOLLOW only fails when the trailing component is a symlink; - // the entire rest of the path can still contain symlinks. - if path.is_symlink() { - return this.set_errno_and_return_neg1_i32(LibcError("ELOOP")); - } + windows => { + use std::os::windows::fs::OpenOptionsExt; + options.custom_flags( + windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT, + ); } } } @@ -554,11 +552,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_errno_and_return_neg1_i32(ErrorKind::PermissionDenied); } - let fd = options - .open(path) - .map(|file| this.machine.fds.insert_new(FileHandle { file, writable, readable })); - - interp_ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) + let file = match options.open(path) { + Ok(file) => file, + Err(err) => return this.set_errno_and_return_neg1_i32(err), + }; + if nofollow && !cfg!(unix) { + // On Windows, FILE_FLAG_OPEN_REPARSE_POINT makes opening still succeed, it just + // opens the symlink rather than the target. Turn that into an error. + if file.metadata().unwrap().is_symlink() { + return this.set_errno_and_return_neg1_i32(LibcError("ELOOP")); + } + } + let fd = this.machine.fds.insert_new(FileHandle { file, writable, readable }); + interp_ok(Scalar::from_i32(fd)) } fn lseek( diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs index 4fe66c338a045..ca67324ee1da2 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs @@ -1,5 +1,4 @@ // Symlink tests are separate since they don't in general work on a Windows host. -//@ignore-host: windows # creating symlinks requires admin permissions on Windows //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation //@run-native From 25fef8822285a3ac0aa6ebfe05a307573708478f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 30 Sep 2026 09:04:06 +0200 Subject: [PATCH 54/65] move libc-fs tests back together we need the test coverage for symlink handling on windows hosts --- .../tests/pass-dep/libc/libc-fs-symlink.rs | 81 ------------------- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 81 ++++++++++++++++--- 2 files changed, 72 insertions(+), 90 deletions(-) delete mode 100644 src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs deleted file mode 100644 index ca67324ee1da2..0000000000000 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Symlink tests are separate since they don't in general work on a Windows host. -//@ignore-target: windows # File handling is not implemented yet -//@compile-flags: -Zmiri-disable-isolation -//@run-native - -use std::ffi::CString; -use std::io::ErrorKind; -use std::os::unix::ffi::OsStrExt; - -#[path = "../../utils/libc.rs"] -mod libc_utils; -#[path = "../../utils/mod.rs"] -mod utils; -use libc_utils::errno_result; - -fn main() { - test_readlink(); - test_nofollow_symlink(); -} - -fn test_readlink() { - let bytes = b"Hello, World!\n"; - let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); - let expected_path = path.as_os_str().as_bytes(); - - let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - - // Test that the expected string gets written to a buffer of proper - // length, and that a trailing null byte is not written. - let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - let symlink_c_ptr = symlink_c_str.as_ptr(); - - // Make the buf one byte larger than it needs to be, - // and check that the last byte is not overwritten. - let mut large_buf = vec![0xFF; expected_path.len() + 1]; - let res = errno_result(unsafe { - libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) - }) - .unwrap(); - // Check that the resolved path was properly written into the buf. - assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); - assert_eq!(large_buf.last(), Some(&0xFF)); - assert_eq!(res, (large_buf.len() - 1) as isize); - - // Test that the resolved path is truncated if the provided buffer - // is too small. - let mut small_buf = [0u8; 2]; - let res = errno_result(unsafe { - libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) - }) - .unwrap(); - assert_eq!(small_buf, &expected_path[..small_buf.len()]); - assert_eq!(res, small_buf.len() as isize); - - // Test that we report a proper error for a missing path. - let err = errno_result(unsafe { - libc::readlink( - c"MIRI_MISSING_FILE_NAME".as_ptr(), - small_buf.as_mut_ptr().cast(), - small_buf.len(), - ) - }) - .unwrap_err(); - assert_eq!(err.kind(), ErrorKind::NotFound); -} - -fn test_nofollow_symlink() { - let path = utils::prepare_with_content("test_nofollow_symlink_target.txt", b"Hello, World!\n"); - - let symlink_path = utils::prepare("test_nofollow_symlink.txt"); - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - - let symlink_cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - - let err = errno_result(unsafe { - libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) - }) - .unwrap_err(); - assert_eq!(err.raw_os_error(), Some(libc::ELOOP)); -} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index d5418d3612a42..05b95e3ac7f50 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -31,6 +31,7 @@ fn main() { test_file_open_unix_needs_three_args(); test_file_open_unix_extra_third_arg(); test_file_open_dir(); + test_file_open_nofollow(); #[cfg(target_os = "linux")] test_o_tmpfile_flag(); test_posix_mkstemp(); @@ -55,7 +56,6 @@ fn main() { test_futimens(); test_isatty(); test_read_and_uninit(); - test_nofollow_not_symlink(); #[cfg(target_os = "macos")] test_ioctl(); test_opendir_closedir(); @@ -77,6 +77,7 @@ fn main() { #[cfg(not(target_os = "solaris"))] test_pwritev(); test_pwrite(); + test_readlink(); test_linkat(); } @@ -297,6 +298,29 @@ fn test_file_open_dir() { // errno_check(unsafe { libc::close(fd) }); } +fn test_file_open_nofollow() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("test_nofollow_not_symlink.txt", bytes); + let cpath = CString::new(path.as_os_str().as_bytes()).unwrap(); + let fd = + errno_result(unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }) + .unwrap(); + errno_check(unsafe { libc::close(fd) }); + + let path = utils::prepare_with_content("miri_test_open_nofollow_symlink_target.txt", bytes); + + let symlink_path = utils::prepare("miri_test_open_nofollow_symlink.txt"); + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + + let symlink_cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + + let err = errno_result(unsafe { + libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) + }) + .unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::ELOOP)); +} + fn test_dup_stdout_stderr() { let bytes = b"hello dup fd\n"; unsafe { @@ -959,14 +983,6 @@ fn test_read_and_uninit() { } } -fn test_nofollow_not_symlink() { - let bytes = b"Hello, World!\n"; - let path = utils::prepare_with_content("test_nofollow_not_symlink.txt", bytes); - let cpath = CString::new(path.as_os_str().as_bytes()).unwrap(); - let ret = unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }; - assert!(ret >= 0); -} - #[cfg(target_os = "macos")] fn test_ioctl() { let path = utils::prepare_with_content("miri_test_libc_ioctl.txt", &[]); @@ -1381,6 +1397,53 @@ fn test_pwrite() { assert_eq!(&write_buffer[0..bytes_written], &read_buffer[OFFSET..(bytes_written + OFFSET)]); } +fn test_readlink() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); + let expected_path = path.as_os_str().as_bytes(); + + let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + + // Test that the expected string gets written to a buffer of proper + // length, and that a trailing null byte is not written. + let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let symlink_c_ptr = symlink_c_str.as_ptr(); + + // Make the buf one byte larger than it needs to be, + // and check that the last byte is not overwritten. + let mut large_buf = vec![0xFF; expected_path.len() + 1]; + let res = errno_result(unsafe { + libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) + }) + .unwrap(); + // Check that the resolved path was properly written into the buf. + assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); + assert_eq!(large_buf.last(), Some(&0xFF)); + assert_eq!(res, (large_buf.len() - 1) as isize); + + // Test that the resolved path is truncated if the provided buffer + // is too small. + let mut small_buf = [0u8; 2]; + let res = errno_result(unsafe { + libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) + }) + .unwrap(); + assert_eq!(small_buf, &expected_path[..small_buf.len()]); + assert_eq!(res, small_buf.len() as isize); + + // Test that we report a proper error for a missing path. + let err = errno_result(unsafe { + libc::readlink( + c"MIRI_MISSING_FILE_NAME".as_ptr(), + small_buf.as_mut_ptr().cast(), + small_buf.len(), + ) + }) + .unwrap_err(); + assert_eq!(err.kind(), ErrorKind::NotFound); +} + fn test_linkat() { let source = utils::prepare_with_content("miri_test_libc_linkat_source.txt", b"hello"); let link = utils::prepare("miri_test_libc_linkat_link.txt"); From c3c4a0ab08f174bd126172594217aab7e2b4dcec Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 30 Sep 2026 09:26:22 +0200 Subject: [PATCH 55/65] unix open: reject opening directories --- src/tools/miri/src/shims/unix/fs.rs | 6 ++- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 42 ++++--------------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 6baf24442e747..a00e18be7eccb 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -556,10 +556,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(file) => file, Err(err) => return this.set_errno_and_return_neg1_i32(err), }; + let metadata = file.metadata().expect("a just-opened file should have metadata"); + if metadata.is_dir() { + throw_unsup_format!("open: opening directories is not supported"); + } if nofollow && !cfg!(unix) { // On Windows, FILE_FLAG_OPEN_REPARSE_POINT makes opening still succeed, it just // opens the symlink rather than the target. Turn that into an error. - if file.metadata().unwrap().is_symlink() { + if metadata.is_symlink() { return this.set_errno_and_return_neg1_i32(LibcError("ELOOP")); } } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 05b95e3ac7f50..f6b6dc2a141d3 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -27,10 +27,9 @@ fn main() { test_ftruncate::(libc::ftruncate); #[cfg(target_os = "linux")] test_ftruncate::(libc::ftruncate64); - test_file_open_unix_allow_two_args(); - test_file_open_unix_needs_three_args(); - test_file_open_unix_extra_third_arg(); - test_file_open_dir(); + test_file_open_allow_two_args(); + test_file_open_needs_three_args(); + test_file_open_extra_third_arg(); test_file_open_nofollow(); #[cfg(target_os = "linux")] test_o_tmpfile_flag(); @@ -252,15 +251,15 @@ fn test_statx_empty_path_on_pipe() { } } -fn test_file_open_unix_allow_two_args() { - let path = utils::prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); +fn test_file_open_allow_two_args() { + let path = utils::prepare_with_content("test_file_open_allow_two_args.txt", &[]); let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY) }).unwrap(); } -fn test_file_open_unix_needs_three_args() { - let path = utils::prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); +fn test_file_open_needs_three_args() { + let path = utils::prepare_with_content("test_file_open_needs_three_args.txt", &[]); let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); let _fd = @@ -268,36 +267,13 @@ fn test_file_open_unix_needs_three_args() { .unwrap(); } -fn test_file_open_unix_extra_third_arg() { - let path = utils::prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); +fn test_file_open_extra_third_arg() { + let path = utils::prepare_with_content("test_file_open_extra_third_arg.txt", &[]); let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY, 42) }).unwrap(); } -fn test_file_open_dir() { - let dir_path = utils::prepare_dir("miri_test_fs_dir"); - create_dir(&dir_path).unwrap(); - let dir_name = CString::new(dir_path.into_os_string().into_encoded_bytes()).unwrap(); - - // Opening it for read-write fails. The error code differs between Unix and Windows hosts. - let err = errno_result(unsafe { libc::open(dir_name.as_ptr(), libc::O_RDWR) }).unwrap_err(); - assert!( - [libc::EISDIR, libc::EPERM].contains(&err.raw_os_error().unwrap()), - "unexpected errno: {err}" - ); - - // Opening it for reading succeeds, but then reading fails. - // FIXME: currently does not behave as expected on Windows hosts. - // See . - // let fd = errno_result(unsafe { libc::open(dir_name.as_ptr(), libc::O_RDONLY) }).unwrap(); - // let mut buf = [0u8; 4]; - // let err = - // errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) }).unwrap_err(); - // assert_eq!(err.raw_os_error().unwrap(), libc::EISDIR, "unexpected errno: {err}"); - // errno_check(unsafe { libc::close(fd) }); -} - fn test_file_open_nofollow() { let bytes = b"Hello, World!\n"; let path = utils::prepare_with_content("test_nofollow_not_symlink.txt", bytes); From 654d6642d918149742558744798c2cdce3996999 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 30 Sep 2026 08:56:04 +0200 Subject: [PATCH 56/65] add regression test --- ...-requires-resolving-regions.current.stderr | 26 +++++++++++++++ .../cycle-requires-resolving-regions.rs | 33 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.current.stderr create mode 100644 tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.rs diff --git a/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.current.stderr b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.current.stderr new file mode 100644 index 0000000000000..477b0ba993104 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.current.stderr @@ -0,0 +1,26 @@ +error[E0275]: overflow evaluating the requirement `Foo<'_>: Send` + --> $DIR/cycle-requires-resolving-regions.rs:31:5 + | +LL | require::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_requires_resolving_regions`) +note: required for `Foo<'_>` to implement `Send` + --> $DIR/cycle-requires-resolving-regions.rs:23:17 + | +LL | unsafe impl<'a> Send for Foo<'a> + | ^^^^ ^^^^^^^ +LL | where +LL | Foo<'a>: Send, + | ---- unsatisfied trait bound introduced here + = note: 128 redundant requirements hidden + = note: required for `Foo<'static>` to implement `Send` +note: required by a bound in `require` + --> $DIR/cycle-requires-resolving-regions.rs:28:15 + | +LL | fn require() {} + | ^^^^ required by this bound in `require` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.rs b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.rs new file mode 100644 index 0000000000000..2e1eaff4b9479 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/cycle-requires-resolving-regions.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +// A test showcasing a behavior change in cycle detection between +// the old and new solver. The old solver detects cycles in both +// fulfill and in evaluate. The cycle detection in fulfillment +// does not resolve regions. Each occurance of the `Foo<'a>: Send` +// impl creates a fresh inference variable even if it is later +// constrainted to `'static`. This means we never detect a cycle. +// +// This is not an issue with builtin auto-trait impls as they don't +// create impl args instead, simply using the generic arguments of +// the self type directly. +// +// The new solver properly resolves regions. The old solver does resolve +// regions in `project`, so there we do detect cycles even if there are +// fresh region variables. + +struct Foo<'a>(&'a ()); + +unsafe impl<'a> Send for Foo<'a> +where + Foo<'a>: Send, +{} + +fn require() {} + +fn main() { + require::>(); + //[current]~^ ERROR: overflow evaluating the requirement `Foo<'_>: Send` +} From b7448a39255dd818099fb81c183594b7c474ebe4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Sep 2026 21:54:04 +0200 Subject: [PATCH 57/65] add fstatat test --- src/tools/miri/src/shims/unix/fs.rs | 10 +- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 119 ++++++++++++++---- src/tools/miri/tests/utils/fs.rs | 13 +- 3 files changed, 110 insertions(+), 32 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index a00e18be7eccb..d67d601df36c9 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -20,14 +20,14 @@ use crate::shims::sig::Varargs; use crate::shims::unix::fd::{EvalContextExt as _, FlockOp, UnixFileDescription}; use crate::*; -/// An open directory stream, tracked by DirTable. +/// An open directory stream (`DIR`), tracked by DirTable. #[derive(Debug)] struct DirStream { - /// The directory reader on the host. + /// The directory stream on the host. read_dir: fs::ReadDir, - /// An FD number for the same handle. Unix directory streams have an "underlying FD" that - /// can be exposed; this is that FD. We also store the FD ID to catch cases where - /// the FD was closed and a different one re-opened. + /// Unix directory streams have an "underlying FD" that can be exposed; this is that FD. This is + /// not a reference; it *can* be separately closed, leading to UB if the DIR is used again. We + /// also store the FD ID to catch cases where the FD was closed and a different one re-opened. fd_num: FdNum, fd_id: FdId, /// The "special" entries that must still be yielded by the iterator. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index f6b6dc2a141d3..af67ca16bd2f1 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -5,10 +5,11 @@ use std::ffi::{CStr, CString, OsString}; use std::fs::{self, File, canonicalize, create_dir, remove_dir, remove_file}; use std::io::{ErrorKind, Write}; +use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; use std::path::PathBuf; -use std::ptr; +use std::{env, ptr}; #[path = "../../utils/mod.rs"] mod utils; @@ -49,7 +50,11 @@ fn main() { test_fallocate::(libc::fallocate64); #[cfg(target_os = "linux")] test_sync_file_range(); + test_opendir_closedir(); + test_readdir(); + test_dirfd(); test_fstat(); + test_fstatat(); test_stat(); test_lstat(); test_futimens(); @@ -57,9 +62,6 @@ fn main() { test_read_and_uninit(); #[cfg(target_os = "macos")] test_ioctl(); - test_opendir_closedir(); - test_readdir(); - test_dirfd(); #[cfg(target_os = "linux")] test_statx_on_file_path(); #[cfg(target_os = "linux")] @@ -116,8 +118,6 @@ fn assert_statx_matches_metadata(stx: &libc::statx, meta: &fs::Metadata, expecte #[cfg(target_os = "linux")] fn test_statx_on_file_descriptor() { - use std::mem::MaybeUninit; - let bytes = b"hello"; let path = utils::prepare_with_content("miri_test_libc_statx_fd.txt", bytes); let file = File::open(&path).unwrap(); @@ -157,18 +157,20 @@ fn test_statx_on_file_descriptor() { #[cfg(target_os = "linux")] fn test_statx_on_file_path() { - use std::mem::MaybeUninit; - let bytes = b"hello"; let path = utils::prepare_with_content("miri_test_libc_statx.txt", bytes); let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); let meta = fs::metadata(&path).unwrap(); + // Relative to working directory. unsafe { + let old_cwd = env::current_dir().unwrap(); + env::set_current_dir(path.parent().unwrap()).unwrap(); + let mut stx = MaybeUninit::::zeroed(); errno_check(libc::statx( libc::AT_FDCWD, - c_path.as_ptr(), + c"miri_test_libc_statx.txt".as_ptr(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, stx.as_mut_ptr(), @@ -176,6 +178,8 @@ fn test_statx_on_file_path() { let stx = stx.assume_init(); assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + + env::set_current_dir(old_cwd).unwrap(); } // Setting AT_EMPTY_PATH is fine even if the path is not actually empty. @@ -218,7 +222,7 @@ fn test_statx_empty_path_on_pipe() { let mut fds = [0; 2]; errno_check(libc::pipe(fds.as_mut_ptr())); - let mut statx_buf = std::mem::MaybeUninit::::zeroed(); + let mut statx_buf = MaybeUninit::::zeroed(); errno_check(libc::statx( fds[0], @@ -770,7 +774,6 @@ fn test_sync_file_range() { } fn test_fstat() { - use std::mem::MaybeUninit; use std::os::unix::io::AsRawFd; let path = utils::prepare_with_content("miri_test_libc_fstat.txt", b"hello"); @@ -789,17 +792,97 @@ fn test_fstat() { check_stat_fields(stat); remove_file(&path).unwrap(); + + // Test it on a pipe as well. + unsafe { + let mut fds = [0; 2]; + errno_check(libc::pipe(fds.as_mut_ptr())); + + let mut stat = MaybeUninit::::uninit(); + errno_check(libc::fstat(fds[0], stat.as_mut_ptr())); + let stat = stat.assume_init_ref(); + + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFIFO); + assert_ne!(stat.st_mode & !libc::S_IFMT, 0, "some permission should be set"); + assert_eq!(stat.st_size, 0); + + errno_check(libc::close(fds[0])); + errno_check(libc::close(fds[1])); + } +} + +fn test_fstatat() { + let testdir = utils::prepare_dir("test_fstatat"); + fs::create_dir(&testdir).unwrap(); + let filename = "file.txt"; + let cfilename = c"file.txt"; + fs::write(testdir.join(filename), b"hello").unwrap(); + let absfilename = testdir.join(filename); + assert!(absfilename.is_absolute()); + + // The only way to get a dirfd in Miri currently is via a `dirfd`. Slightly silly, but whatever. + let dirstream = unsafe { libc::opendir(utils::into_c_string(&testdir).as_ptr()) }; + assert!(!dirstream.is_null()); + let dirfd = unsafe { libc::dirfd(dirstream) }; + + let checkstat = |stat: &libc::stat| { + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFREG); + assert_ne!(stat.st_mode & !libc::S_IFMT, 0, "some permission should be set"); + assert_eq!(stat.st_size, 5); + check_stat_fields(stat); + }; + + // Relative to working directory. + let old_cwd = env::current_dir().unwrap(); + env::set_current_dir(&testdir).unwrap(); + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { libc::fstatat(libc::AT_FDCWD, cfilename.as_ptr(), stat.as_mut_ptr(), 0) }); + checkstat(unsafe { stat.assume_init_ref() }); + env::set_current_dir(old_cwd).unwrap(); + + // Absolute path, dirfd is ignored. + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { + libc::fstatat( + 999, // dirfd + utils::into_c_string(&absfilename).as_ptr(), + stat.as_mut_ptr(), + 0, // flags + ) + }); + checkstat(unsafe { stat.assume_init_ref() }); + + // Relative to dirfd. + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { libc::fstatat(dirfd, cfilename.as_ptr(), stat.as_mut_ptr(), 0) }); + checkstat(unsafe { stat.assume_init_ref() }); + + // Symlink following. + let linkname = testdir.join("link"); + std::os::unix::fs::symlink(&absfilename, &linkname).unwrap(); + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { libc::fstatat(dirfd, c"link".as_ptr(), stat.as_mut_ptr(), 0) }); + checkstat(unsafe { stat.assume_init_ref() }); + let mut stat = MaybeUninit::::uninit(); + errno_check(unsafe { + libc::fstatat(dirfd, c"link".as_ptr(), stat.as_mut_ptr(), libc::AT_SYMLINK_NOFOLLOW) + }); + let stat = unsafe { stat.assume_init_ref() }; + assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFLNK); // not S_IFREG! + assert_ne!(stat.st_mode & !libc::S_IFMT, 0, "some permission should be set"); + check_stat_fields(stat); + + errno_check(unsafe { libc::closedir(dirstream) }); } fn test_stat() { - use std::mem::MaybeUninit; // Also make sure we *do* follow symlinks. let path = utils::prepare_with_content("miri_test_libc_stat.txt", b"hello"); let symlink_path = utils::prepare("miri_test_libc_lstat_symlink.txt"); std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - let cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let cpath = utils::into_c_string(symlink_path); let mut stat = MaybeUninit::::uninit(); errno_check(unsafe { libc::stat(cpath.as_ptr(), stat.as_mut_ptr()) }); @@ -816,13 +899,11 @@ fn test_stat() { } fn test_lstat() { - use std::mem::MaybeUninit; - let path = utils::prepare_with_content("miri_test_libc_lstat.txt", b"hello"); let symlink_path = utils::prepare("miri_test_libc_lstat_symlink.txt"); std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - let cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let cpath = utils::into_c_string(&symlink_path); let mut stat = MaybeUninit::::uninit(); errno_check(unsafe { libc::lstat(cpath.as_ptr(), stat.as_mut_ptr()) }); @@ -839,7 +920,6 @@ fn test_lstat() { } fn test_futimens() { - use std::mem::MaybeUninit; use std::time::{SystemTime, UNIX_EPOCH}; let path = utils::prepare_with_content("miri_test_libc_futimens.txt", b"hello"); @@ -918,7 +998,6 @@ fn test_isatty() { } fn test_read_and_uninit() { - use std::mem::MaybeUninit; { // We test that libc::read initializes its buffer. let path = utils::prepare_with_content("pass-libc-read-and-uninit.txt", &[1u8, 2, 3]); @@ -926,7 +1005,7 @@ fn test_read_and_uninit() { unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); - let mut buf: MaybeUninit = std::mem::MaybeUninit::uninit(); + let mut buf: MaybeUninit = MaybeUninit::uninit(); assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 1), 1); let buf = buf.assume_init(); assert_eq!(buf, 1); @@ -1042,8 +1121,6 @@ fn test_readdir() { } fn test_dirfd() { - use std::mem::MaybeUninit; - let path = utils::prepare_dir("miri_test_libc_opendir_closedir"); create_dir(&path).expect("create_dir failed"); let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs index e312feda7e62b..2fe1f15836b36 100644 --- a/src/tools/miri/tests/utils/fs.rs +++ b/src/tools/miri/tests/utils/fs.rs @@ -1,15 +1,11 @@ -use std::ffi::OsString; +use std::ffi::{CStr, CString, OsString}; use std::path::PathBuf; use std::{fs, io}; use super::miri_extern; pub fn host_to_target_path(path: OsString) -> PathBuf { - use std::ffi::{CStr, CString}; - - // Once into_encoded_bytes is stable we can use it here. - // (Unstable features would need feature flags in each test...) - let path = CString::new(path.into_string().unwrap()).unwrap(); + let path = into_c_string(path); let mut out = Vec::with_capacity(1024); unsafe { @@ -52,3 +48,8 @@ pub fn prepare_dir(dirname: &str) -> PathBuf { fs::remove_dir_all(&path).ok(); path } + +/// Return a `CString` version of the given path. +pub fn into_c_string(path: impl Into) -> CString { + CString::new(path.into().into_string().unwrap()).unwrap() +} From 4912ffd204e4442f21f910a3b10726d4ae747e00 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Sep 2026 22:04:40 +0200 Subject: [PATCH 58/65] use utils::into_c_string in more places --- .../miri/tests/fail-dep/libc/dirfd-closed.rs | 4 +- .../tests/fail-dep/libc/dirfd-replaced.rs | 4 +- .../libc-read-and-uninit-premature-eof.rs | 3 +- .../fail-dep/libc/open_wrong_var_arg_type.rs | 5 +- .../tests/fail/shims/input_arg_mismatch.rs | 5 +- .../tests/fail/shims/return_type_mismatch.rs | 5 +- .../shims/vararg_callee_signature_mismatch.rs | 5 +- .../tests/fail/shims/wrong_fixed_arg_count.rs | 5 +- .../pass-dep/libc/libc-fs-permissions.rs | 7 +- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 79 +++++++++---------- .../pass-dep/libc/libc-socket-invalid-addr.rs | 5 +- src/tools/miri/tests/utils/fs.rs | 9 +-- src/tools/miri/tests/utils/mod.rs | 7 ++ 13 files changed, 63 insertions(+), 80 deletions(-) diff --git a/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs b/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs index 7cab24bc4d4d9..b34a36babb2a9 100644 --- a/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs +++ b/src/tools/miri/tests/fail-dep/libc/dirfd-closed.rs @@ -1,9 +1,7 @@ //@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation -use std::ffi::CString; use std::fs::create_dir; -use std::os::unix::ffi::OsStrExt; #[path = "../../utils/mod.rs"] mod utils; @@ -16,7 +14,7 @@ use libc_utils::errno_check; fn main() { let path = utils::prepare_dir("miri_test_libc_dirfd_closed"); create_dir(&path).expect("create_dir failed"); - let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let cpath = utils::into_c_string(path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); diff --git a/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs b/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs index e5e2336e96d63..dd6d404874401 100644 --- a/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs +++ b/src/tools/miri/tests/fail-dep/libc/dirfd-replaced.rs @@ -1,9 +1,7 @@ //@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation -use std::ffi::CString; use std::fs::create_dir; -use std::os::unix::ffi::OsStrExt; #[path = "../../utils/mod.rs"] mod utils; @@ -16,7 +14,7 @@ use libc_utils::{errno_check, errno_result}; fn main() { let path = utils::prepare_dir("miri_test_libc_dirfd_replaced"); create_dir(&path).expect("create_dir failed"); - let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let cpath = utils::into_c_string(path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs index 4400d52e59d6c..f3e96a9c26f75 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs @@ -4,7 +4,6 @@ // Short FD ops can affect the exact error message here //@compile-flags: -Zmiri-disable-isolation -Zmiri-no-short-fd-operations -use std::ffi::CString; use std::fs::remove_file; use std::mem::MaybeUninit; @@ -17,7 +16,7 @@ mod libc_utils; fn main() { let path = utils::prepare_with_content("fail-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]); - let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(&path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); diff --git a/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs b/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs index 3382670b50f18..39d2acffc0708 100644 --- a/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs +++ b/src/tools/miri/tests/fail-dep/libc/open_wrong_var_arg_type.rs @@ -4,13 +4,10 @@ #![allow(invalid_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr}; -use std::os::unix::ffi::OsStrExt; - use libc::open; fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let _fd = unsafe { open(c_path.as_ptr(), libc::O_CREAT, /* should be mode_t */ 0u64) //~^ ERROR: /expected argument #3 to have type `(u32|i32)` but got incompatible type `u64`/ diff --git a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs index d355da73174e9..5fa07989c895f 100644 --- a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs @@ -3,8 +3,7 @@ #![allow(suspicious_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int}; extern "C" { fn open(path: *const c_char, oflag: c_int, ...) -> c_int; @@ -13,7 +12,7 @@ extern "C" { } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) } as u32; diff --git a/src/tools/miri/tests/fail/shims/return_type_mismatch.rs b/src/tools/miri/tests/fail/shims/return_type_mismatch.rs index bcde5d8040f59..1b22dfbd44872 100644 --- a/src/tools/miri/tests/fail/shims/return_type_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/return_type_mismatch.rs @@ -3,8 +3,7 @@ #![allow(suspicious_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int, c_short}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int, c_short}; extern "C" { fn open(path: *const c_char, oflag: c_int, ...) -> c_int; @@ -13,7 +12,7 @@ extern "C" { } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) }; diff --git a/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs b/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs index ce0c6c289faf0..c00a1f0b22814 100644 --- a/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/vararg_callee_signature_mismatch.rs @@ -3,8 +3,7 @@ #![allow(invalid_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int}; // Declare a variadic function as non-variadic. extern "C" { @@ -12,7 +11,7 @@ extern "C" { } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let _fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) //~^ ERROR: is a c-variadic function, but the caller is using a non-variadic signature diff --git a/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs index 34f6ae24f3e1d..f4e7a02b838fa 100644 --- a/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs +++ b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs @@ -3,15 +3,14 @@ #![allow(invalid_runtime_symbol_definitions)] -use std::ffi::{CString, OsStr, c_char, c_int}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{c_char, c_int}; extern "C" { fn open(path: *const c_char, ...) -> c_int; } fn main() { - let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let c_path = c"./text"; let _fd = unsafe { open(c_path.as_ptr(), /* value does not matter */ 0) //~^ ERROR: takes 2 fixed (non-variadic) arguments, but 1 argument was given diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs index 819717fed8523..dd79c96e179c8 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs @@ -3,9 +3,8 @@ //@compile-flags: -Zmiri-disable-isolation //@run-native -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use std::mem::MaybeUninit; -use std::os::unix::ffi::OsStrExt; #[path = "../../utils/mod.rs"] mod utils; @@ -28,7 +27,7 @@ fn getmod(path: &CStr) -> u32 { fn test_chmod() { let path = utils::prepare_with_content("miri_test_libc_chmod.txt", b"abcdef"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); unsafe { errno_check(libc::chmod(c_path.as_ptr(), 0o777)) }; assert_eq!(getmod(&c_path), 0o777); @@ -38,7 +37,7 @@ fn test_chmod() { fn test_fchmod() { let path = utils::prepare_with_content("miri_test_libc_chmod.txt", b"abcdef"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); let fd = unsafe { errno_result(libc::open(c_path.as_ptr(), libc::O_RDONLY)).unwrap() }; unsafe { errno_check(libc::fchmod(fd, 0o777)) }; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index af67ca16bd2f1..cf25c13a78bcc 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -159,7 +159,7 @@ fn test_statx_on_file_descriptor() { fn test_statx_on_file_path() { let bytes = b"hello"; let path = utils::prepare_with_content("miri_test_libc_statx.txt", bytes); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let meta = fs::metadata(&path).unwrap(); // Relative to working directory. @@ -257,14 +257,14 @@ fn test_statx_empty_path_on_pipe() { fn test_file_open_allow_two_args() { let path = utils::prepare_with_content("test_file_open_allow_two_args.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY) }).unwrap(); } fn test_file_open_needs_three_args() { let path = utils::prepare_with_content("test_file_open_needs_three_args.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_CREAT | libc::O_RDWR, 0o666) }) @@ -273,7 +273,7 @@ fn test_file_open_needs_three_args() { fn test_file_open_extra_third_arg() { let path = utils::prepare_with_content("test_file_open_extra_third_arg.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY, 42) }).unwrap(); } @@ -281,7 +281,7 @@ fn test_file_open_extra_third_arg() { fn test_file_open_nofollow() { let bytes = b"Hello, World!\n"; let path = utils::prepare_with_content("test_nofollow_not_symlink.txt", bytes); - let cpath = CString::new(path.as_os_str().as_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = errno_result(unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }) .unwrap(); @@ -292,7 +292,7 @@ fn test_file_open_nofollow() { let symlink_path = utils::prepare("miri_test_open_nofollow_symlink.txt"); std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - let symlink_cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let symlink_cpath = utils::into_c_string(symlink_path); let err = errno_result(unsafe { libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) @@ -315,7 +315,7 @@ fn test_dup_stdout_stderr() { fn test_dup() { let bytes = b"dup and dup2"; let path = utils::prepare_with_content("miri_test_libc_dup.txt", bytes); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let name = utils::into_c_string(path); unsafe { let fd = errno_result(libc::open(name.as_ptr(), libc::O_RDONLY)).unwrap(); @@ -377,8 +377,8 @@ fn test_rename() { let file = File::create(&path1).unwrap(); drop(file); - let c_path1 = CString::new(path1.as_os_str().as_bytes()).expect("CString::new failed"); - let c_path2 = CString::new(path2.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path1 = utils::into_c_string(&path1); + let c_path2 = utils::into_c_string(&path2); // Renaming should succeed unsafe { libc::rename(c_path1.as_ptr(), c_path2.as_ptr()) }; @@ -408,7 +408,7 @@ fn test_ftruncate>( file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDWR) }; // Truncate to a bigger size @@ -510,11 +510,11 @@ fn test_posix_mkstemp() { /// Test allocating variant of `realpath`. fn test_posix_realpath_alloc() { - use std::os::unix::ffi::{OsStrExt, OsStringExt}; + use std::os::unix::ffi::OsStringExt; let buf; let path = utils::prepare("miri_test_libc_posix_realpath_alloc"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); // Create file. drop(File::create(&path).unwrap()); @@ -533,11 +533,10 @@ fn test_posix_realpath_alloc() { /// Test non-allocating variant of `realpath`. fn test_posix_realpath_noalloc() { - use std::ffi::{CStr, CString}; - use std::os::unix::ffi::OsStrExt; + use std::ffi::CStr; let path = utils::prepare("miri_test_libc_posix_realpath_noalloc"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let mut v = vec![0; libc::PATH_MAX as usize]; @@ -558,16 +557,12 @@ fn test_posix_realpath_noalloc() { /// Test failure cases for `realpath`. fn test_posix_realpath_errors() { - use std::ffi::CString; - use std::io::ErrorKind; - // Test nonexistent path returns an error. - let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed"); + let c_path = c"./nothing_to_see_here"; let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) }; assert!(r.is_null()); let e = std::io::Error::last_os_error(); assert_eq!(e.raw_os_error(), Some(libc::ENOENT)); - assert_eq!(e.kind(), ErrorKind::NotFound); } #[cfg(target_os = "linux")] @@ -619,7 +614,7 @@ fn test_posix_fallocate>( assert_eq!(ret, libc::EINVAL); // fd not writable - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDONLY) }; let ret = unsafe { posix_fallocate(fd, T::from(0), T::from(10)) }; assert_eq!(ret, libc::EBADF); @@ -633,7 +628,7 @@ fn test_posix_fallocate>( file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDWR) }; // Allocate to a bigger size from offset 0 @@ -696,7 +691,7 @@ fn test_fallocate>( assert_eq!(err.raw_os_error().unwrap(), libc::EINVAL); // fd not writable - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(path); let fd = errno_result(unsafe { libc::open(c_path.as_ptr(), libc::O_RDONLY) }).unwrap(); let err = errno_result(unsafe { fallocate(fd, 0, T::from(0), T::from(10)) }).unwrap_err(); assert_eq!(err.raw_os_error().unwrap(), libc::EBADF); @@ -709,7 +704,7 @@ fn test_fallocate>( file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path = utils::into_c_string(&path); let fd = errno_result(unsafe { libc::open(c_path.as_ptr(), libc::O_RDWR) }).unwrap(); // Allocate to a bigger size from offset 0 @@ -1001,7 +996,7 @@ fn test_read_and_uninit() { { // We test that libc::read initializes its buffer. let path = utils::prepare_with_content("pass-libc-read-and-uninit.txt", &[1u8, 2, 3]); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); @@ -1018,7 +1013,7 @@ fn test_read_and_uninit() { // 3 bytes (not 4) will be overwritten, and remaining byte will be left as-is. let data = [1u8, 2, 3]; let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &data); - let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(&path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); @@ -1041,7 +1036,7 @@ fn test_read_and_uninit() { #[cfg(target_os = "macos")] fn test_ioctl() { let path = utils::prepare_with_content("miri_test_libc_ioctl.txt", &[]); - let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let name = utils::into_c_string(path); unsafe { // 100 surely is an invalid FD. @@ -1058,7 +1053,7 @@ fn test_opendir_closedir() { // dir should exist let path = utils::prepare_dir("miri_test_libc_opendir_closedir"); create_dir(&path).expect("create_dir failed"); - let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let cpath = utils::into_c_string(&path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); errno_check(unsafe { libc::closedir(dir) }); @@ -1073,7 +1068,7 @@ fn test_opendir_closedir() { // open normal file as dir should fail let file_path = utils::prepare_with_content("test_not_a_dir.txt", b"hello"); - let cfile = CString::new(file_path.as_os_str().as_bytes()).expect("CString::new failed"); + let cfile = utils::into_c_string(&file_path); let dir: *mut libc::DIR = unsafe { libc::opendir(cfile.as_ptr()) }; assert!(dir.is_null()); let e = std::io::Error::last_os_error(); @@ -1094,7 +1089,7 @@ fn test_readdir() { write(&file1, b"content1").unwrap(); write(&file2, b"content2").unwrap(); - let c_path = CString::new(dir_path.as_os_str().as_bytes()).unwrap(); + let c_path = utils::into_c_string(&dir_path); unsafe { let dirp = libc::opendir(c_path.as_ptr()); @@ -1123,7 +1118,7 @@ fn test_readdir() { fn test_dirfd() { let path = utils::prepare_dir("miri_test_libc_opendir_closedir"); create_dir(&path).expect("create_dir failed"); - let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let cpath = utils::into_c_string(path); let dir: *mut libc::DIR = unsafe { libc::opendir(cpath.as_ptr()) }; assert!(!dir.is_null()); @@ -1170,7 +1165,7 @@ pub fn check_stat_fields(stat: &libc::stat) { fn test_readv() { let file_contents = [1u8, 2, 3, 4, 5, 6]; let path = utils::prepare_with_content("pass-libc-readv.txt", &file_contents); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1206,7 +1201,7 @@ fn test_readv_empty_bufs() { } let path = utils::prepare_with_content("pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); unsafe { assert_eq!(errno_result(libc::readv(fd, ptr::null::(), 0)).unwrap(), 0) }; @@ -1220,7 +1215,7 @@ fn test_readv_empty_bufs() { fn test_preadv() { let file_contents = [1u8, 2, 3, 4, 5, 6]; let path = utils::prepare_with_content("pass-libc-preadv.txt", &file_contents); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1261,7 +1256,7 @@ fn test_preadv() { fn test_pread() { let file_contents = [1u8, 2, 3, 4, 5, 6]; let path = utils::prepare_with_content("pass-libc-pread.txt", &file_contents); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1288,7 +1283,7 @@ fn test_pread() { /// Test vectored writes with multiple buffers. fn test_writev() { let path = utils::prepare_with_content("pass-libc-writev.txt", &[]); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1339,7 +1334,7 @@ fn test_writev_empty_bufs() { } let path = utils::prepare_with_content("pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); unsafe { @@ -1354,7 +1349,7 @@ fn test_writev_empty_bufs() { #[cfg(not(target_os = "solaris"))] fn test_pwritev() { let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1410,7 +1405,7 @@ fn test_pwritev() { /// Test writing with an offset. fn test_pwrite() { let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); - let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1460,7 +1455,7 @@ fn test_readlink() { // Test that the expected string gets written to a buffer of proper // length, and that a trailing null byte is not written. - let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let symlink_c_str = utils::into_c_string(symlink_path); let symlink_c_ptr = symlink_c_str.as_ptr(); // Make the buf one byte larger than it needs to be, @@ -1501,8 +1496,8 @@ fn test_linkat() { let source = utils::prepare_with_content("miri_test_libc_linkat_source.txt", b"hello"); let link = utils::prepare("miri_test_libc_linkat_link.txt"); - let c_source = CString::new(source.as_os_str().as_bytes()).expect("CString::new failed"); - let c_link = CString::new(link.as_os_str().as_bytes()).expect("CString::new failed"); + let c_source = utils::into_c_string(&source); + let c_link = utils::into_c_string(&link); // Call linkat unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs index 4e46870da204b..40a3a60068d90 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-invalid-addr.rs @@ -7,7 +7,6 @@ mod libc_utils; #[path = "../../utils/mod.rs"] mod utils; -use std::ffi::CString; use std::ptr; use libc_utils::*; @@ -15,8 +14,8 @@ use libc_utils::*; /// Test doing address resolution using the `getaddrinfo` syscall. /// This also tests freeing the address linked list using `freeaddrinfo`. fn main() { - let node_c_str = CString::new("this-is-not-a-valid-address").unwrap(); - let service_c_str = CString::new("8080").unwrap(); + let node_c_str = c"this-is-not-a-valid-address"; + let service_c_str = c"8080"; let mut hints: libc::addrinfo = unsafe { std::mem::zeroed() }; hints.ai_socktype = libc::SOCK_STREAM; diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs index 2fe1f15836b36..b18622a1994d7 100644 --- a/src/tools/miri/tests/utils/fs.rs +++ b/src/tools/miri/tests/utils/fs.rs @@ -1,8 +1,8 @@ -use std::ffi::{CStr, CString, OsString}; +use std::ffi::{CStr, OsString}; use std::path::PathBuf; use std::{fs, io}; -use super::miri_extern; +use super::{into_c_string, miri_extern}; pub fn host_to_target_path(path: OsString) -> PathBuf { let path = into_c_string(path); @@ -48,8 +48,3 @@ pub fn prepare_dir(dirname: &str) -> PathBuf { fs::remove_dir_all(&path).ok(); path } - -/// Return a `CString` version of the given path. -pub fn into_c_string(path: impl Into) -> CString { - CString::new(path.into().into_string().unwrap()).unwrap() -} diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index f855f115e43dc..3fe655350776c 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![allow(unused_imports)] +use std::ffi::{CString, OsString}; + #[macro_use] mod macros; @@ -12,6 +14,11 @@ pub use self::fs::*; pub use self::io::*; pub use self::miri_extern::*; +/// Return a `CString` version of the given string. +pub fn into_c_string(str: impl Into) -> CString { + CString::new(str.into().into_string().unwrap()).unwrap() +} + pub fn run_provenance_gc() { // SAFETY: No preconditions. The GC is fine to run at any time. unsafe { miri_run_provenance_gc() } From 5196402951e7b777c211d981518d55c0a91adec4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Sep 2026 22:27:18 +0200 Subject: [PATCH 59/65] organize and extend statx tests --- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 139 ++++++++++++------ 1 file changed, 94 insertions(+), 45 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index cf25c13a78bcc..41c5a3ea3ea69 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -63,11 +63,9 @@ fn main() { #[cfg(target_os = "macos")] test_ioctl(); #[cfg(target_os = "linux")] - test_statx_on_file_path(); + test_statx(); #[cfg(target_os = "linux")] - test_statx_on_file_descriptor(); - #[cfg(target_os = "linux")] - test_statx_empty_path_on_pipe(); + test_statx_on_empty_path(); test_readv(); test_readv_empty_bufs(); #[cfg(not(target_os = "solaris"))] @@ -117,46 +115,7 @@ fn assert_statx_matches_metadata(stx: &libc::statx, meta: &fs::Metadata, expecte } #[cfg(target_os = "linux")] -fn test_statx_on_file_descriptor() { - let bytes = b"hello"; - let path = utils::prepare_with_content("miri_test_libc_statx_fd.txt", bytes); - let file = File::open(&path).unwrap(); - let meta = file.metadata().unwrap(); - - unsafe { - let mut stx = MaybeUninit::::zeroed(); - errno_check(libc::statx( - file.as_raw_fd(), - c"".as_ptr(), - libc::AT_EMPTY_PATH, - libc::STATX_BASIC_STATS | libc::STATX_BTIME, - stx.as_mut_ptr(), - )); - - let stx = stx.assume_init(); - assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); - } - - // If we don't set AT_EMPTY_PATH, we get an error. - unsafe { - let mut stx = MaybeUninit::::zeroed(); - let err = errno_result(libc::statx( - file.as_raw_fd(), - c"".as_ptr(), - 0, - libc::STATX_BASIC_STATS | libc::STATX_BTIME, - stx.as_mut_ptr(), - )) - .unwrap_err(); - assert_eq!(err.raw_os_error().unwrap(), libc::ENOENT); - } - - drop(file); - remove_file(&path).unwrap(); -} - -#[cfg(target_os = "linux")] -fn test_statx_on_file_path() { +fn test_statx() { let bytes = b"hello"; let path = utils::prepare_with_content("miri_test_libc_statx.txt", bytes); let c_path = utils::into_c_string(&path); @@ -213,11 +172,101 @@ fn test_statx_on_file_path() { assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); } + // Symlink following. + let symlinkpath = utils::prepare("miri_test_libc_statx.link"); + let c_symlinkpath = utils::into_c_string(&symlinkpath); + std::os::unix::fs::symlink(&path, &symlinkpath).unwrap(); + unsafe { + let mut stx = MaybeUninit::::zeroed(); + errno_check(libc::statx( + 999, // dirfd + c_symlinkpath.as_ptr(), + libc::AT_EMPTY_PATH, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )); + let stx = stx.assume_init(); + assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + } + unsafe { + let mut stx = MaybeUninit::::zeroed(); + errno_check(libc::statx( + 999, // dirfd + c_symlinkpath.as_ptr(), + libc::AT_SYMLINK_NOFOLLOW, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )); + let stx = stx.assume_init(); + assert!(stx.stx_mask & libc::STATX_TYPE != 0); + assert_eq!((stx.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFLNK); + assert!(stx.stx_mask & libc::STATX_MODE != 0); + assert_ne!((stx.stx_mode as libc::mode_t) & !libc::S_IFMT, 0); + } + + // Relative to a dirfd. + // The only way to get a dirfd in Miri currently is via a `dirfd`. Slightly silly, but whatever. + let dirstream = unsafe { libc::opendir(utils::into_c_string(path.parent().unwrap()).as_ptr()) }; + assert!(!dirstream.is_null()); + let dirfd = unsafe { libc::dirfd(dirstream) }; + unsafe { + let mut stx = MaybeUninit::::zeroed(); + errno_check(libc::statx( + dirfd, + c"miri_test_libc_statx.txt".as_ptr(), + libc::AT_EMPTY_PATH, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )); + + let stx = stx.assume_init(); + assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + } + errno_check(unsafe { libc::closedir(dirstream) }); + remove_file(&path).unwrap(); + remove_file(&symlinkpath).unwrap(); } #[cfg(target_os = "linux")] -fn test_statx_empty_path_on_pipe() { +fn test_statx_on_empty_path() { + let bytes = b"hello"; + let path = utils::prepare_with_content("miri_test_libc_statx_fd.txt", bytes); + let file = File::open(&path).unwrap(); + let meta = file.metadata().unwrap(); + + unsafe { + let mut stx = MaybeUninit::::zeroed(); + errno_check(libc::statx( + file.as_raw_fd(), + c"".as_ptr(), + libc::AT_EMPTY_PATH, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )); + + let stx = stx.assume_init(); + assert_statx_matches_metadata(&stx, &meta, bytes.len() as u64); + } + + // If we don't set AT_EMPTY_PATH, we get an error. + unsafe { + let mut stx = MaybeUninit::::zeroed(); + let err = errno_result(libc::statx( + file.as_raw_fd(), + c"".as_ptr(), + 0, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + stx.as_mut_ptr(), + )) + .unwrap_err(); + assert_eq!(err.raw_os_error().unwrap(), libc::ENOENT); + } + + drop(file); + remove_file(&path).unwrap(); + + // Test it on a pipe as well. unsafe { let mut fds = [0; 2]; errno_check(libc::pipe(fds.as_mut_ptr())); From 2cf7527ce291ce1654f44a94b2b9f647d30a8540 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Sep 2026 23:30:26 +0200 Subject: [PATCH 60/65] prefix all test files with miri --- .../libc-read-and-uninit-premature-eof.rs | 2 +- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 34 +++++++++---------- .../miri/tests/pass-dep/shims/windows-fs.rs | 22 ++++++------ .../miri/tests/pass-dep/tokio/file-io.rs | 4 +-- src/tools/miri/tests/utils/fs.rs | 4 +++ 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs index f3e96a9c26f75..aa9cbe4298105 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs @@ -15,7 +15,7 @@ mod libc_utils; fn main() { let path = - utils::prepare_with_content("fail-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]); + utils::prepare_with_content("miri-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]); let cpath = utils::into_c_string(&path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 41c5a3ea3ea69..b3f063ef07cba 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -305,14 +305,14 @@ fn test_statx_on_empty_path() { } fn test_file_open_allow_two_args() { - let path = utils::prepare_with_content("test_file_open_allow_two_args.txt", &[]); + let path = utils::prepare_with_content("miri_test_file_open_allow_two_args.txt", &[]); let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY) }).unwrap(); } fn test_file_open_needs_three_args() { - let path = utils::prepare_with_content("test_file_open_needs_three_args.txt", &[]); + let path = utils::prepare_with_content("miri_test_file_open_needs_three_args.txt", &[]); let name = utils::into_c_string(path); let _fd = @@ -321,7 +321,7 @@ fn test_file_open_needs_three_args() { } fn test_file_open_extra_third_arg() { - let path = utils::prepare_with_content("test_file_open_extra_third_arg.txt", &[]); + let path = utils::prepare_with_content("miri_test_file_open_extra_third_arg.txt", &[]); let name = utils::into_c_string(path); let _fd = errno_result(unsafe { libc::open(name.as_ptr(), libc::O_RDONLY, 42) }).unwrap(); @@ -329,7 +329,7 @@ fn test_file_open_extra_third_arg() { fn test_file_open_nofollow() { let bytes = b"Hello, World!\n"; - let path = utils::prepare_with_content("test_nofollow_not_symlink.txt", bytes); + let path = utils::prepare_with_content("miri_test_nofollow_not_symlink.txt", bytes); let cpath = utils::into_c_string(path); let fd = errno_result(unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }) @@ -856,7 +856,7 @@ fn test_fstat() { } fn test_fstatat() { - let testdir = utils::prepare_dir("test_fstatat"); + let testdir = utils::prepare_dir("miri_test_fstatat"); fs::create_dir(&testdir).unwrap(); let filename = "file.txt"; let cfilename = c"file.txt"; @@ -1029,7 +1029,7 @@ fn test_isatty() { libc::isatty(libc::STDERR_FILENO); // But when we open a file, it is definitely not a TTY. - let path = utils::prepare("notatty.txt"); + let path = utils::prepare("miri_notatty.txt"); let file = File::create(&path).unwrap(); assert_eq!(libc::isatty(file.as_raw_fd()), 0); @@ -1044,7 +1044,7 @@ fn test_isatty() { fn test_read_and_uninit() { { // We test that libc::read initializes its buffer. - let path = utils::prepare_with_content("pass-libc-read-and-uninit.txt", &[1u8, 2, 3]); + let path = utils::prepare_with_content("miri-pass-libc-read-and-uninit.txt", &[1u8, 2, 3]); let cpath = utils::into_c_string(path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); @@ -1061,7 +1061,7 @@ fn test_read_and_uninit() { // We test that if we requested to read 4 bytes, but actually read 3 bytes, then // 3 bytes (not 4) will be overwritten, and remaining byte will be left as-is. let data = [1u8, 2, 3]; - let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &data); + let path = utils::prepare_with_content("miri-pass-libc-read-and-uninit-2.txt", &data); let cpath = utils::into_c_string(&path); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); @@ -1116,7 +1116,7 @@ fn test_opendir_closedir() { assert_eq!(e.kind(), ErrorKind::NotFound); // open normal file as dir should fail - let file_path = utils::prepare_with_content("test_not_a_dir.txt", b"hello"); + let file_path = utils::prepare_with_content("miri_test_not_a_dir.txt", b"hello"); let cfile = utils::into_c_string(&file_path); let dir: *mut libc::DIR = unsafe { libc::opendir(cfile.as_ptr()) }; assert!(dir.is_null()); @@ -1213,7 +1213,7 @@ pub fn check_stat_fields(stat: &libc::stat) { /// Test vectored reads with multiple buffers. fn test_readv() { let file_contents = [1u8, 2, 3, 4, 5, 6]; - let path = utils::prepare_with_content("pass-libc-readv.txt", &file_contents); + let path = utils::prepare_with_content("miri-pass-libc-readv.txt", &file_contents); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1249,7 +1249,7 @@ fn test_readv_empty_bufs() { return; } - let path = utils::prepare_with_content("pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); + let path = utils::prepare_with_content("miri-pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1263,7 +1263,7 @@ fn test_readv_empty_bufs() { #[cfg(not(target_os = "solaris"))] fn test_preadv() { let file_contents = [1u8, 2, 3, 4, 5, 6]; - let path = utils::prepare_with_content("pass-libc-preadv.txt", &file_contents); + let path = utils::prepare_with_content("miri-pass-libc-preadv.txt", &file_contents); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1304,7 +1304,7 @@ fn test_preadv() { /// Test reading with an offset. fn test_pread() { let file_contents = [1u8, 2, 3, 4, 5, 6]; - let path = utils::prepare_with_content("pass-libc-pread.txt", &file_contents); + let path = utils::prepare_with_content("miri-pass-libc-pread.txt", &file_contents); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; assert_ne!(fd, -1); @@ -1331,7 +1331,7 @@ fn test_pread() { /// Test vectored writes with multiple buffers. fn test_writev() { - let path = utils::prepare_with_content("pass-libc-writev.txt", &[]); + let path = utils::prepare_with_content("miri-pass-libc-writev.txt", &[]); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1382,7 +1382,7 @@ fn test_writev_empty_bufs() { return; } - let path = utils::prepare_with_content("pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); + let path = utils::prepare_with_content("miri-pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1397,7 +1397,7 @@ fn test_writev_empty_bufs() { /// doesn't have `pwritev`. #[cfg(not(target_os = "solaris"))] fn test_pwritev() { - let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); + let path = utils::prepare_with_content("miri-pass-libc-pwritev.txt", &[]); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); @@ -1453,7 +1453,7 @@ fn test_pwritev() { /// Test writing with an offset. fn test_pwrite() { - let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); + let path = utils::prepare_with_content("miri-pass-libc-pwritev.txt", &[]); let cpath = utils::into_c_string(path); let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; assert_ne!(fd, -1); diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs index ecac238240872..b012b7328eaf3 100644 --- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -77,7 +77,7 @@ unsafe fn test_create_dir_file() { } unsafe fn test_create_normal_file() { - let temp = utils::prepare("test_create_normal_file.txt"); + let temp = utils::prepare("miri_test_create_normal_file.txt"); let raw_path = to_wide_cstr(&temp); let handle = CreateFileW( raw_path.as_ptr(), @@ -134,7 +134,7 @@ unsafe fn test_create_normal_file() { /// Tests that CREATE_ALWAYS sets the error value correctly based on whether the file already exists unsafe fn test_create_always_twice() { - let temp = utils::prepare("test_create_always.txt"); + let temp = utils::prepare("miri_test_create_always.txt"); let raw_path = to_wide_cstr(&temp); let handle = CreateFileW( raw_path.as_ptr(), @@ -169,7 +169,7 @@ unsafe fn test_create_always_twice() { /// Tests that OPEN_ALWAYS sets the error value correctly based on whether the file already exists unsafe fn test_open_always_twice() { - let temp = utils::prepare("test_open_always.txt"); + let temp = utils::prepare("miri_test_open_always.txt"); let raw_path = to_wide_cstr(&temp); let handle = CreateFileW( raw_path.as_ptr(), @@ -228,7 +228,7 @@ unsafe fn test_open_always_twice() { // } unsafe fn test_delete_file() { - let temp = utils::prepare("test_delete_file.txt"); + let temp = utils::prepare("miri_test_delete_file.txt"); let raw_path = to_wide_cstr(&temp); let _ = fs::File::create(&temp).unwrap(); @@ -249,7 +249,7 @@ unsafe fn test_ntstatus_to_dos() { } unsafe fn test_file_read_write() { - let temp = utils::prepare("test_file_read_write.txt"); + let temp = utils::prepare("miri_test_file_read_write.txt"); let file = fs::File::create(&temp).unwrap(); let handle = file.as_raw_handle(); @@ -300,7 +300,7 @@ unsafe fn test_file_read_write() { } unsafe fn test_set_file_info() { - let temp = utils::prepare("test_set_file.txt"); + let temp = utils::prepare("miri_test_set_file.txt"); let mut file = fs::File::create(&temp).unwrap(); let handle = file.as_raw_handle(); @@ -326,7 +326,7 @@ unsafe fn test_set_file_info() { } unsafe fn test_dup_handle() { - let temp = utils::prepare("test_dup.txt"); + let temp = utils::prepare("miri_test_dup.txt"); let mut file1 = fs::File::options().read(true).write(true).create(true).open(&temp).unwrap(); @@ -359,7 +359,7 @@ unsafe fn test_dup_handle() { } unsafe fn test_file_seek() { - let temp = utils::prepare("test_file_seek.txt"); + let temp = utils::prepare("miri_test_file_seek.txt"); let mut file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap(); file.write_all(b"Hello, World!\n").unwrap(); @@ -384,7 +384,7 @@ unsafe fn test_file_seek() { } unsafe fn test_flush_buffers() { - let temp = utils::prepare("test_flush_buffers.txt"); + let temp = utils::prepare("miri_test_flush_buffers.txt"); let file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap(); if FlushFileBuffers(file.as_raw_handle()) == 0 { panic!("Failed to flush buffers"); @@ -397,8 +397,8 @@ unsafe fn test_flush_buffers() { } unsafe fn test_move_file() { - let temp = utils::prepare("test_move_file.txt"); - let temp_new = utils::prepare("test_move_file_new.txt"); + let temp = utils::prepare("miri_test_move_file.txt"); + let temp_new = utils::prepare("miri_test_move_file_new.txt"); let mut file = fs::File::options().create(true).write(true).open(&temp).unwrap(); file.write_all(b"Hello, World!\n").unwrap(); diff --git a/src/tools/miri/tests/pass-dep/tokio/file-io.rs b/src/tools/miri/tests/pass-dep/tokio/file-io.rs index 982768111a511..d7704af1402e9 100644 --- a/src/tools/miri/tests/pass-dep/tokio/file-io.rs +++ b/src/tools/miri/tests/pass-dep/tokio/file-io.rs @@ -17,7 +17,7 @@ async fn main() { } async fn test_create_and_write() -> io::Result<()> { - let path = utils::prepare("foo.txt"); + let path = utils::prepare("miri_tokio_test_create_and_write.txt"); let mut file = File::create(&path).await?; // Write 10 bytes to the file. @@ -34,7 +34,7 @@ async fn test_create_and_write() -> io::Result<()> { async fn test_create_and_read() -> io::Result<()> { let bytes = b"more bytes"; - let path = utils::prepare_with_content("foo.txt", bytes); + let path = utils::prepare_with_content("miri_tokio_test_create_and_read.txt", bytes); let mut file = OpenOptions::new().read(true).open(&path).await.unwrap(); let mut buffer = vec![]; diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs index b18622a1994d7..afcd66dd16e58 100644 --- a/src/tools/miri/tests/utils/fs.rs +++ b/src/tools/miri/tests/utils/fs.rs @@ -28,6 +28,8 @@ pub fn tmp() -> PathBuf { /// Prepare: compute filename and make sure the file does not exist. pub fn prepare(filename: &str) -> PathBuf { + assert!(filename.starts_with("miri")); + let path = tmp().join(filename); // Clean the paths for robustness. fs::remove_file(&path).ok(); @@ -43,6 +45,8 @@ pub fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { /// Prepare directory: compute directory name and make sure it does not exist. pub fn prepare_dir(dirname: &str) -> PathBuf { + assert!(dirname.starts_with("miri")); + let path = tmp().join(&dirname); // Clean the directory for robustness. fs::remove_dir_all(&path).ok(); From c6e51b3018f1315079ea2256c27672c21f429ded Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 30 Sep 2026 07:51:58 +0200 Subject: [PATCH 61/65] implement fstatat and the equivalent parts of statx --- src/tools/miri/src/shims/files.rs | 19 +++- .../miri/src/shims/unix/foreign_items.rs | 8 ++ src/tools/miri/src/shims/unix/fs.rs | 103 ++++++++++++++++-- .../src/shims/unix/macos/foreign_items.rs | 8 ++ src/tools/miri/src/shims/windows/fs.rs | 11 +- 5 files changed, 135 insertions(+), 14 deletions(-) diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index cd7a7f4f0250b..d842f2f69b713 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -513,7 +513,24 @@ impl FileDescription for FileHandle { #[derive(Debug)] pub struct DirHandle { - pub(crate) dir: Dir, + pub(super) dir: Dir, + #[cfg(bootstrap)] + pub(super) fallback: std::path::PathBuf, + #[cfg(not(bootstrap))] + #[expect(unused)] + fallback: (), +} + +impl DirHandle { + pub fn open(path: &std::path::Path) -> io::Result { + #[cfg(bootstrap)] + let fallback = path.canonicalize()?; + #[cfg(not(bootstrap))] + let fallback = (); + + let dir = Dir::open(path)?; + Ok(DirHandle { dir, fallback }) + } } impl FileDescription for DirHandle { diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index f78c92de97bcc..54639db6e19cf 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -404,6 +404,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } + "fstatat" => { + let [dirfd, path, buf, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, *_, i32) -> i32), + (link_name, abi, args), + )?; + let result = this.fstatat(dirfd, path, buf, flags)?; + this.write_scalar(result, dest)?; + } "chmod" => { let [path, mode] = this.check_shim_sig( shim_sig!(extern "C" fn(*_, libc::mode_t) -> i32), diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index d67d601df36c9..1b9133785ed4a 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::ffi::OsString; -use std::fs::{self, Dir, DirBuilder, File, FileTimes, FileType, OpenOptions, TryLockError}; +use std::fs::{self, DirBuilder, File, FileTimes, FileType, OpenOptions, TryLockError}; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; use std::path::{self, Path}; use std::time::SystemTime; @@ -782,7 +782,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fstat`", reject_with)?; - // Set error code as "EBADF" (bad fd) + // Set error code as "EBADF" (bad fd); "EACCESS" does not make much sense here. return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); } @@ -793,6 +793,83 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?)) } + fn fstatat( + &mut self, + dirfd_op: &OpTy<'tcx>, + path_op: &OpTy<'tcx>, + buf_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + if !matches!( + &this.tcx.sess.target.os, + Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Linux | Os::Android + ) { + panic!("`fstatat` should not be called on {}", this.tcx.sess.target.os); + } + + let dirfd = this.read_scalar(dirfd_op)?.to_i32()?; + let path = this.read_pointer(path_op)?; + let flags = this.read_scalar(flags_op)?.to_i32()?; + + // Reject if isolation is enabled. + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`fstatat`", reject_with)?; + return this.set_errno_and_return_neg1_i32(LibcError("EACCES")); + } + + // Parse flags. + let mut flags = flags; + // AT_SYMLINK_NOFOLLOW + let at_symlink_nofollow = this.eval_libc_i32("AT_SYMLINK_NOFOLLOW"); + let symlink_nofollow_flag = flags & at_symlink_nofollow == at_symlink_nofollow; + flags &= !at_symlink_nofollow; + // Complain about unknown flags. + if flags != 0 { + throw_unsup_format!("unsupported flags for `fstatat`: {flags:#x}") + } + + let path = this.read_path_from_c_str(path)?.into_owned(); + let metadata = if path.is_empty() { + throw_unsup_format!("fstatat: empty path is not supported"); + } else if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD") { + // Either absolute path (dirfd is ignored) or relative to working directory. + FileMetadata::from_host( + this, + if symlink_nofollow_flag { path.symlink_metadata() } else { path.metadata() }, + )? + } else { + // relative to dirfd, which must be a directory handle + let Some(fd) = this.machine.fds.get(dirfd) else { + return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); + }; + let Some(dir) = fd.downcast::() else { + return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); + }; + + #[cfg(not(bootstrap))] + let metadata = if symlink_nofollow_flag { + dir.dir.symlink_metadata(path) + } else { + dir.dir.metadata(path) + }; + #[cfg(bootstrap)] + let metadata = if symlink_nofollow_flag { + dir.fallback.join(path).symlink_metadata() + } else { + dir.fallback.join(path).metadata() + }; + FileMetadata::from_host(this, metadata)? + }; + + let metadata = match metadata { + Ok(metadata) => metadata, + Err(err) => return this.set_errno_and_return_neg1_i32(err), + }; + interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?)) + } + fn linux_statx( &mut self, dirfd_op: &OpTy<'tcx>, // Should be an `int` @@ -852,18 +929,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Either absolute path (dirfd is ignored) or relative to working directory. FileMetadata::from_host( this, - if symlink_nofollow_flag { fs::symlink_metadata(path) } else { fs::metadata(path) }, + if symlink_nofollow_flag { path.symlink_metadata() } else { path.metadata() }, )? } else { // relative to dirfd, which must be a directory handle let Some(fd) = this.machine.fds.get(dirfd) else { return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); }; - let Some(_dir) = fd.downcast::() else { + let Some(dir) = fd.downcast::() else { return this.set_errno_and_return_neg1_i32(LibcError("EBADF")); }; - throw_unsup_format!("statx relative to a directory handle is not supported"); + #[cfg(not(bootstrap))] + let metadata = if symlink_nofollow_flag { + dir.dir.symlink_metadata(path) + } else { + dir.dir.metadata(path) + }; + #[cfg(bootstrap)] + let metadata = if symlink_nofollow_flag { + dir.fallback.join(path).symlink_metadata() + } else { + dir.fallback.join(path).metadata() + }; + FileMetadata::from_host(this, metadata)? }; let metadata = match metadata { @@ -1133,12 +1222,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // in between above and here. One day, the standard library will support converting // between `Dir` and `ReadDir` (one of the two directions would suffice for our // needs), then we'll use that. - let Ok(dir) = Dir::open(name) else { + let Ok(dir) = DirHandle::open(&name) else { throw_unsup_format!( "cannot `opendir` this directory: failed to create directory handle" ); }; - let dir = this.machine.fds.new_ref(DirHandle { dir }); + let dir = this.machine.fds.new_ref(dir); let dir_fd_id = dir.id(); let dir_fd_num = this.machine.fds.insert(dir); diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index b33de1b713fea..25ff4fe43b659 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -65,6 +65,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.fstat(fd, buf)?; this.write_scalar(result, dest)?; } + "fstatat$INODE64" => { + let [dirfd, path, buf, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, *_, i32) -> i32), + (link_name, abi, args), + )?; + let result = this.fstatat(dirfd, path, buf, flags)?; + this.write_scalar(result, dest)?; + } "opendir$INODE64" => { let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?; let result = this.opendir(name)?; diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index ff8bad7a16423..6fc9d37070f67 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -1,7 +1,6 @@ -use std::fs::{self, Dir}; -use std::io; use std::io::SeekFrom; use std::time::SystemTime; +use std::{fs, io}; use bitflags::bitflags; use rustc_abi::Size; @@ -214,7 +213,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Open this as a directory. // FIXME: shouldn't we check `creation_disposition` here? We do know that it already // exists. - let dir = match Dir::open(&file_name) { + let dir = match DirHandle::open(&file_name) { Ok(dir) => dir, Err(e) => { if e.kind() == io::ErrorKind::NotADirectory { @@ -226,9 +225,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } }; #[cfg(bootstrap)] - let metadata = dir.metadata(); + let metadata = dir.dir.metadata(); #[cfg(not(bootstrap))] - let metadata = dir.self_metadata(); + let metadata = dir.dir.self_metadata(); if !metadata.unwrap().is_dir() { // This changed from a directory to a file. Retry. continue; @@ -239,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; } - let fd_num = this.machine.fds.insert_new(DirHandle { dir }); + let fd_num = this.machine.fds.insert_new(dir); return interp_ok(Handle::File(fd_num)); } else { // Per the documentation: From f02d1d5224d53e14dabd1f1efd6b7de36ce480e6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:30:42 +0200 Subject: [PATCH 62/65] Prepare for merging from rust-lang/rust This updates the rust-toolchain.toml file to nightly-2026-09-30 (5c543b0b8c73c7b72bc8284ced4fb22ead15734d). --- compiler/rustc_codegen_cranelift/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index 9e21c2ed9a9b2..a6468283e1270 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-28" +channel = "nightly-2026-09-30" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" From f6e6de63d263f429a0318d82dc7f3cef15f9a895 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:50:49 +0200 Subject: [PATCH 63/65] Fix rustc test suite --- compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 5a1374fe56e09..648f68588e04b 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -164,6 +164,7 @@ rm -r tests/run-make/const-trait-stable-toolchain # same rm -r tests/run-make/print-request-help-stable-unstable # same rm -r tests/run-make/issue-149402-suggest-unresolve # same rm -r tests/run-make/const-destruct-stable-toolchain # same +rm -r tests/run-make/unstable-target-feature # same rm -r tests/run-make/incr-add-rust-src-component rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path rm -r tests/run-make/export # something about rustc version mismatches From bbf54426a22d640e4db1bafbf0e9d6b497cfe0aa Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:53:33 +0200 Subject: [PATCH 64/65] Fix pushes --- compiler/rustc_codegen_cranelift/scripts/rustup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index 80e9c4f78c4a6..8bd41f98ce550 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -2,7 +2,7 @@ set -e -export GIT_CONFIG_GLOBAL=../rust/josh.gitconfig +export GIT_CONFIG_GLOBAL=$(pwd)/../rust/josh.gitconfig export RUSTC_GIT=../rust case $1 in From 5658d6b8087cc83c28459f2c02def2b57e3c1896 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 30 Sep 2026 12:12:21 +0200 Subject: [PATCH 65/65] update lockfile --- Cargo.lock | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39049cd7e2c6f..9894e374a0bf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2558,8 +2558,10 @@ dependencies = [ "serde", "serde_json", "smallvec", + "socket2", "tempfile", "ui_test", + "windows-sys 0.61.2", ] [[package]] @@ -5472,9 +5474,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2",