diff --git a/Cargo.lock b/Cargo.lock index bf519e0392605..901113bbff561 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2312,6 +2312,7 @@ name = "miri" version = "0.1.0" dependencies = [ "aes", + "bitflags", "chrono", "chrono-tz", "colored", diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index 04c8162932369..323a8fab6d592 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -13,12 +13,12 @@ pub mod typetree; #[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] pub struct StrippedCfgItem { pub parent_module: ModId, - pub name: Ident, + pub ident: Ident, pub cfg: MetaItem, } impl StrippedCfgItem { pub fn map_mod_id(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem { - StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg } + StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg } } } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 9899ee03a513a..2296b05f69b4c 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -47,7 +47,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_middle::ty::{Asyncness, ResolverAstLowering}; -use rustc_span::{Ident, Span}; +use rustc_span::{Ident, Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode}; @@ -234,12 +234,13 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::FnSig { decl, header, span } } - fn generate_param(&mut self, span: Span) -> (hir::Param<'hir>, NodeId) { + fn generate_param(&mut self, idx: usize, span: Span) -> (hir::Param<'hir>, NodeId) { let pat_node_id = self.next_node_id(); let pat_id = self.lower_node_id(pat_node_id); + let ident = Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}"))); let pat = self.arena.alloc(hir::Pat { hir_id: pat_id, - kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, Ident::empty(), None), + kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, ident, None), span, default_binding_modes: false, }); @@ -247,9 +248,9 @@ impl<'hir> LoweringContext<'_, 'hir> { (hir::Param { hir_id: self.next_id(), pat, ty_span: span, span }, pat_node_id) } - fn generate_arg(&mut self, param_id: HirId, span: Span) -> hir::Expr<'hir> { + fn generate_arg(&mut self, idx: usize, param_id: HirId, span: Span) -> hir::Expr<'hir> { let segments = self.arena.alloc_from_iter(iter::once(hir::PathSegment { - ident: Ident::empty(), + ident: Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}"))), hir_id: self.next_id(), res: Res::Local(param_id), args: None, @@ -273,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut args: Vec> = Vec::with_capacity(param_count); for idx in 0..param_count { - let (param, pat_node_id) = this.generate_param(span); + let (param, pat_node_id) = this.generate_param(idx, span); parameters.push(param); let arg = if let Some(block) = block @@ -289,7 +290,7 @@ impl<'hir> LoweringContext<'_, 'hir> { this.ident_and_label_to_local_id.insert(pat_node_id, param.pat.hir_id.local_id); this.lower_target_expr(&block) } else { - this.generate_arg(param.pat.hir_id, span) + this.generate_arg(idx, param.pat.hir_id, span) }; args.push(arg); } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 958a6917dff87..59099e5a55451 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -645,7 +645,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ( // Disallow `impl Trait` in foreign items. this.lower_fn_decl(fdec, i.id, sig.span, FnDeclKind::ExternFn, None), - this.lower_fn_params_to_names(fdec), + this.lower_fn_params_to_idents(fdec), ) }); @@ -833,7 +833,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }) => { // FIXME(contracts): Deny contract here since it won't apply to // any impl method or callees. - let names = self.lower_fn_params_to_names(&sig.decl); + let idents = self.lower_fn_params_to_idents(&sig.decl); let (generics, sig) = self.lower_method_sig( generics, sig, @@ -851,7 +851,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ( *ident, generics, - hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), + hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(idents)), false, ) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6aa6a18ee9a72..446e02e402420 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1247,7 +1247,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { safety: self.lower_safety(f.safety, hir::Safety::Safe), abi: self.lower_extern(f.ext), decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None), - param_names: self.lower_fn_params_to_names(&f.decl), + param_idents: self.lower_fn_params_to_idents(&f.decl), })) } TyKind::UnsafeBinder(f) => { @@ -1494,7 +1494,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { })) } - fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Option] { + fn lower_fn_params_to_idents(&mut self, decl: &FnDecl) -> &'hir [Option] { self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind { PatKind::Missing => None, PatKind::Ident(_, ident, _) => Some(self.lower_ident(ident)), diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs index 4c42dd1f2023f..bc7f22766a5ca 100644 --- a/compiler/rustc_ast_pretty/src/pprust/tests.rs +++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs @@ -7,12 +7,12 @@ use super::*; fn fun_to_string( decl: &ast::FnDecl, header: ast::FnHeader, - name: Ident, + ident: Ident, generics: &ast::Generics, ) -> String { to_string(|s| { s.head(""); - s.print_fn(decl, header, Some(name), generics); + s.print_fn(decl, header, Some(ident), generics); s.end(); // Close the head box. s.end(); // Close the outer box. }) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 1f4eb0c449f8a..8a8ecc3b96e3c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2500,11 +2500,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let ty::Tuple(params) = tupled_params.kind() else { return }; - // Find the first argument with a matching type, get its name - let Some(this_name) = params.iter().zip(tcx.hir_body_param_names(closure.body)).find_map( - |(param_ty, name)| { + // Find the first argument with a matching type and get its identifier. + let Some(this_name) = params.iter().zip(tcx.hir_body_param_idents(closure.body)).find_map( + |(param_ty, ident)| { // FIXME: also support deref for stuff like `Rc` arguments - if param_ty.peel_refs() == local_ty { name } else { None } + if param_ty.peel_refs() == local_ty { ident } else { None } }, ) else { return; @@ -3774,7 +3774,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { method_args, *fn_span, call_source.from_hir_call(), - self.infcx.tcx.fn_arg_names(method_did)[0], + self.infcx.tcx.fn_arg_idents(method_did)[0], ) { err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`")); diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index eb664f1d4f214..f9f63ae92a841 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1026,7 +1026,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { method_args, *fn_span, call_source.from_hir_call(), - self.infcx.tcx.fn_arg_names(method_did)[0], + self.infcx.tcx.fn_arg_idents(method_did)[0], ); return FnSelfUse { diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 8862965c0532c..a91f2d38a93ae 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -20,14 +20,14 @@ use crate::errors; struct ProcMacroDerive { id: NodeId, trait_name: Symbol, - function_name: Ident, + function_ident: Ident, span: Span, attrs: Vec, } struct ProcMacroDef { id: NodeId, - function_name: Ident, + function_ident: Ident, span: Span, } @@ -95,7 +95,7 @@ impl<'a> CollectProcMacros<'a> { fn collect_custom_derive( &mut self, item: &'a ast::Item, - function_name: Ident, + function_ident: Ident, attr: &'a ast::Attribute, ) { let Some((trait_name, proc_attrs)) = @@ -109,7 +109,7 @@ impl<'a> CollectProcMacros<'a> { id: item.id, span: item.span, trait_name, - function_name, + function_ident, attrs: proc_attrs, })); } else { @@ -123,12 +123,12 @@ impl<'a> CollectProcMacros<'a> { } } - fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, function_name: Ident) { + fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, function_ident: Ident) { if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Attr(ProcMacroDef { id: item.id, span: item.span, - function_name, + function_ident, })); } else { let msg = if !self.in_root { @@ -141,12 +141,12 @@ impl<'a> CollectProcMacros<'a> { } } - fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, function_name: Ident) { + fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, function_ident: Ident) { if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Bang(ProcMacroDef { id: item.id, span: item.span, - function_name, + function_ident, })); } else { let msg = if !self.in_root { @@ -303,7 +303,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { ProcMacro::Derive(m) => m.span, ProcMacro::Attr(m) | ProcMacro::Bang(m) => m.span, }; - let local_path = |cx: &ExtCtxt<'_>, name| cx.expr_path(cx.path(span, vec![name])); + let local_path = |cx: &ExtCtxt<'_>, ident| cx.expr_path(cx.path(span, vec![ident])); let proc_macro_ty_method_path = |cx: &ExtCtxt<'_>, method| { cx.expr_path(cx.path( span.with_ctxt(harness_span.ctxt()), @@ -327,7 +327,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { .map(|&s| cx.expr_str(span, s)) .collect::>(), ), - local_path(cx, cd.function_name), + local_path(cx, cd.function_ident), ], ) } @@ -345,8 +345,8 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { harness_span, proc_macro_ty_method_path(cx, ident), thin_vec![ - cx.expr_str(span, ca.function_name.name), - local_path(cx, ca.function_name), + cx.expr_str(span, ca.function_ident.name), + local_path(cx, ca.function_ident), ], ) } diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 6d5df2b00437b..3b48adb7e918a 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -104,7 +104,7 @@ pub(crate) fn maybe_create_entry_wrapper( let termination_trait = tcx.require_lang_item(LangItem::Termination, None); let report = tcx .associated_items(termination_trait) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, Ident::from_str("report"), AssocKind::Fn, diff --git a/compiler/rustc_data_structures/src/sync/freeze.rs b/compiler/rustc_data_structures/src/sync/freeze.rs index 9720b22ea7d1b..6338afb92c34b 100644 --- a/compiler/rustc_data_structures/src/sync/freeze.rs +++ b/compiler/rustc_data_structures/src/sync/freeze.rs @@ -88,7 +88,7 @@ impl FreezeLock { #[inline] #[track_caller] pub fn write(&self) -> FreezeWriteGuard<'_, T> { - self.try_write().expect("still mutable") + self.try_write().expect("data should not be frozen if we're still attempting to mutate it") } #[inline] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 595ac1edd289a..bbc18c371ea0e 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -348,10 +348,6 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) // Make sure name resolution and macro expansion is run. let _ = tcx.resolver_for_lowering(); - if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { - dump_feature_usage_metrics(tcx, metrics_dir); - } - if callbacks.after_expansion(compiler, tcx) == Compilation::Stop { return early_exit(); } @@ -370,6 +366,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) tcx.ensure_ok().analysis(()); + if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { + dump_feature_usage_metrics(tcx, metrics_dir); + } + if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { return early_exit(); } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index d14e476ba3223..49f6d58172ff6 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1102,7 +1102,7 @@ pub trait ResolverExpand { /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); - fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem); + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, ident: Ident, cfg: ast::MetaItem); /// Tools registered with `#![register_tool]` and used by tool attributes and lints. fn registered_tools(&self) -> &RegisteredTools; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index bca846d2ec423..1b539477d51ec 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1169,9 +1169,9 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { collector.cx.dcx().emit_err(RemoveNodeNotSupported { span, descr: Self::descr() }); } - /// All of the names (items) declared by this node. + /// All of the identifiers (items) declared by this node. /// This is an approximation and should only be used for diagnostics. - fn declared_names(&self) -> Vec { + fn declared_idents(&self) -> Vec { vec![] } } @@ -1306,7 +1306,7 @@ impl InvocationCollectorNode for P { res } - fn declared_names(&self) -> Vec { + fn declared_idents(&self) -> Vec { if let ItemKind::Use(ut) = &self.kind { fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec) { match &ut.kind { @@ -2061,10 +2061,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } if let Some(meta_item) = meta_item { - for name in node.declared_names() { + for ident in node.declared_idents() { self.cx.resolver.append_stripped_cfg_item( self.cx.current_expansion.lint_node_id, - name, + ident, meta_item.clone(), ) } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c61477951c920..c9c4936c15640 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3399,9 +3399,9 @@ pub struct BareFnTy<'hir> { pub abi: ExternAbi, pub generic_params: &'hir [GenericParam<'hir>], pub decl: &'hir FnDecl<'hir>, - // `Option` because bare fn parameter names are optional. We also end up + // `Option` because bare fn parameter identifiers are optional. We also end up // with `None` in some error cases, e.g. invalid parameter patterns. - pub param_names: &'hir [Option], + pub param_idents: &'hir [Option], } #[derive(Debug, Clone, Copy, HashStable_Generic)] diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index ea3f396761b16..93d20dfa79966 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -652,10 +652,10 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_ident(foreign_item.ident)); match foreign_item.kind { - ForeignItemKind::Fn(ref sig, param_names, ref generics) => { + ForeignItemKind::Fn(ref sig, param_idents, ref generics) => { try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(sig.decl)); - for ident in param_names.iter().copied() { + for ident in param_idents.iter().copied() { visit_opt!(visitor, visit_ident, ident); } } @@ -1169,9 +1169,9 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_ty_unambig(ty)); visit_opt!(visitor, visit_nested_body, default); } - TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { + TraitItemKind::Fn(ref sig, TraitFn::Required(param_idents)) => { try_visit!(visitor.visit_fn_decl(sig.decl)); - for ident in param_names.iter().copied() { + for ident in param_idents.iter().copied() { visit_opt!(visitor, visit_ident, ident); } } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 2f7c3cb3c7d80..92701e3328e92 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -1,5 +1,5 @@ -hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}` - .label = ambiguous associated {$assoc_kind} `{$assoc_name}` +hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}` + .label = ambiguous associated {$assoc_kind} `{$assoc_ident}` hir_analysis_ambiguous_lifetime_bound = ambiguous lifetime bound, explicit lifetime bound required @@ -12,13 +12,13 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private .label = private {$kind} .defined_here_label = the {$kind} is defined here -hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}` +hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_ident}` not found for `{$qself}` hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> [true] an *[false] a similarly named } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` -hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found +hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_ident}` not found hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg = consider fully qualifying{$identically_named -> diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 32a8f101849c8..29a9931696ffa 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1046,11 +1046,11 @@ fn report_trait_method_mismatch<'tcx>( // argument pattern and type. let (sig, body) = tcx.hir_expect_impl_item(impl_m.def_id.expect_local()).expect_fn(); let span = tcx - .hir_body_param_names(body) + .hir_body_param_idents(body) .zip(sig.decl.inputs.iter()) - .map(|(param_name, ty)| { - if let Some(param_name) = param_name { - param_name.span.to(ty.span) + .map(|(param_ident, ty)| { + if let Some(param_ident) = param_ident { + param_ident.span.to(ty.span) } else { ty.span } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 69b921fccbf03..deded6904d484 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -439,9 +439,9 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { &self, span: Span, def_id: LocalDefId, - assoc_name: Ident, + assoc_ident: Ident, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name)) + self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_ident)) } fn lower_assoc_shared( diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index e90a1cc24c165..ce0f83d0ec288 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -584,12 +584,12 @@ pub(super) fn explicit_super_predicates_of<'tcx>( pub(super) fn explicit_supertraits_containing_assoc_item<'tcx>( tcx: TyCtxt<'tcx>, - (trait_def_id, assoc_name): (DefId, Ident), + (trait_def_id, assoc_ident): (DefId, Ident), ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { implied_predicates_with_filter( tcx, trait_def_id, - PredicateFilter::SelfTraitThatDefines(assoc_name), + PredicateFilter::SelfTraitThatDefines(assoc_ident), ) } @@ -617,7 +617,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( filter: PredicateFilter, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { let Some(trait_def_id) = trait_def_id.as_local() else { - // if `assoc_name` is None, then the query should've been redirected to an + // if `assoc_ident` is None, then the query should've been redirected to an // external provider assert_matches!(filter, PredicateFilter::SelfTraitThatDefines(_)); return tcx.explicit_super_predicates_of(trait_def_id); @@ -834,11 +834,11 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( #[instrument(level = "trace", skip(tcx))] pub(super) fn type_param_predicates<'tcx>( tcx: TyCtxt<'tcx>, - (item_def_id, def_id, assoc_name): (LocalDefId, LocalDefId, Ident), + (item_def_id, def_id, assoc_ident): (LocalDefId, LocalDefId, Ident), ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { match tcx.opt_rpitit_info(item_def_id.to_def_id()) { Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { - return tcx.type_param_predicates((opaque_def_id.expect_local(), def_id, assoc_name)); + return tcx.type_param_predicates((opaque_def_id.expect_local(), def_id, assoc_ident)); } Some(ty::ImplTraitInTraitData::Impl { .. }) => { unreachable!("should not be lowering bounds on RPITIT in impl") @@ -863,7 +863,7 @@ pub(super) fn type_param_predicates<'tcx>( let result = if let Some(parent) = parent { let icx = ItemCtxt::new(tcx, parent); - icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_name) + icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_ident) } else { ty::EarlyBinder::bind(&[] as &[_]) }; @@ -889,7 +889,7 @@ pub(super) fn type_param_predicates<'tcx>( let extra_predicates = extend.into_iter().chain(icx.probe_ty_param_bounds_in_generics( hir_generics, def_id, - PredicateFilter::SelfTraitThatDefines(assoc_name), + PredicateFilter::SelfTraitThatDefines(assoc_ident), )); let bounds = @@ -908,7 +908,7 @@ pub(super) fn type_param_predicates<'tcx>( _ => unreachable!(), }; assert_only_contains_predicates_from( - PredicateFilter::SelfTraitThatDefines(assoc_name), + PredicateFilter::SelfTraitThatDefines(assoc_ident), bounds, self_ty, ); diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 404753875ee5e..9bcda35ee87ad 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1874,13 +1874,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { fn supertrait_hrtb_vars( tcx: TyCtxt<'tcx>, def_id: DefId, - assoc_name: Ident, + assoc_ident: Ident, assoc_kind: ty::AssocKind, ) -> Option<(Vec, &'tcx ty::AssocItem)> { let trait_defines_associated_item_named = |trait_def_id: DefId| { - tcx.associated_items(trait_def_id).find_by_name_and_kind( + tcx.associated_items(trait_def_id).find_by_ident_and_kind( tcx, - assoc_name, + assoc_ident, assoc_kind, trait_def_id, ) @@ -1904,7 +1904,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { if let Some(assoc_item) = trait_defines_associated_item_named(def_id) { break Some((bound_vars.into_iter().collect(), assoc_item)); } - let predicates = tcx.explicit_supertraits_containing_assoc_item((def_id, assoc_name)); + let predicates = tcx.explicit_supertraits_containing_assoc_item((def_id, assoc_ident)); let obligations = predicates.iter_identity_copied().filter_map(|(pred, _)| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index e6090a128b1c8..508970cf2554d 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -23,7 +23,7 @@ pub(crate) struct AmbiguousAssocItem<'a> { #[label] pub span: Span, pub assoc_kind: &'static str, - pub assoc_name: Ident, + pub assoc_ident: Ident, pub qself: &'a str, } @@ -75,7 +75,7 @@ pub(crate) struct AssocItemIsPrivate { pub(crate) struct AssocItemNotFound<'a> { #[primary_span] pub span: Span, - pub assoc_name: Ident, + pub assoc_ident: Ident, pub assoc_kind: &'static str, pub qself: &'a str, #[subdiagnostic] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 55087d1f400f5..24d05b49861c5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -363,10 +363,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for hir_bound in hir_bounds { // In order to avoid cycles, when we're lowering `SelfTraitThatDefines`, // we skip over any traits that don't define the given associated type. - if let PredicateFilter::SelfTraitThatDefines(assoc_name) = predicate_filter { + if let PredicateFilter::SelfTraitThatDefines(assoc_ident) = predicate_filter { if let Some(trait_ref) = hir_bound.trait_ref() && let Some(trait_did) = trait_ref.trait_def_id() - && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) + && self.tcx().trait_may_define_assoc_item(trait_did, assoc_ident) { // Okay } else { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index ecb453bced0a7..d1ee5a5494c00 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -49,13 +49,13 @@ pub(crate) fn validate_cmse_abi<'tcx>( Ok(Err(index)) => { // fn(x: u32, u32, u32, u16, y: u16) -> u32, // ^^^^^^ - let span = if let Some(ident) = bare_fn_ty.param_names[index] { + let span = if let Some(ident) = bare_fn_ty.param_idents[index] { ident.span.to(bare_fn_ty.decl.inputs[index].span) } else { bare_fn_ty.decl.inputs[index].span } .to(bare_fn_ty.decl.inputs.last().unwrap().span); - let plural = bare_fn_ty.param_names.len() - index != 1; + let plural = bare_fn_ty.param_idents.len() - index != 1; dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi }); } Err(layout_err) => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 6e9c178d33a64..5a0524d33fdea 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -117,7 +117,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { all_candidates: impl Fn() -> I, qself: AssocItemQSelf, assoc_kind: ty::AssocKind, - assoc_name: Ident, + assoc_ident: Ident, span: Span, constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> ErrorGuaranteed @@ -129,11 +129,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches. if let Some(assoc_item) = all_candidates().find_map(|r| { tcx.associated_items(r.def_id()) - .filter_by_name_unhygienic(assoc_name.name) - .find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id())) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|item| tcx.hygienic_eq(assoc_ident, item.ident(tcx), r.def_id())) }) { return self.complain_about_assoc_kind_mismatch( - assoc_item, assoc_kind, assoc_name, span, constraint, + assoc_item, + assoc_kind, + assoc_ident, + span, + constraint, ); } @@ -142,18 +146,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a // valid span, so we point at the whole path segment instead. - let is_dummy = assoc_name.span == DUMMY_SP; + let is_dummy = assoc_ident.span == DUMMY_SP; let mut err = errors::AssocItemNotFound { - span: if is_dummy { span } else { assoc_name.span }, - assoc_name, + span: if is_dummy { span } else { assoc_ident.span }, + assoc_ident, assoc_kind: assoc_kind_str, qself: &qself_str, label: None, sugg: None, // Try to get the span of the identifier within the path's syntax context // (if that's different). - within_macro_span: assoc_name.span.within_macro(span, tcx.sess.source_map()), + within_macro_span: assoc_ident.span.within_macro(span, tcx.sess.source_map()), }; if is_dummy { @@ -169,10 +173,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect(); if let Some(suggested_name) = - find_best_match_for_name(&all_candidate_names, assoc_name.name, None) + find_best_match_for_name(&all_candidate_names, assoc_ident.name, None) { err.sugg = Some(errors::AssocItemNotFoundSugg::Similar { - span: assoc_name.span, + span: assoc_ident.span, assoc_kind: assoc_kind_str, suggested_name, }); @@ -201,7 +205,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect(); if let Some(suggested_name) = - find_best_match_for_name(&wider_candidate_names, assoc_name.name, None) + find_best_match_for_name(&wider_candidate_names, assoc_ident.name, None) { if let [best_trait] = visible_traits .iter() @@ -215,11 +219,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { let trait_name = tcx.def_path_str(best_trait); err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait { - span: assoc_name.span, + span: assoc_ident.span, assoc_kind: assoc_kind_str, trait_name: &trait_name, suggested_name, - identically_named: suggested_name == assoc_name.name, + identically_named: suggested_name == assoc_ident.name, }); if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're @@ -246,7 +250,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // The type param already has a bound for `trait_name`, we just need to // change the associated item. err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { - span: assoc_name.span, + span: assoc_ident.span, assoc_kind: assoc_kind_str, suggested_name, }); @@ -265,7 +269,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Applicability::MaybeIncorrect }; - let identically_named = suggested_name == assoc_name.name; + let identically_named = suggested_name == assoc_ident.name; if let DefKind::TyAlias = tcx.def_kind(item_def_id) && !tcx.type_alias_is_lazy(item_def_id) @@ -273,7 +277,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath { lo: ty_param_span.shrink_to_lo(), mi: ty_param_span.shrink_to_hi(), - hi: (!identically_named).then_some(assoc_name.span), + hi: (!identically_named).then_some(assoc_ident.span), trait_ref, identically_named, suggested_name, @@ -294,7 +298,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // We suggested constraining a type parameter, but the associated item on it // was also not an exact match, so we also suggest changing it. err.span_suggestion_verbose( - assoc_name.span, + assoc_ident.span, fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, suggested_name, Applicability::MaybeIncorrect, @@ -311,13 +315,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // suggest using it. if let [candidate_name] = all_candidate_names.as_slice() { err.sugg = Some(errors::AssocItemNotFoundSugg::Other { - span: assoc_name.span, + span: assoc_ident.span, qself: &qself_str, assoc_kind: assoc_kind_str, suggested_name: *candidate_name, }); } else { - err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span }); + err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_ident.span }); } self.dcx().emit_err(err) @@ -805,7 +809,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return None; }; - let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind( + let assoc_item = tcx.associated_items(trait_def).find_by_ident_and_kind( tcx, ident, ty::AssocKind::Type, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index b4a71edc118c4..83aa0d9562015 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -147,7 +147,7 @@ pub trait HirTyLowerer<'tcx> { &self, span: Span, def_id: LocalDefId, - assoc_name: Ident, + assoc_ident: Ident, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>; /// Lower an associated type/const (from a trait) to a projection. @@ -933,11 +933,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, trait_def_id: DefId, assoc_kind: ty::AssocKind, - assoc_name: Ident, + assoc_ident: Ident, ) -> bool { self.tcx() .associated_items(trait_def_id) - .find_by_name_and_kind(self.tcx(), assoc_name, assoc_kind, trait_def_id) + .find_by_ident_and_kind(self.tcx(), assoc_ident, assoc_kind, trait_def_id) .is_some() } @@ -964,7 +964,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } /// Search for a trait bound on a type parameter whose trait defines the associated item - /// given by `assoc_name` and `kind`. + /// given by `assoc_ident` and `kind`. /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. @@ -976,13 +976,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty_param_def_id: LocalDefId, ty_param_span: Span, kind: ty::AssocKind, - assoc_name: Ident, + assoc_ident: Ident, span: Span, ) -> Result, ErrorGuaranteed> { - debug!(?ty_param_def_id, ?assoc_name, ?span); + debug!(?ty_param_def_id, ?assoc_ident, ?span); let tcx = self.tcx(); - let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name); + let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_ident); debug!("predicates={:#?}", predicates); self.probe_single_bound_for_assoc_item( @@ -990,17 +990,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let trait_refs = predicates .iter_identity_copied() .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); - traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) + traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_ident) }, AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), kind, - assoc_name, + assoc_ident, span, None, ) } - /// Search for a single trait bound whose trait defines the associated item given by `assoc_name`. + /// Search for a single trait bound whose trait defines the associated item given by + /// `assoc_ident`. /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. @@ -1010,7 +1011,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { all_candidates: impl Fn() -> I, qself: AssocItemQSelf, assoc_kind: ty::AssocKind, - assoc_name: Ident, + assoc_ident: Ident, span: Span, constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> Result, ErrorGuaranteed> @@ -1020,7 +1021,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let mut matching_candidates = all_candidates().filter(|r| { - self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_kind, assoc_name) + self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_kind, assoc_ident) }); let Some(bound) = matching_candidates.next() else { @@ -1028,7 +1029,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { all_candidates, qself, assoc_kind, - assoc_name, + assoc_ident, span, constraint, ); @@ -1044,7 +1045,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { span, assoc_kind: assoc_kind_str, - assoc_name, + assoc_ident, qself: &qself_str, }); // Provide a more specific error code index entry for equality bindings. @@ -1065,13 +1066,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let bound_id = bound.def_id(); let bound_span = tcx .associated_items(bound_id) - .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id) + .find_by_ident_and_kind(tcx, assoc_ident, assoc_kind, bound_id) .and_then(|item| tcx.hir_span_if_local(item.def_id)); if let Some(bound_span) = bound_span { err.span_label( bound_span, - format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),), + format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),), ); if let Some(constraint) = constraint { match constraint.kind { @@ -1087,7 +1088,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // FIXME(#97583): This isn't syntactically well-formed! where_bounds.push(format!( - " T: {trait}::{assoc_name} = {term}", + " T: {trait}::{assoc_ident} = {term}", trait = bound.print_only_trait_path(), )); } @@ -1096,7 +1097,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } else { err.span_suggestion_verbose( - span.with_hi(assoc_name.span.lo()), + span.with_hi(assoc_ident.span.lo()), "use fully-qualified syntax to disambiguate", format!("<{qself_str} as {}>::", bound.print_only_trait_path()), Applicability::MaybeIncorrect, @@ -1104,7 +1105,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } else { err.note(format!( - "associated {assoc_kind_str} `{assoc_name}` could derive from `{}`", + "associated {assoc_kind_str} `{assoc_ident}` could derive from `{}`", bound.print_only_trait_path(), )); } @@ -2858,7 +2859,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let trait_ref = self.lower_impl_trait_ref(i.of_trait.as_ref()?, self.lower_ty(i.self_ty)); - let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( + let assoc = tcx.associated_items(trait_ref.def_id).find_by_ident_and_kind( tcx, *ident, ty::AssocKind::Fn, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 865209b6a9606..e5ab317685f9f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -397,7 +397,7 @@ impl<'a> State<'a> { self.pclose(); } hir::TyKind::BareFn(f) => { - self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_names); + self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_idents); } hir::TyKind::UnsafeBinder(unsafe_binder) => { self.print_unsafe_binder(unsafe_binder); @@ -473,14 +473,14 @@ impl<'a> State<'a> { self.maybe_print_comment(item.span.lo()); self.print_attrs_as_outer(self.attrs(item.hir_id())); match item.kind { - hir::ForeignItemKind::Fn(sig, arg_names, generics) => { + hir::ForeignItemKind::Fn(sig, arg_idents, generics) => { self.head(""); self.print_fn( sig.decl, sig.header, Some(item.ident.name), generics, - arg_names, + arg_idents, None, ); self.end(); // end head-ibox @@ -899,10 +899,10 @@ impl<'a> State<'a> { ident: Ident, m: &hir::FnSig<'_>, generics: &hir::Generics<'_>, - arg_names: &[Option], + arg_idents: &[Option], body_id: Option, ) { - self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_names, body_id); + self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_idents, body_id); } fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) { @@ -914,8 +914,8 @@ impl<'a> State<'a> { hir::TraitItemKind::Const(ty, default) => { self.print_associated_const(ti.ident, ti.generics, ty, default); } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_names)) => { - self.print_method_sig(ti.ident, sig, ti.generics, arg_names, None); + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_idents)) => { + self.print_method_sig(ti.ident, sig, ti.generics, arg_idents, None); self.word(";"); } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { @@ -2122,7 +2122,7 @@ impl<'a> State<'a> { header: hir::FnHeader, name: Option, generics: &hir::Generics<'_>, - arg_names: &[Option], + arg_idents: &[Option], body_id: Option, ) { self.print_fn_header_info(header); @@ -2134,16 +2134,16 @@ impl<'a> State<'a> { self.print_generic_params(generics.params); self.popen(); - // Make sure we aren't supplied *both* `arg_names` and `body_id`. - assert!(arg_names.is_empty() || body_id.is_none()); + // Make sure we aren't supplied *both* `arg_idents` and `body_id`. + assert!(arg_idents.is_empty() || body_id.is_none()); let mut i = 0; let mut print_arg = |s: &mut Self, ty: Option<&hir::Ty<'_>>| { if i == 0 && decl.implicit_self.has_implicit_self() { s.print_implicit_self(&decl.implicit_self); } else { - if let Some(arg_name) = arg_names.get(i) { - if let Some(arg_name) = arg_name { - s.word(arg_name.to_string()); + if let Some(arg_ident) = arg_idents.get(i) { + if let Some(arg_ident) = arg_ident { + s.word(arg_ident.to_string()); s.word(":"); s.space(); } @@ -2452,7 +2452,7 @@ impl<'a> State<'a> { decl: &hir::FnDecl<'_>, name: Option, generic_params: &[hir::GenericParam<'_>], - arg_names: &[Option], + arg_idents: &[Option], ) { self.ibox(INDENT_UNIT); self.print_formal_generic_params(generic_params); @@ -2467,7 +2467,7 @@ impl<'a> State<'a> { }, name, generics, - arg_names, + arg_idents, None, ); self.end(); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 872861d6289d9..9e1b70f5767b4 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -148,7 +148,7 @@ hir_typeck_never_type_fallback_flowing_into_unsafe_path = never type fallback af hir_typeck_never_type_fallback_flowing_into_unsafe_union_field = never type fallback affects this union access .help = specify the type explicitly -hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> +hir_typeck_no_associated_item = no {$item_kind} named `{$item_ident}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> [true] {""} *[other] {" "}in the current scope } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index b73cd26927a5c..dfaa374592bc2 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -727,7 +727,7 @@ pub(crate) struct NoAssociatedItem { #[primary_span] pub span: Span, pub item_kind: &'static str, - pub item_name: Ident, + pub item_ident: Ident, pub ty_prefix: Cow<'static, str>, pub ty_str: String, pub trait_missing_method: bool, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index f4bd7ec701f8a..81eb8510785b5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1136,7 +1136,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize && let Some(Some(arg)) = - self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit) + self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) && arg.name != kw::SelfLower { format!("/* {} */", arg.name) @@ -2619,7 +2619,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_method: bool, ) -> Option<(IndexVec, FnParam<'_>)>, &hir::Generics<'_>)> { - let (sig, generics, body_id, param_names) = match self.tcx.hir_get_if_local(def_id)? { + let (sig, generics, body_id, params) = match self.tcx.hir_get_if_local(def_id)? { hir::Node::TraitItem(&hir::TraitItem { generics, kind: hir::TraitItemKind::Fn(sig, trait_fn), @@ -2661,7 +2661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } }); - match (body_id, param_names) { + match (body_id, params) { (Some(_), Some(_)) | (None, None) => unreachable!(), (Some(body), None) => { let params = self.tcx.hir_body(body).params; @@ -2678,7 +2678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; debug_assert_eq!(params.len(), fn_inputs.len()); Some(( - fn_inputs.zip(params.iter().map(|&ident| FnParam::Name(ident))).collect(), + fn_inputs.zip(params.iter().map(|&ident| FnParam::Ident(ident))).collect(), generics, )) } @@ -2709,14 +2709,14 @@ impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> { #[derive(Clone, Copy)] enum FnParam<'hir> { Param(&'hir hir::Param<'hir>), - Name(Option), + Ident(Option), } impl FnParam<'_> { fn span(&self) -> Span { match self { Self::Param(param) => param.span, - Self::Name(ident) => { + Self::Ident(ident) => { if let Some(ident) = ident { ident.span } else { @@ -2738,7 +2738,7 @@ impl FnParam<'_> { { Some(ident.name) } - FnParam::Name(ident) + FnParam::Ident(ident) if let Some(ident) = ident && ident.name != kw::Underscore => { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 4008021c3a857..ddfd27ccf6b7f 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -534,12 +534,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok((def_kind, pick.item.def_id)) } - /// Finds item with name `item_name` defined in impl/trait `def_id` + /// Finds item with name `item_ident` defined in impl/trait `def_id` /// and return it, or `None`, if no such item was defined there. - fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option { + fn associated_value(&self, def_id: DefId, item_ident: Ident) -> Option { self.tcx .associated_items(def_id) - .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id) + .find_by_ident_and_namespace(self.tcx, item_ident, Namespace::ValueNS, def_id) .copied() } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 8be4d55542d08..68f13d654d6e8 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -585,7 +585,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, mut span: Span, rcvr_ty: Ty<'tcx>, - item_name: Ident, + item_ident: Ident, expr_id: hir::HirId, source: SelfSource<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, @@ -616,7 +616,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if rcvr_ty.is_enum() { "variant or associated item" } else { - match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { + match (item_ident.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { (Some(name), false) if name.is_lowercase() => "function or associated item", (Some(_), false) => "associated item", (Some(_), true) | (None, false) => "variant or associated item", @@ -631,7 +631,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty, source, span, - item_name, + item_ident, &short_ty_str, &mut ty_file, ) { @@ -643,13 +643,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, span, item_kind, - item_name, + item_ident, &short_ty_str, &mut ty_file, ) { return guar; } - span = item_name.span; + span = item_ident.span; // Don't show generic arguments when the method can't be found in any implementation (#81576). let mut ty_str_reported = ty_str.clone(); @@ -661,7 +661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx .inherent_impls(adt_def.did()) .into_iter() - .any(|def_id| self.associated_value(*def_id, item_name).is_some()) + .any(|def_id| self.associated_value(*def_id, item_ident).is_some()) } else { false } @@ -678,14 +678,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) - }) && item_name.name == sym::write_fmt; + }) && item_ident.name == sym::write_fmt; let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { let mut err = self.dcx().create_err(NoAssociatedItem { span, item_kind, - item_name, + item_ident, ty_prefix: if trait_missing_method { // FIXME(mu001999) E0599 maybe not suitable here because it is for types Cow::from("trait") @@ -699,7 +699,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if is_method { self.suggest_use_shadowed_binding_with_method( source, - item_name, + item_ident, &ty_str_reported, &mut err, ); @@ -710,9 +710,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && let Res::SelfTyAlias { alias_to: impl_def_id, .. } = path.res && let DefKind::Impl { .. } = self.tcx.def_kind(impl_def_id) - && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( self.tcx, - item_name, + item_ident, ty::AssocKind::Type, impl_def_id, ) @@ -722,7 +722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let def_path = tcx.def_path_str(adt_def.did()); err.span_suggestion( - ty.span.to(item_name.span), + ty.span.to(item_ident.span), format!("to construct a value of type `{}`, use the explicit path", def_path), def_path, Applicability::MachineApplicable, @@ -750,7 +750,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.find_builder_fn(&mut err, rcvr_ty, expr_id); } - if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { + if tcx.ty_is_opaque_future(rcvr_ty) && item_ident.name == sym::poll { err.help(format!( "method `poll` found on `Pin<&mut {ty_str}>`, \ see documentation for `std::pin::Pin`" @@ -765,7 +765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { self.suggest_await_before_method( &mut err, - item_name, + item_ident, rcvr_ty, cal, span, @@ -787,7 +787,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let SelfSource::MethodCall(rcvr_expr) = source && let ty::RawPtr(ty, ptr_mutbl) = *rcvr_ty.kind() && let Ok(pick) = self.lookup_probe_for_diagnostic( - item_name, + item_ident, Ty::new_ref(tcx, ty::Region::new_error_misc(tcx), ty, ptr_mutbl), self.tcx.hir_expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)), ProbeScope::TraitsInScope, @@ -808,7 +808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; err.span_note( tcx.def_span(pick.item.def_id), - format!("the method `{item_name}` exists on the type `{ty}`", ty = pick.self_ty), + format!("the method `{item_ident}` exists on the type `{ty}`", ty = pick.self_ty), ); let mut_str = ptr_mutbl.ptr_str(); err.note(format!( @@ -834,7 +834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { let call_expr = self.tcx.hir_expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)); let probe = self.lookup_probe_for_diagnostic( - item_name, + item_ident, output_ty, call_expr, ProbeScope::AllTraits, @@ -873,13 +873,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { static_candidates, rcvr_ty, source, - item_name, + item_ident, args, sugg_span, ); self.note_candidates_on_method_error( rcvr_ty, - item_name, + item_ident, source, args, span, @@ -890,7 +890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if static_candidates.len() > 1 { self.note_candidates_on_method_error( rcvr_ty, - item_name, + item_ident, source, args, span, @@ -904,7 +904,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut restrict_type_params = false; let mut suggested_derive = false; let mut unsatisfied_bounds = false; - if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) { + if item_ident.name == sym::count && self.is_slice_ty(rcvr_ty, span) { let msg = "consider using `len` instead"; if let SelfSource::MethodCall(_expr) = source { err.span_suggestion_short(span, msg, "len", Applicability::MachineApplicable); @@ -1349,7 +1349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let primary_message = primary_message.unwrap_or_else(|| { format!( - "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, \ + "the {item_kind} `{item_ident}` exists for {actual_prefix} `{ty_str}`, \ but its trait bounds were not satisfied" ) }); @@ -1379,7 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `Pin<&Self>`. if targs.len() == 1 { let mut item_segment = hir::PathSegment::invalid(); - item_segment.ident = item_name; + item_segment.ident = item_ident; for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] { let new_args = tcx.mk_args_from_iter(targs.iter().map(|arg| match arg.as_type() { @@ -1423,9 +1423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(adt, _) => self.tcx.is_lang_item(adt.did(), LangItem::String), _ => false, }; - if is_string_or_ref_str && item_name.name == sym::iter { + if is_string_or_ref_str && item_ident.name == sym::iter { err.span_suggestion_verbose( - item_name.span, + item_ident.span, "because of the in-memory representation of `&str`, to obtain \ an `Iterator` over each of its codepoint use method `chars`", "chars", @@ -1439,7 +1439,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .copied() .filter(|def_id| { - if let Some(assoc) = self.associated_value(*def_id, item_name) { + if let Some(assoc) = self.associated_value(*def_id, item_ident) { // Check for both mode is the same so we avoid suggesting // incorrect associated item. match (mode, assoc.fn_has_self_parameter, source) { @@ -1500,7 +1500,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { - if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) + if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_ident, &mut err) && similar_candidate.is_none() && !custom_span_label { @@ -1513,7 +1513,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let confusable_suggested = self.confusable_method_name( &mut err, rcvr_ty, - item_name, + item_ident, args.map(|args| { args.iter() .map(|expr| { @@ -1531,12 +1531,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, span, rcvr_ty, - item_name, + item_ident, expected.only_has_type(self), ); } - self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name); + self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_ident); for (span, mut bounds) in bound_spans { if !tcx.sess.source_map().is_span_accessible(span) { @@ -1547,7 +1547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pre = if Some(span) == ty_span { ty_span.take(); format!( - "{item_kind} `{item_name}` not found for this {} because it ", + "{item_kind} `{item_ident}` not found for this {} because it ", rcvr_ty.prefix_string(self.tcx) ) } else { @@ -1567,7 +1567,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label( span, format!( - "{item_kind} `{item_name}` not found for this {}", + "{item_kind} `{item_ident}` not found for this {}", rcvr_ty.prefix_string(self.tcx) ), ); @@ -1579,7 +1579,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut err, span, rcvr_ty, - item_name, + item_ident, args.map(|args| args.len() + 1), source, no_match_data.out_of_scope_traits.clone(), @@ -1596,7 +1596,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); if let Some(var_name) = edit_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::>(), - item_name.name, + item_ident.name, None, ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name) { @@ -1737,14 +1737,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !find_candidate_for_method { self.lookup_segments_chain_for_no_match_method( &mut err, - item_name, + item_ident, item_kind, source, no_match_data, ); } - self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected); + self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); err.emit() } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 885a7308bdc6d..16c9e08c78d38 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -859,7 +859,7 @@ impl<'tcx> LateContext<'tcx> { ) -> Option> { let tcx = self.tcx; tcx.associated_items(trait_id) - .find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) + .find_by_ident_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) .and_then(|assoc| { let proj = Ty::new_projection(tcx, assoc.def_id, [self_ty]); tcx.try_normalize_erasing_regions(self.typing_env(), proj).ok() diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index df567e80e5568..a3e7c84584d30 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -423,11 +423,11 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(_, hir::TraitFn::Required(pnames)) = item.kind { + if let hir::TraitItemKind::Fn(_, hir::TraitFn::Required(param_idents)) = item.kind { self.check_snake_case(cx, "trait method", &item.ident); - for param_name in pnames { - if let Some(param_name) = param_name { - self.check_snake_case(cx, "variable", param_name); + for param_ident in param_idents { + if let Some(param_ident) = param_ident { + self.check_snake_case(cx, "variable", param_ident); } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 4610a571da088..5798c4b382729 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1313,7 +1313,7 @@ impl<'a> CrateMetadataRef<'a> { fn get_fn_has_self_parameter(self, id: DefIndex, sess: &'a Session) -> bool { self.root .tables - .fn_arg_names + .fn_arg_idents .get(self, id) .expect("argument names not encoded for a function") .decode((self, sess)) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 3dc82ce9d183d..ecc2dcc5318d0 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -286,7 +286,7 @@ provide! { tcx, def_id, other, cdata, rendered_const => { table } rendered_precise_capturing_args => { table } asyncness => { table_direct } - fn_arg_names => { table } + fn_arg_idents => { table } coroutine_kind => { table_direct } coroutine_for_closure => { table } coroutine_by_move_body_def_id => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 55bb984c5b69f..30e2e7fee47be 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1469,7 +1469,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::Fn | DefKind::AssocFn = def_kind { self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id)); - record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id)); + 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); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 96a1f65eeb0fb..93125149fe631 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -451,7 +451,7 @@ define_tables! { rendered_const: Table>, rendered_precise_capturing_args: Table>>, asyncness: Table, - fn_arg_names: Table>>, + fn_arg_idents: Table>>, coroutine_kind: Table, coroutine_for_closure: Table, adt_destructor: Table>, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 21ab06c98a777..7f2b9e9d4ffd5 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -281,7 +281,7 @@ impl<'tcx> TyCtxt<'tcx> { }) } - pub fn hir_body_param_names(self, id: BodyId) -> impl Iterator> { + pub fn hir_body_param_idents(self, id: BodyId) -> impl Iterator> { self.hir_body(id).params.iter().map(|param| match param.pat.kind { PatKind::Binding(_, _, ident, _) => Some(ident), PatKind::Wild => Some(Ident::new(kw::Underscore, param.pat.span)), diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 74369b6636c01..a3177a6416d0b 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -215,9 +215,9 @@ pub fn provide(providers: &mut Providers) { let hir_id = tcx.local_def_id_to_hir_id(def_id); tcx.hir_opt_ident_span(hir_id) }; - providers.fn_arg_names = |tcx, def_id| { + providers.fn_arg_idents = |tcx, def_id| { if let Some(body_id) = tcx.hir_node_by_def_id(def_id).body_id() { - tcx.arena.alloc_from_iter(tcx.hir_body_param_names(body_id)) + tcx.arena.alloc_from_iter(tcx.hir_body_param_idents(body_id)) } else if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(_, TraitFn::Required(idents)), .. @@ -231,7 +231,7 @@ pub fn provide(providers: &mut Providers) { } else { span_bug!( tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)), - "fn_arg_names: unexpected item {:?}", + "fn_arg_idents: unexpected item {:?}", def_id ); } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 40d0028db86db..28a59d3e73e80 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1442,8 +1442,8 @@ rustc_queries! { desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } } - query fn_arg_names(def_id: DefId) -> &'tcx [Option] { - desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } + query fn_arg_idents(def_id: DefId) -> &'tcx [Option] { + desc { |tcx| "looking up function parameter identifiers for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1900,6 +1900,11 @@ rustc_queries! { // The macro which defines `rustc_metadata::provide_extern` depends on this query's name. // Changing the name should cause a compiler error, but in case that changes, be aware. + // + // The hash should not be calculated before the `analysis` pass is complete, specifically + // until `tcx.untracked().definitions.freeze()` has been called, otherwise if incremental + // compilation is enabled calculating this hash can freeze this structure too early in + // compilation and cause subsequent crashes when attempting to write to `definitions` query crate_hash(_: CrateNum) -> Svh { eval_always desc { "looking up the hash a crate" } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index ce4c08aa485e5..e3d332036f1f7 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -199,8 +199,9 @@ impl AssocItems { self.items.get_by_key(name) } - /// Returns the associated item with the given name and `AssocKind`, if one exists. - pub fn find_by_name_and_kind( + /// Returns the associated item with the given identifier and `AssocKind`, if one exists. + /// The identifier is matched hygienically. + pub fn find_by_ident_and_kind( &self, tcx: TyCtxt<'_>, ident: Ident, @@ -212,8 +213,9 @@ impl AssocItems { .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id)) } - /// Returns the associated item with the given name and any of `AssocKind`, if one exists. - pub fn find_by_name_and_kinds( + /// Returns the associated item with the given identifier and any of `AssocKind`, if one + /// exists. The identifier is matched hygienically. + pub fn find_by_ident_and_kinds( &self, tcx: TyCtxt<'_>, ident: Ident, @@ -221,11 +223,12 @@ impl AssocItems { kinds: &[AssocKind], parent_def_id: DefId, ) -> Option<&ty::AssocItem> { - kinds.iter().find_map(|kind| self.find_by_name_and_kind(tcx, ident, *kind, parent_def_id)) + kinds.iter().find_map(|kind| self.find_by_ident_and_kind(tcx, ident, *kind, parent_def_id)) } - /// Returns the associated item with the given name in the given `Namespace`, if one exists. - pub fn find_by_name_and_namespace( + /// Returns the associated item with the given identifier in the given `Namespace`, if one + /// exists. The identifier is matched hygienically. + pub fn find_by_ident_and_namespace( &self, tcx: TyCtxt<'_>, ident: Ident, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 80f1bd7c6f464..a2b3acac3f26b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1939,15 +1939,15 @@ impl<'tcx> TyCtxt<'tcx> { /// Hygienically compares a use-site name (`use_name`) for a field or an associated item with /// its supposed definition name (`def_name`). The method also needs `DefId` of the supposed /// definition's parent/scope to perform comparison. - pub fn hygienic_eq(self, use_name: Ident, def_name: Ident, def_parent_def_id: DefId) -> bool { - // We could use `Ident::eq` here, but we deliberately don't. The name + pub fn hygienic_eq(self, use_ident: Ident, def_ident: Ident, def_parent_def_id: DefId) -> bool { + // We could use `Ident::eq` here, but we deliberately don't. The identifier // comparison fails frequently, and we want to avoid the expensive // `normalize_to_macros_2_0()` calls required for the span comparison whenever possible. - use_name.name == def_name.name - && use_name + use_ident.name == def_ident.name + && use_ident .span .ctxt() - .hygienic_eq(def_name.span.ctxt(), self.expn_that_defined(def_parent_def_id)) + .hygienic_eq(def_ident.span.ctxt(), self.expn_that_defined(def_parent_def_id)) } pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index a484573f0d860..55d52d5075dcc 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -191,7 +191,7 @@ impl<'tcx> MoveCheckVisitor<'tcx> { fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { for impl_def_id in tcx.inherent_impls(def_id) { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + if let Some(new) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( tcx, fn_ident, AssocKind::Fn, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6dc854758da56..56bc826c94f73 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2550,7 +2550,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .iter() .filter_map(|item| { let parent_module = self.opt_local_def_id(item.parent_module)?.to_def_id(); - Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg.clone() }) + Some(StrippedCfgItem { + parent_module, + ident: item.ident, + cfg: item.cfg.clone(), + }) }) .collect::>(); local_items.as_slice() @@ -2558,12 +2562,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.tcx.stripped_cfg_items(module.krate) }; - for &StrippedCfgItem { parent_module, name, ref cfg } in symbols { - if parent_module != module || name.name != *segment { + for &StrippedCfgItem { parent_module, ident, ref cfg } in symbols { + if parent_module != module || ident.name != *segment { continue; } - let note = errors::FoundItemConfigureOut { span: name.span }; + let note = errors::FoundItemConfigureOut { span: ident.span }; err.subdiagnostic(note); if let MetaItemKind::List(nested) = &cfg.kind diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index b62bc6c45e0c5..c2761bd2717f4 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2238,7 +2238,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .get(&def_id) .is_some_and(|sig| sig.has_self), None => { - self.r.tcx.fn_arg_names(def_id).first().is_some_and(|&ident| { + self.r.tcx.fn_arg_idents(def_id).first().is_some_and(|&ident| { matches!(ident, Some(Ident { name: kw::SelfLower, .. })) }) } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 3ac66840d87de..e1476814d5c0f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1648,7 +1648,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .filter_map(|item| { let parent_module = self.node_id_to_def_id.get(&item.parent_module)?.key().to_def_id(); - Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg }) + Some(StrippedCfgItem { parent_module, ident: item.ident, cfg: item.cfg }) }) .collect(), ); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 9d6ae0aa9d130..e2f783d887e4c 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -469,8 +469,8 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.proc_macros.push(id) } - fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem) { - self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg }); + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, ident: Ident, cfg: ast::MetaItem) { + self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, ident, cfg }); } fn registered_tools(&self) -> &RegisteredTools { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 38fcba4ea6256..3583bc8ad8ffc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1988,7 +1988,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { let closure: Vec<_> = self .tcx - .fn_arg_names(fn_def_id) + .fn_arg_idents(fn_def_id) .iter() .enumerate() .map(|(i, ident)| { @@ -5397,7 +5397,7 @@ fn point_at_assoc_type_restriction( ); } if let Some(new) = - tcx.associated_items(data.impl_or_alias_def_id).find_by_name_and_kind( + tcx.associated_items(data.impl_or_alias_def_id).find_by_ident_and_kind( tcx, Ident::with_dummy_span(name), ty::AssocKind::Type, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e0e09b53fc28b..3a2b6974681bc 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -497,7 +497,7 @@ pub(crate) fn build_impl( }; let trait_item = tcx .associated_items(associated_trait.def_id) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, item.ident, assoc_kind, @@ -524,7 +524,7 @@ pub(crate) fn build_impl( if let Some(associated_trait) = associated_trait { let trait_item = tcx .associated_items(associated_trait.def_id) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, item.ident(tcx), item.kind, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ee6008b7851d3..45a915719e9f2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1088,7 +1088,7 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attrib enum FunctionArgs<'tcx> { Body(hir::BodyId), - Names(&'tcx [Option]), + Idents(&'tcx [Option]), } fn clean_function<'tcx>( @@ -1104,8 +1104,8 @@ fn clean_function<'tcx>( FunctionArgs::Body(body_id) => { clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id) } - FunctionArgs::Names(names) => { - clean_args_from_types_and_names(cx, sig.decl.inputs, names) + FunctionArgs::Idents(idents) => { + clean_args_from_types_and_names(cx, sig.decl.inputs, idents) } }; let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args); @@ -1117,7 +1117,7 @@ fn clean_function<'tcx>( fn clean_args_from_types_and_names<'tcx>( cx: &mut DocContext<'tcx>, types: &[hir::Ty<'tcx>], - names: &[Option], + idents: &[Option], ) -> Arguments { fn nonempty_name(ident: &Option) -> Option { if let Some(ident) = ident @@ -1131,7 +1131,7 @@ fn clean_args_from_types_and_names<'tcx>( // If at least one argument has a name, use `_` as the name of unnamed // arguments. Otherwise omit argument names. - let default_name = if names.iter().any(|ident| nonempty_name(ident).is_some()) { + let default_name = if idents.iter().any(|ident| nonempty_name(ident).is_some()) { kw::Underscore } else { kw::Empty @@ -1143,7 +1143,7 @@ fn clean_args_from_types_and_names<'tcx>( .enumerate() .map(|(i, ty)| Argument { type_: clean_ty(ty, cx), - name: names.get(i).and_then(nonempty_name).unwrap_or(default_name), + name: idents.get(i).and_then(nonempty_name).unwrap_or(default_name), is_const: false, }) .collect(), @@ -1193,7 +1193,7 @@ fn clean_poly_fn_sig<'tcx>( did: Option, sig: ty::PolyFnSig<'tcx>, ) -> FnDecl { - let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_names(did)).iter(); + let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_idents(did)).iter(); // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, // but shouldn't change any code meaning. @@ -1270,8 +1270,8 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); MethodItem(m, None) } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => { - let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names)); + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { + let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Idents(idents)); RequiredMethodItem(m) } hir::TraitItemKind::Type(bounds, Some(default)) => { @@ -2612,7 +2612,7 @@ fn clean_bare_fn_ty<'tcx>( .filter(|p| !is_elided_lifetime(p)) .map(|x| clean_generic_param(cx, None, x)) .collect(); - let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names); + let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_idents); let decl = clean_fn_decl_with_args(cx, bare_fn.decl, None, args); (generic_params, decl) }); @@ -3148,8 +3148,8 @@ fn clean_maybe_renamed_foreign_item<'tcx>( let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { let kind = match item.kind { - hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem( - clean_function(cx, &sig, generics, FunctionArgs::Names(names)), + hir::ForeignItemKind::Fn(sig, idents, generics) => ForeignFunctionItem( + clean_function(cx, &sig, generics, FunctionArgs::Idents(idents)), sig.header.safety(), ), hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index adac2f27ea8cf..ad18c7039eedf 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -53,7 +53,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - .not_trait() .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[])) .and_then(|trait_id| { - cx.tcx.associated_items(trait_id).find_by_name_and_kind( + cx.tcx.associated_items(trait_id).find_by_ident_and_kind( cx.tcx, Ident::from_str("Output"), ty::AssocKind::Type, diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs index 041f6228fba2d..4495aeb5953e0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -22,8 +22,8 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id) && !is_from_ignored_trait(trait_ref, ignored_traits) { - let mut param_idents_iter = cx.tcx.hir_body_param_names(body_id); - let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); + let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id); + let mut default_param_idents_iter = cx.tcx.fn_arg_idents(did).iter().copied(); let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter); if !renames.0.is_empty() { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 56ff7e2c61b22..239ee6c729fb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -238,7 +238,7 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - .instantiate_bound_regions_with_erased(sig.rebind(search_ty)) .kind() && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) - && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_name_and_kind( + && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_ident_and_kind( cx.tcx, Ident::with_dummy_span(sym::Item), AssocKind::Type, diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 6fdf4c244f8d8..29cbf62c3d4c1 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -1109,7 +1109,7 @@ pub fn make_projection<'tcx>( assoc_ty: Symbol, args: GenericArgsRef<'tcx>, ) -> Option> { - let Some(assoc_item) = tcx.associated_items(container_id).find_by_name_and_kind( + let Some(assoc_item) = tcx.associated_items(container_id).find_by_ident_and_kind( tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 06e618c2d254b..3db34ed24cc20 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "compiletest" version = "0.0.0" -edition = "2021" +edition = "2024" [lib] doctest = false diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs index 20e3c8dfb9ee7..5126e55aea123 100644 --- a/src/tools/compiletest/src/debuggers.rs +++ b/src/tools/compiletest/src/debuggers.rs @@ -40,7 +40,9 @@ pub(crate) fn configure_gdb(config: &Config) -> Option> { // // we should figure out how to lift this restriction! (run them all // on different ports allocated dynamically). - env::set_var("RUST_TEST_THREADS", "1"); + // + // SAFETY: at this point we are still single-threaded. + unsafe { env::set_var("RUST_TEST_THREADS", "1") }; } Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() })) diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 782f6e0f2d8d2..dfd678a7e2d65 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -529,10 +529,14 @@ pub fn run_tests(config: Arc) { } // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary - env::set_var("__COMPAT_LAYER", "RunAsInvoker"); - - // Let tests know which target they're running as - env::set_var("TARGET", &config.target); + // + // SAFETY: at this point we're still single-threaded. + unsafe { env::set_var("__COMPAT_LAYER", "RunAsInvoker") }; + + // Let tests know which target they're running as. + // + // SAFETY: at this point we're still single-threaded. + unsafe { env::set_var("TARGET", &config.target) }; let mut configs = Vec::new(); if let Mode::DebugInfo = config.mode { diff --git a/src/tools/compiletest/src/raise_fd_limit.rs b/src/tools/compiletest/src/raise_fd_limit.rs index 7b12ba946b9eb..653b125a6b413 100644 --- a/src/tools/compiletest/src/raise_fd_limit.rs +++ b/src/tools/compiletest/src/raise_fd_limit.rs @@ -6,6 +6,7 @@ /// This fixes issue #7772. #[cfg(target_vendor = "apple")] #[allow(non_camel_case_types)] +// FIXME(#139616): document caller contract. pub unsafe fn raise_fd_limit() { use std::ptr::null_mut; use std::{cmp, io}; @@ -21,8 +22,10 @@ pub unsafe fn raise_fd_limit() { let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC]; let mut maxfiles: libc::c_int = 0; let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; - if libc::sysctl(&mut mib[0], 2, &mut maxfiles as *mut _ as *mut _, &mut size, null_mut(), 0) - != 0 + // FIXME(#139616): justify why this is sound. + if unsafe { + libc::sysctl(&mut mib[0], 2, &mut maxfiles as *mut _ as *mut _, &mut size, null_mut(), 0) + } != 0 { let err = io::Error::last_os_error(); panic!("raise_fd_limit: error calling sysctl: {}", err); @@ -30,7 +33,8 @@ pub unsafe fn raise_fd_limit() { // Fetch the current resource limits let mut rlim = libc::rlimit { rlim_cur: 0, rlim_max: 0 }; - if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 { + // FIXME(#139616): justify why this is sound. + if unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) } != 0 { let err = io::Error::last_os_error(); panic!("raise_fd_limit: error calling getrlimit: {}", err); } @@ -41,7 +45,8 @@ pub unsafe fn raise_fd_limit() { rlim.rlim_cur = cmp::min(maxfiles as libc::rlim_t, rlim.rlim_max); // Set our newly-increased resource limit. - if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) != 0 { + // FIXME(#139616): justify why this is sound. + if unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) } != 0 { let err = io::Error::last_os_error(); panic!("raise_fd_limit: error calling setrlimit: {}", err); } diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs index 28ca5589992a2..2213dd07160a7 100644 --- a/src/tools/compiletest/src/read2.rs +++ b/src/tools/compiletest/src/read2.rs @@ -165,6 +165,7 @@ mod imp { mut err_pipe: ChildStderr, data: &mut dyn FnMut(bool, &mut Vec, bool), ) -> io::Result<()> { + // FIXME(#139616): justify why this is sound. unsafe { libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); @@ -175,6 +176,7 @@ mod imp { let mut out = Vec::new(); let mut err = Vec::new(); + // FIXME(#139616): justify why this is sound. let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; fds[0].fd = out_pipe.as_raw_fd(); fds[0].events = libc::POLLIN; @@ -185,6 +187,7 @@ mod imp { while nfds > 0 { // wait for either pipe to become readable using `select` + // FIXME(#139616): justify why this is sound. let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) }; if r == -1 { let err = io::Error::last_os_error(); @@ -256,6 +259,7 @@ mod imp { port.add_handle(0, &out_pipe)?; port.add_handle(1, &err_pipe)?; + // FIXME(#139616): justify why this is sound. unsafe { let mut out_pipe = Pipe::new(out_pipe, &mut out); let mut err_pipe = Pipe::new(err_pipe, &mut err); @@ -284,18 +288,23 @@ mod imp { } impl<'a> Pipe<'a> { + // FIXME(#139616): document caller contract. unsafe fn new(p: P, dst: &'a mut Vec) -> Pipe<'a> { Pipe { dst, - pipe: NamedPipe::from_raw_handle(p.into_raw_handle()), + // FIXME(#139616): justify why this is sound. + pipe: unsafe { NamedPipe::from_raw_handle(p.into_raw_handle()) }, overlapped: Overlapped::zero(), done: false, } } + // FIXME(#139616): document caller contract. unsafe fn read(&mut self) -> io::Result<()> { - let dst = slice_to_end(self.dst); - match self.pipe.read_overlapped(dst, self.overlapped.raw()) { + // FIXME(#139616): justify why this is sound. + let dst = unsafe { slice_to_end(self.dst) }; + // FIXME(#139616): justify why this is sound. + match unsafe { self.pipe.read_overlapped(dst, self.overlapped.raw()) } { Ok(_) => Ok(()), Err(e) => { if e.raw_os_error() == Some(ERROR_BROKEN_PIPE.0 as i32) { @@ -308,15 +317,18 @@ mod imp { } } + // FIXME(#139616): document caller contract. unsafe fn complete(&mut self, status: &CompletionStatus) { let prev = self.dst.len(); - self.dst.set_len(prev + status.bytes_transferred() as usize); + // FIXME(#139616): justify why this is sound. + unsafe { self.dst.set_len(prev + status.bytes_transferred() as usize) }; if status.bytes_transferred() == 0 { self.done = true; } } } + // FIXME(#139616): document caller contract. unsafe fn slice_to_end(v: &mut Vec) -> &mut [u8] { if v.capacity() == 0 { v.reserve(16); @@ -324,6 +336,12 @@ mod imp { if v.capacity() == v.len() { v.reserve(1); } - slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize), v.capacity() - v.len()) + // FIXME(#139616): justify why this is sound. + unsafe { + slice::from_raw_parts_mut( + v.as_mut_ptr().offset(v.len() as isize), + v.capacity() - v.len(), + ) + } } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index a3aae39be81d0..9c03fa141bd36 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -445,8 +445,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( rustc, - self.config.compile_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.compile_lib_path.as_path(), + Some(aux_dir.as_path()), src, ) } @@ -1020,8 +1020,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( test_client, - self.config.run_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.run_lib_path.as_path(), + Some(aux_dir.as_path()), None, ) } @@ -1035,8 +1035,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( wr_run, - self.config.run_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.run_lib_path.as_path(), + Some(aux_dir.as_path()), None, ) } @@ -1050,8 +1050,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( program, - self.config.run_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.run_lib_path.as_path(), + Some(aux_dir.as_path()), None, ) } @@ -1197,8 +1197,8 @@ impl<'test> TestCx<'test> { self.props.unset_rustc_env.iter().fold(&mut rustc, Command::env_remove); self.compose_and_run( rustc, - self.config.compile_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.compile_lib_path.as_path(), + Some(aux_dir.as_path()), input, ) } @@ -1219,8 +1219,7 @@ impl<'test> TestCx<'test> { rustc.args(&["--crate-type", "rlib"]); rustc.arg("-Cpanic=abort"); - let res = - self.compose_and_run(rustc, self.config.compile_lib_path.to_str().unwrap(), None, None); + let res = self.compose_and_run(rustc, self.config.compile_lib_path.as_path(), None, None); if !res.status.success() { self.fatal_proc_rec( &format!( @@ -1332,8 +1331,8 @@ impl<'test> TestCx<'test> { let auxres = aux_cx.compose_and_run( aux_rustc, - aux_cx.config.compile_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + aux_cx.config.compile_lib_path.as_path(), + Some(aux_dir.as_path()), None, ); if !auxres.status.success() { @@ -1373,8 +1372,8 @@ impl<'test> TestCx<'test> { fn compose_and_run( &self, mut command: Command, - lib_path: &str, - aux_path: Option<&str>, + lib_path: &Path, + aux_path: Option<&Path>, input: Option, ) -> ProcRes { let cmdline = { @@ -1806,7 +1805,7 @@ impl<'test> TestCx<'test> { } } - fn make_cmdline(&self, command: &Command, libpath: &str) -> String { + fn make_cmdline(&self, command: &Command, libpath: &Path) -> String { use crate::util; // Linux and mac don't require adjusting the library search path @@ -1819,7 +1818,7 @@ impl<'test> TestCx<'test> { format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path)) } - format!("{} {:?}", lib_path_cmd_prefix(libpath), command) + format!("{} {:?}", lib_path_cmd_prefix(libpath.to_str().unwrap()), command) } } @@ -1980,7 +1979,8 @@ impl<'test> TestCx<'test> { // Add custom flags supplied by the `filecheck-flags:` test header. filecheck.args(&self.props.filecheck_flags); - self.compose_and_run(filecheck, "", None, None) + // FIXME(jieyouxu): don't pass an empty Path + self.compose_and_run(filecheck, Path::new(""), None, None) } fn charset() -> &'static str { diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs index 170b8a8099687..50e733cd31b6b 100644 --- a/src/tools/compiletest/src/runtest/debuginfo.rs +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -104,7 +104,7 @@ impl TestCx<'_> { let debugger_run_result = self.compose_and_run( cdb, - self.config.run_lib_path.to_str().unwrap(), + self.config.run_lib_path.as_path(), None, // aux_path None, // input ); @@ -241,7 +241,8 @@ impl TestCx<'_> { let cmdline = { let mut gdb = Command::new(&format!("{}-gdb", self.config.target)); gdb.args(debugger_opts); - let cmdline = self.make_cmdline(&gdb, ""); + // FIXME(jieyouxu): don't pass an empty Path + let cmdline = self.make_cmdline(&gdb, Path::new("")); logv(self.config, format!("executing {}", cmdline)); cmdline }; @@ -340,7 +341,7 @@ impl TestCx<'_> { gdb.args(debugger_opts).env("PYTHONPATH", pythonpath); debugger_run_result = - self.compose_and_run(gdb, self.config.run_lib_path.to_str().unwrap(), None, None); + self.compose_and_run(gdb, self.config.run_lib_path.as_path(), None, None); } if !debugger_run_result.status.success() { diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index bb228962ecc9c..0c6f4a3dd06e7 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -538,6 +538,7 @@ name = "miri" version = "0.1.0" dependencies = [ "aes", + "bitflags", "chrono", "chrono-tz", "colored", diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 5bb07648f75c6..e9ee19b793266 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -26,6 +26,7 @@ measureme = "12" chrono = { version = "0.4.38", default-features = false } chrono-tz = "0.10" directories = "6" +bitflags = "2.6" # Copied from `compiler/rustc/Cargo.toml`. # But only for some targets, it fails for others. Rustc configures this in its CI, but we can't diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 7155d692ee5c9..b690bd9cd2b97 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -164,7 +164,7 @@ case $HOST_TARGET in # Partially supported targets (tier 2) BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there - TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe + TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe concurrency sync TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random sync concurrency thread epoll eventfd TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index a6f2951087975..d2b8b51473061 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -25a615bf829b9f6d6f22da537e3851043f92e5f2 +7d7de5bf3c3cbf9c2c5bbc5cbfb9197a8a427d35 diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index 6b4f4cdc922a0..42603e784bbd7 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -1,6 +1,7 @@ use std::any::Any; use std::collections::BTreeMap; -use std::io::{IsTerminal, SeekFrom, Write}; +use std::fs::{File, Metadata}; +use std::io::{IsTerminal, Seek, SeekFrom, Write}; use std::marker::CoercePointee; use std::ops::Deref; use std::rc::{Rc, Weak}; @@ -192,7 +193,7 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { false } - fn as_unix(&self) -> &dyn UnixFileDescription { + fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { panic!("Not a unix file descriptor: {}", self.name()); } } @@ -278,6 +279,97 @@ impl FileDescription for io::Stderr { } } +#[derive(Debug)] +pub struct FileHandle { + pub(crate) file: File, + pub(crate) writable: bool, +} + +impl FileDescription for FileHandle { + fn name(&self) -> &'static str { + "file" + } + + fn read<'tcx>( + self: FileDescriptionRef, + communicate_allowed: bool, + ptr: Pointer, + len: usize, + ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + assert!(communicate_allowed, "isolation should have prevented even opening a file"); + + let result = ecx.read_from_host(&self.file, len, ptr)?; + finish.call(ecx, result) + } + + fn write<'tcx>( + self: FileDescriptionRef, + communicate_allowed: bool, + ptr: Pointer, + len: usize, + ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + assert!(communicate_allowed, "isolation should have prevented even opening a file"); + + let result = ecx.write_to_host(&self.file, len, ptr)?; + finish.call(ecx, result) + } + + fn seek<'tcx>( + &self, + communicate_allowed: bool, + offset: SeekFrom, + ) -> InterpResult<'tcx, io::Result> { + assert!(communicate_allowed, "isolation should have prevented even opening a file"); + interp_ok((&mut &self.file).seek(offset)) + } + + fn close<'tcx>( + self, + communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + assert!(communicate_allowed, "isolation should have prevented even opening a file"); + // We sync the file if it was opened in a mode different than read-only. + if self.writable { + // `File::sync_all` does the checks that are done when closing a file. We do this to + // to handle possible errors correctly. + let result = self.file.sync_all(); + // Now we actually close the file and return the result. + drop(self.file); + interp_ok(result) + } else { + // We drop the file, this closes it but ignores any errors + // produced when closing it. This is done because + // `File::sync_all` cannot be done over files like + // `/dev/urandom` which are read-only. Check + // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 + // for a deeper discussion. + drop(self.file); + interp_ok(Ok(())) + } + } + + fn metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result> { + interp_ok(self.file.metadata()) + } + + fn is_tty(&self, communicate_allowed: bool) -> bool { + communicate_allowed && self.file.is_terminal() + } + + fn as_unix<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { + assert!( + ecx.target_os_is_unix(), + "unix file operations are only available for unix targets" + ); + self + } +} + /// Like /dev/null #[derive(Debug)] pub struct NullOutput; @@ -300,10 +392,13 @@ impl FileDescription for NullOutput { } } +/// Internal type of a file-descriptor - this is what [`FdTable`] expects +pub type FdNum = i32; + /// The file descriptor table #[derive(Debug)] pub struct FdTable { - pub fds: BTreeMap, + pub fds: BTreeMap, /// Unique identifier for file description, used to differentiate between various file description. next_file_description_id: FdId, } @@ -339,12 +434,12 @@ impl FdTable { } /// Insert a new file description to the FdTable. - pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 { + pub fn insert_new(&mut self, fd: impl FileDescription) -> FdNum { let fd_ref = self.new_ref(fd); self.insert(fd_ref) } - pub fn insert(&mut self, fd_ref: DynFileDescriptionRef) -> i32 { + pub fn insert(&mut self, fd_ref: DynFileDescriptionRef) -> FdNum { self.insert_with_min_num(fd_ref, 0) } @@ -352,8 +447,8 @@ impl FdTable { pub fn insert_with_min_num( &mut self, file_handle: DynFileDescriptionRef, - min_fd_num: i32, - ) -> i32 { + min_fd_num: FdNum, + ) -> FdNum { // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in // between used FDs, the find_map combinator will return it. If the first such unused FD // is after all other used FDs, the find_map combinator will return None, and we will use @@ -379,16 +474,16 @@ impl FdTable { new_fd_num } - pub fn get(&self, fd_num: i32) -> Option { + pub fn get(&self, fd_num: FdNum) -> Option { let fd = self.fds.get(&fd_num)?; Some(fd.clone()) } - pub fn remove(&mut self, fd_num: i32) -> Option { + pub fn remove(&mut self, fd_num: FdNum) -> Option { self.fds.remove(&fd_num) } - pub fn is_fd_num(&self, fd_num: i32) -> bool { + pub fn is_fd_num(&self, fd_num: FdNum) -> bool { self.fds.contains_key(&fd_num) } } diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index d7c445b47cb0e..fb80a36af9fc2 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -219,16 +219,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let filetime = this.deref_pointer_as(LPFILETIME_op, this.windows_ty_layout("FILETIME"))?; - let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC"); - let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC"); - let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH"); - let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC; - let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC; - - let duration = system_time_to_duration(&SystemTime::now())? - + Duration::from_secs(SECONDS_TO_UNIX_EPOCH); - let duration_ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL)) - .map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?; + let duration = this.system_time_since_windows_epoch(&SystemTime::now())?; + let duration_ticks = this.windows_ticks_for(duration)?; let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap(); let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap(); @@ -281,6 +273,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(-1)) // Return non-zero on success } + #[allow(non_snake_case, clippy::arithmetic_side_effects)] + fn system_time_since_windows_epoch(&self, time: &SystemTime) -> InterpResult<'tcx, Duration> { + let this = self.eval_context_ref(); + + let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC"); + let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH"); + let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC; + + interp_ok(system_time_to_duration(time)? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH)) + } + + #[allow(non_snake_case, clippy::arithmetic_side_effects)] + fn windows_ticks_for(&self, duration: Duration) -> InterpResult<'tcx, u64> { + let this = self.eval_context_ref(); + + let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC"); + let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC"); + let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC; + + let ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL)) + .map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?; + interp_ok(ticks) + } + fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 3f85b9ae9bd94..41be9df7e2da7 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -121,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("unsupported flags {:#x}", op); }; - let result = fd.as_unix().flock(this.machine.communicate(), parsed_op)?; + let result = fd.as_unix(this).flock(this.machine.communicate(), parsed_op)?; // return `0` if flock is successful let result = result.map(|()| 0i32); interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) @@ -273,7 +273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Ok(offset) = u64::try_from(offset) else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; - fd.as_unix().pread(communicate, offset, buf, count, this, finish)? + fd.as_unix(this).pread(communicate, offset, buf, count, this, finish)? } }; interp_ok(()) @@ -333,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Ok(offset) = u64::try_from(offset) else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; - fd.as_unix().pwrite(communicate, buf, count, offset, this, finish)? + fd.as_unix(this).pwrite(communicate, buf, count, offset, this, finish)? } }; interp_ok(()) diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 08d06fe5d4c61..21a386b29272a 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -2,6 +2,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::{Conv, FnAbi}; +use super::sync::EvalContextExt as _; use crate::shims::unix::*; use crate::*; @@ -55,6 +56,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } + // Synchronization primitives + "_umtx_op" => { + let [obj, op, val, uaddr, uaddr2] = + this.check_shim(abi, Conv::C, link_name, args)?; + this._umtx_op(obj, op, val, uaddr, uaddr2, dest)?; + } + // File related shims // For those, we both intercept `func` and `call@FBSD_1.0` symbols cases // since freebsd 12 the former form can be expected. diff --git a/src/tools/miri/src/shims/unix/freebsd/mod.rs b/src/tools/miri/src/shims/unix/freebsd/mod.rs index 09c6507b24f84..50fb2b9d32870 100644 --- a/src/tools/miri/src/shims/unix/freebsd/mod.rs +++ b/src/tools/miri/src/shims/unix/freebsd/mod.rs @@ -1 +1,2 @@ pub mod foreign_items; +pub mod sync; diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs new file mode 100644 index 0000000000000..54650f35b2cb7 --- /dev/null +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -0,0 +1,251 @@ +//! Contains FreeBSD-specific synchronization functions + +use core::time::Duration; + +use crate::concurrency::sync::FutexRef; +use crate::*; + +pub struct FreeBsdFutex { + futex: FutexRef, +} + +/// Extended variant of the `timespec` struct. +pub struct UmtxTime { + timeout: Duration, + abs_time: bool, + timeout_clock: TimeoutClock, +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Implementation of the FreeBSD [`_umtx_op`](https://man.freebsd.org/cgi/man.cgi?query=_umtx_op&sektion=2&manpath=FreeBSD+14.2-RELEASE+and+Ports) syscall. + /// This is used for futex operations on FreeBSD. + /// + /// `obj`: a pointer to the futex object (can be a lot of things, mostly *AtomicU32) + /// `op`: the futex operation to run + /// `val`: the current value of the object as a `c_long` (for wait/wake) + /// `uaddr`: `op`-specific optional parameter, pointer-sized integer or pointer to an `op`-specific struct + /// `uaddr2`: `op`-specific optional parameter, pointer-sized integer or pointer to an `op`-specific struct + /// `dest`: the place this syscall returns to, 0 for success, -1 for failure + /// + /// # Note + /// Curently only the WAIT and WAKE operations are implemented. + fn _umtx_op( + &mut self, + obj: &OpTy<'tcx>, + op: &OpTy<'tcx>, + val: &OpTy<'tcx>, + uaddr: &OpTy<'tcx>, + uaddr2: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let obj = this.read_pointer(obj)?; + let op = this.read_scalar(op)?.to_i32()?; + let val = this.read_target_usize(val)?; + let uaddr = this.read_target_usize(uaddr)?; + let uaddr2 = this.read_pointer(uaddr2)?; + + let wait = this.eval_libc_i32("UMTX_OP_WAIT"); + let wait_uint = this.eval_libc_i32("UMTX_OP_WAIT_UINT"); + let wait_uint_private = this.eval_libc_i32("UMTX_OP_WAIT_UINT_PRIVATE"); + + let wake = this.eval_libc_i32("UMTX_OP_WAKE"); + let wake_private = this.eval_libc_i32("UMTX_OP_WAKE_PRIVATE"); + + let timespec_layout = this.libc_ty_layout("timespec"); + let umtx_time_layout = this.libc_ty_layout("_umtx_time"); + assert!( + timespec_layout.size != umtx_time_layout.size, + "`struct timespec` and `struct _umtx_time` should have different sizes." + ); + + match op { + // UMTX_OP_WAIT_UINT and UMTX_OP_WAIT_UINT_PRIVATE only differ in whether they work across + // processes or not. For Miri, we can treat them the same. + op if op == wait || op == wait_uint || op == wait_uint_private => { + let obj_layout = + if op == wait { this.machine.layouts.isize } else { this.machine.layouts.u32 }; + let obj = this.ptr_to_mplace(obj, obj_layout); + + // Read the Linux futex wait implementation in Miri to understand why this fence is needed. + this.atomic_fence(AtomicFenceOrd::SeqCst)?; + let obj_val = this + .read_scalar_atomic(&obj, AtomicReadOrd::Acquire)? + .to_bits(obj_layout.size)?; // isize and u32 can have different sizes + + if obj_val == u128::from(val) { + // This cannot fail since we already did an atomic acquire read on that pointer. + // Acquire reads are only allowed on mutable memory. + let futex_ref = this + .get_sync_or_init(obj.ptr(), |_| FreeBsdFutex { futex: Default::default() }) + .unwrap() + .futex + .clone(); + + // From the manual: + // The timeout is specified by passing either the address of `struct timespec`, or its + // extended variant, `struct _umtx_time`, as the `uaddr2` argument of _umtx_op(). + // They are distinguished by the `uaddr` value, which must be equal + // to the size of the structure pointed to by `uaddr2`, casted to uintptr_t. + let timeout = if this.ptr_is_null(uaddr2)? { + // no timeout parameter + None + } else { + if uaddr == umtx_time_layout.size.bytes() { + // `uaddr2` points to a `struct _umtx_time`. + let umtx_time_place = this.ptr_to_mplace(uaddr2, umtx_time_layout); + + let umtx_time = match this.read_umtx_time(&umtx_time_place)? { + Some(ut) => ut, + None => { + return this + .set_last_error_and_return(LibcError("EINVAL"), dest); + } + }; + + let anchor = if umtx_time.abs_time { + TimeoutAnchor::Absolute + } else { + TimeoutAnchor::Relative + }; + + Some((umtx_time.timeout_clock, anchor, umtx_time.timeout)) + } else if uaddr == timespec_layout.size.bytes() { + // RealTime clock can't be used in isolation mode. + this.check_no_isolation("`_umtx_op` with `timespec` timeout")?; + + // `uaddr2` points to a `struct timespec`. + let timespec = this.ptr_to_mplace(uaddr2, timespec_layout); + let duration = match this.read_timespec(×pec)? { + Some(duration) => duration, + None => { + return this + .set_last_error_and_return(LibcError("EINVAL"), dest); + } + }; + + // FreeBSD does not seem to document which clock is used when the timeout + // is passed as a `struct timespec*`. Based on discussions online and the source + // code (umtx_copyin_umtx_time() in kern_umtx.c), it seems to default to CLOCK_REALTIME, + // so that's what we also do. + // Discussion in golang: https://github.com/golang/go/issues/17168#issuecomment-250235271 + Some((TimeoutClock::RealTime, TimeoutAnchor::Relative, duration)) + } else { + return this.set_last_error_and_return(LibcError("EINVAL"), dest); + } + }; + + let dest = dest.clone(); + this.futex_wait( + futex_ref, + u32::MAX, // we set the bitset to include all bits + timeout, + callback!( + @capture<'tcx> { + dest: MPlaceTy<'tcx>, + } + |ecx, unblock: UnblockKind| match unblock { + UnblockKind::Ready => { + // From the manual: + // If successful, all requests, except UMTX_SHM_CREAT and UMTX_SHM_LOOKUP + // sub-requests of the UMTX_OP_SHM request, will return zero. + ecx.write_int(0, &dest) + } + UnblockKind::TimedOut => { + ecx.set_last_error_and_return(LibcError("ETIMEDOUT"), &dest) + } + } + ), + ); + interp_ok(()) + } else { + // The manual doesn’t specify what should happen if the futex value doesn’t match the expected one. + // On FreeBSD 14.2, testing shows that WAIT operations return 0 even when the value is incorrect. + this.write_int(0, dest)?; + interp_ok(()) + } + } + // UMTX_OP_WAKE and UMTX_OP_WAKE_PRIVATE only differ in whether they work across + // processes or not. For Miri, we can treat them the same. + op if op == wake || op == wake_private => { + let Some(futex_ref) = + this.get_sync_or_init(obj, |_| FreeBsdFutex { futex: Default::default() }) + else { + // From Linux implemenation: + // No AllocId, or no live allocation at that AllocId. + // Return an error code. (That seems nicer than silently doing something non-intuitive.) + // This means that if an address gets reused by a new allocation, + // we'll use an independent futex queue for this... that seems acceptable. + return this.set_last_error_and_return(LibcError("EFAULT"), dest); + }; + let futex_ref = futex_ref.futex.clone(); + + // Saturating cast for when usize is smaller than u64. + let count = usize::try_from(val).unwrap_or(usize::MAX); + + // Read the Linux futex wake implementation in Miri to understand why this fence is needed. + this.atomic_fence(AtomicFenceOrd::SeqCst)?; + + // `_umtx_op` doesn't return the amount of woken threads. + let _woken = this.futex_wake( + &futex_ref, + u32::MAX, // we set the bitset to include all bits + count, + )?; + + // From the manual: + // If successful, all requests, except UMTX_SHM_CREAT and UMTX_SHM_LOOKUP + // sub-requests of the UMTX_OP_SHM request, will return zero. + this.write_int(0, dest)?; + interp_ok(()) + } + op => { + throw_unsup_format!("Miri does not support `_umtx_op` syscall with op={}", op) + } + } + } + + /// Parses a `_umtx_time` struct. + /// Returns `None` if the underlying `timespec` struct is invalid. + fn read_umtx_time(&mut self, ut: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option> { + let this = self.eval_context_mut(); + // Only flag allowed is UMTX_ABSTIME. + let abs_time = this.eval_libc_u32("UMTX_ABSTIME"); + + let timespec_place = this.project_field(ut, 0)?; + // Inner `timespec` must still be valid. + let duration = match this.read_timespec(×pec_place)? { + Some(dur) => dur, + None => return interp_ok(None), + }; + + let flags_place = this.project_field(ut, 1)?; + let flags = this.read_scalar(&flags_place)?.to_u32()?; + let abs_time_flag = flags == abs_time; + + let clock_id_place = this.project_field(ut, 2)?; + let clock_id = this.read_scalar(&clock_id_place)?.to_i32()?; + let timeout_clock = this.translate_umtx_time_clock_id(clock_id)?; + + interp_ok(Some(UmtxTime { timeout: duration, abs_time: abs_time_flag, timeout_clock })) + } + + /// Translate raw FreeBSD clockid to a Miri TimeoutClock. + /// FIXME: share this code with the pthread and clock_gettime shims. + fn translate_umtx_time_clock_id(&mut self, raw_id: i32) -> InterpResult<'tcx, TimeoutClock> { + let this = self.eval_context_mut(); + + let timeout = if raw_id == this.eval_libc_i32("CLOCK_REALTIME") { + // RealTime clock can't be used in isolation mode. + this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME` timeout")?; + TimeoutClock::RealTime + } else if raw_id == this.eval_libc_i32("CLOCK_MONOTONIC") { + TimeoutClock::Monotonic + } else { + throw_unsup_format!("unsupported clock id {raw_id}"); + }; + interp_ok(timeout) + } +} diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index f8e0c638c90d5..fc0f57694a715 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -2,10 +2,9 @@ use std::borrow::Cow; use std::fs::{ - DirBuilder, File, FileType, Metadata, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, - rename, + DirBuilder, File, FileType, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, rename, }; -use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write}; +use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::time::SystemTime; @@ -14,98 +13,11 @@ use rustc_data_structures::fx::FxHashMap; use self::shims::time::system_time_to_duration; use crate::helpers::check_min_vararg_count; -use crate::shims::files::{EvalContextExt as _, FileDescription, FileDescriptionRef}; +use crate::shims::files::FileHandle; use crate::shims::os_str::bytes_to_os_str; use crate::shims::unix::fd::{FlockOp, UnixFileDescription}; use crate::*; -#[derive(Debug)] -struct FileHandle { - file: File, - writable: bool, -} - -impl FileDescription for FileHandle { - fn name(&self) -> &'static str { - "file" - } - - fn read<'tcx>( - self: FileDescriptionRef, - communicate_allowed: bool, - ptr: Pointer, - len: usize, - ecx: &mut MiriInterpCx<'tcx>, - finish: DynMachineCallback<'tcx, Result>, - ) -> InterpResult<'tcx> { - assert!(communicate_allowed, "isolation should have prevented even opening a file"); - - let result = ecx.read_from_host(&self.file, len, ptr)?; - finish.call(ecx, result) - } - - fn write<'tcx>( - self: FileDescriptionRef, - communicate_allowed: bool, - ptr: Pointer, - len: usize, - ecx: &mut MiriInterpCx<'tcx>, - finish: DynMachineCallback<'tcx, Result>, - ) -> InterpResult<'tcx> { - assert!(communicate_allowed, "isolation should have prevented even opening a file"); - - let result = ecx.write_to_host(&self.file, len, ptr)?; - finish.call(ecx, result) - } - - fn seek<'tcx>( - &self, - communicate_allowed: bool, - offset: SeekFrom, - ) -> InterpResult<'tcx, io::Result> { - assert!(communicate_allowed, "isolation should have prevented even opening a file"); - interp_ok((&mut &self.file).seek(offset)) - } - - fn close<'tcx>( - self, - communicate_allowed: bool, - _ecx: &mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, io::Result<()>> { - assert!(communicate_allowed, "isolation should have prevented even opening a file"); - // We sync the file if it was opened in a mode different than read-only. - if self.writable { - // `File::sync_all` does the checks that are done when closing a file. We do this to - // to handle possible errors correctly. - let result = self.file.sync_all(); - // Now we actually close the file and return the result. - drop(self.file); - interp_ok(result) - } else { - // We drop the file, this closes it but ignores any errors - // produced when closing it. This is done because - // `File::sync_all` cannot be done over files like - // `/dev/urandom` which are read-only. Check - // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 - // for a deeper discussion. - drop(self.file); - interp_ok(Ok(())) - } - } - - fn metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result> { - interp_ok(self.file.metadata()) - } - - fn is_tty(&self, communicate_allowed: bool) -> bool { - communicate_allowed && self.file.is_terminal() - } - - fn as_unix(&self) -> &dyn UnixFileDescription { - self - } -} - impl UnixFileDescription for FileHandle { fn pread<'tcx>( &self, diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index de8bcb54aef5b..b489595b4cd04 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -153,7 +153,7 @@ impl FileDescription for Epoll { interp_ok(Ok(())) } - fn as_unix(&self) -> &dyn UnixFileDescription { + fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { self } } @@ -590,7 +590,7 @@ fn check_and_update_one_event_interest<'tcx>( ecx: &MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, bool> { // Get the bitmask of ready events for a file description. - let ready_events_bitmask = fd_ref.as_unix().get_epoll_ready_events()?.get_event_bitmask(ecx); + let ready_events_bitmask = fd_ref.as_unix(ecx).get_epoll_ready_events()?.get_event_bitmask(ecx); let epoll_event_interest = interest.borrow(); let epfd = epoll_event_interest.weak_epfd.upgrade().unwrap(); // This checks if any of the events specified in epoll_event_interest.events diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 936d436bd82d6..ee7deb8d38308 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -100,7 +100,7 @@ impl FileDescription for EventFd { eventfd_write(buf_place, self, ecx, finish) } - fn as_unix(&self) -> &dyn UnixFileDescription { + fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { self } } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index e183bfdf0e137..135d8f6bee7e1 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -107,7 +107,7 @@ impl FileDescription for AnonSocket { anonsocket_write(self, ptr, len, ecx, finish) } - fn as_unix(&self) -> &dyn UnixFileDescription { + fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription { self } } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index fae6170a9e72c..7d97e333cd9b0 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -9,14 +9,9 @@ use rustc_target::callconv::{Conv, FnAbi}; use self::shims::windows::handle::{Handle, PseudoHandle}; use crate::shims::os_str::bytes_to_os_str; -use crate::shims::windows::handle::HandleError; use crate::shims::windows::*; use crate::*; -// The NTSTATUS STATUS_INVALID_HANDLE (0xC0000008) encoded as a HRESULT by setting the N bit. -// (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a) -const STATUS_INVALID_HANDLE: u32 = 0xD0000008; - pub fn is_dyn_sym(name: &str) -> bool { // std does dynamic detection for these symbols matches!( @@ -26,57 +21,107 @@ pub fn is_dyn_sym(name: &str) -> bool { } #[cfg(windows)] -fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { +fn win_get_full_path_name<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { // We are on Windows so we can simply let the host do this. interp_ok(path::absolute(path)) } #[cfg(unix)] #[expect(clippy::get_first, clippy::arithmetic_side_effects)] -fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { - // We are on Unix, so we need to implement parts of the logic ourselves. +fn win_get_full_path_name<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { + use std::sync::LazyLock; + + use rustc_data_structures::fx::FxHashSet; + + // We are on Unix, so we need to implement parts of the logic ourselves. `path` will use `/` + // separators, and the result should also use `/`. + // See for more + // information about Windows paths. + // This does not handle all corner cases correctly, see + // for more cursed + // examples. let bytes = path.as_os_str().as_encoded_bytes(); - // If it starts with `//` (these were backslashes but are already converted) - // then this is a magic special path, we just leave it unchanged. - if bytes.get(0).copied() == Some(b'/') && bytes.get(1).copied() == Some(b'/') { + // If it starts with `//./` or `//?/` then this is a magic special path, we just leave it + // unchanged. + if bytes.get(0).copied() == Some(b'/') + && bytes.get(1).copied() == Some(b'/') + && matches!(bytes.get(2), Some(b'.' | b'?')) + && bytes.get(3).copied() == Some(b'/') + { return interp_ok(Ok(path.into())); }; - // Special treatment for Windows' magic filenames: they are treated as being relative to `\\.\`. - let magic_filenames = &[ - "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", - "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", - ]; - if magic_filenames.iter().any(|m| m.as_bytes() == bytes) { - let mut result: Vec = br"//./".into(); + let is_unc = bytes.starts_with(b"//"); + // Special treatment for Windows' magic filenames: they are treated as being relative to `//./`. + static MAGIC_FILENAMES: LazyLock> = LazyLock::new(|| { + FxHashSet::from_iter([ + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", + "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + ]) + }); + if str::from_utf8(bytes).is_ok_and(|s| MAGIC_FILENAMES.contains(&*s.to_ascii_uppercase())) { + let mut result: Vec = b"//./".into(); result.extend(bytes); return interp_ok(Ok(bytes_to_os_str(&result)?.into())); } // Otherwise we try to do something kind of close to what Windows does, but this is probably not - // right in all cases. We iterate over the components between `/`, and remove trailing `.`, - // except that trailing `..` remain unchanged. - let mut result = vec![]; + // right in all cases. + let mut result: Vec<&[u8]> = vec![]; // will be a vecot of components, joined by `/`. let mut bytes = bytes; // the remaining bytes to process - loop { - let len = bytes.iter().position(|&b| b == b'/').unwrap_or(bytes.len()); - let mut component = &bytes[..len]; - if len >= 2 && component[len - 1] == b'.' && component[len - 2] != b'.' { - // Strip trailing `.` - component = &component[..len - 1]; + let mut stop = false; + while !stop { + // Find next component, and advance `bytes`. + let mut component = match bytes.iter().position(|&b| b == b'/') { + Some(pos) => { + let (component, tail) = bytes.split_at(pos); + bytes = &tail[1..]; // remove the `/`. + component + } + None => { + // There's no more `/`. + stop = true; + let component = bytes; + bytes = &[]; + component + } + }; + // `NUL` and only `NUL` also gets changed to be relative to `//./` later in the path. + // (This changed with Windows 11; previously, all magic filenames behaved like this.) + // Also, this does not apply to UNC paths. + if !is_unc && component.eq_ignore_ascii_case(b"NUL") { + let mut result: Vec = b"//./".into(); + result.extend(component); + return interp_ok(Ok(bytes_to_os_str(&result)?.into())); } - // Add this component to output. - result.extend(component); - // Prepare next iteration. - if len < bytes.len() { - // There's a component after this; add `/` and process remaining bytes. - result.push(b'/'); - bytes = &bytes[len + 1..]; + // Deal with `..` -- Windows handles this entirely syntactically. + if component == b".." { + // Remove previous component, unless we are at the "root" already, then just ignore the `..`. + let is_root = { + // Paths like `/C:`. + result.len() == 2 && matches!(result[0], []) && matches!(result[1], [_, b':']) + } || { + // Paths like `//server/share` + result.len() == 4 && matches!(result[0], []) && matches!(result[1], []) + }; + if !is_root { + result.pop(); + } continue; - } else { - // This was the last component and it did not have a trailing `/`. - break; } + // Preserve this component. + // Strip trailing `.`, but preserve trailing `..`. But not for UNC paths! + let len = component.len(); + if !is_unc && len >= 2 && component[len - 1] == b'.' && component[len - 2] != b'.' { + component = &component[..len - 1]; + } + // Add this component to output. + result.push(component); + } + // Drive letters must be followed by a `/`. + if result.len() == 2 && matches!(result[0], []) && matches!(result[1], [_, b':']) { + result.push(&[]); } - // Let the host `absolute` function do working-dir handling + // Let the host `absolute` function do working-dir handling. + let result = result.join(&b'/'); interp_ok(path::absolute(bytes_to_os_str(&result)?)) } @@ -231,7 +276,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let filename = this.read_path_from_wide_str(filename)?; - let result = match win_absolute(&filename)? { + let result = match win_get_full_path_name(&filename)? { Err(err) => { this.set_last_error(err)?; Scalar::from_u32(0) // return zero upon failure @@ -246,6 +291,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; this.write_scalar(result, dest)?; } + "CreateFileW" => { + let [ + file_name, + desired_access, + share_mode, + security_attributes, + creation_disposition, + flags_and_attributes, + template_file, + ] = this.check_shim(abi, sys_conv, link_name, args)?; + let handle = this.CreateFileW( + file_name, + desired_access, + share_mode, + security_attributes, + creation_disposition, + flags_and_attributes, + template_file, + )?; + this.write_scalar(handle.to_scalar(this), dest)?; + } + "GetFileInformationByHandle" => { + let [handle, info] = this.check_shim(abi, sys_conv, link_name, args)?; + let res = this.GetFileInformationByHandle(handle, info)?; + this.write_scalar(res, dest)?; + } // Allocation "HeapAlloc" => { @@ -498,52 +569,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "SetThreadDescription" => { let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?; - let handle = this.read_scalar(handle)?; + let handle = this.read_handle(handle, "SetThreadDescription")?; let name = this.read_wide_str(this.read_pointer(name)?)?; - let thread = match Handle::try_from_scalar(handle, this)? { - Ok(Handle::Thread(thread)) => Ok(thread), - Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()), - Ok(_) | Err(HandleError::InvalidHandle) => - this.invalid_handle("SetThreadDescription")?, - Err(HandleError::ThreadNotFound(e)) => Err(e), - }; - let res = match thread { - Ok(thread) => { - // FIXME: use non-lossy conversion - this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes()); - Scalar::from_u32(0) - } - Err(_) => Scalar::from_u32(STATUS_INVALID_HANDLE), + let thread = match handle { + Handle::Thread(thread) => thread, + Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(), + _ => this.invalid_handle("SetThreadDescription")?, }; - - this.write_scalar(res, dest)?; + // FIXME: use non-lossy conversion + this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes()); + this.write_scalar(Scalar::from_u32(0), dest)?; } "GetThreadDescription" => { let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?; - let handle = this.read_scalar(handle)?; + let handle = this.read_handle(handle, "GetThreadDescription")?; let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name - let thread = match Handle::try_from_scalar(handle, this)? { - Ok(Handle::Thread(thread)) => Ok(thread), - Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()), - Ok(_) | Err(HandleError::InvalidHandle) => - this.invalid_handle("GetThreadDescription")?, - Err(HandleError::ThreadNotFound(e)) => Err(e), - }; - let (name, res) = match thread { - Ok(thread) => { - // Looks like the default thread name is empty. - let name = this.get_thread_name(thread).unwrap_or(b"").to_owned(); - let name = this.alloc_os_str_as_wide_str( - bytes_to_os_str(&name)?, - MiriMemoryKind::WinLocal.into(), - )?; - (Scalar::from_maybe_pointer(name, this), Scalar::from_u32(0)) - } - Err(_) => (Scalar::null_ptr(this), Scalar::from_u32(STATUS_INVALID_HANDLE)), + let thread = match handle { + Handle::Thread(thread) => thread, + Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(), + _ => this.invalid_handle("GetThreadDescription")?, }; + // Looks like the default thread name is empty. + let name = this.get_thread_name(thread).unwrap_or(b"").to_owned(); + let name = this.alloc_os_str_as_wide_str( + bytes_to_os_str(&name)?, + MiriMemoryKind::WinLocal.into(), + )?; + let name = Scalar::from_maybe_pointer(name, this); + let res = Scalar::from_u32(0); this.write_scalar(name, &name_ptr)?; this.write_scalar(res, dest)?; @@ -638,11 +694,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?; this.check_no_isolation("`GetModuleFileNameW`")?; - let handle = this.read_target_usize(handle)?; + let handle = this.read_handle(handle, "GetModuleFileNameW")?; let filename = this.read_pointer(filename)?; let size = this.read_scalar(size)?.to_u32()?; - if handle != 0 { + if handle != Handle::Null { throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle"); } diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs new file mode 100644 index 0000000000000..32bab54896938 --- /dev/null +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -0,0 +1,402 @@ +use std::fs::{Metadata, OpenOptions}; +use std::io; +use std::path::PathBuf; +use std::time::SystemTime; + +use bitflags::bitflags; + +use crate::shims::files::{FileDescription, FileHandle}; +use crate::shims::windows::handle::{EvalContextExt as _, Handle}; +use crate::*; + +#[derive(Debug)] +pub struct DirHandle { + pub(crate) path: PathBuf, +} + +impl FileDescription for DirHandle { + fn name(&self) -> &'static str { + "directory" + } + + fn metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result> { + interp_ok(self.path.metadata()) + } + + fn close<'tcx>( + self, + _communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + interp_ok(Ok(())) + } +} + +/// Windows supports handles without any read/write/delete permissions - these handles can get +/// metadata, but little else. We represent that by storing the metadata from the time the handle +/// was opened. +#[derive(Debug)] +pub struct MetadataHandle { + pub(crate) meta: Metadata, +} + +impl FileDescription for MetadataHandle { + fn name(&self) -> &'static str { + "metadata-only" + } + + fn metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result> { + interp_ok(Ok(self.meta.clone())) + } + + fn close<'tcx>( + self, + _communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + interp_ok(Ok(())) + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum CreationDisposition { + CreateAlways, + CreateNew, + OpenAlways, + OpenExisting, + TruncateExisting, +} + +impl CreationDisposition { + fn new<'tcx>( + value: u32, + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, CreationDisposition> { + let create_always = ecx.eval_windows_u32("c", "CREATE_ALWAYS"); + let create_new = ecx.eval_windows_u32("c", "CREATE_NEW"); + let open_always = ecx.eval_windows_u32("c", "OPEN_ALWAYS"); + let open_existing = ecx.eval_windows_u32("c", "OPEN_EXISTING"); + let truncate_existing = ecx.eval_windows_u32("c", "TRUNCATE_EXISTING"); + + let out = if value == create_always { + CreationDisposition::CreateAlways + } else if value == create_new { + CreationDisposition::CreateNew + } else if value == open_always { + CreationDisposition::OpenAlways + } else if value == open_existing { + CreationDisposition::OpenExisting + } else if value == truncate_existing { + CreationDisposition::TruncateExisting + } else { + throw_unsup_format!("CreateFileW: Unsupported creation disposition: {value}"); + }; + interp_ok(out) + } +} + +bitflags! { + #[derive(PartialEq)] + struct FileAttributes: u32 { + const ZERO = 0; + const NORMAL = 1 << 0; + /// This must be passed to allow getting directory handles. If not passed, we error on trying + /// to open directories + const BACKUP_SEMANTICS = 1 << 1; + /// Open a reparse point as a regular file - this is basically similar to 'readlink' in Unix + /// terminology. A reparse point is a file with custom logic when navigated to, of which + /// a symlink is one specific example. + const OPEN_REPARSE = 1 << 2; + } +} + +impl FileAttributes { + fn new<'tcx>( + mut value: u32, + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, FileAttributes> { + let file_attribute_normal = ecx.eval_windows_u32("c", "FILE_ATTRIBUTE_NORMAL"); + let file_flag_backup_semantics = ecx.eval_windows_u32("c", "FILE_FLAG_BACKUP_SEMANTICS"); + let file_flag_open_reparse_point = + ecx.eval_windows_u32("c", "FILE_FLAG_OPEN_REPARSE_POINT"); + + let mut out = FileAttributes::ZERO; + if value & file_flag_backup_semantics != 0 { + value &= !file_flag_backup_semantics; + out |= FileAttributes::BACKUP_SEMANTICS; + } + if value & file_flag_open_reparse_point != 0 { + value &= !file_flag_open_reparse_point; + out |= FileAttributes::OPEN_REPARSE; + } + if value & file_attribute_normal != 0 { + value &= !file_attribute_normal; + out |= FileAttributes::NORMAL; + } + + if value != 0 { + throw_unsup_format!("CreateFileW: Unsupported flags_and_attributes: {value}"); + } + + if out == FileAttributes::ZERO { + // NORMAL is equivalent to 0. Avoid needing to check both cases by unifying the two. + out = FileAttributes::NORMAL; + } + interp_ok(out) + } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +#[allow(non_snake_case)] +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn CreateFileW( + &mut self, + file_name: &OpTy<'tcx>, // LPCWSTR + desired_access: &OpTy<'tcx>, // DWORD + share_mode: &OpTy<'tcx>, // DWORD + security_attributes: &OpTy<'tcx>, // LPSECURITY_ATTRIBUTES + creation_disposition: &OpTy<'tcx>, // DWORD + flags_and_attributes: &OpTy<'tcx>, // DWORD + template_file: &OpTy<'tcx>, // HANDLE + ) -> InterpResult<'tcx, Handle> { + // ^ Returns HANDLE + use CreationDisposition::*; + + let this = self.eval_context_mut(); + this.assert_target_os("windows", "CreateFileW"); + this.check_no_isolation("`CreateFileW`")?; + + // This function appears to always set the error to 0. This is important for some flag + // combinations, which may set error code on success. + this.set_last_error(IoError::Raw(Scalar::from_i32(0)))?; + + let file_name = this.read_path_from_wide_str(this.read_pointer(file_name)?)?; + let mut desired_access = this.read_scalar(desired_access)?.to_u32()?; + let share_mode = this.read_scalar(share_mode)?.to_u32()?; + let security_attributes = this.read_pointer(security_attributes)?; + let creation_disposition = this.read_scalar(creation_disposition)?.to_u32()?; + let flags_and_attributes = this.read_scalar(flags_and_attributes)?.to_u32()?; + let template_file = this.read_target_usize(template_file)?; + + let generic_read = this.eval_windows_u32("c", "GENERIC_READ"); + let generic_write = this.eval_windows_u32("c", "GENERIC_WRITE"); + + let file_share_delete = this.eval_windows_u32("c", "FILE_SHARE_DELETE"); + let file_share_read = this.eval_windows_u32("c", "FILE_SHARE_READ"); + let file_share_write = this.eval_windows_u32("c", "FILE_SHARE_WRITE"); + + let creation_disposition = CreationDisposition::new(creation_disposition, this)?; + let attributes = FileAttributes::new(flags_and_attributes, this)?; + + if share_mode != (file_share_delete | file_share_read | file_share_write) { + throw_unsup_format!("CreateFileW: Unsupported share mode: {share_mode}"); + } + if !this.ptr_is_null(security_attributes)? { + throw_unsup_format!("CreateFileW: Security attributes are not supported"); + } + + if attributes.contains(FileAttributes::OPEN_REPARSE) && creation_disposition == CreateAlways + { + throw_machine_stop!(TerminationInfo::Abort("Invalid CreateFileW argument combination: FILE_FLAG_OPEN_REPARSE_POINT with CREATE_ALWAYS".to_string())); + } + + if template_file != 0 { + throw_unsup_format!("CreateFileW: Template files are not supported"); + } + + // We need to know if the file is a directory to correctly open directory handles. + // This is racy, but currently the stdlib doesn't appear to offer a better solution. + let is_dir = file_name.is_dir(); + + // BACKUP_SEMANTICS is how Windows calls the act of opening a directory handle. + if !attributes.contains(FileAttributes::BACKUP_SEMANTICS) && is_dir { + this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?; + return interp_ok(Handle::Invalid); + } + + let desired_read = desired_access & generic_read != 0; + let desired_write = desired_access & generic_write != 0; + + let mut options = OpenOptions::new(); + if desired_read { + desired_access &= !generic_read; + options.read(true); + } + if desired_write { + desired_access &= !generic_write; + options.write(true); + } + + if desired_access != 0 { + throw_unsup_format!( + "CreateFileW: Unsupported bits set for access mode: {desired_access:#x}" + ); + } + + // Per the documentation: + // If the specified file exists and is writable, the function truncates the file, + // the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS. + // If the specified file does not exist and is a valid path, a new file is created, + // the function succeeds, and the last-error code is set to zero. + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew + // + // This is racy, but there doesn't appear to be an std API that both succeeds if a + // file exists but tells us it isn't new. Either we accept racing one way or another, + // or we use an iffy heuristic like file creation time. This implementation prefers + // to fail in the direction of erroring more often. + if let CreateAlways | OpenAlways = creation_disposition + && file_name.exists() + { + this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; + } + + let handle = if is_dir { + // Open this as a directory. + let fd_num = this.machine.fds.insert_new(DirHandle { path: file_name }); + Ok(Handle::File(fd_num)) + } else if creation_disposition == OpenExisting && !(desired_read || desired_write) { + // Windows supports handles with no permissions. These allow things such as reading + // metadata, but not file content. + file_name.metadata().map(|meta| { + let fd_num = this.machine.fds.insert_new(MetadataHandle { meta }); + Handle::File(fd_num) + }) + } else { + // Open this as a standard file. + match creation_disposition { + CreateAlways | OpenAlways => { + options.create(true); + if creation_disposition == CreateAlways { + options.truncate(true); + } + } + CreateNew => { + options.create_new(true); + // Per `create_new` documentation: + // The file must be opened with write or append access in order to create a new file. + // https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new + if !desired_write { + options.append(true); + } + } + OpenExisting => {} // Default options + TruncateExisting => { + options.truncate(true); + } + } + + options.open(file_name).map(|file| { + let fd_num = + this.machine.fds.insert_new(FileHandle { file, writable: desired_write }); + Handle::File(fd_num) + }) + }; + + match handle { + Ok(handle) => interp_ok(handle), + Err(e) => { + this.set_last_error(e)?; + interp_ok(Handle::Invalid) + } + } + } + + fn GetFileInformationByHandle( + &mut self, + file: &OpTy<'tcx>, // HANDLE + file_information: &OpTy<'tcx>, // LPBY_HANDLE_FILE_INFORMATION + ) -> InterpResult<'tcx, Scalar> { + // ^ Returns BOOL (i32 on Windows) + let this = self.eval_context_mut(); + this.assert_target_os("windows", "GetFileInformationByHandle"); + this.check_no_isolation("`GetFileInformationByHandle`")?; + + let file = this.read_handle(file, "GetFileInformationByHandle")?; + let file_information = this.deref_pointer_as( + file_information, + this.windows_ty_layout("BY_HANDLE_FILE_INFORMATION"), + )?; + + let fd_num = if let Handle::File(fd_num) = file { + fd_num + } else { + this.invalid_handle("GetFileInformationByHandle")? + }; + + let Some(desc) = this.machine.fds.get(fd_num) else { + this.invalid_handle("GetFileInformationByHandle")? + }; + + let metadata = match desc.metadata()? { + Ok(meta) => meta, + Err(e) => { + this.set_last_error(e)?; + return interp_ok(this.eval_windows("c", "FALSE")); + } + }; + + let size = metadata.len(); + + let file_type = metadata.file_type(); + 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") + } else { + this.eval_windows_u32("c", "FILE_ATTRIBUTE_DEVICE") + }; + + // Per the Windows documentation: + // "If the underlying file system does not support the [...] time, this member is zero (0)." + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information + let created = extract_windows_epoch(this, metadata.created())?.unwrap_or((0, 0)); + let accessed = extract_windows_epoch(this, metadata.accessed())?.unwrap_or((0, 0)); + let written = extract_windows_epoch(this, metadata.modified())?.unwrap_or((0, 0)); + + this.write_int_fields_named(&[("dwFileAttributes", attributes.into())], &file_information)?; + write_filetime_field(this, &file_information, "ftCreationTime", created)?; + write_filetime_field(this, &file_information, "ftLastAccessTime", accessed)?; + write_filetime_field(this, &file_information, "ftLastWriteTime", written)?; + this.write_int_fields_named( + &[ + ("dwVolumeSerialNumber", 0), + ("nFileSizeHigh", (size >> 32).into()), + ("nFileSizeLow", (size & 0xFFFFFFFF).into()), + ("nNumberOfLinks", 1), + ("nFileIndexHigh", 0), + ("nFileIndexLow", 0), + ], + &file_information, + )?; + + interp_ok(this.eval_windows("c", "TRUE")) + } +} + +/// Windows FILETIME is measured in 100-nanosecs since 1601 +fn extract_windows_epoch<'tcx>( + ecx: &MiriInterpCx<'tcx>, + time: io::Result, +) -> InterpResult<'tcx, Option<(u32, u32)>> { + match time.ok() { + Some(time) => { + let duration = ecx.system_time_since_windows_epoch(&time)?; + let duration_ticks = ecx.windows_ticks_for(duration)?; + #[allow(clippy::cast_possible_truncation)] + interp_ok(Some((duration_ticks as u32, (duration_ticks >> 32) as u32))) + } + None => interp_ok(None), + } +} + +fn write_filetime_field<'tcx>( + cx: &mut MiriInterpCx<'tcx>, + val: &MPlaceTy<'tcx>, + name: &str, + (low, high): (u32, u32), +) -> InterpResult<'tcx> { + cx.write_int_fields_named( + &[("dwLowDateTime", low.into()), ("dwHighDateTime", high.into())], + &cx.project_field_named(val, name)?, + ) +} diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index c4eb11fbd3f97..eec6c62bebc73 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -3,6 +3,7 @@ use std::mem::variant_count; use rustc_abi::HasDataLayout; use crate::concurrency::thread::ThreadNotFound; +use crate::shims::files::FdNum; use crate::*; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -16,6 +17,8 @@ pub enum Handle { Null, Pseudo(PseudoHandle), Thread(ThreadId), + File(FdNum), + Invalid, } impl PseudoHandle { @@ -47,12 +50,18 @@ impl Handle { const NULL_DISCRIMINANT: u32 = 0; const PSEUDO_DISCRIMINANT: u32 = 1; const THREAD_DISCRIMINANT: u32 = 2; + const FILE_DISCRIMINANT: u32 = 3; + // Chosen to ensure Handle::Invalid encodes to -1. Update this value if there are ever more than + // 8 discriminants. + const INVALID_DISCRIMINANT: u32 = 7; fn discriminant(self) -> u32 { match self { Self::Null => Self::NULL_DISCRIMINANT, Self::Pseudo(_) => Self::PSEUDO_DISCRIMINANT, Self::Thread(_) => Self::THREAD_DISCRIMINANT, + Self::File(_) => Self::FILE_DISCRIMINANT, + Self::Invalid => Self::INVALID_DISCRIMINANT, } } @@ -61,17 +70,27 @@ impl Handle { Self::Null => 0, Self::Pseudo(pseudo_handle) => pseudo_handle.value(), Self::Thread(thread) => thread.to_u32(), + #[expect(clippy::cast_sign_loss)] + Self::File(fd) => fd as u32, + // INVALID_HANDLE_VALUE is -1. This fact is explicitly declared or implied in several + // pages of Windows documentation. + // 1: https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.safehandles.safefilehandle?view=net-9.0 + // 2: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=msvc-170 + Self::Invalid => 0x1FFFFFFF, } } fn packed_disc_size() -> u32 { - // ceil(log2(x)) is how many bits it takes to store x numbers + // ceil(log2(x)) is how many bits it takes to store x numbers. + // We ensure that INVALID_HANDLE_VALUE (0xFFFFFFFF) decodes to Handle::Invalid. + // see https://devblogs.microsoft.com/oldnewthing/20230914-00/?p=108766 for more detail on + // INVALID_HANDLE_VALUE. let variant_count = variant_count::(); - // however, std's ilog2 is floor(log2(x)) + // However, std's ilog2 is floor(log2(x)). let floor_log2 = variant_count.ilog2(); - // we need to add one for non powers of two to compensate for the difference + // We need to add one for non powers of two to compensate for the difference. #[expect(clippy::arithmetic_side_effects)] // cannot overflow if variant_count.is_power_of_two() { floor_log2 } else { floor_log2 + 1 } } @@ -105,6 +124,13 @@ impl Handle { Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null), Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)), Self::THREAD_DISCRIMINANT => Some(Self::Thread(ThreadId::new_unchecked(data))), + #[expect(clippy::cast_possible_wrap)] + Self::FILE_DISCRIMINANT => { + // This cast preserves all bits. + assert_eq!(size_of_val(&data), size_of::()); + Some(Self::File(data as FdNum)) + } + Self::INVALID_DISCRIMINANT => Some(Self::Invalid), _ => None, } } @@ -139,7 +165,7 @@ impl Handle { /// Structurally invalid handles return [`HandleError::InvalidHandle`]. /// If the handle is structurally valid but semantically invalid, e.g. a for non-existent thread /// ID, returns [`HandleError::ThreadNotFound`]. - pub fn try_from_scalar<'tcx>( + fn try_from_scalar<'tcx>( handle: Scalar, cx: &MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, Result> { @@ -171,6 +197,27 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} #[allow(non_snake_case)] pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Convert a scalar into a structured `Handle`. + /// If the handle is invalid, or references a non-existent item, execution is aborted. + #[track_caller] + fn read_handle(&self, handle: &OpTy<'tcx>, function_name: &str) -> InterpResult<'tcx, Handle> { + let this = self.eval_context_ref(); + let handle = this.read_scalar(handle)?; + match Handle::try_from_scalar(handle, this)? { + Ok(handle) => interp_ok(handle), + Err(HandleError::InvalidHandle) => + throw_machine_stop!(TerminationInfo::Abort(format!( + "invalid handle {} passed to {function_name}", + handle.to_target_isize(this)?, + ))), + Err(HandleError::ThreadNotFound(_)) => + throw_machine_stop!(TerminationInfo::Abort(format!( + "invalid thread ID {} passed to {function_name}", + handle.to_target_isize(this)?, + ))), + } + } + fn invalid_handle(&mut self, function_name: &str) -> InterpResult<'tcx, !> { throw_machine_stop!(TerminationInfo::Abort(format!( "invalid handle passed to `{function_name}`" @@ -180,15 +227,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let handle = this.read_scalar(handle_op)?; - let ret = match Handle::try_from_scalar(handle, this)? { - Ok(Handle::Thread(thread)) => { + let handle = this.read_handle(handle_op, "CloseHandle")?; + let ret = match handle { + Handle::Thread(thread) => { this.detach_thread(thread, /*allow_terminated_joined*/ true)?; this.eval_windows("c", "TRUE") } + Handle::File(fd_num) => + if let Some(fd) = this.machine.fds.remove(fd_num) { + let err = fd.close_ref(this.machine.communicate(), this)?; + if let Err(e) = err { + this.set_last_error(e)?; + this.eval_windows("c", "FALSE") + } else { + this.eval_windows("c", "TRUE") + } + } else { + this.invalid_handle("CloseHandle")? + }, _ => this.invalid_handle("CloseHandle")?, }; interp_ok(ret) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_invalid_encoding() { + // Ensure the invalid handle encodes to `u32::MAX`/`INVALID_HANDLE_VALUE`. + assert_eq!(Handle::Invalid.to_packed(), u32::MAX) + } +} diff --git a/src/tools/miri/src/shims/windows/mod.rs b/src/tools/miri/src/shims/windows/mod.rs index 892bd6924fc93..442c5a0dd11fd 100644 --- a/src/tools/miri/src/shims/windows/mod.rs +++ b/src/tools/miri/src/shims/windows/mod.rs @@ -1,12 +1,14 @@ pub mod foreign_items; mod env; +mod fs; mod handle; mod sync; mod thread; // All the Windows-specific extension traits pub use self::env::{EvalContextExt as _, WindowsEnvVars}; +pub use self::fs::EvalContextExt as _; pub use self::handle::EvalContextExt as _; pub use self::sync::EvalContextExt as _; pub use self::thread::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index 5db554044227c..d5f9ed4e968ea 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -62,14 +62,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let handle = this.read_scalar(handle_op)?; + let handle = this.read_handle(handle_op, "WaitForSingleObject")?; let timeout = this.read_scalar(timeout_op)?.to_u32()?; - let thread = match Handle::try_from_scalar(handle, this)? { - Ok(Handle::Thread(thread)) => thread, + let thread = match handle { + Handle::Thread(thread) => thread, // Unlike on posix, the outcome of joining the current thread is not documented. // On current Windows, it just deadlocks. - Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(), + Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(), _ => this.invalid_handle("WaitForSingleObject")?, }; diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index 837cb1c508166..653228a5e3db6 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -25,6 +25,6 @@ page_size = "0.6" tokio = { version = "1", features = ["macros", "rt-multi-thread", "time", "net", "fs", "sync", "signal", "io-util"] } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.59", features = [ "Win32_Foundation", "Win32_System_Threading" ] } +windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_Storage_FileSystem", "Win32_Security"] } [workspace] diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs index 279201df867cf..3ee2bf14f9fe4 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs @@ -13,7 +13,7 @@ use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject}; // XXX HACK: This is how miri represents the handle for thread 0. // This value can be "legitimately" obtained by using `GetCurrentThread` with `DuplicateHandle` // but miri does not implement `DuplicateHandle` yet. -const MAIN_THREAD: HANDLE = (2i32 << 30) as HANDLE; +const MAIN_THREAD: HANDLE = (2i32 << 29) as HANDLE; fn main() { thread::spawn(|| { diff --git a/src/tools/miri/tests/pass-dep/concurrency/freebsd-futex.rs b/src/tools/miri/tests/pass-dep/concurrency/freebsd-futex.rs new file mode 100644 index 0000000000000..38a0bf58148ed --- /dev/null +++ b/src/tools/miri/tests/pass-dep/concurrency/freebsd-futex.rs @@ -0,0 +1,260 @@ +//@only-target: freebsd +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-isolation + +use std::mem::{self, MaybeUninit}; +use std::ptr::{self, addr_of}; +use std::sync::atomic::AtomicU32; +use std::time::Instant; +use std::{io, thread}; + +fn wait_wake() { + fn wake_nobody() { + // Current thread waits on futex. + // New thread wakes up 0 threads waiting on that futex. + // Current thread should time out. + static mut FUTEX: u32 = 0; + + let waker = thread::spawn(|| { + unsafe { + assert_eq!( + libc::_umtx_op( + addr_of!(FUTEX) as *mut _, + libc::UMTX_OP_WAKE_PRIVATE, + 0, // wake up 0 waiters + ptr::null_mut::(), + ptr::null_mut::(), + ), + 0 + ); + } + }); + + // 10ms should be enough. + let mut timeout = libc::timespec { tv_sec: 0, tv_nsec: 10_000_000 }; + let timeout_size_arg = + ptr::without_provenance_mut::(mem::size_of::()); + unsafe { + assert_eq!( + libc::_umtx_op( + addr_of!(FUTEX) as *mut _, + libc::UMTX_OP_WAIT_UINT_PRIVATE, + 0, + timeout_size_arg, + &mut timeout as *mut _ as _, + ), + -1 + ); + // Main thread did not get woken up, so it timed out. + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT); + } + + waker.join().unwrap(); + } + + fn wake_two_of_three() { + // We create 2 threads that wait on a futex with a 100ms timeout. + // The main thread wakes up 2 threads waiting on this futex and after this + // checks that only those threads woke up and the other one timed out. + static mut FUTEX: u32 = 0; + + fn waiter() -> bool { + let mut timeout = libc::timespec { tv_sec: 0, tv_nsec: 100_000_000 }; + let timeout_size_arg = + ptr::without_provenance_mut::(mem::size_of::()); + unsafe { + libc::_umtx_op( + addr_of!(FUTEX) as *mut _, + libc::UMTX_OP_WAIT_UINT_PRIVATE, + 0, // FUTEX is 0 + timeout_size_arg, + &mut timeout as *mut _ as _, + ); + // Return true if this thread woke up. + io::Error::last_os_error().raw_os_error().unwrap() != libc::ETIMEDOUT + } + } + + let t1 = thread::spawn(waiter); + let t2 = thread::spawn(waiter); + let t3 = thread::spawn(waiter); + + // Run all the waiters, so they can go to sleep. + thread::yield_now(); + + // Wake up 2 thread and make sure 1 is still waiting. + unsafe { + assert_eq!( + libc::_umtx_op( + addr_of!(FUTEX) as *mut _, + libc::UMTX_OP_WAKE_PRIVATE, + 2, + ptr::null_mut::(), + ptr::null_mut::(), + ), + 0 + ); + } + + // Treat the booleans as numbers to simplify checking how many threads were woken up. + let t1 = t1.join().unwrap() as usize; + let t2 = t2.join().unwrap() as usize; + let t3 = t3.join().unwrap() as usize; + let woken_up_count = t1 + t2 + t3; + assert!(woken_up_count == 2, "Expected 2 threads to wake up got: {woken_up_count}"); + } + + wake_nobody(); + wake_two_of_three(); +} + +fn wake_dangling() { + let futex = Box::new(0); + let ptr: *const u32 = &*futex; + drop(futex); + + // Expect error since this is now "unmapped" memory. + unsafe { + assert_eq!( + libc::_umtx_op( + ptr as *const AtomicU32 as *mut _, + libc::UMTX_OP_WAKE_PRIVATE, + 0, + ptr::null_mut::(), + ptr::null_mut::(), + ), + -1 + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EFAULT); + } +} + +fn wait_wrong_val() { + let futex: u32 = 123; + + // Wait with a wrong value just returns 0 + unsafe { + assert_eq!( + libc::_umtx_op( + ptr::from_ref(&futex).cast_mut().cast(), + libc::UMTX_OP_WAIT_UINT_PRIVATE, + 456, + ptr::null_mut::(), + ptr::null_mut::(), + ), + 0 + ); + } +} + +fn wait_relative_timeout() { + fn without_timespec() { + let start = Instant::now(); + + let futex: u32 = 123; + + let mut timeout = libc::timespec { tv_sec: 0, tv_nsec: 200_000_000 }; + let timeout_size_arg = + ptr::without_provenance_mut::(mem::size_of::()); + // Wait for 200ms, with nobody waking us up early + unsafe { + assert_eq!( + libc::_umtx_op( + ptr::from_ref(&futex).cast_mut().cast(), + libc::UMTX_OP_WAIT_UINT_PRIVATE, + 123, + timeout_size_arg, + &mut timeout as *mut _ as _, + ), + -1 + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT); + } + + assert!((200..1000).contains(&start.elapsed().as_millis())); + } + + fn with_timespec() { + let futex: u32 = 123; + let mut timeout = libc::_umtx_time { + _timeout: libc::timespec { tv_sec: 0, tv_nsec: 200_000_000 }, + _flags: 0, + _clockid: libc::CLOCK_MONOTONIC as u32, + }; + let timeout_size_arg = + ptr::without_provenance_mut::(mem::size_of::()); + + let start = Instant::now(); + + // Wait for 200ms, with nobody waking us up early + unsafe { + assert_eq!( + libc::_umtx_op( + ptr::from_ref(&futex).cast_mut().cast(), + libc::UMTX_OP_WAIT_UINT_PRIVATE, + 123, + timeout_size_arg, + &mut timeout as *mut _ as _, + ), + -1 + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT); + } + assert!((200..1000).contains(&start.elapsed().as_millis())); + } + + without_timespec(); + with_timespec(); +} + +fn wait_absolute_timeout() { + let start = Instant::now(); + + // Get the current monotonic timestamp as timespec. + let mut timeout = unsafe { + let mut now: MaybeUninit = MaybeUninit::uninit(); + assert_eq!(libc::clock_gettime(libc::CLOCK_MONOTONIC, now.as_mut_ptr()), 0); + now.assume_init() + }; + + // Add 200ms. + timeout.tv_nsec += 200_000_000; + if timeout.tv_nsec > 1_000_000_000 { + timeout.tv_nsec -= 1_000_000_000; + timeout.tv_sec += 1; + } + + // Create umtx_timeout struct with that absolute timeout. + let umtx_timeout = libc::_umtx_time { + _timeout: timeout, + _flags: libc::UMTX_ABSTIME, + _clockid: libc::CLOCK_MONOTONIC as u32, + }; + let umtx_timeout_ptr = &umtx_timeout as *const _; + let umtx_timeout_size = ptr::without_provenance_mut(mem::size_of_val(&umtx_timeout)); + + let futex: u32 = 123; + + // Wait for 200ms from now, with nobody waking us up early. + unsafe { + assert_eq!( + libc::_umtx_op( + ptr::from_ref(&futex).cast_mut().cast(), + libc::UMTX_OP_WAIT_UINT_PRIVATE, + 123, + umtx_timeout_size, + umtx_timeout_ptr as *mut _, + ), + -1 + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT); + } + assert!((200..1000).contains(&start.elapsed().as_millis())); +} + +fn main() { + wait_wake(); + wake_dangling(); + wait_wrong_val(); + wait_relative_timeout(); + wait_absolute_timeout(); +} diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs new file mode 100644 index 0000000000000..312df9eb115db --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -0,0 +1,198 @@ +//@only-target: windows # this directly tests windows-only functions +//@compile-flags: -Zmiri-disable-isolation +#![allow(nonstandard_style)] + +use std::os::windows::ffi::OsStrExt; +use std::path::Path; +use std::ptr; + +#[path = "../../utils/mod.rs"] +mod utils; + +use windows_sys::Win32::Foundation::{ + CloseHandle, ERROR_ALREADY_EXISTS, GENERIC_READ, GENERIC_WRITE, GetLastError, +}; +use windows_sys::Win32::Storage::FileSystem::{ + BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, FILE_ATTRIBUTE_DIRECTORY, + FILE_ATTRIBUTE_NORMAL, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, + FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, GetFileInformationByHandle, OPEN_ALWAYS, + OPEN_EXISTING, +}; + +fn main() { + unsafe { + test_create_dir_file(); + test_create_normal_file(); + test_create_always_twice(); + test_open_always_twice(); + test_open_dir_reparse(); + } +} + +unsafe fn test_create_dir_file() { + let temp = utils::tmp(); + let raw_path = to_wide_cstr(&temp); + // Open the `temp` directory. + let handle = CreateFileW( + raw_path.as_ptr(), + GENERIC_READ, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + let mut info = std::mem::zeroed::(); + if GetFileInformationByHandle(handle, &mut info) == 0 { + panic!("Failed to get file information") + }; + assert!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0); + if CloseHandle(handle) == 0 { + panic!("Failed to close file") + }; +} + +unsafe fn test_create_normal_file() { + let temp = utils::tmp().join("test.txt"); + let raw_path = to_wide_cstr(&temp); + let handle = CreateFileW( + raw_path.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + CREATE_NEW, + 0, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + let mut info = std::mem::zeroed::(); + if GetFileInformationByHandle(handle, &mut info) == 0 { + panic!("Failed to get file information: {}", GetLastError()) + }; + assert!(info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL != 0); + if CloseHandle(handle) == 0 { + panic!("Failed to close file") + }; + + // Test metadata-only handle + let handle = CreateFileW( + raw_path.as_ptr(), + 0, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + OPEN_EXISTING, + 0, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + let mut info = std::mem::zeroed::(); + if GetFileInformationByHandle(handle, &mut info) == 0 { + panic!("Failed to get file information: {}", GetLastError()) + }; + assert!(info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL != 0); + if CloseHandle(handle) == 0 { + panic!("Failed to close 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::tmp().join("test_create_always.txt"); + let raw_path = to_wide_cstr(&temp); + let handle = CreateFileW( + raw_path.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + CREATE_ALWAYS, + 0, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + assert_eq!(GetLastError(), 0); + if CloseHandle(handle) == 0 { + panic!("Failed to close file") + }; + + let handle = CreateFileW( + raw_path.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + CREATE_ALWAYS, + 0, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + assert_eq!(GetLastError(), ERROR_ALREADY_EXISTS); + if CloseHandle(handle) == 0 { + panic!("Failed to close file") + }; +} + +/// 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::tmp().join("test_open_always.txt"); + let raw_path = to_wide_cstr(&temp); + let handle = CreateFileW( + raw_path.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + OPEN_ALWAYS, + 0, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + assert_eq!(GetLastError(), 0); + if CloseHandle(handle) == 0 { + panic!("Failed to close file") + }; + + let handle = CreateFileW( + raw_path.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + OPEN_ALWAYS, + 0, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + assert_eq!(GetLastError(), ERROR_ALREADY_EXISTS); + if CloseHandle(handle) == 0 { + panic!("Failed to close file") + }; +} + +// TODO: Once we support more of the std API, it would be nice to test against an actual symlink +unsafe fn test_open_dir_reparse() { + let temp = utils::tmp(); + let raw_path = to_wide_cstr(&temp); + // Open the `temp` directory. + let handle = CreateFileW( + raw_path.as_ptr(), + GENERIC_READ, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + ptr::null_mut(), + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + ptr::null_mut(), + ); + assert_ne!(handle.addr(), usize::MAX, "CreateFileW Failed: {}", GetLastError()); + let mut info = std::mem::zeroed::(); + if GetFileInformationByHandle(handle, &mut info) == 0 { + panic!("Failed to get file information") + }; + assert!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0); + if CloseHandle(handle) == 0 { + panic!("Failed to close file") + }; +} + +fn to_wide_cstr(path: &Path) -> Vec { + let mut raw_path = path.as_os_str().encode_wide().collect::>(); + raw_path.extend([0, 0]); + raw_path +} diff --git a/src/tools/miri/tests/pass/path.rs b/src/tools/miri/tests/pass/path.rs index 299ee6cfe9dde..7428d0afcc674 100644 --- a/src/tools/miri/tests/pass/path.rs +++ b/src/tools/miri/tests/pass/path.rs @@ -6,7 +6,11 @@ mod utils; #[track_caller] fn assert_absolute_eq(in_: &str, out: &str) { - assert_eq!(absolute(in_).unwrap().as_os_str(), Path::new(out).as_os_str()); + assert_eq!( + absolute(in_).unwrap().as_os_str(), + Path::new(out).as_os_str(), + "incorrect absolute path for {in_:?}" + ); } fn test_absolute() { @@ -29,11 +33,28 @@ fn test_absolute() { assert_absolute_eq(r"\\?\C:\path\to\file", r"\\?\C:\path\to\file"); assert_absolute_eq(r"\\?\UNC\server\share\to\file", r"\\?\UNC\server\share\to\file"); assert_absolute_eq(r"\\?\PIPE\name", r"\\?\PIPE\name"); + assert_absolute_eq(r"\\server\share\NUL", r"\\server\share\NUL"); + // This fails on Windows 10 hosts. FIXME: enable this once GHA runners are on Windows 11. + //assert_absolute_eq(r"C:\path\to\COM1", r"C:\path\to\COM1"); // Verbatim paths are always unchanged, no matter what. assert_absolute_eq(r"\\?\path.\to/file..", r"\\?\path.\to/file.."); - + // Trailing dot is removed here. assert_absolute_eq(r"C:\path..\to.\file.", r"C:\path..\to\file"); + // `..` is resolved here. + assert_absolute_eq(r"C:\path\to\..\file", r"C:\path\file"); + assert_absolute_eq(r"C:\path\to\..\..\file", r"C:\file"); + assert_absolute_eq(r"C:\path\to\..\..\..\..\..\..\file", r"C:\file"); + assert_absolute_eq(r"C:\..", r"C:\"); + assert_absolute_eq(r"\\server\share\to\path\with\..\file", r"\\server\share\to\path\file"); + assert_absolute_eq(r"\\server\share\to\..\..\..\..\file", r"\\server\share\file"); + assert_absolute_eq(r"\\server\share\..", r"\\server\share"); + // Magic filenames. + assert_absolute_eq(r"NUL", r"\\.\NUL"); + assert_absolute_eq(r"nul", r"\\.\nul"); assert_absolute_eq(r"COM1", r"\\.\COM1"); + assert_absolute_eq(r"com1", r"\\.\com1"); + assert_absolute_eq(r"C:\path\to\NUL", r"\\.\NUL"); + assert_absolute_eq(r"C:\path\to\nul", r"\\.\nul"); } else { panic!("unsupported OS"); } diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 289c6aa2fcec9..6ad23055f30eb 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -1,4 +1,3 @@ -//@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation #![feature(io_error_more)] @@ -18,20 +17,23 @@ mod utils; fn main() { test_path_conversion(); - test_file(); - test_file_clone(); - test_file_create_new(); - test_seek(); - test_metadata(); - test_file_set_len(); - test_file_sync(); - test_errors(); - test_rename(); - test_directory(); - test_canonicalize(); - test_from_raw_os_error(); - #[cfg(unix)] - test_pread_pwrite(); + // Windows file handling is very incomplete. + if cfg!(not(windows)) { + test_file(); + test_file_create_new(); + test_seek(); + test_file_clone(); + test_metadata(); + test_file_set_len(); + test_file_sync(); + test_errors(); + test_rename(); + test_directory(); + test_canonicalize(); + test_from_raw_os_error(); + #[cfg(unix)] + test_pread_pwrite(); + } } fn test_path_conversion() { @@ -144,10 +146,10 @@ fn test_metadata() { let path = utils::prepare_with_content("miri_test_fs_metadata.txt", bytes); // Test that metadata of an absolute path is correct. - check_metadata(bytes, &path).unwrap(); + check_metadata(bytes, &path).expect("absolute path metadata"); // Test that metadata of a relative path is correct. std::env::set_current_dir(path.parent().unwrap()).unwrap(); - check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap(); + check_metadata(bytes, Path::new(path.file_name().unwrap())).expect("relative path metadata"); // Removing file should succeed. remove_file(&path).unwrap(); diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index 4e013764d8713..60e80c3f67330 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -40,3 +40,9 @@ unless = ["S-blocked", "S-waiting-on-team", "S-waiting-on-review"] # Automatically close and reopen PRs made by bots to run CI on them [bot-pull-requests] + +# Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree +[canonicalize-issue-links] + +# Prevents mentions in commits to avoid users being spammed +[no-mentions] diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp new file mode 100644 index 0000000000000..872a6a45aede1 --- /dev/null +++ b/tests/pretty/hir-delegation.pp @@ -0,0 +1,23 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-delegation.pp + +#![allow(incomplete_features)]#![feature(fn_delegation)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +fn b(e: C) { } + +trait G { + fn b(arg0: _) -> _ { b({ }) } +} + +mod m { + fn add(a: u32, b: u32) -> u32 { a + b } +} + +fn add(arg0: _, arg1: _) -> _ { m::add(arg0, arg1) } + +fn main() { { let _ = add(1, 2); }; } diff --git a/tests/pretty/hir-delegation.rs b/tests/pretty/hir-delegation.rs new file mode 100644 index 0000000000000..9e351a1e32f3d --- /dev/null +++ b/tests/pretty/hir-delegation.rs @@ -0,0 +1,22 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-delegation.pp + +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +fn b(e: C) {} + +trait G { + reuse b {} +} + +mod m { + pub fn add(a: u32, b: u32) -> u32 { a + b } +} + +reuse m::add; + +fn main() { + _ = add(1, 2); +} diff --git a/tests/run-make/unstable-feature-usage-metrics-incremental/main.rs b/tests/run-make/unstable-feature-usage-metrics-incremental/main.rs new file mode 100644 index 0000000000000..f970d395b2cf2 --- /dev/null +++ b/tests/run-make/unstable-feature-usage-metrics-incremental/main.rs @@ -0,0 +1,16 @@ +#![feature(ascii_char)] // random lib feature +#![feature(box_patterns)] // random lang feature + +// picked arbitrary unstable features, just need a random lib and lang feature, ideally ones that +// won't be stabilized any time soon so we don't have to update this test +fn main() { + for s in quix("foo/bar") { + print!("{s}"); + } + println!(); +} + +// need a latebound var to trigger the incremental compilation ICE +fn quix(foo: &str) -> impl Iterator + '_ { + foo.split('/') +} diff --git a/tests/run-make/unstable-feature-usage-metrics-incremental/rmake.rs b/tests/run-make/unstable-feature-usage-metrics-incremental/rmake.rs new file mode 100644 index 0000000000000..7e070d80c7933 --- /dev/null +++ b/tests/run-make/unstable-feature-usage-metrics-incremental/rmake.rs @@ -0,0 +1,94 @@ +//! This test checks if unstable feature usage metric dump files `unstable-feature-usage*.json` work +//! as expected. +//! +//! - Basic sanity checks on a default ICE dump. +//! +//! See . +//! +//! # Test history +//! +//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming +//! those will be present in this test as well on the same platform + +//@ ignore-windows +//FIXME(#128911): still flakey on i686-mingw. + +use std::path::{Path, PathBuf}; + +use run_make_support::rfs::create_dir_all; +use run_make_support::{ + cwd, filename_contains, has_extension, rfs, run_in_tmpdir, rustc, serde_json, + shallow_find_files, +}; + +fn find_feature_usage_metrics>(dir: P) -> Vec { + shallow_find_files(dir, |path| { + if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") { + true + } else { + dbg!(path); + false + } + }) +} + +fn main() { + test_metrics_dump(); + test_metrics_errors(); +} + +#[track_caller] +fn test_metrics_dump() { + run_in_tmpdir(|| { + let metrics_dir = cwd().join("metrics"); + create_dir_all(&metrics_dir); + rustc() + .input("main.rs") + .incremental("incremental") + .env("RUST_BACKTRACE", "short") + .arg(format!("-Zmetrics-dir={}", metrics_dir.display())) + .run(); + let mut metrics = find_feature_usage_metrics(&metrics_dir); + let json_path = + metrics.pop().expect("there should be one metrics file in the output directory"); + + // After the `pop` above, there should be no files left. + assert!( + metrics.is_empty(), + "there should be no more than one metrics file in the output directory" + ); + + let message = rfs::read_to_string(json_path); + let mut parsed: serde_json::Value = + serde_json::from_str(&message).expect("metrics should be dumped as json"); + // remove timestamps + assert!(parsed["lib_features"][0]["timestamp"].is_number()); + assert!(parsed["lang_features"][0]["timestamp"].is_number()); + parsed["lib_features"][0]["timestamp"] = serde_json::json!(null); + parsed["lang_features"][0]["timestamp"] = serde_json::json!(null); + let expected = serde_json::json!( + { + "lib_features":[{"symbol":"ascii_char", "timestamp":null}], + "lang_features":[{"symbol":"box_patterns","since":null, "timestamp":null}] + } + ); + + assert_eq!(expected, parsed); + }); +} + +#[track_caller] +fn test_metrics_errors() { + run_in_tmpdir(|| { + rustc() + .input("main.rs") + .incremental("incremental") + .env("RUST_BACKTRACE", "short") + .arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist") + .run_fail() + .assert_stderr_contains( + "error: cannot dump feature usage metrics: No such file or directory", + ) + .assert_stdout_not_contains("internal compiler error"); + }); +} diff --git a/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs index 20bbbff8fd20b..4053ccdfbf141 100644 --- a/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs +++ b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs @@ -3,7 +3,7 @@ extern crate foreign_trait_with_assoc; use foreign_trait_with_assoc::Foo; -// Make sure we don't try to call `fn_arg_names` on a non-fn item. +// Make sure we don't try to call `fn_arg_idents` on a non-fn item. impl Foo for Bar {} //~^ ERROR cannot find type `Bar` in this scope diff --git a/tests/ui/sanitizer/cfi/coroutine.rs b/tests/ui/sanitizer/cfi/coroutine.rs index 3ad896afd00b0..39a754f103637 100644 --- a/tests/ui/sanitizer/cfi/coroutine.rs +++ b/tests/ui/sanitizer/cfi/coroutine.rs @@ -26,7 +26,7 @@ use std::async_iter::AsyncIterator; #[test] fn general_coroutine() { - let mut coro = #[coroutine] |x: i32| { + let coro = #[coroutine] |x: i32| { yield x; "done" };